小聪同学
1
皆さん、こんにちは。
私はSTM32F103C8T6開発ボード上で発生する間欠的なUSB CDCの問題をデバッグしています。停止モードから復帰後、PCが仮想シリアルポートを認識できない場合があります(ハードリセットしないと再列挙されません)。皆様の知見を活用して、クロック/USB初期化シーケンスが適切か確認し、検証済みの解決策を教えていただければ幸いです。
ハードウェアおよびデバイス
- MCU:STM32F103C8T6、8MHz HSEをPLLで9倍に倍速し72MHzで動作。
- 開発ボード:“小藍板”(Blue Pill)フォームファクタ;LDOが5Vを3.3Vに変換、VDDピン近くに0.1µF+1µFのデカップリングコンデンサ、8MHz HC-49クリスタル発振器と22pF負荷コンデンサを搭載。
- USB:フルスピードデバイス、D+は1.5kΩ抵抗で3.3Vにプルアップ;D+/D-ラインには22Ωの直列抵抗。
故障現象
- 初回通電時:Windows 10およびUbuntu 22でUSB CDCが正常に列挙される。
- 停止モードに約5-30秒間進入後復帰すると、ホストがCDCデバイスを認識できない場合がある(新しいPID/VIDイベントが発生せず、シリアルポートが消失)。USBケーブルの抜き差しは必ずしも効果なく、NRSTリセットピンを押すと必ず復帰。再現率は約10%-30%。
実施済みの対策
- 停止モード復帰後にHSE/PLLを再有効化し、USBクロックを再起動。
- USB周辺機器クロックを切り替え、USBD_DeInit()→USBD_Init()シーケンスを実行。
- クロック切り替え後にSystemCoreClockUpdate()関数を呼び出すことを確認。
- RCCとUSB初期化ステップ間に1-10msの遅延を追加。
- オシロスコープで3.3V電源リップルを測定:復帰時のリップル<20mVpp;D+アイドル状態は約3.3V。
クロック/RCC設定シーケンス(HALライブラリ)
// 停止モードからの復帰:HSE/PLLを再び有効化し、SYSCLKをPLLクロックに戻す
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
HAL_RCC_OscConfig(&(RCC_OscInitTypeDef){
.OscillatorType = RCC_OSCILLATORTYPE_HSE,
.HSEState = RCC_HSE_ON,
.PLL.PLLState = RCC_PLL_ON,
.PLL.PLLSource = RCC_PLLSOURCE_HSE,
.PLL.PLLMUL = RCC_PLL_MUL9
});
HAL_RCC_ClockConfig(&(RCC_ClkInitTypeDef){
.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2,
.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK,
.AHBCLKDivider = RCC_SYSCLK_DIV1,
.APB1CLKDivider = RCC_HCLK_DIV2,
.APB2CLKDivider = RCC_HCLK_DIV1
}, FLASH_LATENCY_2);
SystemCoreClockUpdate();
USB再初期化コードスニペット
// クロックが安定した後にデバイススタックを完全に再起動
USBD_DeInit(&hUsbDeviceFS);
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(2);
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
USBD_Start(&hUsbDeviceFS);
観察結果
- 停止モードをスキップし、実行モード↔スリープモード間での切り替えのみではシステムが安定。
- 復帰後にSYSCLKをPLLではなくHSIに維持するとシステムがより安定に見えるが、72MHzクロックが必要なタイミング要件を満たす必要がある。
- GPIOを通じてD+を約10ms間引き下げ(切断を模倣)ことで復帰確率が向上するが、100%にはならない。
求助事項
- F103チップで停止モード復帰後の標準的な有効化順序/タイミング(HSE→PLL→USBFS)はありますか?CDCデバイスの異常を回避する方法。
- 復帰後にUSBの「ソフト切断」(D+を引き下げ)を強制することはF1シリーズのベストプラクティスですか?またはプロトコルスタックレベルでのリセット方法がありますか?
- 停止モード後のHSE起動+PLLロックがUSB SOF(フレーム同期信号)または48MHzクロックドメインに影響する既知のエラッタはありますか?
- USBクロック復帰時にHSIからPLLへの切り替えを推奨しますか?またはPLLの安定化を常に優先しますか?
- この現象を引き起こす可能性のあるボードレベルの問題(D+抵抗の許容誤差、ESDダイオード/リーク、クリスタル発振器の負荷過多、LDOの過渡応答不良など)をご経験されたことがありますか?
「いいね!」 1
CCC
2
Hi there,
これはF1シリーズデバイスでSTOPモードからのUSB再列挙後に発生する古典的かつ非常に厄介な問題です! 間違いなくあなたの認識は正しく、標準的なトラブルシューティング手順はすべて試されています。再現率が10–30%と不安定な点から、複雑なクロック再構成中に微妙なタイミング/メタステイビリティの問題が起きている可能性があります。
以下はUSB周辺機器のクロックドメインと必要な48 MHzクロックの安定性に焦点を当てた分析と推奨事項です。
クロックシーケンスのサニティチェックと48 MHzドメイン
RCCシーケンスはHSE/PLLを再有効化するための標準的かつ正しい手順に見えます。問題の原因はPLL自体ではなく、USB周辺機器が電源オフ状態からフルスピード動作に移行する際のPLL / 1.5から導出される48 MHzクロックの処理にある可能性が高いです。
- USBクロックソースの制約: USB FSクロックはPLLから導出される必要があります。具体的には、$48\text{ MHz}$を実現するためPLLCLK / 1.5です。PLLに一時的な不安定さがあると、デバイスが送信する最初の数個のStart-of-Frame (SOF) パケットが破損し、ホストPCがデバイスを無視する原因(ゾンビCDC)になります。
- リセットのタイミング:
__HAL_RCC_USB_FORCE_RESET()と__HAL_RCC_USB_RELEASE_RESET()のシーケンスは適切ですが、PLLロック時間に対するリセットパルスのタイミングや長さが十分でない可能性があります。F103のデータシートにはHSEの起動時間とPLLのロック時間が別途記載されています。あなたが実行しているのは本質的に次の手順です:
- 唤醒 \\rightarrow HSE/PLL起動
- …(PLLロック)…
- USBクロック有効化 + リセットパルス
- …(直ちにUSBD\_Init)…
PLLがわずかに安定したタイミングでUSBリセットを解除した場合、初期クロックが依然としてぎりぎりになる可能性があります。2msの遅延ではPLL/USBクロックチェーン全体が完全に安定するには不十分かもしれません。
推奨回避策: デタッチ/アタッチの処理
D+をローにすることで復旧が改善するという観察が鍵となります。物理的なデタッチ/リアタッチを強制することで、ホストPCにデバイスが消失し再接続されたことを確実に伝える方法が最も信頼性があります。
効果の理由: USB列挙はホストによって処理されます。ウォークアップサイクル中にデバイスクロックが不安定な場合、ホストは応答しないデバイス(またはガベージデータ)としてポートを「サスペンド」または「エラー」状態に保持し続けます。この状態を解除するには物理的なリセットが必要です(NRSTが提供するように)。ソフトデタッチ/アタッチはホストのUSBコントローラーにポート列挙ステートマシンを再起動させます。
クロックが安定した後に以下のシーケンスを試してください:
- クロック安定化: HSE/PLLが完全に安定し、$\text{SYSCLK} = 72\text{ MHz}$になることを確認(現在のRCCブロック)。
- USB無効化(ソフトデタッチ):
- 内部プルアップを使用する場合、内部プルアップを無効化します。Blue Pill/外部1.5 k$\Omega$を使用する場合は、GPIOでD+をローに積極的にプルダウンする必要があります。
- D+ピンをGPIO出力ローとして設定。
HAL_Delay(10);(10msはデタッチ時間として一般的に信頼性があります)。
- USB再初期化(リアタッチ):
- D+ピンをUSB機能に戻す(または内部プルアップを再有効化)。
- 完全なUSB再初期化シーケンスを実行:
USBD_DeInit(&hUsbDeviceFS);
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(2); // 依然として推奨されます
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
// ...(初期化の残り)...
USBD_Start(&hUsbDeviceFS);
- ホストはD+のプルアップ抵抗を検出し、再列挙を開始します。
ベストプラクティスか? F1シリーズでは、STOPモードからの復帰時にUSB再列挙を確実にするため、ソフトデタッチを強制する方法は広く知られた実用的な回避策です。
ボードレベルの落とし穴
電源レールのリップルが$\u003c20\text{ mVpp}$と良好に見えますが、以下の点も考慮してください:
- クリスタル負荷コンデンサ(22 pF): 8 MHzのHC-49クリスタルでは22 pFは高めで、HSE起動時間の増加や復帰直後の周波数安定性に影響する可能性があります。多くの8 MHzクリスタルは$10\text{ pF}$~$15\text{ pF}の負荷コンデンサを推奨します。クリスタルのデータシートで推奨される\text{C}_{\text{L}}$(負荷容量)を確認する価値があります。
- D+抵抗の許容誤差: $1.5\text{ k}\Omega$のプルアップは適切です。$\text{+/-} 5\%$の誤差が生じていないか確認してください。この抵抗値はホストにフルスピードデバイスであることを通知するため重要です。
- ESD保護ダイオード/リーク電流: D+/D-に外部ESD保護ダイオードを使用している場合、$3.3\text{ V}$の信号に干渉する可能性のある容量やリーク電流が追加されていないか確認してください。
D+ GPIOのデタッチ/アタッチを試してみてください。この方法はホストがデバイスの復帰タイミングと同期できない根本的な問題に対処します。
ABC
3
こんにちは。これはF103 + Blue Pillでの古典的な問題で、主にホスト側がデバイスの切断を認識できなかったことと、Blue Pillのハードウェア設計上の欠陥が原因です。
ご質問への直接的な解決策:
1. 根本原因:D+プルアップ抵抗とソフトウェア制御
Blue Pill開発ボードのD+プルアップ抵抗(R10)は通常3.3Vに直接ハンダ付けされており、MCU GPIOの制御対象になっていません。
- 現象: MCUが停止モードやリセット状態になると、D+は抵抗により引き続きプルアップされます。ホスト(PC)はデバイスが接続されたままと認識し、再列挙されません。
- ご操作内容: 「10ms間ローを保持」とのことですが、時間が短すぎます。PC側のUSBプロトコルスタック(特にWindows)は、通常100ms以上(推奨500ms-1s)のD+ロー状態を検出することでデバイス切断(SE0状態)と判定します。
2. 解決策:強制再列挙シーケンス
SystemCoreClockUpdate()の後、USB初期化前に以下の「強制リセット」コードを追加してください:
// 1. PA12 (D+)をGPIO出力ローに設定し、ケーブル抜去を模倣
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET); // D+をローに
// 2. ホストが切断を認識するための十分な遅延(推奨500ms-1000ms)
HAL_Delay(600); // 10msでは絶対に不足
// 3. ピンを解放しUSBペリフェラル制御に戻す(この時プルアップ抵抗がD+をハイにし、ホストの列挙をトリガー)
// 注意:以降のUSBD_Initで再びピンを複用モードに設定します。ロー出力を停止すれば問題ありません。
3. クロック構成の補足
F103のUSBクロックは48MHzでなければなりません。
- 確認ポイント: RCC構成で
RCC_USBCLKSOURCE_PLL_DIV1_5が正しく設定されているか確認してください。
- 72MHz (SysClk) / 1.5 = 48MHz。この設定が誤ると、USBはタイミングエラーで通信不能になり、「認識できないデバイス」とエラーが出ます。
4. ご質問への簡潔な回答
- タイミング順序: HSE → PLL → Flash Latency → SysClk → D+を600msロー保持 → USB初期化
- ベストプラクティス: トランジスタ制御のないボードでは、PA12をソフトウェア制御でローにすることが唯一の「ソフト切断」標準手法です。
- 勘誤: F103の停止モードからの復帰時にUSBが完全に停止するチップレベルの問題は通常なく、ホストのステートマシン未リセットが主因です。
次の推奨対応:
10msの遅延を600msに変更すれば問題が解決するはずです。
海阔天空
4
あなたの問題について、同様の事象を経験したことがあります。停止モードからの復帰後にUSB CDCが異常になる原因は、一般的にクロック回復のシーケンスとUSBペリフェラルの状態リセットのタイミングにあると考えられます。以下の要点と提案を参考にしてください。
1. クロック回復シーケンスとエラッタ
F103は停止モードからの復帰後、デフォルトでHSI(8MHz)をシステムクロックに使用します。HSEとPLLを再起動する手順は正しいですが、重要なタイミングがあります:PLLロック後にUSBクロックを有効にする必要があります。USB FSはPLLから供給される正確な48MHzクロックを必要とします。PLLが不安定な状態でUSBクロックを起動すると、USB PHYが異常動作し、ホストが認識できなくなります。
提案:HAL_RCC_OscConfigの後でPLLRDYフラグのロックを待つ処理を追加してください。
while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET);
その後でHAL_RCC_ClockConfigを実行します。
2. USBペリフェラルの完全なリセット
実行したUSBD_DeInitと強制リセットシーケンスは必要ですが、順序の最適化が可能です。STのエラッタ(一部のF1モデル向け)では、停止モード復帰後のUSBペリフェラルにはより長いリセットパルスが必要と記載されています。USBクロックの無効化→リセット→再有効化の際、ディレイを最低10ミリ秒確保してください。
より信頼性のある方法:復帰後はUSBクロックを無効化したままにし、システムクロックが完全に安定(PLLロック完了とSYSCLK切替後)してから以下のハードリセットシーケンスを実行します。
__HAL_RCC_USB_CLK_DISABLE();
__HAL_RCC_USB_FORCE_RESET();
HAL_Delay(10);
__HAL_RCC_USB_RELEASE_RESET();
__HAL_RCC_USB_CLK_ENABLE();
// その後でUSBD_Initなどの初期化を実行
3. 「ソフトディスコネクト」によるDPラインのプルダウン
DPラインを10ms間強制的にプルダウンすることで物理的なディスコネクトを模倣する方法は、F1シリーズで一般的に推奨される実践手法です。これによりホストが再認識し、再列挙をトリガーします。成功率を高めるため、DPプルダウン前にUSBペリフェラルクロックを停止し、プルダウン後は十分な時間(10-20ms)保持することを提案します。これはプロトコルスタックレベルの方法ではありませんが、ハードウェア的には最も直接的な対策です。
4. クロックソース切替の提案
USB動作中にHSIへの切替は避けてください。正しい手順は:復帰後、72MHzのPLLクロックを迅速かつ安定して復元することを目指します。PLLの安定化とロックを最優先し、その後でUSBを処理します。PLLが安定するまでは、USBペリフェラルにクロックを供給しないようにしてください。
5. ハードウェア要因の可能性
記述からするとハードウェア問題の可能性は低いですが、以下の確認を提案します:
- クリスタルロード容量:8MHz HC-49では22pFが一般的ですが、HSE起動遅延によりPLLロック時間が延長される可能性があります。容量を18-20pFに減らし、クリスタル端子の波形がクリーンか確認してください。
- USB信号線:直列の22Ω抵抗は標準です。D+のプルアップ抵抗(1.5kΩ)がMCUの3.3Vから直接供給され、起動時の電源が安定しているか確認してください。
- 電源:復帰時の電流急増によりLDO出力が一時的に低下する可能性があります。測定したリップルが小さくても、VDDとグランド間に10µFのタンタルコンデンサを追加し、瞬時応答を改善してください。
まとめと推奨手順
- 復帰後のクロック回復:HSEを有効化 → HSE準備完了を待機 → PLLを有効化 → PLLロックを待機 → SYSCLKをPLLに切替 →
SystemCoreClockを更新。
- USBの完全リセット:USBクロックを無効化 → 強制リセットを10ms以上保持 → リセット解除とクロック再有効化。
- USBスタックの再初期化:
USBD_DeInitとUSBD_Initシーケンスを呼び出す。
- オプション:ソフトディスコネクト:上記手順後も問題が発生する場合、USB再初期化前にDP(PA12)をプッシュプル出力に設定し、最低15ms間プルダウン → 初期化とプルアップを実行。
この順序で実行すれば、停止モードからの復帰後のUSB CDCの信頼性が大幅に向上するはずです。
「いいね!」 2