USB CDC库的改造和使用
为了方便使用,对
HAL
库的
USB
设备库例子进行了改造,只保留最核心的
USB
接收和发送功能。如果只是使用
USB
收发数据,可以直接使用本例程,而不用去详细学习
USB
规范。下面是改造过程及使用方法的说明。
一、初始化
HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)函数是初始化
GPIO
接口硬件和时钟的,包括高速模式下的
ULPI
硬件时钟。该函数在
usbd_conf.c
文件中。
二、数据发送
USB
库架构中的数据发送过程 首先,用户数据产生之后,必须要先保存到一个数据缓冲区;然后将缓冲区地址告知
USB
库,并让
USB
库发送数据(不一定每次都成功)。所谓的数据发送过程,实际上是上述过程的后半部分。由于启动新的发送数据过程的时候,
USB
控制器可能还没忙完上次分配的任务,这将导致发送过程失败,需要多次重试,直到成功为止。所以数据产生的过程与数据发送的过程必须相对独立。
数据发送过程又可以分成两个部分:
1
、用户程序将数据缓冲区及数据长度告知
USB
库,启动数据发送过程;
2
、
USB
库在合适的时机自动将缓冲区的数据经由
USB
线发送到主机。第
2
步用户可以暂时不用管,
USB
库已经完成了。在用户程序中只用管第
1
步,这个步骤是通过下面两个函数实现的。
USBD_CDC_SetTxBuffer()和USBD_CDC_TransmitPacket()是启动数据发送过程的一组函数,要成对使用。USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, uint16_t length)函数的作用是将缓存的指针和大小放到USBD_CDC_HandleTypeDef结构变量中暂存。USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev)函数的作用是在
USB
闲下来以后,将暂存的缓存的指针和数据大小告知
USB
发送器,
USB
就会自动发送这些数据。
启动数据发送的时机 数据发送的过程暗示了
USB
控制器什么时候发送数据是用户无法控制的(实际上这是由主机决定的),这与常见的
USART
通讯有很大的不同。用户程序可以在任意时候调用这两个函数将待发送数据提交给
USB
库。
CDC
的例子程序是通过一个
Timer
定时调用这两个函数发送数据的。也有人通过
SOF
同步帧中断回调函数调用这两个函数发送数据的。只要不把
CPU
堵死,频繁点好,这样数据发送的“实时性”略高。
按:从
USB
库的例子看,这两个函数从来就是成对使用的,完全没有必要分成两个函数。而且
UM1734 USB
设备库使用说明书中的使用方法中也仅提到了用USBD_CDC_SetTxBuffer()函数发送数据。可能函数库在设计的初始阶段就没有USBD_CDC_TransmitPacket()函数。很多这样的细节缺陷,让人感觉在设计这个库的架构时,随意性比较大,不够严谨。也许法国人天生浪漫,系统分析师工作时也在任意挥洒奔放的思绪。
注意:USBD_CDC_SetTxBuffer()总是成功的,而USBD_CDC_TransmitPacket()会失败的。只有在USBD_CDC_TransmitPacket()函数成功返回之后,数据才会经
USB
发送出去。
STM32F4Cube
库的
CDC
类例子里有一个小坑:没有对发送结果做任何处理。在
CDC
类的例子里使用的是环形缓存,
USB
忙的时候发送失败了也没有问题,因为
CDC
例子中的缓存指针没有修改,下次USBD_CDC_SetTxBuffer()还会把没有传送的数据再传送一次。但是,如果实用的是乒乓缓存,或者是多个缓存,就不能认为调用过USBD_CDC_SetTxBuffer()发送数据就当完事了,一定要在USBD_CDC_TransmitPacket()函数成功返回之后才可以切换缓冲数据块,否则那个缓冲区的指针就会在下次调用USBD_CDC_SetTxBuffer()被覆盖掉,数据也就神秘失踪了。
CDC
类的例子里使用最简单的环形缓存存储数据,其缺点是当数据填充到缓存的速度大于发送速度时,旧的数据会被覆盖,而发送过程对此一无所知,数据会神秘失踪。
三、数据接收
要实现数据接收,首先在初始化的时候要通过USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff)函数给
USB
库指定一个接收缓冲区,让
USB
控制器收到数据以后可以往里填。
接收数据是通过响应CDC_Itf_Receive(uint8_t* Buf, uint32_t *Len)回调函数实现的,每次
USB
控制器收到数据后都会调用这个回调函数,从
USB
收到的数据就存放在参数表指向的数据缓冲区,参数还指明了收到的数据长度。CDC_Itf_Receive()函数在接收标记完这些数据之前别急着返回,不然你可能就摸不到剩下的数据了。USBD_CDC_ReceivePacket()函数的作用是复位
OUT
端点接收缓冲区,CDC_Itf_Receive()函数在接收完数据之后要调用该函数复位缓冲区。
有一点需要注意,CDC_Itf_Receive()函数是
USB
中断回调函数,因此应该尽快返回,只能在函数内对数据做简单的标记或处理。如果要对数据进行繁重的处理,应该在main()函数或工作进程内处理。
请忽略
UM1734 USB
设备库使用说明书第
49
页关于接收数据的说明文档,呵呵,浪漫的法国人。
四、使用方法示例
1、项目文件的存放位置
将解压生成的USB_Device目录放到STM32Cube_FW_F4_V1.3.0\Projects\ STM32F4-Discovery\Applications
目录下。
Keil
项目文件为
USB_Device\CDC_Standalone\ MDK-ARM\Project.uvproj
。如果是阅读和修改程序,建议使用
Visual Studio 2010
打开
USB_Device\CDC_Standalone\Visual Studio Project\Project.sln
文件。装备了
Visual Assist
的
VS
开发环境,其对编程的辅助作用是
Keil
望尘莫及的,
特此鸣谢Ka_Chen,他做的Keil项目转VS项目工具软件用起来非常地方便。
2
、
USB-->PC
发送数据
发送数据相对比较简单,用UsbSendData()函数发送数据。该函数在
main.c
文件中。只有在UsbSendData()函数返回值为
USBD_OK
时,发送才算完成。
3
、
PC-->USB
接收数据
这个过程略微复杂,需要从回调函数CDC_Itf_Receive()获取
PC
发送来的数据,并在合适的地方处理数据。我的例子中是在main()函数中处理。CDC_Itf_Receive()函数在
usbd_cdc_interface.c
文件中。
4
、实测效果
USB_Device\SerialApp
目录下有通过虚拟串口传输数据的
VC++
例子。使用
USB_Device\SerialApp\Release\SerialApp.exe
程序可以测试串口读写的速度。
全速模式,使用
STM32F4-Discovery
开发板。
OUT
速度约为
950KB/s
,
IN
速度约为
820KB/s
。
高速模式,使用自制的开发板,以
STM32F407IGT6
为核心,以
USB3300
为
PHY
,使用
24M
主晶振,并通过
MCO1
向
USB3300
提供时钟信号。
OUT
速度约
29MB/s
,
IN
速度约
16MB/s
。
(
USB2.0
提速贴:
http://itbbs.pconline.com.cn/soft/16281908.html
。试验过,没有效果。)
五、修改内容清单
本程序改自
STM324xG_EVAL
的CDC Device
示例。以下罗列了修改的内容:
1
、
usbd_cdc_interface.c
删除
USART
相关配置
CDC_Itf_Init(void)仅保留USBD_CDC_SetRxBuffer()配置USB接收缓冲区。
CDC_Itf_DeInit(void)内容全部删除。
CDC_Itf_Control(void)是
CDC
类的控制处理,保留原状。
CDC_Itf_Receive()是接收数据的回调函数,一定要根据需要修改。
2、usbd_conf.c
HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)是初始化
USB
相关硬件接口的,包括
GPIO
接口硬件和时钟(含
UPLI
硬件时钟)。
USBD_LL_Init(USBD_HandleTypeDef *pdev)设置内核初始化参数,在此处设置
DMA
选项。
3、usbd_desc.c
不用修改。
4、stm32f4xx_it.c
删除不再使用的中断函数。包括:
USARTx_DMA_TX_IRQHandler(void)
USARTx_IRQHandler(void)
USARTx_IRQHandler(void)
5、system_stm32f4xx.c
要在这里面设置晶振频率HSE_VALUE。除了这里,还有两个位置要设置晶振频率相关内容:
① main.c
中的SystemClock_Config(void)函数中要设置与晶振匹配的RCC_OscInit.PLL.PLLM
② 项目设置
-->Target
栏中修改晶振频率
Xtal
。
6、stm32f4xx_hal_msp.c
其它硬件初始化用的。现在不需要使用
USB
以外的硬件,就用不着它了,从项目中删除
http://bbs.21ic.com/icview-811704-1-1.html