OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 127|回复: 0

《STM32H7R7开发指南 V1.1 》第六十八章 USB读卡器(Slave)实验

[复制链接]

1359

主题

1375

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5804
金钱
5804
注册时间
2019-5-8
在线时间
1593 小时
发表于 昨天 10:02 | 显示全部楼层 |阅读模式
第六十八章 USB读卡器(Slave)实验

1)实验平台:正点原子STM32H7R7开发板

2)章节摘自【正点原子】STM32H7R7开发指南 V1.1

3)购买链接: https://detail.tmall.com/item.htm?id=820823382459

4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h7rx.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)正点原子STM32开发板技术交流群:756580169


2.jpg

3.png

STM32H7R7系列芯片都自带了USB OTG FS和USB OTG HS(自带高速PHY芯片,速度可达480Mbps),支持USB Host和USB Device,所有USB相关例程,除了USB声卡实验使用USB OTG FS,其他USB例程均使用USB OTG HS实现。
下面,我们将介绍如何使用USB OTG HS在STM32H7R7开发板上实现一个USB读卡器。
本章分为如下几个小节:
68.1 USB简介
68.2 硬件设计
68.3 程序设计
68.4 下载验证


68.1 USB简介
USB,即通用串行总线(Universal Serial Bus),包括USB协议和USB硬件两个方面,支持热插拔功能。现在日常生活的很多方面都离不开USB的应用,如充电和数据传输等方面的应用。
USB经过多次修改,1996年确定了初始规范版本USB1.0,目前由非盈利组织USB-IF(https://www.usb.org)管理。STM32自带的USB符合USB2.0规范,故2.0版本仍是本文的重点介绍对象。


68.1.1 USB简介
USB本身的知识体系非常复杂,本节只能作一点知识点的引入。本书篇幅有限,不可能在这里详细介绍,想更系统地学习USB的知识可以参考《圈圈教你玩 USB》、塞普拉斯提供的《USB 101:通用串行总线 2.0 简介》等文献,下面我们一起来看USB的简单特性:
USB的硬件接口
USB协议有漫长的发展历程,为的不同的场合和硬件功能而发展出不同的接口:Type-A、Type-B、Type-C,Type-C规范碰巧是跟着USB3.1的规范一起发布的。常见的接口类型列出如图68.1.1所示:


第六十八章 USB读卡器778.png
图68.1.1 常见的USB连接器的形状

USB 发展到现在已经有 USB1.0/1.1/2.0/3.x/4等多个版本。目前用的最多的就是版本 USB1.1和USB2.0,USB3.x/USB4目前也在加速推广。从图中可以发现不同的版本的USB接口内的引脚数量是有差异的。USB3.0以后为了提高速度,采用了更多数量的通讯线,比如同样的是Type A接口,USB2.0版本内部只有四根线,采用半双工式广播式通讯,USB3.0版本则将通讯线提高到了9根,并可以支持全双工非广播式的总线,允许两个单向数据管道分别处理一个单向通信。
USB2.0常使用四根线:VCC(5V)、GND、D+(3.3V)和D-(3.3V) (注:五线模式多了一个DI脚用于支持OTG模式,OTG为USB主机+USB设备双重角色),其中数据线采用差分电压的方式进行数据传输。在USB主机上,D-和D+都是接了15K的电阻到地的,所以在没有设备接入的时候,D+、D-均是低电平。而在USB设备中,如果是高速设备,则会在D+上接一个1.5K的电阻到3.3V,而如果是低速设备,则会在D-上接一个1.5K的电阻到3.3V。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。
关于USB硬件还有更多具体的细节规定,硬件设计时需要严格按照USB的器件的使用描述和USB标准所规定的参数来设计。
USB速度
USB规范已经为USB系统定义了以下四种速度模式:低速(Low-Speed)、全速(Full-Speed)、高速(Hi-Speed)和超高速(SuperSpeedUSB)。接口的速度上限与设备支持的USB协议标准和导线长度、阻抗有关,不同协议版本对硬件的传输线数量、阻抗等要求各不相同,各个版本的能达到的理论速度上限对应如图68.1.2。


第六十八章 USB读卡器1555.png
图68.1.2 USB协议发展与版本对应的速度

USB端口和连接器有时会标上颜色,以指示USB规格及其支持的功能。这些颜色不是USB规范所要求的,并且在设备制造商之间不一致。例如,常见的支持USB3.0的U盘和电脑等设备使用蓝色指示,英特尔使用橙色指示充电端口等。
USB系统
USB系统主要包括三个部分:控制器(Host Controller)、集线器 (Hub) 和USB设备。
控制器(Host Controller),主机一般可以有一个或多个控制器,主要负责执行由控制器驱动程序发出的命令。控制器驱动程序(Host Controller Driver)在控制器与USB设备之间建立通信信道。
集线器(Hub)连接到USB主机的根集线器,可用于拓展主机可访问的USB设备的数量。
USB设备(USB Device)则是我们常用的如U盘,USB鼠标这类受主机控制的设备。
USB通讯
USB针对主机、集线器和设备制定了严格的协议。概括来讲,通过检测、令牌、传输控制、数据传输等多种方式,定义了主机和从机在系统中的不同职能。USB系统通过“管道”进行通讯,有“控制管道”和“数据管道”两种,“控制管道”是双向的,而每个“数据管道”则是单向的,这种关系如图68.1.3所示。


第六十八章 USB读卡器2121.png

图68.1.3 USB管道模型

USB通讯中的检测和断开总是由主机发起。USB主机与设备首次进行连接时会交换信息,这一过程叫“USB枚举”。枚举是设备和主机间进行的信息交换过程,包含用于识别设备的信息。此外,枚举过程主机需要分配设备地址、读取描述符(作为提供有关设备信息的数据结构),并分配和加载设备驱动程序,而从机需要提供相应的描述符使主机知悉如何操作此设备。整个过程需要数秒时间。完成该过程后设备才可以向主机传输数据。数据传输也有规定的三种类型,分别是:IN/读取/上行数据传输、OUT/写入/下行数据传输、控制数据传输。
USB通过设备端点寻址,在主机和设备间实现信息交流。枚举发生前有一套专用的端点用于与设备进行通信。这些专用的端点统称为控制端点或端点0,有端点0 IN和端点0 OUT两个不同的端点,但对开发者来说,它们的构建和运行方式是一样的。每一个USB设备都需要支持端点0。因此,端点0不需要使用独立的描述符。除了端点0外,特定设备所支持的端点数量将由各自的设计要求决定。简单的设计(如鼠标)可能仅要一个IN端点。复杂的设计可能需要多个数据端点。
USB规定的数据4种数据传输方式也是通过管道进行,分别是控制传输(Control Transfer)、中断传输(Interrupt Transfer)、批量传输或叫块传输(Bulk Transfer)、实时传输或叫同步传输(Isochronous Transfer ),每种模式规定了各自通讯时使用的管道类型。
关于USB还有很多更详细的时序和要求,像USB描述符、VID/PID的规定、USB类设备和调试等,因为USB2.0和之后的版本有差异,这里就不再为大家列举了,ST对USB2.0也有专门的培训资料,这部分我们也放到“光盘资料A盘→8,STM32参考资料→2,STM32 USB学习资料”中了,感兴趣的朋友自行去查阅更的USB的相关扩展知识,我们对USB的简介就到这里。


68.1.2 STM32H7R7的USB特性
USB发展到现在已经有USB1.0/1.1/2.0/3.0/4等多个版本。目前用的最多的就是USB1.1和USB2.0,USB3.0目前已经开始普及。STM32H7R7自带的USB符合USB2.0规范。
标准USB共四根线组成,除VCC/GND外,另外为D+和D-,这两根数据线采用的是差分电压的方式进行数据传输的。在USB主机上,D-和D+都是接了15K的电阻到地的,所以在没有设备接入的时候,D+、D-均是低电平。而在USB设备中,如果是高速设备,则会在D+上接一个1.5K的电阻到VCC,而如果是低速设备,则会在D-上接一个1.5K的电阻到VCC。这样当设备接入主机的时候,主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。接下来,我们简单介绍一下STM32的USB控制器。
STM32H7R7系列芯片自带有USB2 OTG FS(全速)和USB OTG HS(高速,带PHY)。
STM32H7R7的USB OTG FS是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机功能,完全符合USB 2.0规范的On-The-Go补充标准。此外,该控制器也可配置为“仅主机”模式或“仅从机” 模式,完全符合USB 2.0规范。
STM32H7R7的USB OTG HS主要特性可分为三类:通用特性、主机模式特性和从机模式特性。

1、通用特性
经USB-IF认证,符合通用串行总线规范第2.0版
OTG_HS支持以下物理层接口:
1,一个用于内部HS PHY的UTMI接口
包括对可选的on-go(OTG)协议的详细支持(PHY)
在on-go Supplement Rev 2.0规范中
1,集成支持A-B设备识别(ID线)
2,允许主机在OTG应用程序中关闭Vbus以节省电池电量
3,支持OTG监控Vbus水平与内部比较器
可通过软件配置为以下角色:
1,USB On-Go全速双角色设备
支持HS SOFs和LS Keep-alive令牌
1,SOF脉冲可通过PAD输出
2,SOF脉冲从内部连接到定时器 x (TIMx)
3,可配置的帧周期
1,可配置的帧结束中断
OTG_HS在DMA模式中嵌入了一个具有阈值支持和软件可选择的AHB突发类型的内部DMA
具有省电功能,例如在USB挂起期间停止系统、关闭数字模块时钟、对PHY和DFIFO电源加以管理
具有4KB的专用RAM,采用高级的FIFO控制
1,可将 RAM 空间划分为不同FIFO,以便灵活有效地使用RAM
2,每个FIFO可存储多个数据包
3,动态分配存储区
4,FIFO大小可配置为非2的幂次方值,以便连续使用存储单元
保证最大USB带宽高达一帧(1毫秒),无需系统干预
支持充电端口检测,详见1.2版电池充电规范

2、主机(Host)模式特性
通过外部电荷泵生成VBUS电压。
多达16个(HS)主机通道(管道):每个通道都可以动态实现重新配置,可支持任何类型的USB 传输。
内置硬件调度器可:
1,在周期性硬件队列中存储多达16(HS)个中断加同步传输请求
管理一个共享RX FIFO、一个周期性TX FIFO和一个非周期性TX FIFO,以有效使用USB数据RAM。

3、从机(Slave/Device)模式特性
1个双向控制端点0
8个IN 端点 (EP),可配置为支持批量传输、中断传输或同步传输
8个OUT 端点(EP),可配置为支持批量传输、中断传输或同步传输
管理一个共享Rx FIFO和一个Tx-OUT FIFO,以高效使用USB数据RAM
管理多达9个专用Tx-IN FIFO(分别用于每个使能的IN EP),降低应用程序负荷支持软断开功能。
STM32H7R7 USB OTG HS框图如图68.1.1所示:


第六十八章 USB读卡器4530.png
图68.1.1 USB OTG 框图

对于USB OTG HS功能模块,STM32H7R7通过AHB总线访问(AHB频率必须大于30Mhz),另外,USB OTG的内核时钟必须是48Mhz,由RCC_D2CCIP2R寄存器的USBSEL[1:0]位选择:00,禁止USB内核时钟;01,USB内核时钟来自pll1_q_ck;10,USB内核时钟来自pll3_q_ck;11,USB内核时钟来自hsi48_ck;因为pll1_q_ck和pll3_q_ck很有可能被其他外设用作时钟,不方便设置为48Mhz,因此我们一般使用hsi48_ck作为USB OTG内核时钟(USBSEL[1:0]=11),这样就不会受到其他外设的影响。
要正常使用STM32H7R7的USB,就得编写USB驱动,而整个USB通信的详细过程是很复杂的,本书篇幅有限,不可能在这里详细介绍,有兴趣的朋友可以去看看电脑圈圈的《圈圈教你玩USB》这本书,该书对USB通信有详细讲解。如果要我们自己编写USB驱动,那是一件相当困难的事情,尤其对于从没了解过USB的人来说,基本上不花个一两年时间学习,是没法搞定的。不过,ST提供了我们一个完整的USB OTG 驱动库(包括主机和设备),通过这个库,我们可以很方便的实现我们所要的功能,而不需要详细了解USB的整个驱动,大大缩短了我们的开发时间和精力。
STM32H7R7的USB例程全部是以HAL库的形式提供,为了简化开发设计,我们直接使用ST提供的HAL库版本USB驱动库来设计相关例程。
ST提供的H7 USB OTG库和相关参考例程在en.stm32cubeh7rs-v1-0-0.zip里面可以找到,该文件可以在www.st.com网站搜索:cubeh7rs找到。不过,我们已经帮大家下载到开发板光盘:8,STM32参考资料 1,STM32CubeH7RS固件包 en.stm32cubeh7rs-v1-0-0.zip。解压可以得到STM32H7RS的Cube固件支持包:STM32Cube_FW_H7RS_V1.0.0,该文件夹里面包含了H7RS的USB 主机(Host)和从机(Device)驱动库,并提供了21个例程供我们参考,如图68.1.2所示:


第六十八章 USB读卡器5508.png
图68.1.2 ST提供的USB OTG库

标号1是H7RS USB从机驱动库;
标号2是H7RS USB主机驱动库;
标号3是H7RS USB从机例程(共15个);
标号4是H7RS USB主机例程(共6个);
整个USB OTG库的使用和例程说明,可以参考ST官方提供的:UM1734(从机)和UM1720(主机)这两个文档(在光盘:8,STM32参考资料2,STM32 USB 学习资料 文件夹),这两个文档详细介绍了USB OTG库的各个组成部分以及所提供的例程使用方法,有兴趣学习USB的朋友,这个文档是必须仔细看的。
本实验我们参考的文件有:STM32Cube_FW_H7RS_V1.0.0\Middlewares\ST \STM32_USB_Device_Library和STM32_USB_Host_Library,以实现USB读卡器功能。


68.2 硬件设计
1. 例程功能
本实验代码,开机的时候先检测SD卡、NOR FLASH和SD NAND是否存在,如果存在则获取其容量,并显示在LCD上面(如果不存在,则报错)。之后开始USB配置,在配置成功之后就可以在电脑上发现三个可移动磁盘。USB正在读写会在液晶上显示出来。
LED0闪烁,提示程序运行。

2. 硬件资源
1)LED灯
       LED0:LED0 – PD14
       LED1:LED1 – PC0
