SH7125でsprintf
前回までにSH7125でシリアル通信、ロータリーエンコーダドライバ、モータドライバドライバ、システムタイマを書いた(ソースはこちら)ので、今回は、去年作ったマイコン側メインプログラムを移植。
その前に、sprintfを書いた。SH7125ではstdio.hをインクルードして、libをリンクすればsprintfもあるようだが、よりコンパクトなsprintfを書いた。ついでにシリアル通信ライブラリにもSerialPrintf()という関数も書いた。
メインプログラムの移植は、あっさり。で、オドメトリ精度を計測しようかなと、思って手でロボットを押してみたところ、左のロータリーエンコーダに引っ掛かりがあり、スリップする。接触していそうなところを一生懸命ヤスリで削るがいまいち。結局、右と左のロータリーエンコーダを交換して、少しネジを締めたらうまく回った。さて、計測。というところでミスってしまった。組立分解手順を間違えて、メインスイッチを壊してしまった。というところで時間切れ。次回はなんとかオドメトリ精度を計測したい。
ソースについて
sprintfだが、一応バッファオーバーランチェック用にsnprintf()という関数の型にしているが、実際にはチェックしていない。気が向いたらチェック入れるかも。あと前回、Int32ToStrin()が冗長なので書きなおしたいと書いたが、ちょっと短くした。
しかし可変長引数の関数を書くのは久しぶり。
va_list ap;
va_start(ap,format);
va_arg(ap,int)
va_end(ap);
基本的には、va_start()マクロ、va_arg()マクロ、va_end()マクロの3つを使うと実装できる。va_listの実態はvoid*型、va_start()は、スタックポインタの位置を取ってきて、apに代入する処理。第二引数のformatは、「...」のひとつ前の引数を指定。va_arg()は取得したい型を指定するとその値が取得できて、apポインタを1つ進める(次の引数へ)。va_end()は、apの解放処理。SH7125では特に何もしていないようだ。
SerialPrintf()は、テンポラリバッファを呼び出し元で用意することにした。PC用のプログラムであればSerialPrintf()の中でスタックに用意するところだが、RAM8kbyte(スタックメモリ領域はデフォルト512byte)のため、スタックメモリの消費も危険なのでこうした。
sprintf.h
//sprintf.h //ibis inc. Eiji Kamiya 2009/05/09 //Licence : New BSD #ifndef __SPRINTF_H__ #define __SPRINTF_H__ #include "typedefine.h" //フォーマット //対応しているフォーマットは、%s %nのみ。 //%3dのような桁指定、非対応 //%03dのような左パディング指定も非対応 //%-03dのような位置決めも非対応 //@param size 出力バッファサイズ(現在、内部でチェックしていない) void snprintf(char* out,int32 size,const char* format,...); //フォーマット(va_list版) void snprintf_valist(char* out,int32 size,const char* format,va_list ap); //int32から文字列へ変換 //@param out 出力先バッファ(12バイト以上用意すること) //@return 出力桁数(0終端子含まず) int Int32ToString(int32 n,char* out); #endif//__SPRINTF_H__
sprintf.c
//sprintf.c //ibis inc. Eiji Kamiya 2009/06/07 //Licence : New BSD #include <stdarg.h> #include "sprintf.h" //プロトタイプ宣言 //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- //フォーマット //対応しているフォーマットは、%s %nのみ。 //%3dのような桁指定、非対応 //%03dのような左パディング指定も非対応 //%-03dのような位置決めも非対応 //@param size 出力バッファサイズ(現在、内部でチェックしていない) void snprintf_valist(char* out,int32 size,const char* format,va_list ap) { const char* p; int n; char buf[12]; char* str; for(p=format;*p!=0;p++){ if(*p=='%'){//%のとき p++; if(*p=='%'){//%%のとき *(out++)='%'; } else if(*p=='d'){//%dのとき n=va_arg(ap,int); n=Int32ToString(n,buf); strncpy(out,buf,n); out+=n; } else if(*p=='s'){//%sのとき str=va_arg(ap,char*); strcpy(out,str); out+=strlen(str); } else {//それ以外のとき *(out++)='%'; *(out++)=*p; } } else {//普通のとき *(out++)=*p; } } *out='\0'; } //フォーマット //対応しているフォーマットは、%s %nのみ。 //%3dのような桁指定、非対応 //%03dのような左パディング指定も非対応 //%-03dのような位置決めも非対応 //@param size 出力バッファサイズ(現在、内部でチェックしていない) void snprintf(char* out,int32 size,const char* format,...) { va_list ap; va_start(ap,format); snprintf_valist(out,size,format,ap); va_end(ap); } //int32から文字列へ変換 //@param out 出力先バッファ(12バイト以上用意すること) //@return 出力桁数(0終端子含まず) int Int32ToString(int32 n,char* out) { char* p=out; int32 div; int32 d; bool isOut=false; if(n==0){//0のとき *(p++)='0'; *p='\0'; return 1; } else if(n<0){//負のとき if(n==-2147483647-1){ strcpy(out,"-2147483648"); return 11; } *(p++)='-'; n=-n; } for(div=1000000000;div>0;div/=10){ d=n/div; if(d!=0){ *(p++)='0'+d; isOut=true; } else { if(isOut==true){ *(p++)='0'+d; } } n-=d*div; } *p='\0'; return p-out; }
Serial.h抜粋
//フォーマット付き送信 void SerialPrintf(char* buf,int32 size,const char* format,...);
Serial.c抜粋
//フォーマット付き送信 void SerialPrintf(char* buf,int32 size,const char* format,...) { va_list ap; va_start(ap,format); snprintf_valist(buf,size,format,ap); va_end(ap); SerialPuts(buf); }