PSoC

このページの目次

USBUARTにも使用できる、出力先選択可能なprintfを作成する

printf関数は、C言語の標準関数の一つで、標準出力に書式整形文字列を出力する基本的ながらとても重宝する関数です。 組み込みにおいても、処理でprintf出力を使用できると、便利だと思います。 しかし、組み込みでは、そもそもいわゆるコンソールというものを搭載しているとは限らず、printfの目的も、ユーザーへの表示(UIとしての)の場合もあれば、デバッグ用のログ出力であったりと多種多様です。

compare_process_printf.gif

このページでは、USB搭載PSoCのUSBユーザーモジュールである「USBUART」を使用し、PCにデバッグログを表示する事にも使用できる処理を作成します。但し、出力先はUSBUART固定とせず、printf処理の初期化時に選択できるようにし、汎用性を持たせます。

call_PSoC_printf.gif

PSoCマイコンのCコンパイラで可変長引数を使用する

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+0c
Offset-1s
Offset-2
Offset-3l
Offset-4
Offset-5
Offset-6
Offset-7c

というわけで、第一引数(c)から、それぞれ次の型のサイズ分アドレスを遡っていく事で値のアドレスに到達できます。仮に、変数cのアドレスが0220hの場合、変数sは、0x021e〜0x021fの範囲に存在しています。

stgarg.h va_*互換マクロを作成する

上記から、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(Const書式指定文字列版Tiny printf)を実装する

ctprintf()は、通常のprintf関数同様、可変長引数を使用します。 まずは、printfのTiny版ctprintfを実装、そこから、vsprintfのConst/Tiny版であるcvtsprintfをコールするような構造にします。

tprinf.png

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の場合の指定子を分ける必要がありますので、ある程度変更し、以下のようにしました。

書式指定子内容
%d10進 int型
%x16進 int型(0-9a-f)
%X16進 int型(0-9A-F)
%schar* ASCIIZ文字列
%Sconst char* ASCIIZ文字列
%c1文字表示 int型

USBUARTに出力する関数をコールバックにしてprintfモジュールの汎用性を出す

通常、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で、文字列を出力する為の関数を作成します。

/** 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から出力できるようになります。

ctprintf関数の実装

ここまでで作成した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);
}

PSoC用USBUART printf互換関数全ソースソースコードについて

ここまで紹介させていただきましたvtprintf関数、内部で文字列処理するcvtsprintf関数、その他の全ソースとテスト用のmain()関数、プロジェクトファイルをまとめてここに置いておきます。

このアーカイブの使用方法は以下です。

  1. アーカイブを解いて、USB_UART_UM.SOCをPSoC Designerで開く。
  2. コンパイルする(既にコンパイル済みオブジェクトは含まれていますのでコンパイルしなくても書き込み出来ます)
  3. PSoC MiniProgで書き込みます。
  4. PSoCボード上のUSBポートとPCとを接続します。
  5. デバイスドライバは、アーカイブのlib/USBUART.infを使用して下さい。
  6. COMポートが新規追加されますので、TeraTermまたはハイパーターミナルで当該ポートを開いてください。 ボーレート等は何でもOKです。
  7. 何かキーを押すたび、以下のテストメッセージが表示されるはずです。
    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);

ダウンロード

  • vcディレクトリ内に、VisualStudio2005でソース編集する為のソリューションファイルを作成しておいてあります。なお、VisualC++2005 ExpressEdition?(フリーのVC++)でも参照可能です。

謝辞

元々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
 - 参考にさせていただきました。




アクセスカウント(累計) : 8471 アクセスカウント(本日) : 2 アクセスカウント(昨日) : 0


添付ファイル: filecall_PSoC_printf.gif 768件 [詳細] filecompare_process_printf.gif 708件 [詳細] fileusbuart_printf_sample.zip 1442件 [詳細] fileUSB_UART_UM_NetRelease 301件 [詳細] filetprinf.png 658件 [詳細]

トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-02-12 (月) 01:32:21 (4516d)