Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
USB
瑞萨_RA6M4
基于RT-Thread+RA6M4的USB设备驱动适配
发布于 2022-07-31 21:17:57 浏览:2359
订阅该版
[tocm] #**基于RT-Thread + RA6M4的 USB 设备驱动适配** # 应用背景 rt-thread 自己实现了一套usb 协议栈,放在了component组件里,协议栈有一个core线程负责处理枚举等usb事件,上层是不同的类接口。只要实现了drv_usbd.c/ drv_usbh.c 就可以快速地开发USB设备或者主机应用,同时还生成了rt_device设备,方便管理使用。但是这个协议栈开发时应该是基于STM32做测试实现的,对其他MCU平台的适配做的并不多。在其他平台上准备使用这套usb 协议栈,开发就必须先适配,然后享受其带来的方便。 本文在 RA6M4 硬件平台上尝试初步匹配 rt-thread usb stack ,一方面准备比赛和BSP Pr, 另一方面也希望为小伙伴适配其他平台做一下参考。 # 实现功能 这次主要实现一个CDC类设备,这个比较有代表性,其他类后续再做测试。在Library/driver添加了一个drv_usbd.c接口文件,基本实现了 usb_device_core.c 和 cdc_vcom.c无需改动,就可以使用。 # 系统框架 ## 硬件框架 硬件比较简单,调试需要,外接了一个USB总线分析仪。 ![image-20220731104940398.png](https://oss-club.rt-thread.org/uploads/20220731/d6f1712fcc422a92d787a2aeba926da1.png.webp "image-20220731104940398.png") ## 软件框架 一开始打算参考自带的 threadx os 架构,虽然基本搞懂了,但是如果使用他们的API, 就需要提供OS 兼容层了。 改了一些,最后放弃了,搞的太复杂了! ![Renesas USB OS.png](https://oss-club.rt-thread.org/uploads/20220731/f6854670f77dfdbc0746ab9d232fe0b6.png.webp "Renesas USB OS.png") 最终选用 Renesas Bare USB + RT-Thread USB 放在一起实现, 1. R_UB_Open 会初始化USB 外设, 同时该函数也会注册配置,接口,描述符,并根据它们配置一些寄存器(主要是端点类的)。 2. rt-thread USB 配置的描述符要和 Renesas的保持一致 3. USB FS 产生中断,更新到全局数组g_usb_pstd_usb_int中,通过函数 R_USB_EventGet ->usb_pstd_pcd_task -> usb_pstd_interruper最终在r_usb_pdriver.c 中处理 4. 把Demo 中轮询R_USB_EventGet ,改成在一个usbd_event_thread 线程中调用处理,该线程在drv_usbd.c中初始化,调用rt_device_register后启动 5. 在usb_pstd_interruper中设备枚举和数据传输的中断会被替换成rt-thread usb 进行处理、 > 这只是个初始混合版本,只是为了方便一步步调试,搞清楚流程,后面会精简优化,把Renesas 这边除了寄存器访问的全部排除构建。 ![RA6M4_USB.png](https://oss-club.rt-thread.org/uploads/20220731/9daf01ab2f3ea91393bf097b3b5ffe75.png.webp "RA6M4_USB.png") # 实现过程 ## 准备 ### 工程准备 1. 首先需要准备一个Renesas 官方的Demo 例程**EK-RA6M4 Example Project Bundle - Sample Code** 里的usb_pcdc 例程 > 具体下载 [RA6M4页面-](https://www2.renesas.cn/jp/zh/products/microcontrollers-microprocessors/ra-cortex-m-mcus/ra6m4-200mhz-arm-cortex-m33-trustzone-high-integration-ethernet-and-octaspi#design_development)>设计开发->样例程序, 该例程集合是基于e2studio IDE的,请自行安装 > > ![image-20220731153646191.png](https://oss-club.rt-thread.org/uploads/20220731/323bc09fc061eb297d61dfa86a5f3df9.png.webp "image-20220731153646191.png") 2. 再准备一个STM32系列基于 rt-thread usb cdc,可以正常运行的工程 3. rt-thread studio 新建一个 RA6M4的基础例程 ![image-20220731152637362.png](https://oss-club.rt-thread.org/uploads/20220731/c1e51d78bc8cf780965d396d54136ed7.png.webp "image-20220731152637362.png") ### 工程配置 1. 参考《 CPK-RAA6M4 评估板快速入门》 chapter 5 使用FSP配置USB 外设,具体细节不在详述。 ![image-20220731153036357.png](https://oss-club.rt-thread.org/uploads/20220731/d4ca636f7f4b5e02723be762a8148c40.png.webp "image-20220731153036357.png") > 本来我们参考板子自带的 CPK_RA6M4_USBProject 例程,这个写的不太精简,就直接参考EK-RA6M4 Example Project Bundle里的了 > > 初始化配置都一样的 2. 同时打开rt-thread usb cdc device ![image-20220731154022379.png](https://oss-club.rt-thread.org/uploads/20220731/0db1a4aa6a6389a9ce30722466fba11f.png.webp "image-20220731154022379.png") ## 初始化 ### R_USB 初始化 1)copy 一份 stm32的 library/HAL_Drivers/drv_usbd.c 到 RA6M4同级目录下 2)把Renesas USB 的初始化放到 _init里,替换原来stm32的初始化,并创建一个usbd_event_thread 3)usbd_event_thread 里调用R_USB_EventGet处理中断 ``` static void usbd_event_entry(void* parameter) { static usb_pcdc_linecoding_t g_line_coding; while(1) { rt_thread_mdelay(2); /* Obtain USB related events */ R_USB_EventGet (&g_basic0_ctrl, &usb_event); } } static rt_err_t _init(rt_device_t device) { fsp_err_t err = FSP_SUCCESS; /* Open USB instance */ err = R_USB_Open (&g_basic0_ctrl, &g_basic0_cfg); /* Get USB class type */ err = R_USB_ClassTypeGet (&g_basic0_ctrl, &g_usb_class_type); /* Get module number */ err = R_USB_ModuleNumberGet(&g_basic0_ctrl, &g_usb_module_number); /* init usb device thread */ rt_thread_init(&usb_event_thread, "usbe", usbd_event_entry, RT_NULL, usb_event_stack, RT_USBD_THREAD_STACK_SZ, RT_USBD_THREAD_PRIO-1, 20); /* rt_thread_init should always be OK, so start the thread without further * checking. */ rt_thread_startup(&usb_event_thread); return RT_EOK; } ``` 4) 删除一些STM32 相关的定义后编译,下载运行,会发现效果和renesas 例程差不多。 ``` msh >ps thread pri status sp stack size max used left tick error ------m- --- ------- ---------- ---------- ------ ---------- --- tshell 200 running 0x00000258 0z00001000 15% 0x00000001 000 usbe 7 suspend 0x000000b0 0x00001000 14% 0x00000005 000 vcom 16 suspend 0x0000013c 0x00000800 30% 0x00000014 000 usbd 8 suspend 0x000000c4 0x00001000 15% 0x00000007 000 tidlg0 31 ready 0x00000060 0x00000100 53% 0x00004016 000 timer 4 suspend 0x00000080 0x00002200 25% 0x00000009 000 mqin 10 !suspend 0x000000bc 0x00000800 20% 0x00000009 000 msh >list_device device" type ref count -------- -------------------- ---------- vcom $Character Device 0 usbd USB Slave Device 0 uart7 Character Device 2 pin Miscellaneous Device 0 msh > ``` 虽然我们已经初始化了rt-thread usb 相关的内容,同时也生成了vcom 和usbd device, 但不起任何作用,现在的USB 所以事宜还都是renesas api 接管。我们下面要做的事情就算逐步把 USB 中断 和数据传输,使用 rt-thread usb 接管处理,架空renesas bare usb的部分。 ### RT_USB 初始化 ### _ep_pool 首先看一下当前 renesas usb 的枚举过程 ![image-20220731160718953.png](https://oss-club.rt-thread.org/uploads/20220731/94ca3c8bcfc05979e1dc7ce8c087fff0.png.webp "image-20220731160718953.png") rt-thread usb 的ep 配置在 drv_usbd.c 中的_ep_pool 数组里,根据上面的描述符配置如下 ``` static struct ep_id _ep_pool[] = { {0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED }, {0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED}, {0x1, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED}, {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}, #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 }, }; ``` > 部分ep 双向,不影响 ## 替换前准备 在替换前,要搞清楚 renesas usb 的中断体系,知道在什么地方替换。renesas usb 在usb_pstd_interrupt函数中处理USB 中断相关事宜。 ![image-20220731163512200.png](https://oss-club.rt-thread.org/uploads/20220731/1c59ecf68ea0b0c4fa5eadd3481dc67b.png.webp "image-20220731163512200.png") rt-thread usbdevice_core.c中 有一个usb_thread 线程,入口函数为rt_usbd_thread_entry,里面会接收消息队列usb_mq,处理不同的USB 相关事宜(包含中断和软件事务),如下: ![image-20220731164441212.png](https://oss-club.rt-thread.org/uploads/20220731/c1f39fb527e43429876a4d3b1ad19a28.png.webp "image-20220731164441212.png") ## 特殊中断 这部分包含了 attach, deattach, reset, sof,只要向usb_mq发送对应消息即可。 ## 枚举 枚举属于控制传输,这部分是重头戏,先看下对应的 USB_INT_CTRL 中断处理 ![image-20220731165335838.png](https://oss-club.rt-thread.org/uploads/20220731/d2e377dfa685da546d24e91607c5b157.png.webp "image-20220731165335838.png") 上图可知renesas usb ip 把控制传输分为了5个状态,根据情况不同,具体请求会使用一个或多个状态 ### 获取设备描述符 #### send msg 获取设备描述符,会先后产生USB_CS_RDDS ,USB_CS_RDSS两个INT。 对应这类标准请求,直接向rt-thread usb 内核发送一个msg 即可,同时屏蔽renesas这边的处理。 ![image-20220731170323667.png](https://oss-club.rt-thread.org/uploads/20220731/56714f36043f683f6859057523301991.png.webp "image-20220731170323667.png") #### _setup_request rt-thread usb_thread 根据 USB_MSG_SETUP_NOTIFY 消息类型,调用如下 ``` _setup_request->_standard_request->_get_descriptor->_get_device_descriptor- ``` 在_get_device_descriptor最终会调用rt_usbd_ep0_write 向主机返回设备描述符, ``` static rt_err_t _get_device_descriptor(struct udevice* device, ureq_t setup) { rt_size_t size; /* parameter check */ RT_ASSERT(device != RT_NULL); RT_ASSERT(setup != RT_NULL); RT_DEBUG_LOG(RT_DEBUG_USB, ("_get_device_descriptor\n")); /* device descriptor wLength should less than USB_DESC_LENGTH_DEVICE*/ size = (setup->wLength > USB_DESC_LENGTH_DEVICE) ? USB_DESC_LENGTH_DEVICE : setup->wLength; /* send device descriptor to endpoint 0 */ rt_usbd_ep0_write(device, (rt_uint8_t*) &device->dev_desc, size); return RT_EOK; } ``` 继续调用如下 ``` rt_usbd_ep0_write->dcd_ep_write->dcd->ops->ep_write ``` #### _ep_write 根据rt-thread usb的初始化,注册过程可知 dcd->ops->ep_write就是 drv_usbd.c中的_ep_write,实现如下: ``` static rt_size_t _ep_write(rt_uint8_t address, void *buffer, rt_size_t size) { if ((address & EP_ADDR_MSK) == 0U) { R_USB_PeriControlDataSet (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size); } else { R_USB_Write (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size, (uint8_t) g_usb_class_type); } return size; } ``` ### 设置地址 set address虽然产生USB_CS_WRND INT,但比较特殊,在datasheet 中提到 ![image-20220731170756672.png](https://oss-club.rt-thread.org/uploads/20220731/999b91b7a8c54b9a831fb80083f698bd.png.webp "image-20220731170756672.png") > 即只要是正确的address, ip会auto respond, 无需处理 ### 获取配置描述符 和获取设备描述符,会先后产生USB_CS_RDDS ,USB_CS_RDSS两个INT,处理也类似。但config descriptor的长度大于MPS 64,一包是发不完的,需要继续发下一包,这就需要一个发送完成中断,知道什么时候继续发。 ![image-20220731174752188.png](https://oss-club.rt-thread.org/uploads/20220731/50e76de71e2c41a5e6e694648f7c053f.png.webp "image-20220731174752188.png") datasheet 可知,renesas ep0/pipe0(DCP)发送完成会产生一个 bemp中断,而 non-DCP则产生 BRDY中中断(稍后我们也会使用到) 在usb_pstd_interrupt - > usb_pstd_bemp_pipe里直接调用rt_usbd_ep0_in_handler发送剩余的长度。 ![image-20220731175056463.png](https://oss-club.rt-thread.org/uploads/20220731/76f76692bcb62687fd1bcf60d4862e2b.png.webp "image-20220731175056463.png") ### 获取字符串描述符 标准输入请求和get configuration, 无论长度大小,都可以正常发送了。 ### 获取qualifier描述符 如果只能进行全速(full-speed)操作的设备(设备描述符的版本号等于0200H)接收到请求设备限定符的Get Descriptor请求,它必须用请求错误响应,回复STALL来响应。否则host 会一直尝试获取,直到超时,会让设备枚举变的很长,具体参考我之前的文章[rtthread USB CDC调用流程](https://club.rt-thread.org/ask/article/57a61f02c06f1852.html ) ![image-20220731175832052.png](https://oss-club.rt-thread.org/uploads/20220731/931c5cdd44249f1b8b0fa9ddf081963c.png.webp "image-20220731175832052.png") 代码里最后调用的是 ``` rt_usbd_ep0_set_stall -> dcd_ep_set_stall -> _ep_set_stall ``` #### _ep_set_stall drv_usbd.c中的_ep_write,实现如下: ``` static rt_err_t _ep_set_stall(rt_uint8_t address) { usb_utr_t tran_data_peri; tran_data_peri.ip = g_basic0_ctrl.module_number; extern uint16_t g_usb_usbmode[]; if (USB_MODE_PERI == g_usb_usbmode[g_basic0_ctrl.module_number]) { if ((address & EP_ADDR_MSK) == 0U) { usb_pstd_set_stall_pipe0(&tran_data_peri); } else { usb_pstd_set_stall(address & EP_ADDR_MSK, &tran_data_peri); } } return RT_EOK; } ``` ### 设置配置 会产生USB_CS_WRND INT, 无数据阶段,发送msg后,最终会调用到usbdevice_core.c 中的 _set_config: ``` /** * This function will handle set_config bRequest. * * @param device the usb device object. * @param setup the setup bRequest. * * @return RT_EOK on successful. */ static rt_err_t _set_config(struct udevice* device, ureq_t setup) { struct rt_list_node *i, *j, *k; uconfig_t cfg; uintf_t intf; ualtsetting_t setting; uep_t ep; /* parameter check */ RT_ASSERT(device != RT_NULL); RT_ASSERT(setup != RT_NULL); RT_DEBUG_LOG(RT_DEBUG_USB, ("_set_config\n")); if (setup->wValue > device->dev_desc.bNumConfigurations) { rt_usbd_ep0_set_stall(device); return -RT_ERROR; } if (setup->wValue == 0) { RT_DEBUG_LOG(RT_DEBUG_USB, ("address state\n")); device->state = USB_STATE_ADDRESS; goto _exit; } /* set current configuration */ rt_usbd_set_config(device, setup->wValue); cfg = device->curr_cfg; for (i=cfg->func_list.next; i!=&cfg->func_list; i=i->next) { /* run all functiones and their endpoints in the configuration */ ufunction_t func = (ufunction_t)rt_list_entry(i, struct ufunction, list); for(j=func->intf_list.next; j!=&func->intf_list; j=j->next) { intf = (uintf_t)rt_list_entry(j, struct uinterface, list); setting = intf->curr_setting; for(k=setting->ep_list.next; k != &setting->ep_list; k=k->next) { ep = (uep_t)rt_list_entry(k, struct uendpoint, list); /* first disable then enable an endpoint */ dcd_ep_disable(device->dcd, ep); dcd_ep_enable(device->dcd, ep); } } /* after enabled endpoints, then enable function */ FUNC_ENABLE(func); } device->state = USB_STATE_CONFIGURED; _exit: /* issue status stage */ dcd_ep0_send_status(device->dcd); return RT_EOK; } ``` _set_config 做的事情比较多, > 1. set device->curr_cfg > > 2. 查找该配置下 ,描述符中不同接口下,不同端点, 并配置enbale > > ``` > dcd_ep_disable(device->dcd, ep); > dcd_ep_enable(device->dcd, ep); > ``` > > 3. enable func; > 4. 返回状态阶段 ### 端点使能 #### Bulk ep enable 追踪dcd_ep_disable和dcd_ep_enable,发现他们最终会调到drv_usbd.c 中的_ep_disable,_ep_enable, 接口实现如下: ``` static rt_err_t _ep_enable(uep_t ep) { usb_utr_t utr; uint8_t pipe_no; RT_ASSERT(ep != RT_NULL); RT_ASSERT(ep->ep_desc != RT_NULL); utr.ip = g_basic0_ctrl.module_number; pipe_no = ep->ep_desc->bEndpointAddress & EP_ADDR_MSK; rt_kprintf("_ep_enable: %d \n",pipe_no); /* Initialization of registers associated with specified pipe. */ usb_cstd_pipe_init(&utr, pipe_no); return RT_EOK; } static rt_err_t _ep_disable(uep_t ep) { usb_utr_t utr; uint8_t pipe_no; RT_ASSERT(ep != RT_NULL); RT_ASSERT(ep->ep_desc != RT_NULL); utr.ip = g_basic0_ctrl.module_number; pipe_no = ep->ep_desc->bEndpointAddress & EP_ADDR_MSK; /* Clear specified pipe configuration register. */ usb_cstd_clr_pipe_cnfg(&utr, pipe_no); return RT_EOK; } ``` , #### INT ep enable 上面配置完了,这部其实不需要拿出来说,datasheet里明确写道 ![image-20220731181818467.png](https://oss-club.rt-thread.org/uploads/20220731/278ef10548ad34c2373851bc1440ffb5.png.webp "image-20220731181818467.png") 一旦配置成非中断pipe ,就会导致打开设备出现问题 ![image-20220731181943827.png](https://oss-club.rt-thread.org/uploads/20220731/707b465c590d5530d75f6319c1446da5.png.webp "image-20220731181943827.png") 这个问题卡了我一周,本来我看过文档知道这个事情,但是我看官方demo 配置pipe3 没问题啊,我在rt-thread descriptor也配成pipe3,应该也没问题的,但是我不知道的是 ,代码里最后改动了默认的端点, USB_CFG_PCDC_INT_IN是 pipe6 ![image-20220731182620093.png](https://oss-club.rt-thread.org/uploads/20220731/d2f8ad22a0ca3ee2728fd621a13afe45.png.webp "image-20220731182620093.png") 最终修改 drv_usbd.c 中的 _ep_pool 如下 ``` static struct ep_id _ep_pool[] = { {0x0, USB_EP_ATTR_CONTROL, USB_DIR_INOUT, 64, ID_ASSIGNED }, {0x1, USB_EP_ATTR_BULK, USB_DIR_IN, 64, ID_UNASSIGNED}, {0x2, USB_EP_ATTR_BULK, USB_DIR_OUT, 64, ID_UNASSIGNED}, {0x6, USB_EP_ATTR_INT, USB_DIR_IN, 64, ID_UNASSIGNED}, }; ``` ### function enable FUNC_ENABLE(func) 最终调用的是cdc_vcom.c 中的 ``` static rt_err_t _function_enable(ufunction_t func) { struct vcom *data; struct udev_msg msg; RT_ASSERT(func != RT_NULL); RT_DEBUG_LOG(RT_DEBUG_USB, ("cdc function enable\n")); _vcom_reset_state(func); data = (struct vcom*)func->user_data; data->ep_out->buffer = rt_malloc(CDC_RX_BUFSIZE); RT_ASSERT(data->ep_out->buffer != RT_NULL); data->ep_out->request.buffer = data->ep_out->buffer; data->ep_out->request.size = EP_MAXPACKET(data->ep_out); /* add remain_size as the start condition */ data->ep_out->request.remain_size = data->ep_out->request.size; data->ep_out->request.req_type = UIO_REQUEST_READ_BEST; /* notify usb core to process the data request(read from host) */ msg.type = USB_MSG_DATA_NOTIFY; msg.dcd = func->device->dcd; msg.content.ep_msg.ep_addr = data->ep_out->ep_desc->bEndpointAddress; /* simulate a out interrupter to send a msg for the transmission * so the received msg.size must be zero */ msg.content.ep_msg.size = 0; rt_usbd_event_signal(&msg); return RT_EOK; } ``` 即开始准备在out ep上接收数据,这是CDC的特性,感觉应该放在打开设备后最好。enable out ep直接使用它去接收数据后在STM32上可能没问题,但Renesas上出现了问题。 #### function enable 过早的问题 _set_config 最后返回状态才结束这次控制传输, renesas usb ip 在未收到状态,是不认为配置成功的。不会更新INTSTS0 中的DVSQ 到Configured state ![image-20220731183158768.png](https://oss-club.rt-thread.org/uploads/20220731/31927e05d9db36c3eafaba6e3ac185d6.png.webp "image-20220731183158768.png") 而很多API ,都会检测DVSQ,未配置的情况下,一些非零的端点的数据是不能transfer的,这就导致 FUNC_ENABLE(func) 会失败了的。 #### function enable解决方案 解决方案就是 _function_enable 并不直接发起一个io requeset, 模拟USB 中断,最后通过_ep_out_handler 发送 ![image-20220731184211261.png](https://oss-club.rt-thread.org/uploads/20220731/7f2cc986231b87f1e851f03c575cac60.png.webp "image-20220731184211261.png") 因为_ep_out_handler 也在 usb_thread 线程中,所以只有 setup handler完了,才有可能执行_data_notify 到它,即发送完状态,控制传输结束后才开始。 测试中发现控制传输结束后, DVSQ =Configured state 的中断输出会有些慢, 有可能setup handler了,再执行到了ep_out_handler -> data transfer,它还没发生,同样会导致问题,所以我们在ep_out_handler 发送前要判断一下模拟触发的第一次的情况,加个1ms的延迟 ![image-20220731190653750.png](https://oss-club.rt-thread.org/uploads/20220731/f8416863dd036636650148d68725aaad.png.webp "image-20220731190653750.png") #### ep0返回状态 最终会调到drv_usbd.c 中的_ep0_send_status, 接口实现如下: ``` static rt_err_t _ep0_send_status(void) { usb_utr_t tran_data_peri; tran_data_peri.ip = g_basic0_ctrl.module_number; extern uint16_t g_usb_usbmode[]; if (USB_MODE_PERI == g_usb_usbmode[g_basic0_ctrl.module_number]) { usb_cstd_set_buf(&tran_data_peri, (uint16_t) USB_PIPE0); usb_pstd_ctrl_end((uint16_t) USB_CTRL_END, &tran_data_peri); /* Control transfer stop(end) */ } return RT_EOK; } ``` ### 类请求 #### GetLineCoding, 无需特殊处理 #### SetControlLineSate 无需特殊处理 #### SetLineCoding 这个比较特殊,控制传输里的写数据请求,带有数据阶段,就需要调用控制读,最终调用的是cdc_vcom.c 中的_ep_read_prepare 的 R_USB_PeriControlDataGet 部分 ``` static rt_size_t _ep_read_prepare(rt_uint8_t address, void *buffer, rt_size_t size) { uint32_t ret ; if ((address & EP_ADDR_MSK) == 0U) { R_USB_PeriControlDataGet (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size); } else { ret = R_USB_Read (&g_basic0_ctrl, (uint8_t*) buffer, (uint32_t) size, (uint8_t) g_usb_class_type); if (FSP_SUCCESS != ret) { rt_kprintf("R_USB_Read error : %d",R_USB_Read); } } return size; } ``` 上面全面做完后 ,就会发现设备现在使用rt-thread usb的描述符,再通过其接口实现了枚举 ![image-20220731192912551.png](https://oss-club.rt-thread.org/uploads/20220731/7e1d7fbfbeaeb8f94b76e8759e327fcc.png.webp "image-20220731192912551.png") ## 数据传输 ### rt_usbd_ep_out_handler 前面提到的,非0端点,接收到数据后会产生一个USB_INT_BRDY 接收中断 #### USB_INT_BRDY 因为我们的数据请求协议里都是一个整包,其处理流程如下: ``` usb_pstd_brdy_pipe -> usb_pstd_brdy_pipe_process -> usb_pstd_fifo_to_buf -> usb_pstd_data_end->usb_pcdc_read_complete ``` #### callback 处理 usb_pcdc_read_complete 直接添加 rt_usbd_ep_out_handler回调即可,注意传入已读到数据长度参数。 ![image-20220731194014290.png](https://oss-club.rt-thread.org/uploads/20220731/c6ff2172f11b135021d161c7d51dd7a0.png.webp "image-20220731194014290.png") ### rt_usbd_ep_in_handler 前面提到的,非0端点,发送完数据会产生一个USB_INT_BEMP发送中断 #### USB_INT_BEMP 因为我们的数据请求协议里都是一个整包,其处理流程如下: ``` usb_pstd_bemp_pipe->usb_pstd_bemp_pipe_process->usb_pstd_data_end->usb_pcdc_write_complete ``` #### callback 处理 usb_pcdc_write_complete 直接添加 rt_usbd_ep_in_handler回调即可,注意pipe不带方向,需要或上0x80, size参数无需关注。 ![image-20220731194625482.png](https://oss-club.rt-thread.org/uploads/20220731/d0656196d1f303ac26d87affade83dc7.png.webp "image-20220731194625482.png") ### ## echo 测试 在当前rt-thread cdc 架构中很容易实现echo 功能: 在_ep_out_handler 接收处理中,直接把接收的数据放到data->tx_ringbuffer,等待发送即可 ![image-20220731202058236.png](https://oss-club.rt-thread.org/uploads/20220731/4de59e4fe51352e1a5699119c5ebc359.png.webp "image-20220731202058236.png") 串口测试 ![image-20220731202923959.png](https://oss-club.rt-thread.org/uploads/20220731/540001c3763d52ccf91dc728efb102c2.png "image-20220731202923959.png") > 也可以参考之前的文章[在STM32F407 Disc1上使用RT-Thread CDC](https://club.rt-thread.org/ask/article/3373253faa7cdd56.html) 中 **6 CDC ECHO测试** > > 操作生成的vcom device 实现 ## 总结和后续计划 感谢RT-Thread社区和 Renesas 提供的开发板,有机会测试使用RA6M4开发板。USB 调试中踩了一些pipe的坑,当然还是高估了自己的能力,一开始不应该尝试基于Renesas USB OS 版本改动,浪费了很多时间,拖到了Deadline。还好后面选择了基于 USB Bare版本,混合模式一步步调试,最终初步完成,调试方向很重要!当然USB分析仪也帮了很大的忙,工具同样重要! 现在只完成了第一步:通过混合的方式熟悉Renesas usb ip, 同时实现了drv_usbd.c中需要的接口,初步实现了rt-thread cdc 设备可以正常运行, 后续计划如下: 1. 把 Renesas的 r_usb_api.c 和hal drivers中一些不必要的api全部去掉,尽量在一个drv_usbd.c中完成 2. 参考 Renesas usb os, 把USB 中断通过mq发送给处理线程 3. 整理工程配置,第一次提交PR 4. 直接使用寄存器方式实现,DMA配置,再次PR # 视频演示: https://www.bilibili.com/video/BV1nB4y1r79c/?vd_source=8804b583a42ba86b4dcfe924fc05b57d # 代码地址 https://gitee.com/bltas/RTT_RA6M4_USB_CDC
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
blta
这家伙很懒,什么也没写!
文章
12
回答
9
被采纳
2
关注TA
发私信
相关文章
1
请教USB Host
2
STM32F4调试USB 读卡器(Slave)提示格式化
3
急求 STM32F4 USB Device MSC+SD 的相关问题
4
USB 框架问题
5
USB键盘
6
LPC17xx 如何添加USB HOST设备
7
RT-Thread目前支持USB HOST了吗?
8
USB HOST的支持问题
9
RTT 2.0.1 USB存储设备问题,枚举到USBREQ_GET_MAX_LUN后复位
10
USB库已经很久没更新了
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部