2)串口1(PB14/PB15连接在板载USB转串口芯片CH340上面)
3)正点原子2.8/3.5/4.3/7/10寸TFTLCD模块(包括MCU屏和RGB屏,都支持)
4)USB_SLAVE接口(D+/D-连接在PM6/PM5上)
5)norflash
6)SD卡,通过SDMMC(SDIO_D0~D4(PC8~PC11),
       SDMMC_SCK(PC12),SDMMC_CMD(PD2))连接
7)SD NAND

3. 原理图
开发板采用的是Type-C USB接头,用来和电脑的USB相连接,Type-C USB接口与STM32的连接电路图,如下图所示:


第六十八章 USB读卡器6383.png
图68.2.3.1 Type-C USB接口与STM32的连接电路图

从上图可以看出,USB座没有直接连接到STM32H7R7上面的,而是通过P11转接,所以我们需要通过跳线帽将DM和DP 分别接到HS D-和HS D+,如图68.2.3.2所示:

第六十八章 USB读卡器6510.png
图68.2.3.2硬件连接示意图

需要注意的是:这个Type-C USB座和USB-A座(USB_HOST)是共用D+和D-的,所以他们不能同时使用。这个在使用的时候,要特别注意!!本实验测试时,USB_HOST不能插入任何USB设备!另外,如果只有STM32H7R7核心板的,可以利用核心板上面的Type-C USB接电脑,同样也实现本例程的功能。

