本文将基于STM32F407VET芯片介绍如何在RT-Thread Studio开发环境下实现USB虚拟串口。
硬件及开发环境如下:
打开RT-Thread Studio软件新建基于芯片的项目,并使用外部时钟系统,具体参见《RT-Thread Studio学习(一)新建工程》。
RT-Thread Setting
中借助图形化配置工具打开组件中的USB设备的驱动框架,设置设备类型为Enable to use device as CDC device
,如下图所示:board.h
文件中使能宏定义#define BSP_USING_USBDEVICE
.\cubemx\Src\usbd_conf.c
中的函数HAL_PCD_MspInit
复制到board.c
的末尾。void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
#define HAL_PCD_MODULE_ENABLED
修改main.c
的代码,主要添加的代码如下:
/* 用于接收消息的信号量 */
static struct rt_semaphore rx_sem;
static rt_device_t serial;
//
/* 接收数据回调函数 */
static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
/* 串口接收到数据后产生中断,调用此回调函数,然后发送接收信号量 */
rt_sem_release(&rx_sem);
return RT_EOK;
}
static void serial_thread_entry(void *parameter)
{
char ch;
LOG_D("uart_rx_thread_entry runing..\n");
while (1)
{
/* 从串口读取一个字节的数据,没有读取到则等待接收信号量 */
while (rt_device_read(serial, -1, &ch, 1) != 1)
{
/* 阻塞等待接收信号量,等到信号量后再次读取数据 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
}
rt_kprintf("%c",ch);
/* 读取到的数据通过串口错位输出 */
rt_device_write(serial, 0, &ch, 1);
}
}
static int serial_init()
{
rt_err_t ret = RT_EOK;
/* 查找设备 */
serial = rt_device_find("vcom");
if (serial == RT_NULL)
{
rt_kprintf("can't find vcom device!\n");
return RT_ERROR;
}
ret = rt_device_init(serial);
if (serial == RT_NULL)
{
rt_kprintf("can't initialize vcom device!\n");
return RT_ERROR;
}
/* 打开设备,可读写,中断接收 */
ret = rt_device_open(serial, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
//初始化信号量
rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
// 设置接收回调函数
rt_device_set_rx_indicate(serial, uart_input);
rt_thread_t thread = rt_thread_create("serial", serial_thread_entry, RT_NULL, 1024, 25, 10);
if (thread == RT_NULL)
{
LOG_E("rt_thread_create failed...\n");
}
rt_thread_startup(thread);
return RT_EOK;
}
在主函数main中,添加
char buf2[64];
for(int i=0; i<64; i++)
buf2[i] = i;
rt_device_write(serial, 0, buf2, 64);
rt_thread_mdelay(1);
编译后下载,发现在串口调试助手中,能打开CDC串口并发送数据。发送数据不但能通过Bus Hound软件抓包到,而且STM32通过USART1调试口回传至PC,但调试助手的CDC串口却收不到任何数据。
原因在于STM32虚拟的串口为USB设备,PC端需主动“索取”才能获得rt_device_write发出的数据。
后面在PC端编写了python串口读取程序,才能接收到rt_device_write发出的数据,并且数据速率非常高,达到数百KBps。