目录
基于stm32的USB的CDC类虚拟串口(VCP)简单通讯F429验证
本文目标:基于stm32的USB的CDC类虚拟串口(VCP)简单通讯F429验证
按照本文的描述,跑通USB的CDC类来进行简单交互。
先决条件:拥有C语言基础,装有编译和集成的开发环境,比如:Keil uVision5
使用外设:USB、USART1、GPIO
HAL库版本:STM32F4xx HAL Driver version number 1.26.0
STMCubeMX版本:6.10.0
Keil uVision5版本:V5.38.0.0
实验目的
新手学习,在不久之前的F4的实验中,不能成功实现USB模拟串口的通讯实验,于是在网络上寻找了一下资料,结合自己的编程经验有了这篇文章,能够实现简单的USB串口通讯了。在本次实验中,将使用STM32作为从设备使用USB的CDC类虚拟串口(VCP)进行简单的通讯。
场景使用原理图
在我的这个应用场景中,原理图的内容如下:
基于以上的原理设计,使用usb接口进行接口通讯实验。USB相对来说是一个比较复杂的东西,涉及的东西挺多,学得也不是很深入,只能简单的跑通实验。
配置USB_OTG_FS
打开STMCubeMX工具,找到交互通讯,配置一下USB_OTG_FS
配置USB类型
找到USB_DEVICE,配置一下CDC的类型。
配置系统时钟
我这里还使用了一个freertos来进行实验,同时也配置了一个串口1来进行实验,
修改usbd_cdc_if.c函数
配置好底层之后,生成代码,主要是去修改这个接口配置函数
在usbd_cdc_if.c中,我修改了下面的函数
同时还新增了两个函数
// send data to pc
int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout)
{
if(CDC_Transmit_FS(datas, len) == USBD_OK)
{
if(g_xUSBUARTSend)
{
if (xSemaphoreTake(g_xUSBUARTSend, timeout) == pdTRUE)
{
return 0;
}
else
{
return -1; // timeout
}
}
}
return USBD_BUSY;
}
int ux_device_cdc_acm_getchar(uint8_t *pData, uint32_t timeout)
{
if (g_xUSBUART_RX_Queue)
{
if (pdTRUE == xQueueReceive(g_xUSBUART_RX_Queue, pData, timeout))
{
return 0;
}
else
{
return -1;
}
}
else
{
return -1;
}
}
我这么修改是因为,我想要用板子往pc端传数据,int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout)函数里面,调用CDC_Transmit_FS往pc传数据,而什么时候完成传输则由static int8_t CDC_TransmitCplt_FS(uint8_t *Buf, uint32_t *Len, uint8_t epnum)函数决定,发送完成函数中添加了信号量的用法。
而int ux_device_cdc_acm_getchar(uint8_t *pData, uint32_t timeout)这个函数中,则可以实现接收一个数据的用法。
上层调用
static void PrintTaskFunction( void *pvParameters )
{
char buf[100];
int cnt = 0;
if(g_xUSBUARTSend == NULL)
{
g_xUSBUARTSend = xSemaphoreCreateBinary();
}
while (1)
{
sprintf(buf, "printf Task Test : %d", cnt++);
int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);
ux_device_cdc_acm_send((uint8_t *)buf, strlen(buf), 1000);
// CDC_Transmit_FS((uint8_t *)buf, strlen(buf));
vTaskDelay(1000);
}
}
static void ReceiveTaskFunction( void *pvParameters )
{
uint8_t ch;
int err;
int cnt = 0;
char buf[100];
if(!g_xUSBUART_RX_Queue)
{
g_xUSBUART_RX_Queue = xQueueCreate(200, sizeof(uint8_t));
}
while (1)
{
int ux_device_cdc_acm_getchar(uint8_t *pData, uint32_t timeout);
err = ux_device_cdc_acm_getchar((uint8_t *)&ch, 1000);
if(err == 0)
{
sprintf(buf, "Recv Data : 0x%02x, Cnt : %d", ch, cnt++);
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)buf, strlen(buf));
}
vTaskDelay(1000);
}
}
xTaskCreate(
PrintTaskFunction, // 函数指针, 任务函数
"print_task", // 任务的名字
200, // 栈大小,单位为word,10表示40字节
NULL, // 调用任务函数时传入的参数
osPriorityNormal, // 优先级
NULL); // 任务句柄, 以后使用它来操作这个任务
xTaskCreate(
ReceiveTaskFunction, // 函数指针, 任务函数
"receive_task", // 任务的名字
200, // 栈大小,单位为word,10表示40字节
NULL, // 调用任务函数时传入的参数
osPriorityNormal, // 优先级
NULL); // 任务句柄, 以后使用它来操作这个任务
在上层的代码中,我这里简单粗暴创建3个任务来执行,一个PrintTaskFunction函数主要是板子往pc端发送数据,ReceiveTaskFunction函数板子接收pc发来的数据,然后使用HAL库的代码将数据往串口工具进行发送。
上机现象
烧写运行程序后,接上 USB 线,在电脑上可以识别出 USB 串口,查看设备管理器,可以看到如下设备:
打开两个串口助手来进行实验:
后记
工程调试起来挺费劲的,还是需要多实践。起初我创建队列和信号量是在usbd_cdc_if.c中的static int8_t CDC_Init_FS(void)函数里面,后来实战发现老是失败,问了一下AI和查了一下资料没能找到很好的定位到问题点,索性将创建信号量和创建队列的函数放在别的地方了。好在经过一番摸索之后,成功调通实验。
本文记录到此,算是自己工程的一次实践,本文完!!感谢阅读,感谢关注。
本文中使用的测试工程
https://download.csdn.net/download/weixin_44317448/89251468