ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

STM32 USB HOST驱动CH340扩展多路虚拟串口实战

STM32 USB HOST驱动CH340扩展多路虚拟串口实战 做多传感器采集板的时候我遇到过最尴尬的一件事板子上一共需要6个串口GNSS定位、4G模块、激光雷达、姿态传感器、两路RS485总线设备F407虽然有6个UART但全部焊完之后想再挂一块MCU做协处理器一看引脚表一个口都没剩。当时的第一反应是拿IO口模拟UART结果波特率一上57600就各种丢字节查了一下午时序发现根本没法稳定换WK2124这类SPI转串口芯片又要重新画板而且一片WK2124价格不便宜。后来灵机一动——既然CH340在PC上插上就能用那能不能让STM32当USB主机直接认CH340当USB串口用顺着这个思路折腾了大概两周最后在F407上跑通了USB HOST CH340虚拟串口再挂一个四口USB Hub一口气扩出四路串口。这篇文章就把整个方案的选型思路、硬件接线、软件移植、以及我踩过的那些坑完整写出来。这套方案的核心是用STM32的USB OTG外设做主控通过USB HOST协议栈枚举并驱动CH340芯片把USB转串口模块变成即插即用的串口卡。适合几类人看一是项目里串口不够用、又不想改板子加UART芯片的二是想把手头USB转串口模块直接用进嵌入式系统里的三是对USB HOST协议栈感兴趣想找一个具体设备做练手的。文章中所有代码以STM32F407 HAL库 USB_HOST中间件为基础思路同样适用于F429、H7等带OTG外设的芯片。1. 为什么绕一圈走USB HOST而不是加UART1.1 串口不够用时的几种常见方案串口不够用嵌入式工程师最常用的招数就那么几招但每一招都有明显的短板。第一招是换更多串口的芯片。比如从STM32F103换到STM32F407再从F407换到F429UART数量确实多了但引脚密集、BGA封装、成本上涨而且到了6个UART以上芯片内部还有信号交叉、DMA通道冲突的问题改板换片子的工作量一点也不小。第二招是IO口模拟串口。这个方案听起来省钱实际用起来想哭。一个8位定时器模拟9600波特率勉强能跑但要同时模拟三路、每路还要开RX中断收不定长数据CPU占用率直接飙到30%以上。而且IO模拟串口对中断延迟极其敏感系统里一旦跑RTOS或者频繁进临界区数据就开始错位永远是调试时正常、联调时抽风。第三招是用SPI/I2C转UART芯片比如WK2124、SC16IS752这些。这个方案稳定一个SPI口能扩出4路串口但需要重新画板、加芯片、写SPI驱动而且这类芯片不能热插拔串口参数要在初始化时一次性配置好后期想接个临时设备调试很不方便。第四招就是我最终选的USB HOST USB转串口模块。STM32作为USB主机CH340作为USB设备中间Type-C线一连CH340的串口侧直接接目标设备主控这边用BULK端点收发数据。表面上看多绕了一层USB协议实际上换来几个实打实的好处CH340模块满大街都是、几块钱一个坏了直接换支持热插拔调试时想接哪台设备就接哪台一个USB HOST口挂上Hub就能扩多路不需要重新改板。1.2 USB HOST方案的真实优势与代价不过USB HOST方案也不是白捡的它的代价非常清晰协议栈复杂、调试链路长、对主控性能有要求。USB HOST要走完枚举流程解析设备描述符、配置描述符、端点描述符然后才能建立通信通道。这一整套动作对单片机来说不是简单活协议栈得占至少20KB Flash和4KB RAM而且枚举期间主循环基本不能干别的。但换一个角度看这套方案解决的是一个长期问题。以前板子上想加串口要么硬件就已经焊死要么得飞线改板现在我的做法是任何串口不够用的时刻直接从抽屉里拿一个CH340模块插上系统自动识别、自动注册一条虚拟串口链路。这种感觉是一劳永逸的。选型时的核心判断标准我总结成一句如果只是缺1路串口、项目已经冻结硬件那不要碰USB HOST老老实实IO模拟或者飞线如果是新项目、缺3路以上串口、而且以后大概率会有临时接设备的需求那USB HOST CH340值得投入。我这次是4路扩展平均摊下来每一路成本不到5块钱。2. 硬件连接与芯片选型哪些型号能玩引脚怎么接2.1 支持USB HOST的STM32型号很多人拿着STM32F103C8T6想折腾USB HOST直接卡在第一步因为F103的USB外设是Device Only根本没有HOST模式。这里必须先理清硬件前提USB HOST功能要求芯片有USB OTG外设且型号手册上明确写了支持HOST模式。我整理了一张选型表按我自己的使用经验排了个优先级。主流选择是STM32F405/407/415/417它们带OTG_FS和OTG_HS两个USB控制器OTG_FS可以直接当HOST用外设丰富、参考资料多、CubeMX配置也成熟是我认为折腾这套方案最舒服的型号。F429/439同样可以OTG_FS结构一样最大的好处是主频更高跑协议栈和大数据量收发时余量更足。H7系列性能最强OTG_HS还能跑High Speed也就是480Mbps但H7的USB驱动底子和F4的HAL库不完全一样代码迁移时有些细节要留意新手不建议直接起步。F1全系不支持USB HOST这一点没有任何争议F1的USB只是Device想玩HOST只能换芯片。L4的部分型号比如L4R5/L4S5也带OTG_FS可以HOST低功耗场景才能体现它的价值但资料量少除非功耗卡得很死否则我还是推荐F4。还有一个容易忽略的坑同一个芯片封装不同USB引脚是否全部引出也不一样比如LQFP64封装的F407PA9/PA10/PA11/PA12都在但少数精简封装会砍掉PA9或者VBUS检测脚画板前一定去翻封装Pinout别等板子打回来才发现引脚被复用了。2.2 引脚与最小系统接线以STM32F407VET6为例USB OTG_FS真正用到的引脚是这四个引脚功能HOST模式下的接法PA11 (OTG_FS_DM)USB D-直连CH340模块的D-接口PA12 (OTG_FS_DP)USB D直连CH340模块的D接口PA10 (OTG_FS_ID)主/从识别HOST模式下必须接GNDPA9 (OTG_FS_VBUS)VBUS检测接5V检测点一般通过分压接ID脚接GND这一步千万别省。很多人第一次上电发现USB枚举完全没反应查了半天发现ID脚悬空OTG控制器分不清自己是Master还是Slave整个会话协商直接卡住。PA9是VBUS检测脚它必须监控5V总线电压所以一般会在VBUS上做电阻分压把5V分到3.3V以下再送PA9。同时需要外供5V给整个USB总线如果你的开发板上有USB电源管理芯片要确认它的使能信号没被漏配置很多开发板的VBUS是通过GPIO控制的默认不上电。CH340模块这边的接线很常规VCC接5VGND共地D/D-分别连PA12/PA11。注意这里是让CH340作为USB设备挂到STM32的HOST控制器上和控制芯片本身是否有多余UART完全无关。CH340模块另一侧的TXD/RXD再接目标设备这样目标设备的串口数据经过CH340变成USB BULK包再被STM32接收等于把目标设备的串口虚拟到了主控的USB总线上。2.3 VBUS供电HOST模式最容易忽视的电源问题USB HOST模式下5V VBUS要由主机主动提供而很多STM32最小系统板上压根没有5V输出电路或者5V能力弱得可怜。CH340模块本身功耗不大但挂上Hub之后Hub芯片、多个CH340、指示灯、电平转换电路加一起电流就可能超过单个开发板LDO的承受能力。我吃过一次亏用开发板自带的5V引脚直接给一个4口Hub供电结果Hub上接了3个CH340模块其中一个模块的蓝色LED一亮整条5V瞬间掉到4.2VUSB信号直接畸变表现为设备反复枚举、偶尔能识别但一收发就断开。后来我在5V输入侧单独加了一个DC-DC降压模块输出5V/2A配上470uF电解电容做储能问题瞬间消失。经验USB HOST的5V电源要单独走线不要和主控电源串在一起。如果5V源本身就是电池供电正极线上加个500mA的PTC保险丝防止设备短路烧坏整板。电源质量直接决定USB信号质量这一点在USB身上比UART严格得多丢包、误码、枚举失败有一半以上其实是电源纹波造成的不要一开始就去怀疑协议栈。3. CH340不是标准CDC设备这一步决定成败3.1 CH340的设备描述符和通信模型先说一个大部分人没意识到的关键点CH340在USB层面并不完全符合CDC ACM标准而是一个厂商自定义类(Vendor-Specific)设备。在PC上Windows装驱动后能直接用是因为WCH提供了专用串口驱动在Linux上能直接识别是因为内核里有一个ch341驱动模块。但在STM32的USB HOST协议栈里官方给的标准CDC类驱动USBH_CDC默认处理的是标准CDC描述符拿它去枚举CH340大概率失败或者只能枚举、不能通信。CH340的典型描述符是VID 0x1A86PID 0x7523接口类代码为0xFFvendor specific这意味着系统没法用通用类驱动去适配它必须针对VID/PID做匹配然后自己实现一套厂商命令序列。这也是为什么我用CH340而不是CP2102当首选——CP2102是标准CDC设备STM32的USBH_CDC可以直接驱动但CH340便宜、常见、抗干扰性也不错而且我手头全是CH340模块。CH340的通信模型是这样的它内部有一个USB转串口引擎USB侧提供两个BULK端点——一个IN端点用于把串口收到的数据上传给主机一个OUT端点用于接收主机下发的串口数据。主机要设置波特率、数据位、停止位、流控需要发送一组厂商特定的控制请求这些请求不在标准USB规范里必须在驱动代码里硬编码。3.2 在STM32 Host端处理Vendor请求既然CH340走Vendor类那就不能指望HAL库自动搞定配置。我的做法是在USBH_HOST中间件的基础上自己实现一个vendor class驱动核心工作有三块第一块是枚举阶段的设备识别。在类驱动注册列表里添加上自定义的Class在Init阶段调用HAL_HCD_GetCurrentSpeed等接口读取设备信息判断VID/PID是否匹配CH3400x1A86 / 0x7523匹配成功才继续初始化否则跳过。这样挂标准CDC设备时不会误绑定。第二块是发送CH340初始化序列。CH340上电后必须由主机发送一系列Vendor Write请求具体字节序列可以参考WCH官方提供的CH341PAR库或者Linux内核drivers/usb/serial/ch341.c源码。初始化序列的核心内容是设置DTR/RTS状态、配置波特率分频因子。以波特率9600为例需要根据CH340内部基准时钟一般是12MHz或改进型的内部时钟计算分频值再通过Vendor请求写入控制端点。不同批次芯片内部时钟可能略有差异所以严格来说还要做时钟校准读取这里不展开细节具体计算公式和命令码以官方手册为准代码里预留一个配置结构体就好。第三块是BULK收发。初始化完成后CH340的数据通道就是纯粹的BULK端点。发送数据走OUT端点接收数据走IN端点和CDC设备的数据收发在代码层面几乎一样只是端点号不同。我的发送函数直接调用HAL_HCD_SubmitURB往OUT端点写接收则用HAL_HCD_DataIn回调在中断里把数据放进环形队列。下面这段是我在项目里实际在用的初始化代码框架命令码部分我模糊处理了具体参数请对照你手头芯片的官方手册调整typedef struct { uint8_t baud_div_low; uint8_t baud_div_high; uint8_t line_ctrl; uint8_t flow_ctrl; } ch340_config_t; USBH_StatusTypeDef CH340_Init(USBH_HandleTypeDef *phost) { uint8_t init_seq[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 检查 VID/PID if ((phost-device.Data[USBH_DESC_VIDL] ! 0x86) || // 0x1A86 的低字节 (phost-device.Data[USBH_DESC_VIDH] ! 0x1A)) { return USBH_FAIL; } // 发送Vendor setup请求设置DTR、RTS USBH_CtlReq(phost, init_seq, sizeof(init_seq), 0x0000); // 计算并写入波特率分频因子 uint16_t div CH340_ComputeDiv(9600); uint8_t baud_cmd[2]; baud_cmd[0] div 0xFF; baud_cmd[1] (div 8) 0xFF; USBH_CtlReq(phost, baud_cmd, sizeof(baud_cmd), 0x0001); return USBH_OK; }这套初始化流程看起来很绕但一旦跑通后续换波特率只需要再次发送分频命令就行。我建议把波特率配置函数做成公共接口后续所有串口业务逻辑都只面对一个抽象的虚拟串口句柄完全不需要关心底层是USB还是硬件UART。3.3 不折腾CH340的替代路线用标准CDC芯片如果你手头预算够、不想啃Vendor驱动我强烈建议第一版先用标准CDC芯片把功能跑通再回头决定要不要适配CH340。标准CDC设备在STM32 HAL库的USBH_CDC驱动下几乎开箱即用典型的芯片有CP2102、FT232RL、CH343部分型号。把这些芯片插上枚举结束就能收发省掉厂商命令序列那一大堆活儿。我用过CP2102做对比实验STM32 HOST侧把USBH_CDC类驱动注册好初始化阶段会主动读设备描述符解析出CDC接口的BULK端点开箱跑通全程没在厂商特性上花一分钟。而CH340同样一套板子光是初始化序列就调了四个小时。所以我的建议是项目工期紧选CDC芯片追求性价比、手头有存货选CH340但要做好啃命令序列的心理准备。还需要提醒一点无论选哪颗USB转串口芯片芯片串口侧的波特率由芯片自身分频决定而不是由USB总线的传输速度决定USB总线始终以12Mbps全速跑通过BULK包把数据送过去真正决定串口线上快慢的是你在厂商命令里配置的分频系数。这个逻辑很多人弄反了以为USB是高速的串口就一定快实际上串口波形速度完全取决于配置。4. 软件移植从CubeMX生成到数据收发打通4.1 CubeMX里的一次性配置我在项目里用的是STM32CubeMX 6.x STM32CubeF4固件包1.27左右配置分几步。RCC开启外部高速晶振HSE调试口SWD保留串口1用作日志输出方便打印调试信息然后关键在于USB_OTG_FS的配置。在CubeMX里把USB_OTG_FS的Mode选为Host_OnlySpeed选为Full SpeedUSB_HOST中间件使能并且在中间件配置里注册你需要的类。如果用的是标准CDC设备勾上CDC类如果自己实现CH340的Vendor类可以不勾CDC但需要在USB_HOST的类驱动注册表里填上自己的Class。时钟树是一定要认真配的。USB OTG FS在F407上要求48MHz的时钟一般通过PLLQ输出如果配置错误USB模块直接不工作而且CubeMX默认的时钟树有时会把USB时钟配成不合法状态编译不报错、上电无反应检查点务必放在USB时钟这一项上。我踩过一次时钟树里漏开PLLQUSB寄存器全部读出来是0xFFFFFFFF排查了很久还以为是芯片坏了。工程生成后主循环里调用MX_USB_HOST_Process()同时确保中断里USB_OTG_FS_IRQHandler被正确处理。USB_HOST中间件是状态机驱动的主循环必须在1ms以内轮询一次否则枚举超时。如果你在主循环里做了大量阻塞操作比如Flash擦写、长时间延时务必将USB_HOST_Process放在高优先级任务里或者放到RTOS的独立线程中。4.2 主循环里发生的完整通信流程USB HOST初始化完成后一次完整的串口数据收发在软件层面经历的过程大致是设备连接时主机检测到D上拉开始复位总线、分配地址、读取设备描述符、配置描述符然后装载类驱动。我的打印日志里枚举成功后的标志是CH340的VID/PID被打印出来此时CH340已经处于可以收发数据的状态。然后自己的协议栈会调用CH340_Init发送初始化命令完成串口参数配置。发送数据时上层业务代码把待发送的字节放入一个环形发送队列发送任务从队列里取出64字节以下的数据块调用BULK OUT端点URB把数据交给USB外设之后在下一次Process调用中检查URB完成状态。接收方向则完全走中断HAL_HCD_DataIn回调被触发把从CH340 IN端点收到的数据搬进接收环形队列业务代码再异步从队列里读。我用一个很直观的比喻解释这个流程USB总线像一条快递通道BULK端点是通道上的货车CH340则把串口侧的字节打包成最多64字节一箱的包裹主机这边只管收货拆包。和普通UART中断的实时性不同USB的BULK传输有毫秒级延迟因此不能把USB虚拟串口当成硬实时串口用如果你的目标设备对时序要求特别严格比如需要微秒级精确控制这个方案不适合。4.3 缓冲设计、丢包和流控虚拟串口和硬件UART一个显著区别是硬件UART有硬件FIFO一般只有几字节但配置简单USB虚拟串口的缓冲全靠软件实现所以缓冲区的设计直接决定丢包率。我的做法是发送和接收各维护一个环形队列接收队列大小定在4KB发送队列2KB。实测在115200波特率下目标设备连续大量上传数据时如果接收队列小于1KB系统被其他任务占用200ms以上就会出现队列满丢包。流控方面CH340支持RTS/CTS硬件流控。当接收队列快满时主机需要通过厂商命令拉高CH340的RTS引脚通知对端设备暂停发送。这个操作在PC驱动里有现成的但在自研Host驱动里需要自己实现具体命令要参考CH340的寄存器定义。我项目里的目标设备大多没有硬件流控线所以我直接禁用了流控靠加大缓冲区来扛瞬时流量。丢包问题还有一个隐藏点CRC错误和STALL状态处理。USB传输本身有CRC校验如果总线信号质量差或者电源不稳IN端点可能出现CRC错误导致主机重试。HAL库默认的重试次数有限超过次数就会丢URB。我在代码里对接收URB做了异常处理一旦URB状态异常就重新提交接收URB保证链路不中断。void HAL_HCD_DataInStageCallback(USBH_HandleTypeDef *phost, uint8_t epnum) { if (g_ch340.rx_urb_ready) { uint32_t len g_ch340.rx_len; ring_buf_write(g_ch340.rx_queue, g_ch340.rx_buffer, len); // 重新提交接收URB持续接收 HAL_HCD_SubmitURB(phost, epnum, 0, g_ch340.rx_buffer, MAX_RX_LEN, 0); } }这段代码的关键在于每次接收完成必须重新提交URB否则只收到一包就再也不进数据了这是HAL库USB HOST中间件最容易踩的坑官方例程里通常只演示一次收发实际项目里忘了重新提交URB的大有人在。我在代码里写了状态保护用一个标志位防止重复提交导致URB冲突。5. 多路CH340并联实战Hub连接与枚举调度5.1 Hub方案与硬件连接一个USB HOST口只能接一个设备这是USB协议的根本约束所以要扩展多路CH340必须引入USB Hub。我选用的是一个很常见的4口USB 2.0 Hub模块主控芯片一般是GL850G或者类似方案自带一个上行口、四个下行口。每个下行口插一个CH340模块这样STM32就能管理4路虚拟串口。硬件连接上有一个细节要特别注意USB Hub下行口的5V供电要单独考虑。很多Hub模块的5V是直接从上行口输入的如果上行口接开发板四个CH340同时工作电流可能到300-400mA开发板那条USB 5V线容易顶不住。我的做法是把Hub模块的5V和GND单独接一组电源和信号线D/D-一起接入但电源不再取自主控板。这么干以后所有模块单独插拔都不会影响主控稳定。5.2 端口枚举流程Hub接入后USB HOST先枚举Hub本身然后Hub中断端点会报告下行端口的事件比如某个端口有设备插入。主机收到事件后需要对该端口做复位、分配设备地址、枚举子设备整个过程和直接连接USB设备类似只是多了一层端口选择逻辑。老版本STM32 HAL库没有内置Hub驱动我当时是从Linux驱动里把Hub状态机的逻辑移植过来的工作量不小新版本固件包已经带了USBH_HUB类直接用就行省了很多事。枚举时序是这样的主循环每次调用USBH_HUB_Process它会读取Hub中断IN端点的状态变化位图找出端口事件然后对对应端口执行复位和枚举。枚举期间主控会依次读取子设备的描述符如果VID/PID匹配CH340就初始化对应通道。我代码里维护了一个端口结构体数组每个端口记录设备地址、通道ID、数据端点号、环形队列指针业务层拿到的是一个干净的virt_uart_t数组根本不用关心某路串口到底挂在哪个Hub端口上。5.3 实测带宽与稳定性挂4路CH340、全部以115200波特率跑我做了几轮压力测试。全速USB总线的实际带宽大约1MB/s12Mbps协议带宽扣除协议开销后4路115200波特率每路约11.5KB/s四路加起来不到50KB/s带宽完全够用而且BULK传输模式下每路还能抢到还算稳定的时隙所以大流量下不太会出现互相饿死的情况。但我确实遇到了一个和Hub相关的问题某个CH340模块拔插次数多了以后对应端口偶尔枚举失败具体表现是枚举到配置描述符阶段卡住。排查之后发现是Hub下行端口上的电容导致信号上升沿变缓加上劣质模块D上拉电阻取值不标准最终USB眼图不合格。后来我把所有CH340模块换成同一批次的正品模块问题不再复现。USB对信号质量比对UART敏感得多不要省钱买那种几块钱还包邮的散新模块稳定联调的效率远大于省下的那两块钱。多路并发时的吞吐稳定性和主循环的调度关系也很大。我实测过如果每路都用独立URB发送、发送回调里打印日志4路同时跑满115200时CPU占用率在F407168MHz下大约是20%还有余量跑协议解析和显示刷新。如果把发送缓冲区提高、合并小数据包理论上还能再压一些CPU但也没必要为了省CPU牺牲代码复杂度。6. 调试中遇到的几个典型问题和排查链路6.1 枚举失败三类原因USB项目调试起来比UART痛苦因为报错不像串口那样有一堆十六进制数据可看往往只有一个hcd fail状态。我把枚举失败的排查链路理成下面这张表照着查效率能高不少现象大概率原因排查手段枚举完全无反应USB中断都不进VBUS没供上、ID脚没接地、USB时钟配置错测5V、测ID脚电平优先查电源和时钟树能读到设备描述符但配置描述符失败电源纹波大、信号线过长或阻抗异常换短杜邦线加10uF电容稳定VBUS示波器看D/D-波形枚举成功但类驱动不匹配VID/PID没匹配上或设备被识别为标准CDC但实际是Vendor类把枚举阶段打印的设备描述符原始字节打出来逐字节对照插上设备后反复枚举、偶尔成功供电能力不足、USB线接触不良单独供5V换线检查Hub下行端口电容我在调第一版时最常犯的错误是VBUS供电不足。用万用表量5V电是有的但一插上设备就掉到4.5V以下量出来是静态电压带载之后暴露问题。所以排查USB电源问题一定要带载测量插上CH340模块再量5V而不是空载量。6.2 收发乱码和丢字节数据通了之后乱码是最折磨人的问题。我遇到过乱码的原因有两类一类是CH340的波特率分频算错串口波形频率和PC侧不一样收出来的字节全是错位另一类是USB BULK包的边界问题就是数据本身是对的但被拆包后顺序乱了。第二类问题比较隐蔽。USB BULK传输每次最多64字节CH340从串口侧收满64字节或者遇到某个特殊条件才会打包上传。如果目标设备发送一串80字节的帧USB可能会出现一包64字节、一包16字节的分拆而业务层如果每一包都当成完整帧处理必然出错。解决办法是串口协议层必须自己做组帧按帧头帧尾或者长度字段来切分不能依赖USB包边界。我在接收队列后面加了一层帧解析器按超时长度双重判帧问题立刻消失。另外SPI/I2C转串口芯片和USB虚拟串口在乱码排查上有很大区别前者是寄存器配置错导致波特率不对后者除了配置错还有可能因为厂商命令里的校准时序和芯片批次不符导致波特率有微小偏差。CH340的波特率误差一般很小但如果遇到偶尔乱码优先怀疑分频系数而不是硬件。6.3 热插拔与异常复位处理USB相比HS-UART最大的优势就是热插拔但热插拔也会引入状态管理问题。插拔瞬间D电平跳变HOST状态机可能进入错误状态如果代码不做复位下一次插入可能直接无法枚举。我的做法是在Hub状态机里检测到端口断开事件后主动对对应端口的通道做清理释放URB、清空环形队列并把通道标记为未连接。还有一种情况是CH340模块被静电打掉线表现为枚举成功、收发几秒钟后总线进入挂起状态。遇到这种问题一是硬件层面在D/D-上加TVS管二是软件层面启用看门狗定期检测USB端口状态超过一定时间没有数据就强制重建连接。我最终在量产板上加了ESD保护软件也做了超时重连双保险后整机跑了半年没再出现设备丢失。另外要提一下系统进入低功耗模式时USB的处理。USB HOST在低功耗模式下必须停掉VBUS和时钟否则电流会超标唤醒后再重新初始化USB外设。这个坑在电池供电的项目里尤其重要我在低功耗唤醒回调里做了完整的外设复位和重新枚举否则设备从睡眠唤醒后USB链路是死的只能断电恢复。6.4 一点个人体会整套方案从立项到稳定运行其实花在协议栈调试上的时间只占一小半更多时间花在电源、信号完整性和软件边界处理上。USB HOST 多路CH340这套架构给我最大的收获是让串口这个资源不再是硬件稀缺资源而是像插U盘一样可以随时扩展的通用资源。后面我又在另一个项目里把这个方案推广到H7上挂了一个7口Hub同时接CH340、MSC存储和HID键盘照样稳定说明这套思路在STM32全系OTG芯片上是可复用的。如果看完这篇文章你想动手试但心里没底我建议从最简单的单CH340 STM32F407最小系统板开始先把枚举打通再谈多路扩展。USB协议栈的坑确实多但只要熬过一次完整的枚举过程后续再碰USB HOST就轻车熟路了。
返回列表