Mini210S 開発キット詳細URL
第XIII章 printfやscanf関数移植
第一節 移植方法
printfやscanf関数の移植方法は、選択が多い:
1) linux版のprintk関数移植、バージョンが新しいほど、移植はより困難ですが、機能はより豊富だ;
2)ubootのprintfやscanf機能移植、実際uboot関数もlinuxカーネルから移植するものです;
3)完全に自分を書くこともありますが、機能が比較的弱いです;
全体のOSなしの他のコード部分にとコンパイラは共に問題がないの場合、上記の3つの方法ともに実現可能です。
次はlinux版のprintk関数を利用して、OSなしプログラムに移植し、機能を加えます。
第二節 移植手順
ステップ 1 13.uart_stdioディレクトリにprintf.rarをアンパックして、解凍が成功すると include や include ディレクトリが確認できます、include は対応のヘッドファイル、libでは printfやscanfのコードを置きます。
ステップ 2 13.uart_stdioディレクトリ下のmakefileを修正し、 lib ディレクトリ下のデータをlib.aにコンパイラリンクした後、lib.aを bin にコンパイルします、ソースコードご参照ください。
ステップ 3 main関数をコンパイル、テストします。
第三節 プログラム説明
完全なコードは、ディレクトリ13.uart_stdioご参照ください。前章と比べったら、BL1ディレクトリでは変更がありません、BL2ディレクトリではinclude や libディレクトリを追加致し、main.cも修正します。
1. /lib/printf.c
<1> printf 定義:
int printf(const char *fmt、 ...)
{
int i;
int len;
va_list args; // va_list 即 char *
va_start(args、 fmt);
len = vsprintf(g_PCOutBuf、fmt、args); // 内部は va_arg()を使用します
va_end(args);
for (i = 0; i < strlen(g_PCOutBuf); i++)
{
putc(g_PCOutBuf[i]);
}
return len;
}
<2> printf 関数は可変引数の関数であり、゛可変引数の関数゛とは:
可変引数の関数のプロトタイプ宣言はtype VAFunction(type arg1、 type arg2、 … ); 引数ーは二部分に分けます:数が固定の固定引数と数が可変の可選引数。関数に最小一つの固定引数が必要で、固定引数の宣言は普通関数のと同じ;可選引数は数が確定しないため、宣言時は"..."で表示します。固定引数と可選引数は共同で引数リストを構成しています。
<3> printf 関数では三つの重要なマクロに関わります:
マクロ1: #define va_start(ap、 A) (void) ((ap) = (((char *) &(A)) + (_bnd (A、_AUPBND))))
マクロ2: #define va_arg(ap、 T) (*(T *)(((ap) += (_bnd (T、 _AUPBND))) - (_bnd(T、_ADNBND))))
マクロ3: #define va_end(ap) (void) 0
これらのマクロ中、va は variable argument(可変引数)です;
ap: 可変個引数リストのポインタ;
A : 可変個引数リストの固定引数;
T : 可変個引数のタイプ。
va_list も一つのマクロで、、定義は typedef char * va_list、実際は char 型ポインタ。
<4> 三つのマクロの機能:
1) va_start マクロ
• 機能:
Vにより、変数引数リストのポインタをフェッチし、値をAPに与えます。方法:最後の固定引数Aのアドレス+ 最初の可変引数とAにとってのオフセット•アドレス、その値を apに与えて、 apは変数引数リストの最初のアドレス先頭アドレスになります。。
• 例を挙げます:
可変引数の宣言は void va_test(char a、 char b、char c、 …)の場合、その固定引数はa、b、c、最後の固定引数は cで、すなわち va_start(ap、 c)。
2) va_arg マクロ
• 機能:
現在apポインタの可変引数を取り出して、apポインタを可変引数に次の指します。
3) va_end マクロ
• 機能:
可変引数の受け取りを終了します。va_end ( list )は空きと定義して、実際対応コードはありません。コード対称性のために、va_startと対応します。
<5> 可変引数の数を得るには三つの方法があります:
1) 関数の第一個の引数が次の引数個数を定義します、例え、 func(int num、...);
2) 隠し引数によって、引数の個数を判断します、例えprintfシリーズ、文字列での%の数で判定します;
3) 特別な状況(パラメータが0xFFFFより小さいのint)な場合、常にリターンアドレスまで、より低いスタックにアクセスすることができます。
上記の知識で printf()関数は理解できます。まずva_startマクロ(args、 fmt);は可変引数の先頭アドレスをargsに保存され、そしてvsprintf(g_PCOutBuf、fmt、args)を呼び出し処理します、続いてvsprintf()でva_arg()を使用し、可変引数を取り出して、解析します。普通の文字な場合は変換せず、直接g_PCOutBuf保存します;文字列の場合、変数引数リストで文字列へのポインタを取得し、文字列の内容をg_PCOutBufにコピーします;数字である場合は、 number 関数で処理します、そして解析結果g_PCOutBufに保存します。
最後に、PUTC関数でg_PCOutBuf内の文字をプリントアウトします。scanf 関数の原理は printf 類似して、特別説明しません。
2. main.c
完全コード:
int main()
{
int a = 0;
int b = 0;
char *str = "hello world";
uart_init();
printf("%s\n"、str);
while (1)
{
printf("please enter two number: \r\n");
scanf("%d %d"、 &a、 &b);
printf("\r\n");
printf("the sum is: %d\r\n"、 a+b);
}
return 0;
}
まず“hello world”をプリントアウトして、シリアルポートから2つの数字を受け取り、最後に、それらの和を出力する。
第四節 コードコンパイルとプログラミングの実行
コードをコンパイルし、Fedora端末で下記のコマンドを実行します:
# cd 13.uart_stdio
# make
13.uart_stdioのディレクトリ下にstdio.binを生成し、それを開発ボードにプログラムします。
第五節 実験現象
シリアルケーブルを接続、secure CRTやスーパー端末などのポート工具でポートを開きます、そして端末で“hello world”をプリントアウトされます、2つの数字を入力と提示します、入力すると、その和をプリントアウトします。
---続く