目次
前
次
リングバッファ処理
通信でデータを受信する場合、リングバッファを
使います。
リングバッファを利用すると、取りこぼしがないのと
データをある程度の時間の履歴を残しておけます。
コマンドインタプリタでは、1レコードが到着したなら
即座に反応しなければならない場面が多いので、次の
ように受信バッファを操作してきました。
char sbuf[16] ;
byte sindex ;
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
受信バッファに1レコードが到着したなら、フラグを利用して
コマンドインタプリタ側に通知しています。
イベント通知フラグの値で、コマンドを処理。
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* initialilze buffer */
if ( cmd == 'I' ) { init_rbuf(); }
/* display */
if ( cmd == 'D' ) { display_line(OFF); }
/* shift */
if ( cmd == 'S' ) { display_line(ON) ; }
/* write 1 byte */
if ( cmd == 'W' ) {
for ( ii = 1 ; ii < 11 ; ii++ ) {
/* get 1 byte from receive buffer */
tmp = *(sbuf+ii) ;
/* judge */
if ( tmp == '\r' ) break ;
/* store it to ring buffer */
store_rbuf( tmp );
}
}
}
リングバッファを扱うために、配列を宣言し
読出しポインタ、書込みポインタを使っての
1文字入力と複数文字表示を考えます。
リングバッファのイメージは、以下。
上図では、rdp、wdpを扱うときに、次の規則を適用します。
リングバッファにデータを書き込むときは、wdpが示す位置に
格納。データを格納後、wdpを+1して、サイズで割った剰余
をwdpに代入する。
リングバッファからデータを読み出すときは、rdpが示す位置の
データを取り出す。データを取出後、rdpを+1して、サイズで
割った剰余をrdpに代入する。
言葉での説明は、回りくどい表現になりますが、コードに変換
してみると簡単。
リングバッファに1文字書き込む
void store_rbuf(byte x)
{
*(rbuf+wdp) = x ;
wdp++ ;
wdp %= RSIZE ;
}
リングバッファから1文字取り出す
byte load_rbuf()
{
byte result ;
result = *(rbuf+rdp) ;
rdp++ ;
rdp %= RSIZE ;
return result
}
リングバッファのサイズを20バイトとし
10バイトだけを表示してみます。
動作イメージは、以下。
端末操作で処理したいので、コマンドを用意します。
- ? help
- I initialilze buffer
- D display(10 bytes)
- S display(10 bytes) and shift
- W write 1 byte
各コマンドを定義していきます。
help
使えるコマンドのリストを表示すればよいので
文字列表示関数を使って、端末に文字を送信。
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("I initialilze buffer") ; crlf();
rs_puts("D display") ; crlf();
rs_puts("S shift") ; crlf();
rs_puts("W write 1 byte") ; crlf();
}
initialilze buffer
リングバッファに割り当てる配列に、スペースを格納。
void init_rbuf()
{
byte i ;
for ( i = 0 ; i < RSIZE ; i++ ) {
*(rbuf+i) = ' ' ;
}
rdpb = 0 ;
wdp = 0 ;
}
display(10 bytes)
リングバッファから10バイトを取り出して、表示。
void display_line()
{
byte i ;
byte j ;
/* delimiter */
*(lineU+10) = '\0' ;
/* copy */
j = rdpb ;
for ( i = 0 ; i < 10 ; i++ ) {
*(lineU+i) = *(rbuf+j) ;
j++ ;
j %= RSIZE ;
}
/* show */
rs_puts( lineU );
crlf();
}
rdpbは、別途処理すると仮定。
display(10 bytes) and shift
リングバッファから10バイトを取り出して、表示するのは
ひとつ前と同じ。先頭情報の位置が更新されるように工夫。
void display_line_shift()
{
byte i ;
byte j ;
/* delimiter */
*(lineU+10) = '\0' ;
/* copy */
j = rdpb ;
for ( i = 0 ; i < 10 ; i++ ) {
*(lineU+i) = *(rbuf+j) ;
j++ ;
j %= RSIZE ;
}
/* show */
rs_puts( lineU );
crlf();
/* update */
rdpb++ ;
rdpb %= RSIZE ;
}
ひとつ前の関数との差異は、先頭情報の位置が更新されるだけ
なので、選択可能にします。
void display_line(byte x)
{
byte i ;
byte j ;
/* delimiter */
*(lineU+10) = '\0' ;
/* copy */
j = rdpb ;
for ( i = 0 ; i < 10 ; i++ ) {
*(lineU+i) = *(rbuf+j) ;
j++ ;
j %= RSIZE ;
}
/* show */
rs_puts( lineU );
crlf();
/* update */
if ( x ) { rdpb++ ; rdpb %= RSIZE ; }
}
write 1 byte
1バイトの書き込みは、次の関数で対応。
void store_rbuf(byte x)
{
*(rbuf+wdp) = x ;
wdp++ ;
wdp %= RSIZE ;
}
1バイト書き込み処理を元にし、コマンドインタプリタでは
複数バイトの入力ができるようにします。
/* write 1 byte */
if ( cmd == 'W' ) {
for ( ii = 1 ; ii < 11 ; ii++ ) {
/* get 1 byte from receive buffer */
tmp = *(sbuf+ii) ;
/* judge */
if ( tmp == '\r' ) break ;
/* store it to ring buffer */
store_rbuf( tmp );
}
}
}
受信バッファからのデータ転送と複数バイト書き込みを
1レコードが10バイト以内として処理。
ここまでで必要な関数、手続きを用意したので、まとめます。
#include <MsTimer2.h>
#define OFF 0
#define ON OFF+1
/* pin assignment */
#define LED_BIT 5
/* ring buffer size */
#define RSIZE 20
#define XINTERVAL 1500
/* function prototype */
void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
/* variables */
boolean tflag ;
boolean eflag ;
boolean uflag ;
char cmd ;
char sbuf[16] ;
byte sindex ;
byte xcnt ;
/* ring buffer */
byte rdpb ;
byte wdp ;
byte rbuf[RSIZE];
char lineU[11] ;
void init_rbuf();
void store_rbuf(byte x);
void display_line(byte x);
void setup()
{
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
rs_puts("Hello !");
crlf();
show_help();
/* clear flags */
tflag = OFF ;
eflag = OFF ;
uflag = OFF ;
/* initialize port values */
PORTB = 0x00 ;
PORTC = 0x00 ;
PORTD = 0x00 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0xff ;
DDRD = 0xfe ;
/* others */
xcnt = 0 ;
init_rbuf();
/* 1500ms period */
MsTimer2::set(XINTERVAL,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
byte tmp ;
byte ii ;
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* initialilze buffer */
if ( cmd == 'I' ) { init_rbuf(); }
/* display */
if ( cmd == 'D' ) { display_line(OFF); }
/* shift */
if ( cmd == 'S' ) { display_line(ON) ; }
/* write 1 byte */
if ( cmd == 'W' ) {
for ( ii = 1 ; ii < 11 ; ii++ ) {
/* get 1 byte from receive buffer */
tmp = *(sbuf+ii) ;
/* judge */
if ( tmp == '\r' ) break ;
/* store it to ring buffer */
store_rbuf( tmp );
}
}
}
/* timer handling */
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* flash LED */
PORTB &= ~(1 << LED_BIT);
if ( xcnt & ON ) { PORTB |= (1 << LED_BIT); }
/* increment */
xcnt++ ;
}
}
void update_trigger()
{
/* set flag */
tflag = ON ;
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("I initialilze buffer") ; crlf();
rs_puts("D display") ; crlf();
rs_puts("S shift") ; crlf();
rs_puts("W write 1 byte") ; crlf();
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void display_line(byte x)
{
byte i ;
byte j ;
/* delimiter */
*(lineU+10) = '\0' ;
/* copy */
j = rdpb ;
for ( i = 0 ; i < 10 ; i++ ) {
*(lineU+i) = *(rbuf+j) ;
j++ ;
j %= RSIZE ;
}
/* show */
rs_puts( lineU );
crlf();
/* update */
if ( x ) { rdpb++ ; rdpb %= RSIZE ; }
}
void init_rbuf()
{
byte i ;
for ( i = 0 ; i < RSIZE ; i++ ) {
*(rbuf+i) = ' ' ;
}
rdpb = 0 ;
wdp = 0 ;
}
void store_rbuf(byte x)
{
*(rbuf+wdp) = x ;
wdp++ ;
wdp %= RSIZE ;
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
Teratermを利用して、スケッチを動かしてみます。
接続した状態は、以下。
現在のリングバッファの内容を表示。
起動時の初期化で、スペースが出力されてます。
複数バイトを書き込んで、表示。
表示後、シフトをしていってみます。
表示コマンドの'D'と'S'には、違いがあるのが
わかります。
電光掲示板や電車の中にある情報表示装置では
リングバッファを利用した左にシフトしていく
表示が見られます。
リングバッファを使っていることがわかります。
目次
前
次