68.3 程序设计

68.3.1 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。
本实验,我们在:实验42 SD卡实验的基础上修改, 代码移植自ST官方例程:STM32Cube_FW_H7RS_V1.0.0\Projects\STM32H7S78-DK\Applications\USB_Device\MSC_Standalone。该目录下提供了三种开发环境的工程:EWARM、MDK和STM32CubeIDE,我们使用的是MDK,打开MDK工程,然后就可以知道和USB相关的代码有哪些,如图68.3.2.1所示:


第六十八章 USB读卡器6961.png
图68.3.2.1 ST官方例程USB相关代码

有了这个官方例程做指引,我们就知道具体需要哪些文件,从而实现本章例程。
首先,在本例程(即实验42 SD卡实验)工程的Middlewares文件夹下面,新建一个USB文件夹,并拷贝官方USB驱动库相关代码到该文件夹下,即拷贝:光盘→8,STM32参考资料→1,STM32CubeH7RS固件包→STM32Cube_FW_H7RS_V1.0.0→Middlewares→ST文件夹下的: STM32_USB_Device_Library、STM32_USB_Host_Library两个文件夹及源码拷贝到该文件夹下面。
然后,在USB文件夹下,新建一个USB_APP文件夹用于存放MSC实现相关代码,即:STM32Cube_FW_H7RS_V1.0.0→Projects→STM32H7S78-DK→Applications→USB_Device→MSC_Standalone→Appli→Src下的部分代码:usbd_conf.c、usbd_storage_if.c和usbd_desc.c等3个.c文件,同时拷贝:STM32Cube_FW_H7RS_V1.0.0ProjectsSTM32H7S78-DKApplicationsUSB_ DeviceMSC_StandaloneAppliInc下面的:usbd_conf.h、usbd_storage.h和usbd_desc.h等三个文件到USB_APP文件夹下。
由于官方提供的USB_APP文件不太好用,我们正点原子团队对usbd_conf_template.c、usbd_desc_template.c和usbd_msc_storage_template.c这三个模板文件进行了修改并且重新命名,所以移植过程中直接复制我们例程中的代码。我们这里只是提下这些文件的来源,感兴趣的可以自己尝试重写,最后USB_APP文件夹下的文件如图68.3.2.2所示:


