こん○○は、よふかしわーくすの、よふかしさんです
6回目はBlack Pillと呼ばれるWeAct製かそれ系のSTM32F411を使って
Cube環境でPCとのシリアル通信をxprintfを使用してやってみます
特に今回はSTM32F411に搭載されているUSB機能と、
Black Pillに搭載されているUSB Type-Cポートを使用、それ以外のデバイスを使用しないで
VCP(Virtual COM Port)で通信してみたいと思います
今回使用している開発環境は、
CubeMX:Version 6.13.0
CubeIDE:Version 1.17.0
で解説していきます
xprintfとは?
xprintfとは何ぞや?は、製作者であるChanさんの公式ページに記載があります
xprintfは組み込み用に特化したコンパクトなprintfとサポート関数群です。標準入出力関数のサポートされない組み込みシステムにおいて、既存の入出力デバイス(UARTやLCD)に結合することにより、それらに対してprintfで簡単に整形文字列を出力することができます。このため、LCDやUARTなどに手軽に整形出力したいときや、デバッグ・メンテナンス・コンソールなどに有効です。
xprintfは構成オプション(xprintf.h内に定義)で必要な機能のみ組み込むことでモジュールサイズを削減することができます。例としてCortex-M3でのコンパイル結果を次の表に示します。(gcc -Os) なお、long longと浮動小数点は、C99を必要とします。
要は、
- PCだとprintfでコンソール画面に表示させられる
- 組み込みの場合は接続ディスプレイへの表示や、デバッグ用途でPC等の何らか表示させられる機器に通信で出したいよね?
- じゃあそれを実現しましょ
的な感じ
xprintfをDownloadする
Chanさんのトップページにアクセス

おまけのソフトウェア、をクリック
(全然おまけじゃない…)
組み込み用printfモジュール、をクリック

下までスクロールすると、ダウンロードの項目があるので
組み込み用printfモジュールをクリックしてダウンロード

そうすると、xprintf.zipがダウンロードされます
xprintfを組み込む
xprintf.zipを解凍すると、srcフォルダ内に
- xprintf.c
- xprintf.h
があります
STM32 Projectフォルダ→Core→Src、にxprintf.cを
STM32 Projectフォルダ→Core→Inc、にxprintf.hを
配置します
こんな感じ

main.cにインクルード宣言を追加
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usbd_cdc_if.h"
#include "xprintf.h"
/* USER CODE END Includes */
main.cに下記のオリジナル関数を追加
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void usb_putc(char c)
{
while(CDC_Transmit_FS((uint8_t*)&c, 1) != USBD_OK) // USB VCPを使って1文字送信
{
// 何もしない
}
}
/* USER CODE END 0 */
main.cのループ処理にxprintfでの送信処理を追加
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
xprintf("USB VCP Tx\n");
HAL_Delay(500);
}
/* USER CODE END 3 */
解説
公式ページでは下記の記述があります
入出力デバイスとの結合
UARTやLCDなどの出力デバイスに結合するには、デフォルト出力デバイスとしてモジュールのグローバル変数xfunc_outputに、そのデバイスの1文字出力関数へのポインタを代入するだけでOKです。これにより、xputc/xputs/xprintf/put_dump関数の出力は指定された関数に渡されます。xfputc/xfputs/xfprintf/xsprintf関数の出力先は、それぞれの引数で直接指定されます。この設定のためマクロも用意されているので、例えば、void uart1_putc (uint8_t chr);に結合する場合、xdev_out(uart1_putc);とすればOKです。出力関数が複数の引数をとる場合や単純な1文字出力が無い場合は、グルー関数を間にかませる必要があるかも知れません。
UARTなどの入力デバイスに結合するには、デフォルト入力デバイスとしてモジュールのグローバル変数xfunc_inputに、そのデバイスの1文字読み出し関数へのポインタを代入するだけでOKです。(設定マクロを使用した例:xdev_in(uart1_getc);) xgets関数はデフォルト入力デバイスからライン入力を行います。xfgets関数は入力関数を引数で直接指定します。読みだされた文字は、順にバッファにストアされます。’\r’、’\b’以外の制御文字は無視されます。’\r’が読み出されると読み出しを終了してxgets関数は1を返します。’\r’はバッファにはストアされず、文字列は’\0’で終端されます。通常、入力があるまで読み出し関数は制御を返しませんが、入力デバイスはコンソールに限られるわけではない(ファイルに結合した例もあります)ので、入力ストリームの終端が明確なときは-1を返すべきです。これにより、xgets関数は中断して0を返すので、アプリケーションがストリーム終端を知ることが可能になります。
ということで、USB VCPでの1文字送信関数、usb_putcをオリジナルで作成し、
xdev_out関数に渡しています
TeraTermを起動して、受信の改行コードをLFに変更
下記の様に受信できていれば成功です

終わりに
PCへのUSB VCPでのシリアル送信をxprintfで実装した事例を紹介しました
次回は実装系ではなく、Config等のTipsを書いておきたいと思います
公開日時:2024/12/26 4:44:59
コメント