printf関数は、C言語の標準関数の一つで、標準出力に書式整形文字列を出力する基本的ながらとても重宝する関数です。 組み込みにおいても、処理でprintf出力を使用できると、便利だと思います。 しかし、組み込みでは、そもそもいわゆるコンソールというものを搭載しているとは限らず、printfの目的も、ユーザーへの表示(UIとしての)の場合もあれば、デバッグ用のログ出力であったりと多種多様です。
このページでは、USB搭載PSoCのUSBユーザーモジュールである「USBUART」を使用し、PCにデバッグログを表示する事にも使用できる処理を作成します。但し、出力先はUSBUART固定とせず、printf処理の初期化時に選択できるようにし、汎用性を持たせます。
PSoCマイコンのCコンパイラには、可変長引数を実現するstgarg.hや、va_*マクロなどの提供が標準では無いようです。
そこで、可変長引数を取り扱うための実験をしてみます。
C言語では、可変長引数を取り扱う場合、以下のようなプロトタイプ宣言を使用します。
TEST_printf(const char* format, ...);
「...」の部分は、可変個数の引き数を持つ場合の宣言方法です。 この宣言をする事で関数を呼ぶ場合に引数の個数は固定されません。
TEST_function(char c,short s,long l,char c){
// ここでc、s、l、c各変数のアドレス値を確認する
}
| アドレス[byte] | 変数名 |
| Offset+0 | c |
| Offset-1 | s |
| Offset-2 | |
| Offset-3 | l |
| Offset-4 | |
| Offset-5 | |
| Offset-6 | |
| Offset-7 | c |
というわけで、第一引数(c)から、それぞれ次の型のサイズ分アドレスを遡っていく事で値のアドレスに到達できます。仮に、変数cのアドレスが0220hの場合、変数sは、0x021e〜0x021fの範囲に存在しています。
上記から、stgarg.hで提供される可変長引数アクセス用マクロva_*()を作成してみます。
typedef char* va_list; #define va_start(ap, p) (ap = (va_list)(&p)) #define va_arg(ap,type) (*(type*)((ap-=sizeof(type)))) #define va_end(ap)
va_arg(引数の値にアクセスするためのマクロ)は、型を指定する度にアドレスを遡って参照していくため、sizeof(type)をアドレスから減算しています。
ctprintf()は、通常のprintf関数同様、可変長引数を使用します。 まずは、printfのTiny版ctprintfを実装、そこから、vsprintfのConst/Tiny版であるcvtsprintfをコールするような構造にします。
cvtsprintfでは、引数で指定した書式指定文字列を解析し、%dや%sなどの指定子に応じて、続く引数で指定された変数から値を読み取り、文字列に追加していきます。
%dや%xなどの数値を文字列データに変換するモジュールを作成し、そのモジュールに値を渡すことで変換します。したがって、cvtsprintf関数には各形式毎の変換関数がぶら下がっています。
printfでは、以下のように書式指定文字列を書き込みます。
printf("Hello your name is %s and your age is %d old.\n",YourName,YourAge);
ここで作成するctprintfは、標準のprintfをベースとしますが、文字列指定(%s)に関しては、参照先がconstの場合、non-constの場合の指定子を分ける必要がありますので、ある程度変更し、以下のようにしました。
| 書式指定子 | 内容 |
| %d | 10進 int型 |
| %x | 16進 int型(0-9a-f) |
| %X | 16進 int型(0-9A-F) |
通常、printf関数の出力先は標準出力(STDOUT)です。このモジュールでは、ctsprintfから最終的に文字出力を行う関数をコールバック関数とし、初期状態でtprintf_SetCallBack関数で出力用関数を登録して使用するようにすることで、printf関数モジュール群に出力デバイス依存処理が排除できるため、汎用性を持たせるようにしました。多分、通常のUARTでも使用できるかと思います。
void tprintf_SetCallBack(void (*)(char*));
static void (*uart_txs)(char*);
/** Type printf標準出力用関数をコールバック登録する
* @param func ASCIIZ文字列を出力できるvoid func(char*)型関数へのポインタ
* @note NULLを指定しないこと
*/
void tprintf_SetCallBack(void (*func)(char*)){
uart_txs = func;
}
USBUARTで、文字列を出力する為の関数を作成します。
/** USBUARTへ文字列出力する
* @param str 出力したいASCIIZ文字列に対するポインタ
*/
static void usbuart_stdout(char* str){
int len;
len = tstrlen(str) + 1;
while (!USBUART_bTxIsReady());
USBUART_Write(str,len);
}
こちらは簡単で、文字数をtstrlenで調べてろUSBUART_Write(USBUARTのAPI関数)に渡すだけです。
M8C_EnableGInt; // Global Interrupts有効化 USBUART_Start(USBUART_5V_OPERATION); tprintf_SetCallBack(usbuart_stdout); // 文字列出力用コールバック関数を登録 while(!USBUART_Init()); // USBUART初期化終了待ち
このようにusbuart_stdout()関数を初期化時に登録しておくことで、ctprintf()で文字列を表示する時にusbuart_stdout関数を使用してUSBUARTから出力できるようになります。
ここまでで作成したva_*マクロを使用し、最終的な表示にコールバック関数をコールするstprintfを実装します。
/** const文字列用Tiny printf関数
* @param fmt 書式指定文字列
* @param [argument]... 省略可能な引数
* @return 生成文字の文字数
* @note fmtに指定する文字列はROM内に確保したものを指定してください
*/
int ctprintf(const char* fmt, ...){
char buf[80];
va_list ap;
int len;
// コールバックが設定されていない場合の確認
if (uart_txs == NULL){
return(-1);
}
va_start(ap, fmt);
len = cvtsprintf(buf, fmt, ap);
va_end(ap);
// 出力用コールバック関数をコール
(*uart_txs)(buf);
return(len);
}
ここまで紹介させていただきましたvtprintf関数、内部で文字列処理するcvtsprintf関数、その他の全ソースとテスト用のmain()関数、プロジェクトファイルをまとめてここに置いておきます。
このアーカイブの使用方法は以下です。
i = 31(Hex:1F)(ROM:Hello)(RAM:R A M!) 123 i = 32(Hex:20)(ROM:Hello)(RAM:R A M!) 123 i = 33(Hex:21)(ROM:Hello)(RAM:R A M!) 123 i = 34(Hex:22)(ROM:Hello)(RAM:R A M!) 123 i = 35(Hex:23)(ROM:Hello)(RAM:R A M!) 123 i = 36(Hex:24)(ROM:Hello)(RAM:R A M!) 123 i = 37(Hex:25)(ROM:Hello)(RAM:R A M!) 123
【テストメッセージ表示で使用しているctprintf関数コール部分】
i++; // インクリメント
tstrcpy(ram,"R A M!");
ctprintf("i = %d(Hex:%X)(ROM:%S)(RAM:%s) %c%c%c\n\r",i,i,"Hello",ram,0x31,0x32,0x33);
元々H8用に作成していたprintf処理をPSoCに移植しようとして、あまり気力が無くて諦めていたときに、以下のお二つの記事/サイトの影響を受け、作成を続けることが出来ました。 ありがとうございます。
PastelMagic 桑野さんのpr.c(UART用printf処理) - PSoCマイコンをご販売いただきました。 また、PSoC USBUART用printfの実装を諦めていたときに、桑野さん作のUART用printfを見て影響を受けました。
Atelier Blue アトリエブルーさんのC言語での可変個の引数 http://www.atelier-blue.com/memo/memo2006-4-16.htm - 参考にさせていただきました。
アクセスカウント(累計) : - アクセスカウント(本日) : - アクセスカウント(昨日) : -