簡(jiǎn)介
因項(xiàng)目需要,需要使用USB組合設(shè)備實(shí)現(xiàn)兩路虛擬串口并同時(shí)掛載虛擬U盤,rtthread目前默認(rèn)只支持一路虛擬串口,現(xiàn)在需要增加一路虛擬串口。
測(cè)試環(huán)境
Rtthread版本:v4.1.0;
開發(fā)板:野火F407霸天虎V2;
計(jì)算機(jī):windows11;
其他:串口調(diào)試助手、MobaXterm(作為shell終端)
已完成工作
新增一路CDC vcom,初步完成了2路虛擬串口的掛載,用USB連接開發(fā)板和計(jì)算機(jī)后,計(jì)算機(jī)能夠識(shí)別出兩路串口,并且虛擬U盤也能夠使用;
遺留問題
串口調(diào)試助手打開串口時(shí),存在卡死的情況;
兩路串口無法同時(shí)正常使用,在開發(fā)板寫的發(fā)送數(shù)據(jù)測(cè)試代碼,總有一路串口啟動(dòng)后,計(jì)算機(jī)打開串口助手無法收到數(shù)據(jù)。
出現(xiàn)上面兩個(gè)問題后,計(jì)算機(jī)顯示USB斷開,然后自動(dòng)重新連接,但無法正常連接,顯示無法找到設(shè)備描述符。
開發(fā)記錄
前提條件
已經(jīng)實(shí)現(xiàn)了USB組合設(shè)備配置模式下掛載虛擬串口和虛擬U盤。具體實(shí)現(xiàn)過程可參考其他開發(fā)者的文章,在此不做重復(fù)描述。
新增vcom
通過查看rt-thread源碼,可知,各種USB設(shè)備的驅(qū)動(dòng)代碼位于rt-threadcomponentsdriversusbusbdeviceclass目錄下,并且看到了虛擬串口設(shè)備驅(qū)動(dòng)文件cdc_vcom.c以及其他各類如大容量存儲(chǔ)設(shè)備mstorage.c(用于虛擬U盤)等。所以我的思路就是最簡(jiǎn)單直接暴力的方法,拷貝cdc_vcom.c并重命名為cdc_vcom2.c作為第二路虛擬串口驅(qū)動(dòng)。
由于拷貝過來后避免編譯錯(cuò)誤,所以需要修改,主要對(duì)cdc_vcom2.c修改如下:
修改設(shè)備名
由于設(shè)備名必須是唯一的,以及存在了vcom,故這里重新命名為vcom2。
#define VCOM_DEVICE "vcom2" // vcom->vcom2
修改事件名和線程名
修改函數(shù)rt_usb_vcom_init內(nèi)代碼
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);
- 修改注冊(cè)函數(shù)
避免函數(shù)重復(fù)定義,修改注冊(cè)函數(shù)部分,主要就是在函數(shù)名添加了后綴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
編譯后下載,運(yùn)行后調(diào)試終端顯示錯(cuò)誤:
endpoint assign error
端點(diǎn)分配錯(cuò)誤。
通過定位發(fā)現(xiàn)在文件usbdevice_core.c的rt_usbd_device_add_config()函數(shù)中報(bào)錯(cuò):
進(jìn)入函數(shù)rt_usbd_ep_assign()后通過調(diào)試發(fā)現(xiàn),USB設(shè)備的端點(diǎn)列表無法有效分配給各接口設(shè)備,而端點(diǎn)列表的定義位于librariesHAL_Driversdrv_usbd.c,新增了一個(gè)虛擬串口后,需要在該列表中增加端點(diǎn),修改如下:
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},
// 添加一個(gè)vcom, 需要2個(gè)BULK,1個(gè)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 },
};
重新編譯,下載,連接開發(fā)板,計(jì)算機(jī)正常識(shí)別出了兩個(gè)串口:
目前就剩下前面所說的遺留問題了。
附錄
串口測(cè)試代碼
添加測(cè)試文件example_vcom.c,加入編譯,啟動(dòng)后在調(diào)試終端輸入如下命令即可:
$ cmd_vcom vcom # 測(cè)試虛擬串口1
$ cmd_vcom vcom2 # 測(cè)試虛擬串口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)
評(píng)論
查看更多