打印
[STM32F4]

STM32F4Cube库CDC类试用手记(含USB全速/高速和VC测试程序)

[复制链接]
119596|749
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 碧云天书 于 2015-11-6 00:43 编辑

  首先说整体主观感受:比较起以前的USB设备库,HAL库的USB库有诸多改进,看来ST发心还是蛮大的。总体上是个好的库,说明文档需要再完善。
  USB库提供了一些接口函数,你可以根据需要填充这些函数,来完成你需要的功能。对使用者来说,这个库是以“完形填空”的方式设计的架构。也就是说,必须把你的需要嵌合到给定的架构中,不要越雷池半步。
  这个库的说明文档已经默认读者熟知USB规范了,所以在阅读和使用HAL库之前,最好读一下USB规范。

  下面跟帖是我试用CDC设备类的笔记,与大家一起分享。如果有人只需要用USB收发数据,可以直接使用本示例的收发模块,无须研读USB规范。
  程序改自STM324xG_EVAL的CDC Device示例,可以在STM32F4-Discovery开发板(全速)和自制高速开发板上(也可以用STM32F4-Discovery开发板,加一个PHY模块就行)运行。
  实测传输效果,全速下OUT速度约为950KB/sIN速度约为820KB/s。高速下OUT速度约29MB/sIN速度约16MB/s,条件好时可以达到40MB/s

  (因为以前的版本在压缩包里没有带库,有些朋友用起来比较困难,因此将最新版的STM32CubeF4 v1.8.0库打包进附件中了。)
  源代码: USB Device v1.1.rar (1.87 MB)
评论
teltium 2020-3-22 18:49 回复TA
竟然没有收藏帖子的功能 还是 
评分
参与人数 11威望 +19 收起 理由
moticsoft + 1
army_niuniu + 1 很给力!
song147liang + 1 淡定
0aaa + 1 厉害了word哥
衔胆栖冰 + 1 很给力!

查看全部评分

沙发
碧云天书|  楼主 | 2014-10-9 10:07 | 只看该作者
本帖最后由 碧云天书 于 2015-11-6 00:49 编辑

USB CDC库的改造和使用


为了方便使用,对HAL库的USB设备库例子进行了改造,只保留最核心的USB接收和发送功能。如果只是使用USB收发数据,可以直接使用本例程,而不用去详细学习USB规范。下面是改造过程及使用方法的说明。



一、初始化
HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)函数是初始化GPIO接口硬件和时钟的,包括高速模式下的ULPI硬件时钟。该函数在usbd_conf.c文件中。



二、数据发送
USB库架构中的数据发送过程  首先,用户数据产生之后,必须要先保存到一个数据缓冲区;然后将缓冲区地址告知USB库,并让USB库发送数据(不一定每次都成功)。所谓的数据发送过程,实际上是上述过程的后半部分。由于启动新的发送数据过程的时候,USB控制器可能还没忙完上次分配的任务,这将导致发送过程失败,需要多次重试,直到成功为止。所以数据产生的过程与数据发送的过程必须相对独立。

数据发送过程又可以分成两个部分:1、用户程序将数据缓冲区及数据长度告知USB库,启动数据发送过程;2USB库在合适的时机自动将缓冲区的数据经由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设备库使用说明书May 2014Rev 149页关于接收数据的说明文档,呵呵,浪漫的法国人。



四、使用方法示例

1、项目文件的存放位置
<CDC_Standalone>目录下为STM32F407程序源代码。Keil项目文件为CDC_Standalone\ MDK-ARM\Project.uvproj。如果是阅读和修改程序,建议使用Visual Studio 2010打开CDC_Standalone\Visual Studio Project\Project.sln文件。装备了Visual Assist的VS开发环境,其对编程的辅助作用是Keil望尘莫及的,特此鸣谢Ka_Chen,他做的Keil项目转VS项目工具软件用起来非常地方便
<SerialApp>目录下为上位机通信测试程序,用VS2010编写。
<MSTM32Cube_FW_F4_V1.8.0>目录下是删节版STM32F4Cube HAL库,删除了很多与本示例无关的文件。

2USB-->PC发送数据
发送数据相对比较简单,用UsbSendData()函数发送数据。该函数在main.c文件中。只有在UsbSendData()函数返回值为USBD_OK时,发送才算完成。

3PC-->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/sIN速度约为820KB/s
高速模式,使用自制的开发板,以STM32F407IGT6为核心,以USB3300PHY,使用24M主晶振,并通过MCO1USB3300提供时钟信号。OUT速度约29MB/sIN速度约16MB/s
USB2.0提速贴:http://itbbs.pconline.com.cn/soft/16281908.html。试验过,没有效果。)



五、修改内容清单
本程序改自STM324xG_EVALCDC Device示例。以下罗列了修改的内容:

1usbd_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以外的硬件,就用不着它了,从项目中删除

7startup_stm32f407xx.s
HAL库默认的Heap_Size0x200,要扩大为0x1500。实际上STM32F4Cube HAL库的USB设备库例子已经对此做了修改,但是,如果要自己用Stm32CubeMx生成CDC类程序的时侯,一定要记得手动修改Heap_Size
HAL库默认的Stack_Size也从0x400扩大为0x500

使用特权

评论回复
评分
参与人数 1威望 +6 收起 理由
dong_abc + 6
板凳
碧云天书|  楼主 | 2014-10-9 10:24 | 只看该作者
有图有真相


