[STM32メモ]uVisionでSTM32CubeMXのUSBミドルウェアを利用する

Keil社はSTM32F0,L0,G0マイコン向けにMDK(統合開発環境やソフトウェアパッケージ群一式)をフリーで提供してくれている。ゼロから何でも作れる人でない限り、こうした環境とパッケージの一式があることは助かる。

ただ、STM32CubeMXが提供するUSBミドルウェアをuVisionで使おうとしたところで嵌まってしまったので、調べたことをまとめる。

#つまづいたこと
STM32向けMDKはいくつかのソフトウェアパッケージが利用できない。例えばUSBデバイスのミドルウェア群は"Manage Run-Time Environment"の画面から選択することはできるが、コンパイルしようとすると以下の様なエラーメッセージが出てしまう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/7405f27b-058c-a3cf-5ab7-a6c3fa7304ee.png)
> Error #603: 'Keil.MDK-Plus::USB:CORE:Release:6.13.0': component requires valid MDK-ARM Plus or MDK-ARM Professional license

そこで代わりにSTM32CubeMXが提供するUSBミドルウェアを使おうとしたところ、設定もコード生成もできるのに、ビルドには失敗することが分かった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/4077f2b2-5318-6ec3-f6ed-e74d59a69bab.png)
>.\RTE\Device\STM32F072RBTx\STCubeGenerated\Inc\usb_device.h(33): error:  #5: cannot open source input file "usbd_def.h": No such file or directory

#原因: 必要なファイル・パスが自動で追加されないため
MDKのドキュメントに書かれているように、MDKが提供するソフトウァアパッケージ(CMSIS-Driver)はSTM32CubeMXが生成するHAL(Hardware Abstraction Layer)を利用するため、STM32CubeMXが生成したHALは自動的にuVisionプロジェクトに追加される。一方で、MDKが関知しないSTM32CubeMXのミドルウェアはuVisionプロジェクトに追加されないことが分かった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/f146c926-e1a2-4590-a4aa-b158db5c18a7.png)

例えばUSBデバイスのミドルウェアの場合、STCubeGenerated以下に生成されるUSB関連のミドルウェアやユーザカスタムのコードやインクルードパスが必要だが、元のuVisionプロジェクトに追加されない。そのために、"usbd_def.h"が無いというエラーにつながった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/fc513dfb-ec4e-5da8-c1b2-8ccefe74a075.png)

#対策: 手動でファイル・パスを追加する
STM32CubeMXが生成したuVisionプロジェクト`STCubeGenerated.uvprojx`には、以下のように元のuVisionプロジェクトにないUSBミドルウェア関連のファイル`usbd_*.c`が含まれている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/6bc5221d-bb2d-c86e-6804-6bd67a54c1ad.png)
これを参考に、元のuVisionプロジェクトに以下の8ファイルを追加する。

* プロジェクトルート\RTE\Device\STM32F072RBTx\STCubeGenerated\Middlewares\ST\STM32_USB_Device_Library\Class\Core\Src
 * usbd_core.c
 * usbd_ctlreq.c
 * usbd_ioreq.c
* プロジェクトルート\RTE\Device\STM32F072RBTx\STCubeGenerated\Middlewares\ST\STM32_USB_Device_Library\Class\CDC\Src
 * usbd_cdc.c
* プロジェクトルート\RTE\Device\STM32F072RBTx\STCubeGenerated\Src
 * usb_device.c
 * usbd_conf.c
 * usbd_desc.c
 * usbd_cdc_if.c

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/a0fe376e-c4ea-a15b-b0f9-2114747f30a2.png)

更に`usbd_*.c`から参照されるヘッダを探索できるよう、USBミドルウェアのヘッダが格納されたパスをインクルードパスに追加する。

* プロジェクトルート\RTE\Device\STM32F072RBTx\STCubeGenerated\Middlewares\ST\STM32_USB_Device_Library\Class\Core\Inc
* プロジェクトルート\RTE\Device\STM32F072RBTx\STCubeGenerated\Middlewares\ST\STM32_USB_Device_Library\Class\CDC\Inc
* プロジェクトルート\RTE\Device\STM32F072RBTx\STCubeGenerated\Inc

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/269967ce-6339-a396-15d8-a90aab513942.png)

これらの設定をすることで、元のuVisionプロジェクトもビルドできるようになる。

#注意
STM32CubeMXのUSBミドルウェアを使用する場合、以下に示すCMSIS-DriverのUSB Device(API)を追加してはならない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/393086dd-c02e-628b-e2a0-ddc3d58d9684.png)

追加すると、CMSIS-DriverとSTM32CubeMXで同名の関数(HAL関連)を定義してるためにリンカエラーが発生する。

```
linking...
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_ConnectCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_DataInStageCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_DataOutStageCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_DisconnectCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_ISOINIncompleteCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_ISOOUTIncompleteCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_ResetCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_ResumeCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_SetupStageCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
.\Objects\mdk.axf: Error: L6200E: Symbol HAL_PCD_SuspendCallback multiply defined (by usbd_stm32f0xx.o and usbd_conf.o).
Not enough information to list image symbols.
Not enough information to list load addresses in the image map.
Finished: 2 information, 0 warning and 10 error messages.
".\Objects\mdk.axf" - 10 Error(s), 18 Warning(s).
```

#まとめ
Keil社のMDKは、ソフトウェアパッケージに必要なHAL・main・startupなどをSTM32CubeMXで生成したら、uVisionプロジェクトに追加してくれる。一方で、MDKと無関係にSTM32CubeMXで生成したコード(USBデバイスのミドルウェアなど)には関知しないので、手動でuVisionプロジェクトに追加する必要がある。

#参考: MDKのドキュメント
MDKの"Manage Run-Time Environment"からDeviceを選択し、STM32Cube Frameworkの説明へのリンクを開くと、MDKとSTM23CubeMXの関係について説明を読むことができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/296170/c09ccc1a-3bdc-ed4b-ea84-d1dc73462c3c.png)