STM32CubeMX系列教程25:USB Device
stm32F746系列芯片有USB_OTG_FS和USB_OTG_HS两种接口,FS为全速,速度12M Bit/s,HS为高速,最高速度为480M Bit/s,此时需要外接USB HS PHY,例如USB3300。HS接口也可以作为FS接口使用。由于FS和HS接口使用是相同的USB设备库,只是初始化时配置的引脚不一样,本章以FS接口为例讲解USB设备库的使用。以下为USB OTG FS的电路图:
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len) { /* USER CODE BEGIN 6 */ USBD_CDC_SetRxBuffer(hUsbDevice_0, &Buf[0]); USBD_CDC_ReceivePacket(hUsbDevice_0); return (USBD_OK); /* USER CODE END 6 */ }
修改接收处理函数,接收到的字符打印输出在LCD屏幕上。
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len) { /* USER CODE BEGIN 6 */ uint8_t result = USBD_OK; uint8_t msg[Len[0]+1]; uint32_t i; result = USBD_CDC_ReceivePacket(&hUsbDeviceFS); for(i=0;i<len[0];i++) {="" msg[i]="*" buf++;="" }="" ;="" printf("%s\n",msg);="" return="" result;="" *="" user="" code="" end="" 6="" }<="" pre=""></len[0];i++)>
如下为发送函数,程序中先设置发送字符,然后发送包。这里注意一点,Cube软件初始化的USB结构体是hUsbDeviceFS,这里操作的结构体是hUsbDevice_0。故这个函数不能直接调用,必须先CDC_Init_FS()函数初始化才能用这个函数,初始化中包含有这个语句hUsbDevice_0 = &hUsbDeviceFS。
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) { uint8_t result = USBD_OK; /* USER CODE BEGIN 7 */ USBD_CDC_SetTxBuffer(hUsbDevice_0, Buf, Len); result = USBD_CDC_TransmitPacket(hUsbDevice_0); /* USER CODE END 7 */ return result; }
本教程不调用这个发送函数。在main函数中while循环中添加语法每秒发送一次字符串。
/* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ USBD_CDC_SetTxBuffer(&hUsbDeviceFS, (uint8_t*)&UserTxBuffer, sizeof(UserTxBuffer)); USBD_CDC_TransmitPacket(&hUsbDeviceFS); HAL_Delay(1000); } /* USER CODE END 3 */
在前面声明发送字符串。
/* USER CODE BEGIN 1 */ uint8_t UserTxBuffer[] = "WaveShare Open7XXI-C Board STM32 Virtual COM Port Driver \r\n"; /* USER CODE END 1 */
在main函数中,while循环前面添加程式初始化LCD。
/* USER CODE BEGIN 2 */ /* Initialize the SDRAM */ BSP_SDRAM_Init(); /* Initialize the LCD */ BSP_LCD_Init(); BSP_LCD_SetLayerVisible(1, DISABLE); BSP_LCD_SelectLayer(0); /* Initialize LCD Log module */ LCD_LOG_Init(); /* Show Header and Footer texts */ LCD_LOG_SetHeader((uint8_t *)"Waveshare Electronics"); LCD_LOG_SetFooter((uint8_t *)"WaveShare Open7XXI-C board"); /* USER CODE END 2 */
最后添加usbd_cdc.h头文件。
/* USER CODE BEGIN Includes */ #include "stm32746g_sdram.h" #include "stm32746g_LCD.h" #include "lcd_log.h" #include "usbd_cdc.h" /* USER CODE END Includes */
最后编译程序,并下载到开发板,电脑usb线接到Open746I-C的核心板的USB接口中。打开串口助手会接到开发板发送的字符串,串口助手发送的字符会在LCD上显示。设置的串口传输格式是无效的,程序中没有设置串口传输格式,可以修改usbd_cdc_if.c文件的CDC_Control_FS()函数设置。
删掉原来的应用程序,重新编写应用程序。在main.c文件最后面添加应用程序。程序GetPointerData()为读取五向摇杆按键状态更新坐标,CURSOR_STEP为每次移动的步长,输入参数为当前的坐标位置。
/* USER CODE BEGIN 4 */ /** * @brief Gets Pointer Data. * @param pbuf: Pointer to report * @retval None */ static void GetPointerData(uint8_t *pbuf) { int8_t x = 0, y = 0 ; switch(BSP_JOY_GetState()) { case JOY_LEFT: x -= CURSOR_STEP; break; case JOY_RIGHT: x += CURSOR_STEP; break; case JOY_UP: y -= CURSOR_STEP; break; case JOY_DOWN: y += CURSOR_STEP; break; default: break; } pbuf[0] = 0; pbuf[1] = x; pbuf[2] = y; pbuf[3] = 0; } /** * @brief SYSTICK callback. * @retval None */ void HAL_SYSTICK_Callback(void) { /* NOTE : This function Should not be modified, when the callback is needed, the HAL_SYSTICK_Callback could be implemented in the user file */ static __IO uint32_t counter=0; /* check Joystick state every polling interval (10ms) */ if (counter++ == USBD_HID_GetPollingInterval(&hUsbDeviceFS)) { GetPointerData(HID_Buffer); /* send data though IN endpoint*/ if((HID_Buffer[1] != 0) || (HID_Buffer[2] != 0)) { USBD_HID_SendReport(&hUsbDeviceFS, HID_Buffer, 4); } counter =0; } BSP_LED_Toggle(LED1); } /* USER CODE END 4 */
HAL_SYSTICK_Callback()为SysTick定时器中断回调函数,时间为1ms。程序中先调用USBD_HID_GetPollingInterval函数读取HID轮询间隔。每隔10MS根据五向摇杆按键更新坐标,并通过USB发到电脑。
添加变量声明
/* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ #define CURSOR_STEP 5 uint8_t HID_Buffer[4]; /* USER CODE END PV */
添加usbd_hid.h头文件
/* USER CODE BEGIN Includes */ #include "stm32746g_sdram.h" #include "stm32746g_LCD.h" #include "lcd_log.h" #include "usbd_hid.h" /* USER CODE END Includes */
编译程序,并下载到开发板,电脑usb线接到Open746I-C的核心板的USB接口中。按五向遥控按键电脑上的鼠标会移动。
三、USB MSC
选择SDMMC接口为4线。
配置系统时钟频率为216MHZ,USB,SDMMC频率均为48MHz。
SDMMC添加收发DMA,其他为默认不作修改。
打开usbd_storage_if文件,我们只需修改三个函数既可,第一个是获取存储器容量大小函数,返回块大小,已经块数目。
/******************************************************************************* * Function Name : STORAGE_GetCapacity_FS * Description : * Input : None. * Output : None. * Return : None. *******************************************************************************/ int8_t STORAGE_GetCapacity_FS (uint8_t lun, uint32_t *block_num, uint16_t *block_size) { /* USER CODE BEGIN 3 */ HAL_SD_CardInfoTypedef info; int8_t ret = USBD_FAIL; // if(BSP_SD_IsDetected() != SD_NOT_PRESENT) { HAL_SD_Get_CardInfo(&hsd1, &info); *block_num = (info.CardCapacity)/STORAGE_BLK_SIZ - 1; *block_size = info.CardBlockSize; ret = USBD_OK; } return ret; /* USER CODE END 3 */ }
第二个是块读取函数,SD卡是通过DMA传输数据。
/******************************************************************************* * Function Name : STORAGE_Read_FS * Description : * Input : None. * Output : None. * Return : None. *******************************************************************************/ int8_t STORAGE_Read_FS (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 6 */ int8_t ret = USBD_FAIL; // if(BSP_SD_IsDetected() != SD_NOT_PRESENT) { /* Read block(s) in DMA transfer mode */ if(HAL_SD_ReadBlocks_DMA(&hsd1,(uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len) == SD_OK) { ret = USBD_OK; } /* Wait until transfer is complete */ if(ret == USBD_OK) { if(HAL_SD_CheckReadOperation(&hsd1, (uint32_t)100000000) != SD_OK) { ret = USBD_FAIL; } else { ret = USBD_OK; } } ret = USBD_OK; } return ret; /* USER CODE END 6 */ }
第三个是写操作函数
/******************************************************************************* * Function Name : STORAGE_Write_FS * Description : * Input : None. * Output : None. * Return : None. *******************************************************************************/ int8_t STORAGE_Write_FS (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 7 */ int8_t ret = USBD_FAIL; // if(BSP_SD_IsDetected() != SD_NOT_PRESENT) { /* Write block(s) in DMA transfer mode */ if(HAL_SD_WriteBlocks_DMA(&hsd1, (uint32_t *)buf, blk_addr * STORAGE_BLK_SIZ, STORAGE_BLK_SIZ, blk_len) == SD_OK) { ret = USBD_OK; } /* Wait until transfer is complete */ if(ret == USBD_OK) { if(HAL_SD_CheckWriteOperation(&hsd1, (uint32_t)100000000) != SD_OK) { ret = USBD_FAIL; } else { ret = USBD_OK; } } } return ret; /* USER CODE END 7 */ }
注意:Open746I-C中可以通过PC13管脚判断SD卡是否插入到卡槽中,这里为了方便没有用到这个功能。所以上面程序中注释掉了检测SD卡是否准备好这一步。