Cubli(3次元倒立振子)のような高速なフィードバック制御システムにおいて、内部状態(姿勢角、角速度、制御入力など)の可視化は極めて重要です。制御周期が1ms(1kHz)の場合、データロギングシステムにはそれ以上のスループットが求められます。
本記事では、従来のUART通信が抱える帯域幅の限界を定量的に示し、その解決策としてSTM32G4シリーズの「クリスタルレスUSB機能」を用いたUSB CDC(Communication Device Class)の実装手法を解説します。また、実装に先立ち、UARTとUSBの通信プロトコル、物理層、および構造的な違いについて技術的な詳細を述べます。
1. 制御周期と通信帯域のボトルネック
本プロジェクトにおける倒立振子の制御ループは 1kHz(1ms周期) です。各周期ごとに、姿勢データやPID制御の内部変数をPCへ送信し、リアルタイムでモニタリングする必要があります。
UARTによる帯域幅の制限
組み込み開発で一般的に用いられるUART(Universal Asynchronous Receiver-Transmitter)の標準的なボーレートは 115,200 bps です。
UARTの通信フレームは、スタートビット(1bit) + データ(8bit) + ストップビット(1bit) の計10bitで1バイトを構成するため、実効転送速度 $V_{UART}$ は以下のようになります。
$$V_{UART} = \frac{115,200 \text{ [bps]}}{10 \text{ [bits/byte]}} = 11,520 \text{ [bytes/sec]}$$
一方、1回のログ出力に必要なデータ量を約40バイト(浮動小数点数8個分+区切り文字等)と仮定すると、1kHzの制御ループで要求されるデータ転送量 $D_{req}$ は以下の通りです。
$$D_{req} = 40 \text{ [bytes/sample]} \times 1,000 \text{ [samples/sec]} = 40,000 \text{ [bytes/sec]}$$
これより、$D_{req} > V_{UART}$ となり、UARTでは要求帯域の約1/4しか満たせないことがわかります。
実際に送信可能な最大周波数 $f_{max}$ は次のようになります。
$$f_{max} = \frac{11,520}{40} \approx 288 \text{ [Hz]}$$
1kHzの制御ループに対し、UARTを使用すると約3.5msに1回しかデータを送信できず、リアルタイム性が著しく損なわれます。このボトルネックを解消するため、より高速なUSB通信への移行を行います。
2. 技術解説:UARTとUSB CDCの構造と規格比較
実装の前に、UARTとUSB CDCの違いを物理層およびプロトコル層の観点から整理します。
2.1 UART (Universal Asynchronous Receiver-Transmitter)
UARTは「調歩同期式」と呼ばれる通信方式です。
- 物理層: TX(送信)とRX(受信)の2本の信号線とGNDを使用します。電圧レベルはMCU直結の場合はTTL/CMOSレベル(3.3Vや5V)です。
- 同期方式: クロック信号線を持ちません。あらかじめ双方が合意した通信速度(ボーレート)に従い、スタートビットの検出をトリガとしてタイミングを生成し、データを読み取ります。
- フレーム構造: [Start Bit (1)] + [Data (5-9)] + [Parity (0-1)] + [Stop Bit (1-2)]
- 課題: クロック信号がないため、高速通信(数Mbps以上)ではタイミング誤差が蓄積しやすく、通信エラーの原因となります。また、エラー訂正機能もパリティチェック程度しか持ちません。
2.2 USB (Universal Serial Bus)
USBはホスト主導のポーリング方式を採用したバス規格です。
-
物理層: D+ と D- の2本の信号線を用いた「差動伝送(Differential Signaling)」を行います。これにより同相ノイズ(コモンモードノイズ)に強く、高速通信が可能です。また、NRZI(Non Return to Zero Invert)符号化により、データ自身にクロック成分を含ませることで同期を取ります。
-
通信速度 (USB 2.0):
-
Low Speed: 1.5 Mbps
-
Full Speed: 12 Mbps (今回のSTM32での実装ターゲット)
-
High Speed: 480 Mbps
-
プロトコル構造: パケットベースで通信を行います。CRC(巡回冗長検査)による強力なエラー検出と、ハンドシェイクによる再送制御がハードウェアレベルで実装されています。
2.3 USB CDC (Communication Device Class)
USB CDCは、USB上で仮想的なシリアルポート(COMポート)をエミュレートするためのデバイスクラスです。
- 仕組み: ホストPC側からは従来のCOMポートとして認識されますが、物理的にはUSBの Bulk Transfer(バルク転送) モードを使用しています。
- バルク転送の利点: データの正確性が保証されます。帯域保証はありませんが、バスが空いている限り最大限のデータを送ることができます。
- UARTとの違い: USB CDCにおける「ボーレート設定」は仮想的なパラメータに過ぎず、実際の通信速度には影響しません。USBバスの許容する最高速度(Full Speedなら理論値最大12Mbps程度)で通信が行われます。これが、今回の高速化の核心です。
3. ハードウェア実装:STM32G4におけるクリスタルレスUSB
通常、USB通信を行うためには、周波数偏差 $\pm 0.25%$ 以内という高精度なクロックが必要であり、外部水晶発振子(Crystal Oscillator)の実装が必須とされてきました。しかし、STM32G4シリーズは CRS (Clock Recovery System) を搭載しており、外部結晶なしでUSBを駆動可能です。
3.1 CRS (Clock Recovery System) の動作原理
CRSは、USBホスト(PC)から1msごとに送信される SOF (Start Of Frame) パケットを基準信号として利用します。
マイコン内部のHSI48(48MHz 高速RC発振器)を、受信したSOFパケットのタイミングに合わせてトリミング(微調整)することで、USB通信に必要なクロック精度を動的に維持します。これにより、外付け部品を削減しつつUSB通信が可能になります。
3.2 ハードウェア構成
Nucleo-G474REにはユーザー用USBコネクタがないため、USBケーブルを加工して直接配線を行います。
ちょっと乱暴ですがそのあたりにあったUSBケーブルに犠牲になってもらって実験を進めました。
配線仕様:
-
VBUS (赤): 未接続(絶縁処理)
-
理由: Nucleoボードは既にST-LINK経由で給電されており、PCからの5Vを直接印加すると電源競合によりボードまたはPCのポートを破損する危険性があるためです。
-
D- (白): PA11
-
D+ (緑): PA12
-
GND (黒): GND
4. ソフトウェア実装
STM32CubeMXを用いて、USB DeviceスタックとCRSの設定を行います。
4.1 STM32CubeMXの設定
-
Connectivity:
USBを有効化し、Device (FS)を選択。 -
Middleware:
USB_DEVICEを選択し、Class For FS IP をCommunication Device Class (Virtual Port Com)に設定。 -
LinkerSettings: USB CDCドライバは内部で多量のバッファを使用するため、
Heap Sizeを 0x600 以上、Stack Sizeを 0x800 以上に拡張します。これを行わないと、動作不安定やハングアップの原因となります。