第六十八章 USB读卡器7802.png
图68.3.2.2 USB_APP代码

之后,根据ST官方MSC例程,在我们本章例程的基础上新建分组添加相关代码,具体细节请参考例程,这里就不详细介绍了,添加好之后,如图68.3.2.3所示:

第六十八章 USB读卡器7902.png
图68.3.2.3 添加USB驱动等相关代码

1. USB驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。接下来我们看看USB_APP里面的几个.c文件:
usbd_conf.c提供了USB设备库(从机库,下同)的回调及MSP初始化函数,当USB状态机处理完不同事务的时候,会调用这些回调函数,我们通过这些回调函数,就可以知道USB当前状态,比如:是否枚举成功了?是否连接上了?是否断开了?等,根据这些状态,用户应用程序可以执行不同操作,完成特定功能。我们重点介绍3个函数,首先是HAL_PCD_MspInit和OTG_HS_IRQHandler函数,它们的定义如下:

  1. /**
  2. * @brief   OTG HS中断服务函数
  3. * [url=home.php?mod=space&uid=271674]@param[/url]   无
  4. * @retval  无
  5. */
  6. void OTG_HS_IRQHandler(void)
  7. {
  8.     HAL_PCD_IRQHandler(&g_pcd_handle);
  9. }

  10. /**
  11. * @brief   HAL库PCD初始化MSP函数
  12. * @param   hpcd: PCD句柄指针
  13. * @retval  无
  14. */
  15. void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd)
  16. {
  17.     RCC_PeriphCLKInitTypeDef rcc_periph_clk_init_struct = {0};
  18.     GPIO_InitTypeDef gpio_init_struct = {0};
  19.    
  20.     if (hpcd->Instance == USB_OTG_HS)
  21.     {
  22.         /* 使能USB HS电压调节器 */
  23.         HAL_PWREx_EnableUSBHSregulator();
  24.         
  25.         /* 配置时钟 */
  26.         rcc_periph_clk_init_struct.PeriphClockSelection=RCC_PERIPHCLK_USBPHYC;
  27.         rcc_periph_clk_init_struct.UsbPhycClockSelection =
  28. RCC_USBPHYCCLKSOURCE_PLL3Q;
  29.         HAL_RCCEx_PeriphCLKConfig(&rcc_periph_clk_init_struct);

  30.         /* 使能USB电源监测器 */
  31.         HAL_PWREx_EnableUSBVoltageDetector();
  32.         
  33.         /* 使能时钟 */
  34.         __HAL_RCC_USB_OTG_HS_CLK_ENABLE();
  35.         __HAL_RCC_USBPHYC_CLK_ENABLE();
  36.         __HAL_RCC_GPIOM_CLK_ENABLE();
  37.         
  38.         /* 初始化DM、DP引脚 */
  39.         gpio_init_struct.Pin = GPIO_PIN_5 | GPIO_PIN_6;
  40.         gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  41.         gpio_init_struct.Pull = GPIO_NOPULL;
  42.         gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  43.         gpio_init_struct.Alternate = GPIO_AF10_OTG_HS;
  44.         HAL_GPIO_Init(GPIOM, &gpio_init_struct);
  45.         
  46.         /* 配置中断 */
  47.         HAL_NVIC_SetPriority(OTG_HS_IRQn, 0, 0);
  48.         HAL_NVIC_EnableIRQ(OTG_HS_IRQn);
  49.     }
  50. }
