[STM32メモ] Virtual COM Portを取り敢えず動かす

STM32マイコンは10年以上前に触って以来ご無沙汰だったが、最近では開発環境がかなり進歩しているようだったので、再入門してみた。

#ハード
USBとCANをつなぎたかったので、両方のインターフェースを同時に使うことができる & [Keilの開発環境(OSとか各種ライブラリ)がフリーで使える](https://www2.keil.com/stmicroelectronics-stm32/mdk)STM32F072を搭載した安価なマイコンボード「[NUCLEO-F072RB](https://www.stmcu.jp/design/hwdevelop/nucleo/51860/)」を使うことにした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/df6dc955-5d10-ba56-fd24-a61391d89a5b.png)

ただ、この基板のUSBコネクタはオンボードのST-LINKにつながっているだけで、ターゲットマイコンのUSBにはつながっていない。USBの機能を試すためには別途保護素子とUSBコネクタが必要になる([AN4879: USBデザインガイド： STM32MCUを使用したUSBハードウェアとPCBガイドライン](https://www.stmcu.jp/design/document/application_note/62520/))。とはいえ、サージを保護するダイオードを付けることが推奨されている位なので、実験程度なら必要ないのかもしれない。

##回路
保護素子を付けないなら直接USBに繋ぐだけで良い。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/ca04c7b2-e21a-bb67-2fb9-12f70eadfa65.png)
保護素子を付けるなら、電源と信号ラインにそれぞれダイオードを付ける。(D1は信号ラインのかと電圧をVBUSまたはGNDに逃がし、D2はVBUSの過電圧を抑える)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/c40264e3-47af-5360-9103-1794a0c826ee.png)

##基板
[Fusion PCBで作った基板](https://github.com/stm32p103/pcb-practice/tree/master/nucleo)は右側にUSBコネクタ(Mini-B)があり、そこから保護素子を介してNUCLEO-F072RB基板のUSBのDP,DMポートにUSBの信号を接続している。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/84d87fb7-1e50-1929-ca48-d0324b8122b9.png)


#Keil uVisionプロジェクトの設定
Keilの開発環境uVisionは、STM32F0, L0向け限定で容量制限等のないフリーの開発環境を提供されている。インストール・アクティベーション後、プロジェクトを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/969777d4-b81c-0632-519f-582af6fa4fe8.png)

ターゲットデバイスを検索し、NUCLEO-F072RBに搭載されているSTM32F072RBT6に対応したでバスSTM32F072RBTxを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/94f1bcd7-936c-1d7d-69b8-d6263b6071e1.png)

RTE(Run Time Environment)として、とりあえずUSB Deviceだけを選択する。(選択したライブラが依存する別のライブラリがあれば、Validation Outputに警告が表示される。その時はResolveを選択すると必要なものが自動的に追加できる)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/18d6e9ab-911a-5562-ad8d-7e881b6c07f0.png)

#STM32CubeMXの設定
uVisionでドライバを選択すると、STM32CubeMXとの連携して必要な設定ができるようになっている。(自動で色々と設定してくれるので便利な反面、勝手に色々設定されるのが嫌な人がいるという話も…)

とりあえずVCP(Virtual COM Port)を動かすだけなら、以下の設定でOKだった。複合デバイスなら時前でUSBのドライバを操作する必要があるものの、HIDやVCPのような決まった使い方なら必要な箇所だけ実装すれば良いよう枠組みが用意されているようだった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/ee49ed1e-5c84-8fe8-419a-2cce64fe35a3.png)

クロックの設定はUSB Fullspeedに必要な48MHzを供給する必要があるが、STM32F072であればUSBデバイス(≠ホスト)向けに内部発振HSI48 RCが使用できるので、水晶がいらない。最近のマイコンは便利になっていて助かる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/30f5ba1b-f028-5844-0c86-c7b53f81b49e.png)

#VCPのミドルウェアを使う
VCPはCDC(Communication Class Device)の一種で、STM32CubeMXがミドルウェアとして提供してくれる。ミドルウェアからユーザカスタムのコード`usbd_cdc_if.c`の4つの関数`Init`, `DeInit`, `Control_FS`, `Receive_FS`が呼ばれるので、テンプレートコードに必要なことを付け加えるだけで、VCPとして機能するうようになっている。

##エコーバックするだけのコード
`usbd_cdc_if.c`で定義されている4つの一つである`CDC_Receive_FS`は、仮引数に受信データのバッファと受信データ長が与えられてコールされる。

この関数の中で受信したデータをそのまま`CDC_Transmitt_FS`で送り返せばエコーバックになる。
たった1行追加するだけで、とりあえずCOMポートで送った文字がそのまま返ってくるようになった。

```C 
/**
  * @brief  Data received over USB OUT endpoint are sent over CDC interface
  *         through this function.
  *
  *         @note
  *         This function will block any OUT packet reception on USB endpoint
  *         untill exiting this function. If you exit this function before transfer
  *         is complete on CDC interface (ie. using DMA controller) it will result
  *         in receiving more data while previous ones are still not sent.
  *
  * @param  Buf: Buffer of data to be received
  * @param  Len: Number of data received (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);

  CDC_Transmit_FS( &Buf[0], *Len); /* エコーバック */

  return (USBD_OK);
  /* USER CODE END 6 */
}
```

##エコーバックの動作確認
TeraTermで動作確認をしたところ、地味ではあるが打った文字がそのまま返ってくることが確認できた。(エコーバックが無ければ、打った文字すら表示されない)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/915b456c-dea4-4e65-1c89-5661d550bc1a.png)

#参考
* [AN4879: USBデザインガイド： STM32MCUを使用したUSBハードウェアとPCBガイドライン](https://www.stmcu.jp/design/document/application_note/62520/)
* [AN1734: STM32Cube USB device library](https://www.stmcu.jp/design/document/users_manual/53066/)
* [Keil: MDK for STM32F0, STM32G0, and STM32L0](https://www2.keil.com/stmicroelectronics-stm32/mdk)
* [基板とソフト](https://github.com/stm32p103/pcb-practice/tree/master/nucleo)