完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
官方库例程: ..STM32CubeRepositorySTM32Cube_FW_F4_V1.23.0ProjectsSTM324xG_EVALApplicationsUSB_DeviceCDC_StandaloneMDK-ARM 环境: STM32CubeMX STM32F429IGT6 STlink 首先要确保硬件电路USB部分没问题;USB相关的概念知识大概需要了解一下,网上挺多这类文章的,自行百度。 STM32CubeMX 配置: 点击USB_OTG_FS,模式选择Device_Only,其他保持默认。 点击USB_DEVICE,选择IP 为VPC(虚拟串口),其他保持默认。 我使用的芯片是F429IGT6,最大时钟180MHz,但是USB时钟必须为48MHz(详情看STM32中文参考手册930页),180MHz是分频不出来48MHz的USB时钟,所以把系统配置成168MHz就能分频出48MHz的USB时钟。 堆空间需要改大一点,不然在USB插入电脑的时候,设备管理器会显示虚拟串口设备黄色感叹号。 因为在USB插入电脑,STM32会创建一个实例,malloc申请内存,但内存不足的时候就失败了,驱动工作不正常了。 造成这一现象的原因:源码在u***d_cdc.c中: pdev->pClassData = USBD_malloc(sizeof (USBD_CDC_HandleTypeDef)); 如果pdev->pClassData = NULL 即出现设备黄色感叹号。 最后生成代码即可,打开工程。 图中Application/User文件夹中多了几个文件: u***_device.czhi只有一个USB设备函数初始化函数 MX_USB_DEVICE_Init()。 u***_conf.c是USB协议参数、IO初始化、中断回调函数、端点打开关闭停止操作等等函数。 u***d_cdc_if.c有虚拟串口的接收和发送等函数。 u***_desc.c有USB的描述符和USB枚举处理等。 文件夹Middlewares/USB_Device_Library是STM32Cube库。 检测VCP连接状态 在中断函数OTG_FS_IRQHandler HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); HAL_PCD_ConnectCallback(hpcd); //连接事件回调 HAL_PCD_DisconnectCallback(hpcd); //断开事件回调 发现两个连接状态事件回调函数,但是经过测试发现这两个回调函数根本不会发生。具体往下就没去深究了。 USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef *pdev) { /* Free Class Resources */ pdev->dev_state = USBD_STATE_DEFAULT; pdev->pClass->DeInit(pdev, pdev->dev_config); return USBD_OK; } void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) { USBD_LL_DevDisconnected((USBD_HandleTypeDef*)hpcd->pData); } 但是偶然发现断开事件有改变一个变量pdev->dev_state = USBD_STATE_DEFAULT; 所以发现hU***DeviceFS.dev_state的状态才是真正的连接状态标志位。 /* Device Status */ #define USBD_STATE_DEFAULT 1 //初始化状态 #define USBD_STATE_ADDRESSED 2 //建立地址 #define USBD_STATE_CONFIGURED 3 //配置完成,连接成功 #define USBD_STATE_SUSPENDED 4 //u***挂起,断开成功 检测USB状态的函数 void VCP_Status(void) { static uint8_t old_status = 0; if(hU***DeviceFS.dev_state != old_status) { if(hU***DeviceFS.dev_state == USBD_STATE_CONFIGURED) printf("连接成功rn"); else if (hU***DeviceFS.dev_state == USBD_STATE_SUSPENDED) printf("断开成功rn"); old_status = hU***DeviceFS.dev_state; } } 打印函数 写一个u***_printf打印函数,在u***d_cdc_if.c里面末尾USER CODE BEGIN及USER CODE END之间添加 /* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */ #include void u***_printf(const char *format, ...) { va_list args; uint32_t length; va_start(args, format); length = vsnprintf((char *)UserTxBufferFS, APP_TX_DATA_SIZE, (char *)format, args); va_end(args); CDC_Transmit_FS(UserTxBufferFS, length); } /* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */ 关于接收有几点注意事项,认真看看。 1、u***虚拟串口每次接收最大的数据包ReceivePacket是64个字节;且每包数据以末尾追加 rn 表示一包数据接收完整。 当包长小于64个字节的时候 修改USB接收打印函数,在u***d_cdc_if.c里先找到static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)这个函数, 把它改成下图类似: PC的u***虚拟串口收到MCU的数据通过UARTx发送回PC。演示一下发现什么问题: //Len是每包数据的有效数据长度。值不会超过64 static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) { USBD_CDC_ReceivePacket(&hU***DeviceFS); printf("%srn", Buf); memset(Buf, 0, APP_RX_DATA_SIZE); return (USBD_OK); } 可以看到第一次发送内容 01 >>> MUC收到的数据是:01 81 7F 04 0D 0A 。数据异常,多了中间的 81 7F 04。 可以看到第二次发送内容 01 02 03 04 >>> MUC收到的数据是:01 02 03 04 0D 0A 。数据正常。 注意static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)的 *Len 是每包数据真实有效长度; 如 第一次发送的内容,*Len是等于1的; 第二次发送的内容,*Len是等于4的; *Len最大不会超过64。 出现第一种情况的原因是:当包长小于64个字节的时候,数据会强制32bit对齐。也就是发的字节必须要是4的倍数,不够补够4的倍数,最后末尾加上rn表示包的完整。 为什么会32位对齐,下面是STM32的源码: typedef struct { uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE/4]; /* Force 32bits alignment */ ... ... } USBD_CDC_HandleTypeDef; 当包长大于64个字节的时候 发现一共发送了306个字节。但是MCU收到了318个字节。318怎么来的? 其实每64个字节加rn2个字节,最后一包数据不够4倍数再补充够4的倍数。 306 / 64 = 4......50 4 * 13 >= 50 也就是4 * (64 + rn) + 52个字节 + rn = 318。 小结一下: 1、当数据小于等于64字节,那么进行4字节对齐,故真实长度以(*Len)为准。评论说加' |