突然ですが、改行コード (newline) はテキストの行末を表す文字コードの一つで、データの区切り文字、区切り記号を意味します。
この改行コードは3種類あります。行頭復帰を意味するCR(Carriage Return)、改行を意味するLF(Line Feed)、2種類の組み合わせであるCR+LFがあります。
デバイスとパソコン、デバイス間の通信では、連続する文字を送受信させているので、どこまでがデータの区切りなのかを識別するために上記の改行コードを使用します。デバイスにコマンドを送信する時は、コマンドの末尾に改行をつけて送信します。
今回はターミナルでコマンドを入力して、Enterキーを押したらコマンドに応じてマイコンから返信が返ってくるプログラムを開発します。
このプログラムを実現するには、マイコン側でコマンド(連続する文字列)を受信する処理や改行コードを検知する処理が必要です。
これらの処理が機能していないと、コマンドでマイコンを制御することはできません。かなり重要な処理と言っても過言ではありません。
UART通信については、こちらのサイトにポーリングによるUART通信のサンプルがあるので参考にしてもらいました。
RL78マイコンのUART通信で、連続する文字列の受信データ取得方法や改行コードの検知方法について記述していますので、これからUART通信にチャレンジする方は是非参考にしてみてくださいね。
ポーリングて何だろう?
複数の機器やソフトウェアを円滑に連携させる制御方式の一つです。主に通信などの競合を回避するために、ホスト側が各機器に対して定期的に問い合わせを行い、条件を満たした場合に送受信や各種処理を行います。
ポーリングを行わないと、各システムが自らのタイミングで勝手に通信を行ってしまい、処理の途中で別の処理を実施してしまうので、各処理や通信信号などが邪魔をし合って不具合を発生する場合があります。
ポーリングを行うことで通信などのトラブル要因を無くすことができるのです。
プログラムを記述する前の準備をする
マイコンの通信条件をUART通信のコード生成で設定をします。プロジェクト・ツリー内にあるコード生成(設計ツール)を展開し、シリアルを選択します。SAU0タブ内のチャンネルタブを選択して以下の条件で設定をします。
コード生成をすると、各種関数が自動で生成されます。今回はコード生成の初期化のみしか使用しないので、コード生成のプロパティで「ファイル生成モード」の「API関数の出力制御」を「初期化関数のみ出力する」にしておきます。
この設定でコード生成すると、初期化関数のみで出力され、”r_cg_serial_user.c”という名前のファイルは生成されません。また、”r_cg_serial.c”ファイルの中には、R_SAU0_Create関数とR_UART0_Create関数だけになります。
コード生成やUART通信については、前回の記事に詳しく記載していますので、参考にしてください。
ここまで設定ができたら、次はコードの実装をしていきましょう。
ポーリングでUART通信をやってみよう
本記事は、UART0のステータスの状態をポーリング(問い合わせ)して送受信をおこいます。
UART0のステータスの状態は、シリアル・ステータス・レジスタ(SSR)の各種フラグをチェックすることでデータを送信状態、受信状態を把握することができます。このレジスタは、各チャンネルに1ずつあります。
受信処理について
uint16_t uart0_poll_Rx(void)
{
uint16_t work; // ステータスレジスタ、受信データを保管する変数
work = SSR01; // フラグをチェック
if ( work & 0x20 ) // 受信完了しているかチェック
{
SIR01 = ( work & 0x07 ); // エラー・フラグをクリア
work = ( work & 0x07 ) << 8; // ステータスを上位にシフト
work |= RXD0; // 受信データを読み出し
}
else
{
work = 0x8000; // 受信が完了していない
}
return(work);
}
上から処理をみていきましょう。
まずは16ビットの変数を用意して、変数にSSRレジスタを代入します。このレジスタの構成は以下になります。5ビット目のBFFをチェックすることでSDRレジスタに有効なデータが格納されているか判断することができます。
- TSF チャンネルの通信状態を表示するフラグ
0:通信動作停止状態または通信動作待機状態
1:通信動作状態 - BFF チャンネルのバッファ・レジスタ状態を表示するフラグ
0:有効なデータがSDRレジスタに格納されていない
1:有効なデータがSDRレジスタに格納されている - FEF チャンネルのフレーミング・エラー検出をするフラグ
0:エラーなし
1:エラー発生あり - PEF チャンネルのパリティ / ACKエラー検出するフラグ
0:エラーなし
1:パリティ・エラー発生、ACK未検出発生あり - OVF チャンネルのオーバーラン・エラー検出をするフラグ
0:エラーなし
1:エラー発生あり
BFFのビットが1なら受信が完了しているので、if条件の中の処理をします。
シリアル・フラグ・クリア・トリガ・レジスタ(SIR)の全フラグをクリアにします。
- FECT チャンネルのフレーミング・エラー・フラグのクリア・トリガ
0:クリアしない
1:SSR01レジスタのEFEビットを0にクリアする - PECT チャンネルのパリティ・エラー・フラグのクリア・トリガ
0:クリアしない
1:SSR01レジスタのPEFビットを0にクリアする - OVCT チャンネルのオーバーラン・エラー・フラグのクリア・トリガ
0:クリアしない
1:SSR01レジスタのOVFビットを0にクリアする
ビット演算子であるビットANDでSSRレジスタのエラーフラグ部分だけを取り出します。
ビットANDは演算子の左辺と右辺の同じ位置にあるビットを比較して、両方のビットが共に1の場合だけ1にします。例えば、SSRのFEFビットが1なら、0x0004になります。
次にシフト演算子で8回右へシフトさせて、上位8ビットに移動させます。
代入演算子(|=)を使って、右辺の上位8ビットと左辺の下位8ビットを結合させます。
送信処理について
シリアル・ステータス・レジスタ(SSR)のBFFビットのフラグをチェックして0なら送信可能です。引数の送信データをTXD0に書き込み送信を開始します。このとき,戻り値は0x0000になります。BFFビットが1なら送信不可で、戻り値のビット15をセットし、下位8ビットに送信データをセットして戻ります。
uint16_t uart0_poll_Tx( uint8_t tx_data )
{
uint16_t work; // ステータスレジスタ、送信データを保管する変数
work = ( SSR00 & 0x20 ); // フラグをチェック
if ( 0 == work )
{
TXD0 = tx_data; // データ送信
work = 0x0000; // 戻り値をクリア
}
else
{
work = ( 0x8000 | tx_data ); // データ送信不可能
}
return(work);
}
uart0_poll_Txは、uart0_poll_Rxの処理と似たような処理でデータを送信することができます。
送受信の関数を使って実装
さて、マイコンが対応するコマンドをIEEE 488.2に定義されている共通コマンドから3つ選定しました。
*IDN? | 測定器の識別文字列を返します。 |
---|---|
*RST | 測定器を工場設定状態にリセットします。 |
*TRG | 測定器をトリガーします。 |
この3つのコマンドのいずれかを受信したら(改行コードを含む)、そのコマンドに応じて文字列を送信します。
uint16_t status = 0x8000; // UART0ステータス確認用
uint8_t flag = 0x00; // 改行コード受信フラグ
uint8_t index = 0x00; // 配列の要素インデックス
uint8_t buffer[100]; // 受信データ保管用(配列)
uint8_t cmd_idn[] = "*IDN?\r\n"; // 識別コマンド
uint8_t cmd_rst[] = "*RST\r\n"; // リセットコマンド
uint8_t cmd_trg[] = "*TRG\r\n"; // トリガーコマンド
void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
while (1U)
{
if ( 0 == flag )
{
status = uart0_poll_Rx(); // ポーリングでデータの受信
if(0x0000 == (status & 0xFF00)) // 受信データがある場合
{
buffer[index] = (status & 0x00FF); // 下位8ビットにある受信データを配列に保管
index++;
if(buffer[index - 1] == '\n') // 受信データに改行コードがある場合
{
flag = 0x01;
}
}
else if(0x0001 == (status & 0xFF00)) // オーバーラン・エラーがある場合
{
;
}
else if(0x0002 == (status & 0xFF00)) // パリティ・エラーがある場合
{
;
}
else if(0x0004 == (status & 0xFF00)) // フレミング・エラーがある場合
{
;
}
else if(0x8000 == (status & 0xFF00)) // 受信データがない場合
{
;
}
}
else if ( 1 == flag)
{
int i;
if(strncmp((uint8_t*)cmd_idn, (uint8_t*)buffer, 7) == 0) // 受信データが識別コマンド
{
uint8_t response[] = {"SAMPLE ver.1\r\n"}; // 送信データをセット
for(i=0; i<14; i++)
{
status = uart0_poll_Tx(response[i]);
if(0x0000 != status)
{
; // エラー処理
}
}
}
else if(strncmp((uint8_t*)cmd_rst, (uint8_t*)buffer, 6) == 0) // 受信データがリセットコマンド
{
uint8_t response[] = {"RESET\r\n"}; // 送信データをセット
for(i=0; i<7; i++)
{
status = uart0_poll_Tx(response[i]);
if(0x0000 != status)
{
; // エラー処理
}
}
}
else if(strncmp((uint8_t*)cmd_trg, (uint8_t*)buffer, 6) == 0) // 受信データがトリガーコマンド
{
uint8_t response[] = {"TRIGGER\r\n"}; // 送信データをセット
for(i=0; i<9; i++)
{
status = uart0_poll_Tx(response[i]);
if(0x0000 != status)
{
; // エラー処理
}
}
}
flag = 0x00;
index = 0x00;
memset(buffer, 0, 100);
}
}
/* End user code. Do not edit comment generated here */
}
パソコンとマイコンをシリアルケーブルで接続します。パソコン上でTeraTermを起動してコマンドを入力して動作を確認してみましょう。
こんな感じでTeraTermにマイコンから応答があれば成功です。
この方法以外にもUART通信を行う方法がありますので参考にしてみてはいかがでしょうか。
以上、最後まで読んでいただきありがとうございます。