STM32でprintfを簡単に使う

LEDの点滅を除けば、シリアルコンソールへの情報メッセージの出力は、おそらく最も単純かつ簡単で、組み込みプロジェクトのデバッグに最もよく使われる手法です。ほとんどのプラットフォームは、UARTバス上でデータを転送できる独自のAPIを持っていますが、いずれもprintf()関数のような能力と知名度がありません。残念ながら、組み込みCアプリケーションでこの関数を利用するには、stdioライブラリをインクルードするだけでは不十分な場合がほとんどです。STM32プロジェクトの場合、数行の追加コードが必要です。

フォーマットされたデータをUARTバスで送受信したい方は、関連記事「STM32でscanfを簡単に使う」もご覧ください。

手順

この手順では、適切なUART周辺機器を設定して有効にするという前段階と、そのUARTインスタンスをprintf()関数にマッピングするという第一段階、そして浮動小数点数を出力したい人のための第二段階(オプ ション)を説明します。STM32CubeIDE(バージョン1.9.0)で開発されるプロジェクト向けに書かれていますが、他の環境でも拡張できるはずです。

0.UARTインスタンスを作成する

次のステップで説明するように、stdioライブラリのprintf()関数を適切にインクルードするには、UART(またはUSART)周辺機器がフォーマットされた文字列を送信するように設定されている必要があります。通常、ST開発ボードで作業する場合、ST-LINKプログラマ/デバッガにRXおよびTXラインが接続されたUART周辺機器がこの目的のために選択されます。これにより、ST-LINKのUSBバーチャルCOMポートブリッジのおかげで、文字列をシリアルコンソールに送信することができます。幸運なことに、STM32CubeIDEまたはSTM32CubeMXで新しいプロジェクトを開始すると、このUARTインスタンスがデフォルトで設定されます!プログラマはどのUARTが設定されたかをメモして、次のステップに進めばよいのです。

既存のプロジェクトでUARTが設定されていない場合は、プロジェクトの.iocファイルを開き、図1のように修正するだけです。具体的には、

  • 適切な周辺機器のインスタンスを選択する
  • モードとコンフィギュレーションパラメータを設定する(図1に示す設定が最もよく使われます)
  • 適切なGPIOピンがUART RXおよびTXとして構成されていることを確認する

最後に、.iocファイルを保存して、プロジェクト用のコードを生成します。

uart

図1:目的のU(S)ART周辺機器の有効化と設定の例

1.printf()をUARTインスタンスにリダイレクトする

UARTの準備ができたので、printf()を追加するには、数行のコードを追加するだけです。

a.main.cファイルの先頭のIncludesセクションに#include <stdio.h>を追加してください(図2)。

include
図2:stdioライブラリのインクルード

b. 以下のコードをコピーして、main()関数の前、UARTハンドルの宣言の後のmain.cファイルに貼り付けてください。適切なオプションは、以下の図3に示すように、Private Function Prototypesセクションにあります。UARTのハンドルは、&huart?から目的のUART周辺機器のハンドル(例:&huart2)に変更する必要があることに注意してください。

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart?, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}

putchar_prototype
図3:main.cファイルにリターゲティングコードを追加する

printf()関数は、浮動小数点フォーマットの指定子以外では、期待通りの動作をするはずです。これらを有効にするには、次のステップに進みます。

2.浮動小数点演算のサポートを有効にする(オプション)

浮動小数点フォーマットの問題を説明するために、printf() reference pageで提供されているサンプルコードを使用することができます。

printf("Characters: %c %c\n", 'a', 65);
printf("Decimals: %d %ld\n", 1977, 650000L);
printf("Preceding with blanks: %10d\n", 1977);
printf("Preceding with zeros: %010d\n", 1977);
printf("Some different radices: %d %x %o %#x %#o\n", 100, 100, 100, 100, 100);
printf("floats: %4.2f %+.0e %E\n", 3.1416, 3.1416, 3.1416);
printf("Width trick: %*d\n", 5, 10);
printf("%s\n", "A string");

このコードをプロジェクトに追加すると、「浮動小数点フォーマットのサポートが有効になっていない」と説明する警告が表示されます。このままプロジェクトをビルドして実行すると、シリアルコンソールの出力で浮動小数点数がプリントされないことが確認できます(図4)。

ex_output_noFloat
図4:浮動小数点フォーマットのサポートを有効にしない場合の出力例

これは、アプリケーションで浮動小数点数を利用しない場合は問題ないでしょう。しかし、もし有効にしたい場合は、プロジェクトエクスプローラでプロジェクト名を右クリックし、「Properties(プロパティ)」を選択するだけで、有効化することができます。 「C/C++Build」カテゴリの「Settings(設定)」を選択し、 Tool Settings(ツール設定)タブの「MCU Settings(MCU設定)」を選択します。下の図5に示すように、「Use float with printf from newlib-nano」の隣にあるチェックボックスをオンにします。Apply and Close(適用して閉じる)をクリックします。

printf()で浮動小数点フォーマットのサポートを有効にすると、かなりの量の追加メモリを消費することに注意してください。具体的には、浮動小数点演算をサポートしないprintf()関数が消費する量に加え、約0.35KBのRAMと10.30KBのFlashを消費することになります。低価格帯の端末では問題となる可能性があります。

printf_float

図5:浮動小数点フォーマットのサポートを有効にする

これで、プロジェクトを再ビルドしてコードを実行すると、図6に示すように、浮動小数点値が適切にフォーマットされて表示されるようになりました。

ex_output_float
図6:浮動小数点フォーマットのサポートを有効にした場合の出力例




オリジナル・ソース(English)