复制代码
HAL_PCD_MspInit函数,用于使能USB时钟,初始化IO口,设置中断等。该函数在HAL_PCD_Init函数里面被调用。
OTG_HS_IRQHandler函数,是USB的中断服务函数,通过调用HAL_PCD_IRQHandler函数,实现对USB各种事务的处理。
下面介绍的USBD底层初始化函数,其定义如下:

  1. /**
  2.   * @brief  Initializes the Low Level portion of the Device driver.
  3.   * @param  pdev: Device handle
  4.   * @retval USBD Status
  5.   */
  6. USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
  7. {
  8.     HAL_StatusTypeDef hal_status = HAL_OK;
  9.     USBD_StatusTypeDef usb_status = USBD_OK;
  10.    
  11.     if (pdev->id == DEVICE_HS)
  12.     {
  13.         /* PCD初始化 */
  14.         g_pcd_handle.pData = pdev;
  15.         pdev->pData = &g_pcd_handle;
  16.         g_pcd_handle.Instance = USB_OTG_HS;
  17.         g_pcd_handle.Init.dev_endpoints = 9;
  18.         g_pcd_handle.Init.speed = PCD_SPEED_HIGH;
  19.         g_pcd_handle.Init.phy_itface = USB_OTG_HS_EMBEDDED_PHY;
  20.         g_pcd_handle.Init.dma_enable = DISABLE;
  21.         g_pcd_handle.Init.Sof_enable = DISABLE;
  22.         g_pcd_handle.Init.low_power_enable = DISABLE;
  23.         g_pcd_handle.Init.lpm_enable = DISABLE;
  24.         g_pcd_handle.Init.use_dedicated_ep1 = DISABLE;
  25.         g_pcd_handle.Init.vbus_sensing_enable = DISABLE;
  26.         hal_status = HAL_PCD_Init(&g_pcd_handle);
  27.         usb_status = USBD_Get_USB_Status(hal_status);
  28.         if (usb_status != USBD_OK)
  29.         {
  30.             return usb_status;
  31.         }
  32.         
  33.         /* 配置接收、发送FiFo */
  34.         HAL_PCDEx_SetRxFiFo(&g_pcd_handle, 0x200);
  35.         HAL_PCDEx_SetTxFiFo(&g_pcd_handle, 0, 0x40);
  36.         HAL_PCDEx_SetTxFiFo(&g_pcd_handle, 1, 0x80);
  37.     }
  38.    
  39.     return USBD_OK;
  40. }
