2018年3月3日土曜日

STM32F4 USART1の送信でUSBが切断される問題

 とりあえず、原因と対症療法がわかったので、メモしておきます。


 まず原因ですが、STM32F405xx/STM32F407xx Datasheetによると、USART1_TXで使っているPA9には、OTG_FS_VBUSという機能が割り当てられています。これは、USBの5Vをセンスすることにより、USBが接続されているか否かを判断するための入力ピンです。USBメモリ等はUSBを接続しないと電源が入りませんが、プリンタ等の外部電源で動作するものはUSBの接続如何にかかわらず電源が入っていますから、USBの挿抜状態を知るための方法が必要になります。
 で、STM32F4のUSBには「VBUSセンスを無効化する」というオプションがあります。というよりは、CubeはデフォルトでVBUSセンスを行いません。しかし、実際にはPA9をVBUSセンスに使おうが使うまいが、Cubeから出力されるコードは(PA9の初期化を除けば)全く同じコードが出力されています。つまり、VBUSセンスON/OFFレジスタは操作していません。
 レジスタの初期値は0ですが、0の場合はVBUSセンスを行い、1の場合はVBUSセンスを行いません。つまり、レジスタの操作を行わない=VBUSセンスを行う、ということです。
 そのままで初期化を行うと、マイコンは当然VBUSを監視します。しかし、すでにそのポートはUSART1_TXで初期化されていますから、外部電圧を知ることはできません。また、PA9とVBUSの接続もありません。
 この状態でVBUSを読み取ると、通常の状態はUARTのリセット値であるHighが読み出されます。Highを読み出すと「電圧がかかっているぞ。じゃぁUSBが接続されているんだな」ということを認識してマイコンはUSB接続を行います。一方、USART1_TXから何らかの値を出すと、ビット0を出すたびにPA9がLowに駆動され、VBUSもLowになりますから、マイコンはUSBが切断されたと認識し、USBの切断処理を行い、それによりホスト側もUSBが切断されたと認識するわけです。

 USB Full Speed/High SpeedはUSB D+をプルアップすることによりホスト側にUSBデバイスが存在することを通知しますが、USBデバイスの存在を通知したくない時はD+を開放すれば、ホスト側のプルダウンによりLowに固定されます。
 STMはVBUSがLowになるとD+を開放し、VBUSがHighになるとD+をプルアップします。ということで、VBUSに十分低速なデジタル波形が流れると、それがそのままD+に出力されてしまいます。それが前回の「USART1_TXがUSB D+に漏れたような」波形というわけです。


 さて、対症療法ですが、MX_USB_DEVICE_Init();の後でUSB_OTG_FS->GCCFG |= USB_OTG_GCCFG_NOVBUSSENS;によりVBUSセンスを停止することができます。

 今のところバスパワーでしか使っていませんが、セルフパワーで動かしても、ホストからのリセット信号が来ない限りはUSBは動きませんし、一定時間ホストから通信がなければ切断されたと認識するはずなので、VBUSセンスを行わなくても問題ないかな、と思っています。VBUSセンスだけを使うと、ACアダプタやモバイルバッテリーのような給電機能しか持たないホストに接続した際に問題が起きますから、あまり強くVBUSセンスには依存していないはずです。じゃぁそもそもVBUSセンスいらねーじゃん。という話になってしまうんですが。。。

 ちなみに、Nucleoとかの回路図を見てみると、PA9はUSB V+に10kOhm程度で接続しているようです。PA9は5Vトレラントなので、5Vを直結しても問題ありません。ただ、サージとかの保護を兼ねて10kOhmを挟んでいるようです。
 また、PA10はUSB IDに直結しています。ここはプルアップで固定し、必要に応じてGNDに地絡するだけで、配線長も自分のUSBコネクタ止まりですから、あまり保護はしなくて良さそうです。
 ということで、PA9をVBUSに接続し、必要に応じて(OTGを使うなら)PA10をUSB IDに接続しておく、という感じで使うのが良いようです。USART1はPB6/7に割り当てられるので、I2CやCANとの兼ね合いもありますが、PA9/10は出来る限り使わないのが良いのかもしれません。


 あと、なぜか唐突にHSIの初期化コードがCubeの出力から消えました。HSIの初期化コードが出力される問題は、STBee F4miniを買った当初に散々悩まされた問題です。クロック48MHzの設定を適切にやったからかな、とも思いましたが、たしか昨日48MHzを設定した時点ではHSIのコードは残っていたはずだし、何が原因かわかりません。
 今のところHSEしか使っていないので、HSIを殺すコードは有ってじゃまになるものでもないので、しばらくは残しておこうと思います。


 ということで、とりあえずUSB CDC VCPが最低限動くようになりました。あとはFreeRTOSのQueueとかを使って送受信できるようにして、それをシステムコールに渡したりできるようになれば、USB1本でDFU書き込みとprintfデバッグができるようになります。
 MSCとCDCを併用できればmbed的な使い方ができて面白いかなーと思ったんですが、Cubeではそういうコードは出せないようです。そういうのが必要なら自分でいろいろ書く必要があるみたいですね。
 STM32F4のFlashの512KiBくらいをストレージとして、MSCでLuaスクリプトファイル投げたらそれを実行できる、みたいな仕組みがあれば便利かなーと思ったんですが。スクリプトファイルにこだわらないなら、CDC経由でテキストデータを投げたり、Vimを動かして、というのでもいいですが、それはそれで面倒な感じ。

0 件のコメント:

コメントを投稿