さて、コンパイルも通ったことなので、もう少し真面目にコードをチェックしてみる。まずはSTM32F207VGのVCPのコードを見てみよう。app.cの中にmain()があるわけだが、その中身はこれだけである(List 1)。最初にUSBD_Init()を呼び出しただけで、後はLEDをぐるぐる点滅させるだけ、という代物だ。では通信はどうやっているか? というと、RS232Cの通信割り込みにあわせて割り込みルーチンが動き、そこで各々の処理を行う形になる。で、USBD_Init()は? というと、こちらはusbd_usr.cの中にある。余談だが、IAR Embedded Workbenchはこれを一発で探せる(Photo01)のが非常に便利である。

Photo01: 探したい定義名(変数でも関数でも何でもあり)を選択しておいて右クリックでコンテクストメニューを開くと、「定義に移動」があってすぐに見つかる。

そのUSBD_Init()はList 2の様になっている。ざっくり言えば、

(1) USB_OTG_BSP_Init()で、オンチップのUSB OTGドライバの初期化
(2) USBD_DeInit()でデバイスライブラリの再初期化
(3) 構造体に必要なパラメータを積む
(4) DCD_Init()でデバイスにパラメータを設定
(5) ユーザーCallbackの初期化ルーチンを起動
(6) USB_OTG_BSP_EnableInterrupt()を呼び出し、割り込みを許可=通信開始

といった流れになっている。このうち(2)に関しては、USBD_Init()の直後にコードがあるのだが、List 3の通り「何もしていない」ので無視していいのだろう。おそらくこれはソフトウェア的に何か初期化&再初期化の際に処理が必要な場合に定義を追加せよ、というためのStubと思われる。

さて、まず(1)。USB_OTG_BSP_Init()はusb_bsp.cに含まれている。これはSTM32のUSB Driverに属する部分で、デバイス別の設定になっている。今回の場合、STM32F207VG用とSTM32F4-Discovery用は殆ど同じながら若干異なる部分が散見される。このあたりはいじる必要はなさそうだ。(2)はそういうわけで無視するとして、ちょっと飛んで(6)。こちらもusb_bsp.cの中に含まれており、中身はSTM32F207VG用とSTM32F4-Discovery用で完全に一緒だった。このusb_bsp.cの中身はそういうわけで一切手をいれなくてもよさそう、という感じだ(最終的には動くまで判らないけど)。

ではいじらないといけないあたりはどこか? というと(3)~(5)の部分である。まずmain()からのUSBD_Initの呼び出しを見ると、

USBD_Init(&USB_OTG_dev,
#ifdef USE_USB_OTG_HS 
            USB_OTG_HS_CORE_ID,
#else            
            USB_OTG_FS_CORE_ID,
#endif  
            &USR_desc, 
            &USBD_CDC_cb, 
            &USR_cb);

となっている。最初のUSB_OTG_devはデバイスハンドルで。main()の手前でグローバル変数定義されている。次の引数はFS(Full-Speed:12Mbps)かHS(High-Speed:480Mbps)かの選択なので置いておくとして、続くUSB_desc/USB_CDC_cb/USR_cbの定義を見てみる。

USR_descはusbd_desc.cの中で定義されており、

USBD_DEVICE USR_desc =
{
  USBD_USR_DeviceDescriptor,
  USBD_USR_LangIDStrDescriptor, 
  USBD_USR_ManufacturerStrDescriptor,
  USBD_USR_ProductStrDescriptor,
  USBD_USR_SerialStrDescriptor,
  USBD_USR_ConfigStrDescriptor,
  USBD_USR_InterfaceStrDescriptor,

};

てな感じになっている。これはSTM32F207VG用もSTM32F4-Discovery用も同じで、名前から見るとUSB DeviceのDevice Descriptionと思われる。次のUSBD_CDC_cbは、usbd_cdc_core.cの中で定義されており、

USBD_Class_cb_TypeDef  USBD_CDC_cb = 
{
  usbd_cdc_Init,
  usbd_cdc_DeInit,
  usbd_cdc_Setup,
  NULL,                 /* EP0_TxSent, */
  usbd_cdc_EP0_RxReady,
  usbd_cdc_DataIn,
  usbd_cdc_DataOut,
  usbd_cdc_SOF,
  NULL,
  NULL,     
  USBD_cdc_GetCfgDesc,
#ifdef USE_USB_OTG_HS   
  USBD_cdc_GetOtherCfgDesc, /* use same cobfig as per FS */
#endif /* USE_USB_OTG_HS  */
};