复制代码
USBD_LL_Init函数,用于初始化USB底层设置,因为我们定义的是:USE_USB_HS,因此会设置USB OTG使用USB_OTG_HS,然后完成各种设置,比如,使用内部PHY,使用全速模式,不使能VBUS检测等,详见以上代码。该函数在USBD_Init函数里面被调用。
usbd_desc.c提供了USB设备类的描述符,直接决定了USB设备的类型、端点、接口、字符串、制造商等重要信息。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,这里:usbd_desc.c里面的:usbd即device类,同样:usbh即host类,所以通过文件名我们就可以很容易区分该文件是用在device还是host,而只有usb字样的那就是device和host可以共用的。
usbd_msc_storage.c提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化和读写等函数。本章我们设置了3个磁盘:NOR FLASH卡、SD NAND和SD卡。usbd_msc_storage.c我们重点介绍3个函数,首先是初始化存储设备函数,其定义如下:

  1. /**
  2. * @brief   磁盘初始化
  3. * @param   lun: 磁盘编号
  4. * @retval  初始化结果
  5. * @arg     0: 成功
  6. * @arg     -1: 失败
  7. */
  8. int8_t STORAGE_Init(uint8_t lun)
  9. {
  10.     uint8_t res = 0;
  11.    
  12.     switch (lun)
  13.     {
  14.         case LUN_NOR_FLASH:
  15.         {
  16. //            res = norflash_ex_init();
  17.             break;
  18.         }
  19.         case LUN_SD_NAND:
  20.         {
  21.             res = sdnand_init();
  22.             break;
  23.         }
  24.         case LUN_SD:
  25.         {
  26.             res = sd_init();
  27.             break;
  28.         }
  29.     }
  30.    
  31.     if (res != 0)
  32.     {
  33.         return -1;
  34.     }
  35.    
  36.     return 0;
  37. }