4.2 コード実装:標準出力のリダイレクト
printf 関数の出力をUARTからUSB CDCへ変更するため、システムコールである _write 関数をオーバーライドします。
main.c / usbd_cdc_if.h のインクルード
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include <string.h>
#include "usbd_cdc_if.h" // USB送信関数へのアクセスに必要
/* USER CODE END Includes */
_write関数の実装
CDC_Transmit_FS 関数は、ユーザーバッファのデータをUSBの送信エンドポイントバッファへコピーし、送信リクエストを発行します。
/* USER CODE BEGIN 0 */
// printf等の標準出力をUSB CDCへリダイレクトする
int _write(int file, char *ptr, int len) {
CDC_Transmit_FS((uint8_t*)ptr, len);
//HAL_UART_Transmit(&hlpuart1, (uint8_t *)ptr, len, 100);//UARTで通信する場合は切り替える
return len;
}urn len;
}
/* USER CODE END 0 */
4.3 ベンチマーク用コード
スループットを計測するため、1秒間に何回データ送信ループを実行できるかをカウントするプログラムを作成しました。
/* USER CODE BEGIN 2 */
printf("USB Speed Test Start!\r\n");
HAL_Delay(1000);
uint32_t loop_count = 0;
uint32_t start_tick = HAL_GetTick();
/* USER CODE END 2 */
/* USER CODE BEGIN WHILE */
while (1)
{
// --- 計測フェーズ (1000ms間) ---
if (HAL_GetTick() - start_tick < 1000)
{
// 実運用を想定した約40バイトのCSVデータ送信
printf("1.11,2.22,3.33,4.44,5.55,6.66,7.77,8.88\r\n");
loop_count++;
}
// --- 結果表示フェーズ ---
else
{
HAL_Delay(50); // 送信バッファのフラッシュ待ち
printf("\r\n");
printf("==================================\r\n");
printf(" Speed: %lu Hz (Lines/sec) \r\n", loop_count);
printf("==================================\r\n");
printf("\r\n");
HAL_Delay(3000); // 結果確認用ウェイト
// カウンタとタイマーのリセット
loop_count = 0;
start_tick = HAL_GetTick();
printf("Go!\r\n");
}
}
5. ベンチマーク結果:UART vs USB CDC
同一のデータペイロードを用い、_write 関数内の送信先を切り替えて速度比較を行いました。
ラウンド1:従来の UART (115200 bps)
- 実測値: 280 Hz
- 考察: 理論値(288 Hz)に近く、1kHz制御ループではデータのドロップ(欠落)あるいは制御遅延が避けられない結果となりました。
ラウンド2:USB CDC (Full Speed)
- 実測値: 約 42,000 Hz
- 考察: UART比較で約150倍の速度向上を確認しました。これは、物理層の高速化に加え、パケットによるまとめての転送(バルク転送)が効率的に機能しているためです。
6. 結論
本記事では、Cubliの制御システムにおける通信ボトルネックを解消するため、STM32G4のCRS機能を活用したクリスタルレスUSB CDCを実装しました。
| 通信方式 | 実測ループ速度 | 1kHz制御への影響 | 判定 |
|---|---|---|---|
| UART (115.2kbps) | 280 Hz | 致命的 (3ms以上の送信時間により制御周期破綻) | × |
| USB CDC (FS) | 42,000 Hz | 無視可能 (負荷数%以下で全データ送信可能) | ◎ |
USB CDCの導入により、40kHz以上のスループットを確保しました。これにより、1msの制御周期ごとに全ての内部変数をログ出力しても、通信処理がCPU負荷や制御周期に与える影響は軽微です。
UARTとUSB CDCの技術的な差異、特にポーリングベースのパケット通信とバルク転送の特性を理解することで、組み込みシステムにおける適切な通信インターフェースの選定が可能となります。
次回は、この高速テレメトリ環境を用いて、物理的な筐体製作とモータ制御の統合へ進みます。
アドベントカレンダー参加中
STM32×AIで「3軸倒立振子」を作る25日間(ひとりアドカレ)Advent Calendar 2025