使用特权

评论回复
地板
wxlhonker| | 2014-10-9 10:27 | 只看该作者
好贴要顶,LZ牛啊

使用特权

评论回复
5
想做大牛的小马| | 2014-10-9 10:28 | 只看该作者
牛啊,赞~

使用特权

评论回复
6
碧云天书|  楼主 | 2014-10-9 10:44 | 只看该作者
高速USB电路图

使用特权

评论回复
评论
HBC666 2023-6-29 18:49 回复TA
发送大数据包,比如10MB,会存在丢包问题吗?我现在用的GD32做USB CDC,就存在这样的情况,好像规避不了? 
7
碧云天书|  楼主 | 2014-10-9 10:57 | 只看该作者
补丁。这么快就要打补丁了:L
main.c文件的UsbSendData()函数的返回值注释有些错误,忘记改了。请下载压缩包的同仁用下面的main.c文件替换原文件。
main.rar (3.5 KB)

使用特权

评论回复
8
icecut| | 2014-10-9 11:50 | 只看该作者
呵呵。试试stm32cube mx自动生成的cdc。我试过fs的。

使用特权

评论回复
9
lio| | 2014-10-9 12:10 | 只看该作者
这个太牛了吧

使用特权

评论回复
10
mmuuss586| | 2014-10-9 12:39 | 只看该作者

这个速度比较给力啊

使用特权

评论回复
11
bobde163| | 2014-10-9 13:13 | 只看该作者
厉害啊

使用特权

评论回复
12
碧云天书|  楼主 | 2014-10-9 14:08 | 只看该作者
本帖最后由 碧云天书 于 2014-10-9 15:02 编辑
icecut 发表于 2014-10-9 11:50
呵呵。试试stm32cube mx自动生成的cdc。我试过fs的。


试过,失败了:L,原因没细究


自动生成的cdc不太好用,一开始我也是自动生成,发现没法运行。
我一开始就是想省事儿,不去看USB规范,直接使用自动生成的程序。但发现Cube自动生成的程序也不够给力,后来还是不得不把USB规范梳理了一遍。
我发这个贴的一个重要目的就是给那些需要使用USB传输数据,又没时间钻研USB规范的朋友一个方便。让他们拿上程序就可以直接用,就像使用串口一样方便。
本来就是传输个数据嘛,整这么多麻烦事儿没必要。
自动生成的cdc是单缓存OUT传输。为了提高速度,本贴的程序还用了双缓存接收数据。也是为了方便需要的朋友。

HAL库比以前的固件库好多了,其中的USB设备库有质的飞跃。但是,最主要的问题还是文档写得不好,初上手时,完全不知道该从哪儿开始。如果没有把HAL USB设备库仔细地梳理过一遍,根本就不知道自动生成的程序里,收发数据的函数在usbd_cdc_if.c文件中,也无从知道该从哪里下手修改,哪些该改,哪些不该改。
其实这个问题也很简单,就俩函数,在readme.txt或者main.c里带一句就行。可是,ST就是不说。

使用特权

评论回复
13
ticomi| | 2014-10-9 14:18 | 只看该作者
这个不需要在计算机端安装驱动程序吗?单纯使用串口的速度能有这么高吗?很想请教下这个问题!

使用特权

评论回复
14
碧云天书|  楼主 | 2014-10-9 14:47 | 只看该作者
ticomi 发表于 2014-10-9 14:18
这个不需要在计算机端安装驱动程序吗?单纯使用串口的速度能有这么高吗?很想请教下这个问题! ...

要装驱动的。要在PC端安装ST的虚拟串口驱动程序。下载链接:http://www.stmcu.org/download/index.php?act=down&id=5041
这不是串口,而是USB虚拟串口。串口的速度就比较低了。

使用特权

评论回复
评论
15150006197 2022-6-30 10:28 回复TA
你好,博主,请问要从哪里下载什么驱动,之前你发的网址打不开了,谢谢 
15
bhkjcg| | 2014-10-9 15:17 | 只看该作者
好贴要顶

使用特权

评论回复
16
zook0k| | 2014-10-9 16:42 | 只看该作者
支持下,有空研读下,谢谢楼主

使用特权

评论回复
17
ticomi| | 2014-10-9 16:55 | 只看该作者
碧云天书 发表于 2014-10-9 14:47
要装驱动的。要在PC端安装ST的虚拟串口驱动程序。下载链接:http://www.stmcu.org/download/index.php?ac ...

学习了,非常感谢!我一直认为需要在计算机端开发驱动程序适合ST的CDC通讯,如果可以使用ST的虚拟串口技术真是很方便的!

非常感谢!!

使用特权

评论回复
18
franki_18| | 2014-10-9 19:02 | 只看该作者
牛人 ,顶一个  

使用特权

评论回复
19
icecut| | 2014-10-9 21:08 | 只看该作者
碧云天书 发表于 2014-10-9 14:08
试过,失败了,原因没细究

呵呵。通用的东西,总会有坑。
你的东西给更多人用的时候也会遇到这种问题。

还是让他们自己看usb协议好。永远逃不了。

使用特权

评论回复
20
1240335988| | 2014-10-9 21:28 | 只看该作者
这般功夫如何练就啊

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:红尘俗世偏逍遥 看风乍起 笑雨正飘

24

主题

330

帖子

23

粉丝