といった具合だ。このUSBD_Class_cb_TypeDefの定義はusb_core.hの中にあり、

typedef struct _Device_cb
{
  uint8_t  (*Init)         (void *pdev , uint8_t cfgidx);
  uint8_t  (*DeInit)       (void *pdev , uint8_t cfgidx);
 /* Control Endpoints*/
  uint8_t  (*Setup)        (void *pdev , USB_SETUP_REQ  *req);  
  uint8_t  (*EP0_TxSent)   (void *pdev );    
  uint8_t  (*EP0_RxReady)  (void *pdev );  
  /* Class Specific Endpoints*/
  uint8_t  (*DataIn)       (void *pdev , uint8_t epnum);   
  uint8_t  (*DataOut)      (void *pdev , uint8_t epnum); 
  uint8_t  (*SOF)          (void *pdev); 
  uint8_t  (*IsoINIncomplete)  (void *pdev); 
  uint8_t  (*IsoOUTIncomplete)  (void *pdev);   

  uint8_t  *(*GetConfigDescriptor)( uint8_t speed , uint16_t *length); 
#ifdef USB_OTG_HS_CORE 
  uint8_t  *(*GetOtherConfigDescriptor)( uint8_t speed , uint16_t *length);   
#endif

#ifdef USB_SUPPORT_USER_STRING_DESC 
  uint8_t  *(*GetUsrStrDescriptor)( uint8_t speed ,uint8_t index,  uint16_t *length);   
#endif  

} USBD_Class_cb_TypeDef;

となっている。どうもこのなかに、コールバックルーチンを定義している様だ。

(続く)

List 1:

int main(void)
{
  __IO uint32_t i = 0;  

  /*!< At this stage the microcontroller clock setting is already configured, 
  this is done through SystemInit() function which is called from startup
  file (startup_stm32fxxx_xx.s) before to branch to application main.
  To reconfigure the default setting of SystemInit() function, refer to
  system_stm32fxxx.c file
  */  

  USBD_Init(&USB_OTG_dev,
#ifdef USE_USB_OTG_HS 
            USB_OTG_HS_CORE_ID,
#else            
            USB_OTG_FS_CORE_ID,
#endif  
            &USR_desc, 
            &USBD_CDC_cb, 
            &USR_cb);

  /* Main loop */
  while (1)
  {
    if (i++ == 0x100000)
    {
      STM_EVAL_LEDToggle(LED1);
      STM_EVAL_LEDToggle(LED2);
      STM_EVAL_LEDToggle(LED3);
      STM_EVAL_LEDToggle(LED4);
      i = 0;
    }
  }
}

List 2:

/**
* @brief  USBD_Init
*         Initailizes the device stack and load the class driver
* @param  pdev: device instance
* @param  core_address: USB OTG core ID
* @param  class_cb: Class callback structure address
* @param  usr_cb: User callback structure address
* @retval None
*/
void USBD_Init(USB_OTG_CORE_HANDLE *pdev,
               USB_OTG_CORE_ID_TypeDef coreID,
               USBD_DEVICE *pDevice,                  
               USBD_Class_cb_TypeDef *class_cb, 
               USBD_Usr_cb_TypeDef *usr_cb)
{
  /* Hardware Init */
  USB_OTG_BSP_Init(pdev);  

  USBD_DeInit(pdev);

  /*Register class and user callbacks */
  pdev->dev.class_cb = class_cb;
  pdev->dev.usr_cb = usr_cb;  
  pdev->dev.usr_device = pDevice;    

  /* set USB OTG core params */
  DCD_Init(pdev , coreID);

  /* Upon Init call usr callback */
  pdev->dev.usr_cb->Init();

  /* Enable Interrupts */
  USB_OTG_BSP_EnableInterrupt(pdev);
}

List 3:

USBD_Status USBD_DeInit(USB_OTG_CORE_HANDLE *pdev)
{
  /* Software Init */

  return USBD_OK;
}