本帖最后由 zhs2007 于 2017-8-24 15:26 编辑
这几年STM32F系列单片机很是流行,本人也用STM32F1XX、STM32F4XX做了几个产品和项目,作为一个从51时代走过来的码农,深感ARM核的强大。
STM32F单片机的成功,我归结为以下几方面:丰富的外设、适中的价格、强大的运算(相比51)、低功耗、良好的生态。
以上这些是本帖的题外话。
下面我就以STM32F4XX做的一个项目为例,说一下我对STM32F4XX USB模块硬件和原厂提供的USB驱动的认识和感受。
1. 先说说STM32F4XX USB硬件,片内有两个USB模块,USB_FS全速模块,USB_HS高速模块,USB_HS模块需要外接ULPI USB PHY,估计是集成的代价较大。这两个模块硬件寄存器相当多,真是个重量级的大家伙,看了都头大。这些寄存器不仅多,而且分布的有点乱,光是名字中含有CFG的就有好几个,相信是有优化的空间的。
2. 原厂提供的USB底层驱动,真的不敢恭维,恕我说的直接一点,就如裹脚布一般又臭又长!
以USB HS DEVICE HID示例来说,相关的C文件有10个,粗略统计这10个C文件中总行数6300行左右(包含注释和空行),头文件懒得去统计了,要想把这些程序完整的梳理一遍,谈何容易啊?我做的项目是用USB_HS模块外接USB3300 PHY芯片做高速HID设备,想在原厂驱动基础上做些修改,真的是太困难了,无从下手。就算改改满足要求了,对于我这样一个略有强迫症的人来说,也很难忍受程序中有那么多不明不白的代码。
原厂USB底层驱动的问题,我总结为以下几点:
(1)文件模块过多。
并且这些文件功能作用不是很明确,行话叫“内聚不够高”。
(2)结构体嵌套层数过多。
啥玩意都塞进USB_OTG_CORE_HANDLE结构体中, 啥函数都要传递个USB_OTG_CORE_HANDLE*pdev参数。
(3)函数调用层数过多。
发送或接收个数据包,最深处应该不下10层函数调用,特别是含有很多函数指针的调用,想调试跟踪一下,简直就像捉迷藏。
(4)函数指针过多。
我不反对适当的地方用下函数指针作为回调函数,例如有些方案中利用函数指针注册中断或事件处理函数。
但在STM32F4XX USB驱动中,用了过多的函数指针,并且还将这些函数指针“巧妙地”塞进了一层又一层的结构体中,导致用户很难理解在何时何处调用了这些函数。
(5)基于中断的程序结构。
基于中断来实现USB底层驱动,这似乎已经成了很多厂商的思维定势。
这种方式,相当于USB驱动与main()函数主流程是两个并发运行“线程”,那么必然就会带来双线程之间的通信与同步问题,并且造成程序执行流程与状态的复杂。
从我理解与实践来看,USB DEVICE底层驱动是完全可以不使用中断的,并且也丝毫不会影响通信速度。
main()函数中每隔几毫秒检查一下,是否有数据包到来,有就处理,没有就该干嘛干嘛,退一步讲,就算有个数据包来了,没有及时处理,也不会引起逻辑错误,最多也就总线上多点NAK罢了,这一点是硬件的基本功能。
3. 我最终还是做了一个艰难的决定,基于硬件寄存器,重写一套USB底层驱动,目标是要简洁、稳定、便于调试。
重写驱动的过程比较曲折,也发现了一个疑似USB硬件BUG,发邮件给原厂确认,至今杳无音信,NND差评!
经过将近一个月的奋战,最终还是重写出了一个还算满意的驱动,原厂6000多行的代码,被我变成了600多行,虽说过程痛苦,但后面的项目还都可以用。
重写的高速USB HID驱动,共包含两个C文件:
usb_dcd.c (300行左右),负责将硬件寄存器抽象为几个基本函数,主要包含初始化函数、SETUP接收函数、控制接收函数、控制发送函数、INT端点发送函数等。这个模块提供的接口函数中有的是非阻塞的,有的是阻塞的。
usb_hid.c (300行左右),负责枚举与HID协议相关命令处理,调用SETUP接收函数,根据8字节SETUP包,再调用控制接收函数,或调用控制发送函数。该文件中代码其实只有100行左右,其他为描述符的定义。
在这个驱动基础上,只需稍加改动,就可以实现键盘、鼠标、MSC等其他协议。
写的比较乱,纯属一家之言,不指望原厂能看到改进,只希望给大家提供一个前车之鉴!
|
楼主厉害了