复制代码
STORAGE_Init函数,用于初始化存储设备,我们定义了三个存储设备:NOR FLASH、SD NAND和SD卡,因此需要根据输入参数(lun),执行不同存储设备的初始化。
下面要介绍的是从存储设备读取数据函数。其定义如下:

  1. /**
  2. * @brief   从磁盘读取数据
  3. * @param   lun: 磁盘编号
  4. * @param   buf: 数据
  5. * @param   blk_addr: 块地址
  6. * @param   blk_len: 块数量
  7. * @retval  读取结果
  8. * @arg     0: 成功
  9. * @arg     -1: 失败
  10. */
  11. int8_t STORAGE_Read(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
  12. {
  13.     uint8_t res = 0;
  14.    
  15.     g_usb_msc_state |= (1 << 1);
  16.     switch (lun)
  17.     {
  18.         case LUN_NOR_FLASH:
  19.         {
  20.             norflash_ex_read(NORFLASH_MSC_BASE + (blk_addr << 9),
  21. buf, blk_len << 9);
  22.             break;
  23.         }
  24.         case LUN_SD_NAND:
  25.         {
  26.             res = sdnand_read_disk(buf, blk_addr, blk_len);
  27.             break;
  28.         }
  29.         case LUN_SD:
  30.         {
  31.             res = sd_read_disk(buf, blk_addr, blk_len);
  32.             break;
  33.         }
  34.     }
  35.    
  36.     if (res != 0)
  37.     {
  38.         g_usb_msc_state |= (1 << 3);
  39.         return -1;
  40.     }
  41.    
  42.     return 0;
  43. }
复制代码
STORAGE_Read函数,用于从存储设备读取数据,同样是根据存储设备(lun)的不同,调用不同的读取函数,完成数据读取。
下面要介绍的是向存储设备写数据函数。其定义如下:

  1. /**
  2. * @brief   往磁盘写入数据
  3. * @param   lun: 磁盘编号
  4. * @param   buf: 数据
  5. * @param   blk_addr: 块地址
  6. * @param   blk_len: 块数量
  7. * @retval  写入结果
  8. * @arg     0: 成功
  9. * @arg     -1: 失败
  10. */
  11. int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
  12. {
  13.     uint8_t res = 0;
  14.    
  15.     g_usb_msc_state |= (1 << 0);
  16.     switch (lun)
  17.     {
  18.         case LUN_NOR_FLASH:
  19.         {
  20.             norflash_ex_write(NORFLASH_MSC_BASE + (blk_addr << 9), buf,
  21. blk_len << 9);
  22.             break;
  23.         }
  24.         case LUN_SD_NAND:
  25.         {
  26.             res = sdnand_write_disk(buf, blk_addr, blk_len);
  27.             break;
  28.         }
  29.         case LUN_SD:
  30.         {
  31.             res = sd_write_disk(buf, blk_addr, blk_len);
  32.             break;
  33.         }
  34.     }
  35.    
  36.     if (res != 0)
  37.     {
  38.         g_usb_msc_state |= (1 << 2);
  39.         return -1;
  40.     }
  41.    
  42.     return 0;
  43. }
复制代码
STORAGE_Write函数,用于往存储设备写入数据,也是根据存储设备(lun)的不同,调用不同的写入函数,完成数据写入。
以上3个.c文件和对应.h文件的详细代码和修改方法,我们就不详细介绍了,请大家参考光盘本例程源码。
下面提几个重点地方讲解下:
1、通过修改usbd_conf.h里面的MSC_MEDIA_PACKET定义值大小,可以一定程度提高USB读写速度(越大越快),本例程我们设置32*1024U,也就是32KB大小。另外,我们通过修改:MSC_STORAGE_LUN_NBR宏定义的值为3U,可以支持3个磁盘,STM32H7R7开发板支持3个磁盘的。
2、修改usbd_msc_bot.c里面修改MSC_BOT_CBW_Decode函数,将hmsc->cbw.bLUN > 1U改为:hmsc->cbw.bLUN > MSC_STORAGE_LUN_NBR-1,以支持多个磁盘。
以上几点,就是我们移植的时候需要特别注意的,其他我们就不详细介绍了(USB相关源码解释,请参考:UM1734(STM32Cube USB device library).pdf这个文档)。

2. main.c代码
下面是main.c的程序,具体如下:

  1. /* USBD句柄 */
  2. USBD_HandleTypeDef g_usbd_handle = {0};

  3. int main(void)
  4. {
  5.     uint8_t t = 0;
  6.     uint8_t offline_cnt = 0;
  7.     uint8_t usb_msc_sta;
  8.     uint8_t conn_sta;
  9.    
  10.     sys_mpu_config();                   /* 配置MPU */
  11.     sys_cache_enable();                 /* 使能Cache */
  12.     HAL_Init();                         /* 初始化HAL库 */
  13.     sys_stm32_clock_init(300, 6, 2);    /* 配置时钟,600MHz */
  14.     delay_init(600);                    /* 初始化延时 */
  15.     usart_init(115200);                 /* 初始化串口 */
  16.     led_init();                         /* 初始化LED */
  17.     hyperram_init();                    /* 初始化HyperRAM */
  18.     lcd_init();                         /* 初始化LCD */
  19.     my_mem_init(SRAMIN);                /* 初始化AXI-SRAM1~4内存池 */
  20.     my_mem_init(SRAMEX);                /* 初始化XSPI2 HyperRAM内存池 */
  21.     my_mem_init(SRAM12);                /* 初始化AHB-SRAM1~2内存池 */
  22.     my_mem_init(SRAMDTCM);              /* 初始化DTCM内存池 */
  23.     my_mem_init(SRAMITCM);              /* 初始化ITCM内存池 */
  24.    
  25.     lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
  26.     lcd_show_string(30, 70, 200, 16, 16, "USB Card Reader TEST", RED);
  27.     lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  28.    
  29.     if (norflash_ex_init() != 0)
  30.     {
  31.         lcd_show_string(30, 110, 200, 16, 16, "NOR Flash Error!", RED);
  32.     }
  33.     else
  34.     {
  35.         lcd_show_string(30, 110, 200, 16, 16, "NOR Flash Size: 24MB", RED);
  36.     }
  37.    
  38.     if (sdnand_init() != 0)
  39.     {
  40.         lcd_show_string(30, 130, 200, 16, 16, "SD NAND Error!", RED);
  41.     }
  42.     else
  43.     {
  44.         lcd_show_string(30, 130, 200, 16, 16, "SD NAND Size:    MB", RED);
  45.         lcd_show_num(142, 130, (uint32_t)(((uint64_t)
  46. sdnand_info.logic_block_num * sdnand_info.logic_block_size) >> 20),
  47. 3, 16, RED);
  48.     }
  49.    
  50.     if (sd_init() != 0)
  51.     {
  52.         lcd_show_string(30, 150, 200, 16, 16, "SD Card Error!", RED);
  53.     }
  54.     else
  55.     {
  56.         lcd_show_string(30, 150, 200, 16, 16, "SD Card Size:      MB", RED);
  57.         lcd_show_num(142, 150, (uint32_t)(((uint64_t)
  58. g_sd_card_info_struct.LogBlockNbr * g_sd_card_info_struct.LogBlockSize)
  59. >> 20), 5, 16, RED);
  60.     }
  61.    
  62.     USBD_Init(&g_usbd_handle, &MSC_Desc, DEVICE_HS);
  63.     USBD_RegisterClass(&g_usbd_handle, USBD_MSC_CLASS);
  64.     USBD_MSC_RegisterStorage(&g_usbd_handle, &USBD_MSC_fops);
  65.     USBD_Start(&g_usbd_handle);
  66.    
  67.     while (1)
  68.     {
  69.         if (usb_msc_sta != g_usb_msc_state)
  70.         {
  71.             usb_msc_sta = g_usb_msc_state;
  72.             
  73.             lcd_fill(30, 170, 240, 170 + 16, WHITE);
  74.             
  75.             /* 正在写入磁盘 */
  76.             if ((g_usb_msc_state & (1 << 0)) != 0)
  77.             {
  78.                 LED1(0);
  79.                 lcd_show_string(30, 170, 200, 16, 16, "USB MSC Writing...",
  80. BLUE);
  81.             }
  82.             
  83.             /* 正在读取磁盘 */
  84.             if ((g_usb_msc_state & (1 << 1)) != 0)
  85.             {
  86.                 LED1(0);
  87.                 lcd_show_string(30, 170, 200, 16, 16, "USB MSC Reading...",
  88. BLUE);
  89.             }
  90.             
  91.             /* 磁盘写入错误 */
  92.             if ((g_usb_msc_state & (1 << 2)) != 0)
  93.             {
  94.                 lcd_show_string(30, 190, 200, 16, 16, "USB MSC Write Error",
  95. BLUE);
  96.             }
  97.             else
  98.             {
  99.                 lcd_fill(30, 190, 240, 190 + 16, WHITE);
  100.             }
  101.             
  102.             /* 磁盘读取错误 */
  103.             if ((g_usb_msc_state & (1 << 3)) != 0)
  104.             {
  105.                 lcd_show_string(30, 210, 200, 16, 16, "USB MSC Read Error",
  106. BLUE);
  107.             }
  108.             else
  109.             {
  110.                 lcd_fill(30, 210, 240, 250 + 16, WHITE);
  111.             }
  112.         }
  113.         
  114.         if (conn_sta != g_usb_conn_state)
  115.         {
  116.             conn_sta = g_usb_conn_state;
  117.             
  118.             if (g_usb_conn_state != 0)
  119.             {
  120.                 lcd_show_string(30, 190, 200, 16, 16, "USB Connected  ", RED);
  121.             }
  122.             else
  123.             {
  124.                 lcd_show_string(30, 190, 200, 16, 16, "USB Disconnected ",RED);
  125.             }
  126.         }
  127.         
  128.         if (++t == 200)
  129.         {
  130.             t = 0;
  131.             LED1(1);
  132.             LED0_TOGGLE();
  133.             
  134.             /* USB断连检测 */
  135.             if ((g_usb_msc_state & (1 << 4)) != 0)
  136.             {
  137.                 offline_cnt = 0;
  138.                 g_usb_conn_state = 1;
  139.             }
  140.             else
  141.             {
  142.                 if (++offline_cnt > 10)
  143.                 {
  144.                     g_usb_conn_state = 0;
  145.                 }
  146.             }
  147.             
  148.             g_usb_msc_state = 0;
  149.         }
  150.         
  151.         delay_ms(1);
  152.     }
  153. }
复制代码
其中,USBD_HandleTypeDef是一个用于处理USB设备类通信处理的结构体类型,它包含了USB设备类通信的各种变量、结构体参数、传输状态和端点信息等。凡是USB设备类通信,都必须要用定义一个这样的结构体,这里定义成:g_usbd_device。
使用ST官方提供的USB库以后,整个USB初始化就变得比较简单了:
1,调用USBD_Init函数,初始化USB从机内核;
2,调用USBD_RegisterClass函数,链接MSC设备类驱动程序到设备内核;
3,调用USBD_MSC_RegisterStorage函数,为MSC设备类驱动添加回调函数;
4,调用USBD_Start函数,启动USB通信;
经过以上四步处理,USB就启动了,所有USB事务,都是通过USB中断触发,并由USB驱动库自动处理。USB中断服务函数在usbd_conf.c里面:

  1. /**
  2. * @brief       USB OTG 中断服务函数
  3. * [url=home.php?mod=space&uid=60778]@note[/url]        处理所有USB中断
  4. * @param       无
  5. * @retval      无
  6. */
  7. void OTG_HS_IRQHandler(void)
  8. {
  9.     HAL_PCD_IRQHandler(&g_pcd_handle);
  10. }
复制代码
该函数调用HAL_PCD_IRQHandler函数来处理各种USB中断请求。因此在main函数里面,我们的处理过程就非常简单。

68.4 下载验证
将程序下载到开发板后,在USB配置成功后(假设已经插入SD卡,注意:USB数据线,要插在USB_SLAVE口!而不是USB_UART端口!),如图68.4.1所示:

第六十八章 USB读卡器19838.png
图68.4.1 USB连接成功显示界面

此时,电脑提示发现新硬件,并开始自动安装驱动。
USB配置成功后,LED1熄灭,LED0闪烁,在电脑上可以看到磁盘,如图68.4.3所示:


第六十八章 USB读卡器19932.png
图68.4.3 电脑找到USB读卡器的两个盘符

我们打开设备管理器,在通用串行总线控制器里面可以发现多出了一个USB 大容量存储设备,同时看到磁盘驱动器里面多了三个磁盘,如图68.4.4所示:

第六十八章 USB读卡器20031.png
图68.4.4 通过设备管理器查看磁盘驱动器

此时,我们就可以通过电脑读写NOR FLASH、SD卡和SD NAND里面的内容了。在执行读写操作的时候,就可以看到LED1亮,并且会在液晶上显示当前的读写状态。
注意,在对NOR FLASH操作的时候,最好不要频繁的往里面写数据,否则很容易将NOR FLASH写爆!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-7-2 20:50

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表