関連:
シリアル通信関連記事一覧ここから少々話がややこしくなります。
データの送受信を行いたい場合、送受信フォーマットの取り決めを行う必要があります。
受信完了の判定
送受信フォーマットの取り決めを行うには、受信完了状態について理解する必要があります。
"
HELLO WORLD\n"
を送受信することを考えます。送受信する文字やデータの列を
電文と言います。
この電文を受信する場合、受信完了状態が2つ存在することになります。
一つは
"
HELLO WORLD\n"をすべて受信し終わったとき、
もう一つは
'
H'、'
E'、'
L'、'
L'、'
O'、' '、'
W'、'
O'、'
R'、'
L'、'
D'、'
\n'をそれぞれ受信し終わったとき
です。
状況としては"HELLO WORLD\n"を
すべて受信し終わったときを受信完了としたいのですが、
受信側は
どこまで文字が続いているのか知る術がありません。
もしかすると
"HELLO"で終わるのかもしれませんし、
"HELLO WORLD\nCHAMPION"なんて続くかもしれません。
文字列が
どこまで続くのかを知っているのは
送信側のみで受信側にはそんな情報はありません。
そこで、何処で文字列が終わるのかを送信側と受信側で
取り決めを行っておく必要があります。
4通りの方法を考えてみます。
方法1:一回に送信する文字数を固定してしまう
方法2:しばらく文字が送られなくなったら受信完了とみなす
方法3:何文字送るかを電文中に入れておく
方法4:送信が終わったことを知らせる文字を最後に送る
方法1:一回に送信する文字数を固定してしまう
手っ取り早い方法ですが、
送りたい文字数が変わると送信側受信側共にプログラムを修正する必要があり実行中に送信文字数を変更できません。
方法2:しばらく文字が送られなくなったら受信完了とみなす
分かりやすい方法ですが、短い周期で送信を繰り返すと
送信完了から次の送信開始まで時間が短くなり、うまく判定できないことがあります。
方法3:何文字送るかを電文中に入れておく
方法1の改良版です。今から
何文字送るか電文の最初に書いておき受信側はこの数字で何文字送られてくるか判定します。
バイナリ通信を行う場合にはこの方法が使われます。RS232C以外だとCANではDLCとして標準でこの方法が使用されています。
方法4:送信が終わったことを知らせる文字を最後に送る
"HELLO WORLD\n"の例では'
\n'を受信完了とみなすといった感じです。
ASCIIコードには電文の開始と終了を示すための文字
STX(0x02)、
ETX(0x03)が用意されています。
バイナリ通信では送る数値として0x02、0x03が混ざる可能性があるためこの方法を単独で使うことはできません。
今回は
方法4を使います。
STX(0x02)、
ETX(0x03)を使った方が良いのですが、キーボード入力が難しいので、電文の開始を'
['、電文の終了を'
]'としてみます。
[ ]の中には送りたい文字が入ります。
ついでにPC側で見やすいように最後に'
\n'をつけておきます。
"HELLO WORLD"を送る場合
"
[HELLO WORLD
]\n"となります。
SUMチェック
シリアル通信を行うとノイズなどにより少なからず
受信エラーが発生し
情報が書き換わってしまう事があります。
パリティーチェックを行っておけばある程度のエラーは弾くことができますが、それでもすり抜けてしまう事はあります。
データの記録をしているだけならこのエラーは大きな問題にはならないかもしれませんが、
物を動かす時はそうはいきません。
情報が書き換わった事で
本来動いてほしくない部分が動いて物を壊してしまう事があります。
そこで、エラーチェックとして
チェックサムを追加します。
送信したい各文字の
文字コードの合計値を計算し、これを
電文の最後に追加します。
受信側でも同じ計算を行い、送られてきた合計値と一致するか確認を行います。
情報が書き換わっていた場合
受信した文字コードの合計値と
電文の最後の合計値が一致しないため、エラーとして処理することができます。
文字数が多くなると合計値がどんどん増えていくため、合計値のうち
下位数ビット(8ビットの場合が多い)のみを使用します。これを
SUM(合計)と呼びます
今回は
電文の開始と終了を示す文字は含めないものとします。
また、算出結果を
16進数表記の2文字として追加します。
HELLO WORLDの例では
文字 16進 10進
'H' 0x48 72
'E' 0x45 69
'L' 0x4C 76
'L' 0x4C 76
'O' 0x4F 79
' ' 0x20 32
'W' 0x57 87
'O' 0x4F 79
'R' 0x52 82
'L' 0x4C 76
'D' 0x44 68
なので合計値は
16進:0x31C、10進:796
下位8bitの値は
16進:0x
1C、10進:28
となります。
これを文字列の後に追加します。
最終的に送る文字は
"[HELLO WORLD
1C]\n"となります。
いちいち手計算するわけにはいかないので、
"HELLO WORLD"
から
"
[HELLO WORLD
1C]\n"
を自動生成する関数を作成します。
関数作成
その他設定は、
シリアル通信設定(2)の回をそのまま使用します。
まずは
SUMを計算する関数を作成します。
<設定>short calc_sum(char* buf){
int i;
short sum_16bit=0;
short sum_8bit=0;
short buf_size=0;
buf_size=
strlen(buf);
//文字数をカウント if(buf_size<=0)
//計算できない場合 return -1;
if(buf_size>255)
//文字設定がおかしい場合 return -1;
for(i=0;i<buf_size;i++){
sum_16bit+=buf[i];
//文字を加算する }
sum_8bit=(sum_16bit&0xFF);
//下位8bitを取り出し return sum_8bit;
//SUM値を返す}
<解説>strlenで文字列長を求めます。これは標準で用意されている関数です。
文字列長が0以下の場合と
255を超えている場合はエラーとして-1を返すように設定しました。
続いてこれを使う関数を作成します。
<設定>#define STX0 (0x02)
#define ETX0 (0x03)
#define STX1 '['
#define ETX1 ']'
void write_sci1_sum(char* trans_buf){
char buf[255];
short sum;
sum=calc_sum(trans_buf);
if(sum==-1)
//文字列設定がおかしい return;
sprintf(buf,"
%c%s%02X%c\n",STX1,trans_buf,sum,ETX1);
//文字列を組み立て write_sci1(buf);
//文字列を送信}
電文の終了と開始に
0x02、
0x03が使える様に準備もしておきました。
printfおよび
sprintfでは
文字コードを文字に変換する際に
%cを使います。
文字列をそのまま表示するときは
%sを使います。
整数型の数字を
10進数表示にする場合は
%d16進数表示にする場合は
%Xを使用します。
%とdの間に数字を入れると表示する桁数を固定できます。
更に
数字の前に0を入れると
空いた桁の部分が0になります。(0埋め)
今回の
%02Xは
16進数、
2桁、
0埋めを意味します。
intSCI.cの中に新規作成した関数
void write_sci1_sum(char* trans_buf)
short calc_sum(char* buf)
は
intSCI.hの中に
プロトタイプ宣言を追加します。
define4つ
#define STX0 (0x02)
#define ETX0 (0x03)
#define STX1 '['
#define ETX1 ']'
も
intSCI.hに記述するのが良いでしょう。
メインの処理は以下となります。
#include "iodefine.h"
#include "io_setup.h"
#include "intSCI.h"
#include <stdio.h>
void main(){
hardware_setup();
//汎用IOの初期化 init_SCI1();
//シリアル通信1の初期化 write_sci1_sum("HELLO WORLD");
while(1){
//無限ループ開始 }
//end while}
//end main送信に使う関数名が変わったぐらいで他は
シリアル通信設定(2)と同じです。
TMZで受信し、
[HELLO WORLD1C]が表示されれば成功です。