简介
因项目需要,需要使用USB组合设备实现两路虚拟串口并同时挂载虚拟U盘,rtthread目前默认只支持一路虚拟串口,现在需要增加一路虚拟串口。
测试环境
Rtthread版本:v4.1.0;
开发板:野火F407霸天虎V2;
计算机:windows11;
其他:串口调试助手、MobaXterm(作为shell终端)
已完成工作
新增一路CDC vcom,初步完成了2路虚拟串口的挂载,用USB连接开发板和计算机后,计算机能够识别出两路串口,并且虚拟U盘也能够使用;
遗留问题
串口调试助手打开串口时,存在卡死的情况;
两路串口无法同时正常使用,在开发板写的发送数据测试代码,总有一路串口启动后,计算机打开串口助手无法收到数据。
出现上面两个问题后,计算机显示USB断开,然后自动重新连接,但无法正常连接,显示无法找到设备描述符。
开发记录
前提条件
已经实现了USB组合设备配置模式下挂载虚拟串口和虚拟U盘。具体实现过程可参考其他开发者的文章,在此不做重复描述。
新增vcom
通过查看rt-thread源码,可知,各种USB设备的驱动代码位于rt-threadcomponentsdriversusbusbdeviceclass目录下,并且看到了虚拟串口设备驱动文件cdc_vcom.c以及其他各类如大容量存储设备mstorage.c(用于虚拟U盘)等。所以我的思路就是最简单直接暴力的方法,拷贝cdc_vcom.c并重命名为cdc_vcom2.c作为第二路虚拟串口驱动。
由于拷贝过来后避免编译错误,所以需要修改,主要对cdc_vcom2.c修改如下:
修改设备名
由于设备名必须是唯一的,以及存在了vcom,故这里重新命名为vcom2。
#define VCOM_DEVICE "vcom2" // vcom->vcom2
修改事件名和线程名
修改函数rt_usb_vcom_init内代码
rt_event_init(&data- >tx_event, “vcom2”, RT_IPC_FLAG_FIFO); // vcom- >vcom2
rt_thread_init(&vcom_thread, "vcom2", // vcom- >vcom2
vcom_tx_thread_entry, func,
vcom_thread_stack, VCOM_TASK_STK_SIZE,
16, 20);
- 修改注册函数
避免函数重复定义,修改注册函数部分,主要就是在函数名添加了后缀2,在文件最后代码,修改如下:
```c
struct udclass vcom_class2 =
{
.rt_usbd_function_create = rt_usbd_function_cdc_create2
};
int rt_usbd_vcom_class_register2(void)
{
rt_usbd_class_register(&vcom_class2);
return 0;
}
INIT_PREV_EXPORT(rt_usbd_vcom_class_register2);
#endif
编译并解决bug
编译后下载,运行后调试终端显示错误:
endpoint assign error
端点分配错误。
通过定位发现在文件usbdevice_core.c的rt_usbd_device_add_config()函数中报错:
进入函数rt_usbd_ep_assign()后通过调试发现,USB设备的端点列表无法有效分配给各接口设备,而端点列表的定义位于librariesHAL_Driversdrv_usbd.c,新增了一个虚拟串口后,需要在该列表中增加端点,修改如下:
static struct ep_id _ep_pool[] =
{
{0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED },
#ifdef BSP_USBD_EP_ISOC
{0x1, USB_EP_ATTR_ISOC, USB_DIR_IN, 64, ID_UNASSIGNED},
{0x1, USB_EP_ATTR_ISOC, USB_DIR_OUT, 64, ID_UNASSIGNED},
#else
{0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED},
{0x1, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED},
#endif
{0x2, USB_EP_ATTR_INT, USB_DIR_IN, 64, ID_UNASSIGNED},
{0x2, USB_EP_ATTR_INT, USB_DIR_OUT, 64, ID_UNASSIGNED},
{0x3, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED},
// 添加一个vcom, 需要2个BULK,1个INT
{0x5, USB_EP_ATTR_INT, USB_DIR_IN, 64, ID_UNASSIGNED},
{0x6, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED},
{0x6, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED},
#if !defined(SOC_SERIES_STM32F1)
{0x3, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED},
#endif
{0xFF, USB_EP_ATTR_TYPE_MASK, USB_DIR_MASK, 0, ID_ASSIGNED },
};
重新编译,下载,连接开发板,计算机正常识别出了两个串口:
目前就剩下前面所说的遗留问题了。
附录
串口测试代码
添加测试文件example_vcom.c,加入编译,启动后在调试终端输入如下命令即可:
$ cmd_vcom vcom # 测试虚拟串口1
$ cmd_vcom vcom2 # 测试虚拟串口2
代码如下:
#include
#include
#include
#ifdef RT_USING_ULOG
#define LOG_TAG "example_vcom"
#define LOG_LVL LOG_LVL_DBG
#include
#endif
int example_vcom(int argc, char *argv[]) {
if(argc<2){
return RT_ERROR;
}
rt_device_t dev = RT_NULL;
char buf[] = "hello rt-thread!rn";
dev = rt_device_find(argv[1]);
if (dev) {
LOG_I("open usb %s", argv[1]);
rt_device_open(dev, RT_DEVICE_FLAG_RDWR);
} else {
LOG_E("could not open vcom");
return -RT_ERROR;
}
for (int i = 0; i < 10; ++i) {
LOG_I("send %d", i);
rt_device_write(dev, 0, buf, rt_strlen(buf));
rt_thread_mdelay(500);
}
rt_device_close(dev);
return RT_EOK;
}
// MSH_CMD_EXPORT(example_vcom, USB Device vcom example)
MSH_CMD_EXPORT_ALIAS(example_vcom, cmd_vcom, USB Device vcom example)
评论
查看更多