这里会显示出您选择的修订版和当前版本之间的差别。
后一修订版 | 前一修订版 | ||
icore3_arm_hal_22 [2020/04/20 10:37] fmj 创建 |
icore3_arm_hal_22 [2022/03/18 15:10] (当前版本) sean |
||
---|---|---|---|
行 2: | 行 2: | ||
|技术支持电话|**0379-69926675-801** ||| | |技术支持电话|**0379-69926675-801** ||| | ||
|技术支持邮件|Gingko@vip.163.com ||| | |技术支持邮件|Gingko@vip.163.com ||| | ||
- | |技术论坛|http://www.eeschool.org ||| | ||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | ||
| V1.0 | 2020-04-20 | gingko | 初次建立 | | | V1.0 | 2020-04-20 | gingko | 初次建立 | | ||
行 10: | 行 9: | ||
\\ | \\ | ||
\\ | \\ | ||
+ | |||
===== STM32CubeMX教程二十二_USBD_MSC实验--虚拟U盘 ===== | ===== STM32CubeMX教程二十二_USBD_MSC实验--虚拟U盘 ===== | ||
行 25: | 行 25: | ||
{{ :icore3:icore3_cube_22_5.png?direct | }} | {{ :icore3:icore3_cube_22_5.png?direct | }} | ||
6. 引脚模式配置 | 6. 引脚模式配置 | ||
- | {{ :icore3:icore3_cube_21_6.png?direct | }} | + | {{ :icore3:icore3_cube_22_6.png?direct | }} |
7. 配置USB_OTG_HS | 7. 配置USB_OTG_HS | ||
{{ :icore3:icore3_cube_22_7.png?direct | }} | {{ :icore3:icore3_cube_22_7.png?direct | }} | ||
行 33: | 行 33: | ||
{{ :icore3:icore3_cube_22_9.png?direct | }} | {{ :icore3:icore3_cube_22_9.png?direct | }} | ||
10. 时钟源设置,选择外部高速时钟源,配置为最大主频 | 10. 时钟源设置,选择外部高速时钟源,配置为最大主频 | ||
- | {{ :icore3:icore3_cube_21_11.png?direct | }} | + | {{ :icore3:icore3_cube_22_11.png?direct | }} |
11. 工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK5 | 11. 工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK5 | ||
{{ :icore3:icore3_cube_22_12.png?direct | }} | {{ :icore3:icore3_cube_22_12.png?direct | }} | ||
行 62: | 行 62: | ||
\\ | \\ | ||
- | ===== STM32CubeMX教程二十一——虚拟串口 ===== | + | ===== 实验二十二:USBD_MSC实验——虚拟U盘 ===== |
==== 一、 实验目的与意义 ==== | ==== 一、 实验目的与意义 ==== | ||
- 了解STM32 USB SLAVE结构。 | - 了解STM32 USB SLAVE结构。 | ||
- 了解STM32 USB SLAVE特征。 | - 了解STM32 USB SLAVE特征。 | ||
- | - 掌握STM32 HAL库中USB SLAVE的配置方法。 | + | - 掌握USB SLAVE MSC使用方法。 |
- | - 掌握USB SLAVE VCP使用方法。 | + | |
- 掌握Keil MDK集成开发环境使用方法。 | - 掌握Keil MDK集成开发环境使用方法。 | ||
行 80: | 行 79: | ||
- 装有WIN XP(及更高版本)系统的计算机。 | - 装有WIN XP(及更高版本)系统的计算机。 | ||
==== 三、 实验原理 ==== | ==== 三、 实验原理 ==== | ||
- | === 1、USB_VCP设备 === | + | === 1、SDMMC简介 === |
- | * USB_VCP是属于USB_CDC类设备中的一种,是通过USB接口虚拟串口实现串口通讯的功能。 | + | * USDMMC指的是SD、SDIO 、MMC卡主机接口,提供APB2外设总线和多媒体卡(MMCs),数字安全记忆卡(SD)和SDIO卡。 |
- | * USB虚拟串口,简称VCP,是Virtual COM Port的简写,它是利用USB的CDC类来实现的一种通信接口。 | + | * MMC的全称是”Multi Media Card”――所以也通常被叫做”多媒体卡”,是一种小巧大容量的快闪存储卡,特别应用于移动电话 和数字影像及其他移动终端中。 |
- | * USB虚拟串口属于USB通信设备类。在物理层通过USB总线,采用虚拟串口的方式为主机提供一个物理串口。在系统内部,USB控制器提供了一个批量传输IN端点和一个批量传输的OUT端点,用于数据的接收和发送,模拟串口的RX和TX线。另外USB控制器还提供中断IN端点,发送当前串口的状态,实现对串口传输的控制。串口设备的数据,由系统的串口采集,在芯片内完成USB包的封装,通过USB总线上传至主机,再由相应的串口应用程序进行处理。对用户来说,看到的是基于串口的数据采集和传输,而实际上实现的是基于USB协议包的数据传输。 | + | * SD卡,数字安全记忆卡(Secure Digital Memory Card),是 用于移动设备的标准记忆卡。SD卡数据传送和物理规范由MMC发 展而来,大小和MMC差不多。长宽和MMC一样,比MMC稍微厚了一点。兼容性方面SD卡向下兼容多媒体卡(Multi Media Card)。 |
- | * 我们可以利用STM32自带的USB功能,来实现一个USB虚拟串口,从而通过USB,实现电脑与STM32的数据互传。上位机无需编写专门的USB程序,只需要一个串口调试助手即可调试,非常实用。 | + | *SDIO是在SD内存卡接口的基础上发展起来的外设接口,SDIO接 口兼容以前的SD内存卡,并且可以连接SDIO接口的设备。目前,根据SDIO协议的SPEC,SDIO接口支持的设备总类有蓝牙,网卡,电视卡等。 |
- | * 本实验利用STM32自带的USB功能,连接电脑USB,虚拟出一个USB串口,实现电脑和开发板的数据通信。 | + | |
- | === 2、USB_CDC === | + | === 2、USB模块介绍 === |
- | * USB2.0标准下定义了很多子类,有音频类,CDC类,HID,打印,大容量存储类,HUB,智能卡等等,这些在usb.org官网上有具体的定义,这里主要介绍通信类CDC。CDC(Communication Device Class)类是USB2.0标准下的一个子类,定义了通信相关设备的抽象集合。 | + | * USB模块为PC主机和微控制器所实现的功能之间提供了符合USB规范的通信连接。PC主机和微控制器之间的数据传输是通过共享一专用的数据缓冲区来完成的,该数据缓冲区能被USB外设直接访问。这块专用数据缓冲区的大小由所使用的端点数目和每个端点最大的数据分组大小所决定,每个端点最大可使用512字节缓冲区,最多可用于16个单向或8个双向端点。 |
- | * USB的CDC类是USB通信设备类(Communication Device Class)的简称。CDC类是USB组织定义的一类专门给各种通信设备(电信通信设备和中速网络通信设备)使用的USB子类。根据CDC类所针对通信设备的不同,CDC类又被分成以下不同的模型:USB传统纯电话业务(POTS)模型,USBISDN模型和USB网络模型,其中USB传统纯电话业务模型又可分为直线控制模型(Direct Line Control Model)、抽象模型、电话模型。虚拟串口就属于USB传统纯电话业务模型下的抽象控制模型。 | + | * USB模块同PC主机通信,根据USB规范实现令牌分组的检测,数据发送/接收的处理,和握手分组的处理。整个传输的格式由硬件完成,其中包括CRC的生成和校验。每个端点都有一个缓冲区描述块,描述该端点使用的缓冲区地址、大小和需要传输的字节数。当USB模块识别出一个有效的功能/端点的令牌分组时,(如果需要传输数据并且端点已配置)随之发生相关的数据传输。 |
- | * USB CDC类的通信部分主要包含三部分:枚举过程、虚拟串口操作和数据通信。其中虚拟串口操作部分并不一定强制需要,因为若跳过这些虚拟串口的操作,实际上USB依然是可以通信的。之所以会有虚拟串口操作,主要是我们通常使用PC作为Host端,在PC端使用一个串口工具来与其进行通信,PC端的对应驱动将其虚拟成一个普通串口,这样一来,可以方便PC端软件通过操作串口的方式来与其进行通信,但实际上,Host端与Device端物理上是通过USB总线来进行通信的,与串口没有关系,这一虚拟化过程,起决定性作用的是对应驱动,包含如何将每一条具体的虚拟串口操作对应到实际上的USB操作。需要注意的是,Host端与Device端的USB通信速率并不受所谓的串口波特率影响,它就是标准的USB2.0全速(12Mbps)速度,实际速率取决于总线的实际使用率、驱动访问USB外设有效速率(两边)以及外部环境对通信本身造成的干扰率等因素组成。 | + | * USB模块通过一个内部的16位寄存器实现端口与专用缓冲区的数据交换。在所有的数据传输完成后,如果需要,则根据传输的方向,发送或接收适当的握手分组。在数据传输结束时,USB模块将触发与端点相关的中断,通过读状态寄存器和/或者利用不同的中断处理程序,微控制器可以确定:哪个端点需要得到服务;产生如位填充、格式、CRC、协议、缺失ACK、缓冲区溢出/缓冲区未满等错误时,正在进行的是哪种类型的传输。 |
- | * CDC类软件框架 | + | |
- | {{ :icore3:icore3_arm_hal_21_1.png?direct |}} | + | === 3、USB MSC简介 == |
- | * 如上图所示,黄色USB Device Core部分为USB设备库文件,属于中间件,它为USB协议栈的核心源文件,一般不需要修改: | + | * USB大容量存储设备(英语:USB mass storage device class,也称为USB MSC或UMS)是一个协议,允许一个USB接口的设备与主计算设备相连接,以便在两者之间传输文件。对于主计算设备来说,USB设备看起来就像一个移动硬盘,允许拖放型文件传送。它实际上是由USB实施者论坛所通过许多通讯协议的汇总,这一标准提供了许多设备的界面。包括移动硬盘、闪存盘、移动光学驱动器、读卡器、数码相机、数码音乐播放器、PDA以及手机等等。 |
- | * USB Device Core中,Log/debug为打印/调试开关; | + | === 4、USBD_MSC实验介绍: === |
- | * core为USB设备核心; | + | * 对于iCore3想要变为一个U盘,首先需要存储介质,TF卡作为iCore3开发板中的文件存储器,可以将TF卡虚拟为U盘。此时iCore3设备中的STM32核心将作为协议载体,承载着分别与TF卡和主机通信的任务。 |
- | * USB request中定义了枚举过程中各种标准请求的处理; | + | * USBD_MSC实验是用STM32F407的USB接口通过STM32_HAL库生成的代码达到iCore3上的TF卡虚拟成为一个U盘。 |
- | * I/O request为底层针对USB通信接口的封装。 | + | === 5、实验介绍: === |
- | * 黄色USB Device Class部分为USB类文件,也属于中间件,USB设备库,目前ST DEMO中支持的类有HID, Customer HID, CDC, MSC, DFU, Audio, ST提供了这些类的源码框架,其他的Class或者是复合设备需要自己根据实际需求情况进行扩展或定制。如果用户需求只是需要一个标准类,比如CDC通信,那么最好就使用现成的代码,不需要做任何修改就可以实现这个CDC类通信的功能。 | + | * 通过移植ST官方提供的代码来实现STM32将TF卡虚拟成U盘,然后就可以像操作U盘一样操作TF卡,本实验使用HAL库生成代码,生成方法参考iCore3_CubeMX教程二十二_USBD_MSC。硬件连接示意图如下图: |
- | * 蓝色USB Device HAL Driver为HAL库部分,是对USB外设接口的封装,属于底层驱动,不需要修改,它分为PCD和LL Driver,PCD处于LL Driver之上。 | + | {{ :icore3:icore3_arm_hal_22_1.png?direct |}} |
- | * 洋红色USB Device Configuration为USB配置封装,位于USB底层HAL层驱动与中间件USB协议栈之间,一方面向上层(USB设备库)提供各种操作调用接口,另一方面,向底层USB驱动提供各种回调接口。正是由于它的存在,使得USB协议栈(USB设备库)与底层硬件完全分离,从而使USB设备库具有更加兼容所有STM32的通用性。USB Device Configuration为开放给用户的源文件,用户可以根据自己的某些特殊需要进行修改,也可以使用默认的源文件,假如没有任何特殊要求的话,我们使用默认即可。 | + | |
- | * Application为应用层,USB Device Class有可能将自己对应该的操作接口封装在一个操作数据结构中,由应用来具体实现这些操作,在系统初始化时,由应用将已经定义好的操作接口注册到对应的USB类中,比如usbd_cdc_if, 就这样,使得应用层的应用代码与属于中间件层的USB协议栈分离。同时,USB协议栈会将一些字符串描述符放到APP中,当USB初始化时将这些已经定义好的字符串通过指针初始化到USB协议栈中,以便后续需要时获取。 | + | |
- | * USB_CDC类详细内容可参考《USB_CDC类入门培训》。 | + | |
- | === 3、USBD_VCP实验介绍 == | + | |
- | * USBH_VCP实验是用STM32F407的USB接口通过STM32_HAL库生成的代码达到iCore3上的USB接口实现虚拟串口的功能。 | + | |
- | === 4、实验内容: === | + | |
- | * 通过移植STM32_HAL库提供的代码来实现STM32虚拟串口功能,然后就可以像操作串口一样操作USB,本实验实现终端发送数据,STM32将接收到的数据直接返回给终端,验证发送和接收。 | + | |
- | {{ :icore3:icore3_arm_hal_21_2.png?direct |}} | + | |
==== 四、 实验程序 ==== | ==== 四、 实验程序 ==== | ||
行 113: | 行 103: | ||
<code c> | <code c> | ||
int main(void) | int main(void) | ||
- | { | + | { |
HAL_Init(); | HAL_Init(); | ||
SystemClock_Config(); | SystemClock_Config(); | ||
MX_GPIO_Init(); | MX_GPIO_Init(); | ||
MX_USB_DEVICE_Init(); | MX_USB_DEVICE_Init(); | ||
- | LED_GREEN_ON; | + | MX_SDIO_SD_Init(); |
- | | + | |
while (1) | while (1) | ||
{ | { | ||
- | + | LED_RED_ON; | |
+ | LED_GREEN_OFF; | ||
+ | LED_BLUE_OFF; | ||
+ | HAL_Delay(500); | ||
+ | LED_RED_OFF; | ||
+ | LED_GREEN_ON; | ||
+ | LED_BLUE_OFF; | ||
+ | HAL_Delay(500); | ||
+ | LED_RED_OFF; | ||
+ | LED_GREEN_OFF; | ||
+ | LED_BLUE_ON; | ||
+ | HAL_Delay(500); | ||
} | } | ||
+ | } | ||
+ | </code> | ||
+ | * Main函数中对SDIO与USB分别进行了初始化,在while循环中用三色灯循环点亮表明程序正在运行。 | ||
+ | === 2. 初始化函数 === | ||
+ | <code c> | ||
+ | void MX_SDIO_SD_Init(void) { | ||
+ | hsd.Instance = SDIO; | ||
+ | hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; | ||
+ | hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; | ||
+ | hsd.Init.ClockPowerSave=SDIO_CLOCK_POWER_SAVE_DISABLE; | ||
+ | hsd.Init.BusWide = SDIO_BUS_WIDE_1B; | ||
+ | hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_ENABLE; | ||
+ | hsd.Init.ClockDiv = 0; | ||
+ | if (HAL_SD_Init(&hsd) != HAL_OK) { | ||
+ | Error_Handler(); | ||
+ | } | ||
+ | if (HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B) != HAL_OK) { | ||
+ | Error_Handler(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | === 3. 接口函数 === | ||
+ | * 在usbd_storage_if.c文件中是USB_MSC初始化与读写相关函数。需要在这个文件下编写相关读写SDIO的相关函数,分别为STORAGE_GetCapacity_HS获取容量信息函数STORAGE_Read_HS读SDIO函数与STORAGE_Write_HS写SDIO函数。分别引入SD卡的接口函数。HAL_SD_GetCardInfo获取SD卡信息函数,HAL_SD_ReadBlocks读取SD卡函数,HAL_SD_WriteBlocks写SD卡函数以及HAL_SD_GetCardState获取SD卡状态信息函数。 | ||
+ | * 获取容量信息函数 | ||
+ | <code c> | ||
+ | int8_t STORAGE_GetCapacity_HS(uint8_t lun, uint32_t *block_num, uint16_t *block_size) | ||
+ | { | ||
+ | /* USER CODE BEGIN 10 */ | ||
+ | HAL_SD_GetCardInfo(&hsd,&SDCardInfo); | ||
+ | |||
+ | *block_num = STORAGE_BLK_NBR; | ||
+ | *block_size = STORAGE_BLK_SIZ; | ||
+ | return (USBD_OK); | ||
+ | /* USER CODE END 10 */ | ||
} | } | ||
</code> | </code> | ||
- | * 主程序主要打开了LED_GREEN,在while循环中并没有写任何内容,接受发送处理文件主要在usb_cdc_if.c中。 | + | * 读SDIO函数 |
- | === 2. 接受回调函数: === | + | |
<code c> | <code c> | ||
- | static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len) | + | int8_t STORAGE_Read_HS(uint8_t lun, uint8_t *buf, |
+ | uint32_t blk_addr, uint16_t blk_len) | ||
{ | { | ||
- | /* USER CODE BEGIN 11 */ | + | /* USER CODE BEGIN 13 */ |
- | USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]); | + | uint32_t timeout = 10000; |
- | USBD_CDC_ReceivePacket(&hUsbDeviceHS); | + | |
- | CDC_Transmit_HS(Buf,(uint16_t)(*Len)); | + | if(HAL_SD_ReadBlocks(&hsd,buf,blk_addr,(uint32_t)blk_len, timeout) == USBD_OK){ |
+ | while(HAL_SD_GetCardState(hsd)!= HAL_OK) | ||
+ | { | ||
+ | if (timeout-- == 0) | ||
+ | { | ||
+ | return -1; | ||
+ | } | ||
+ | } | ||
+ | } | ||
return (USBD_OK); | return (USBD_OK); | ||
- | /* USER CODE END 11 */ | + | /* USER CODE END 13 */ |
- | } | + | } |
</code> | </code> | ||
- | === 3. 发送函数 === | + | * 写SDIO函数 |
<code c> | <code c> | ||
- | uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len) | + | int8_t STORAGE_Write_HS(uint8_t lun, uint8_t *buf, |
+ | uint32_t blk_addr, uint16_t blk_len) | ||
{ | { | ||
- | uint8_t result = USBD_OK; | + | /* USER CODE BEGIN 14 */ |
- | /* USER CODE BEGIN 12 */ | + | uint32_t timeout = 10000; |
- | USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData; | + | |
- | if (hcdc->TxState != 0){ | + | if(HAL_SD_WriteBlocks(hsd,buf, blk_addr, blk_len, timeout) == USBD_OK){ |
- | return USBD_BUSY; | + | while(HAL_SD_GetCardState(hsd)!= HAL_OK) |
- | } | + | { |
- | USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len); | + | if (timeout-- == 0) |
- | result = USBD_CDC_TransmitPacket(&hUsbDeviceHS); | + | { |
- | /* USER CODE END 12 */ | + | return -1; |
- | return result; | + | } |
- | } | + | } |
+ | } | ||
+ | return (USBD_OK); | ||
+ | /* USER CODE END 14 */ | ||
+ | } | ||
</code> | </code> | ||
- | * CDC_Transmit_HS是发送函数,指定数据首地址和字节长度,数据就会发送到串口,底层是USB库实现的。CDC_Receive_HS是接收到收据后的回调函数,数据是收到一帧后才调用的CDC_Receive_HS,所以每次的字节长度不一定相同,传入的两个参数是数据缓存首地址和数据长度。这里简单的将接收到的数据原样返回,实现数据回环,只需要在CDC_Receive_HS函数添加一行调用CDC_Transmit_HS函数即可。 | ||
==== 五、 实验步骤 ==== | ==== 五、 实验步骤 ==== | ||
- 把仿真器与iCore3的SWD调试口相连(直接相连或者通过转接器相连); | - 把仿真器与iCore3的SWD调试口相连(直接相连或者通过转接器相连); | ||
- | - 将跳线帽插在USB_UART; | + | - 将跳线帽插在USB OTG; |
- | - 把iCore3通过Micro USB线与计算机相连,为iCore3供电; | + | - 将Micro SD卡插入TF卡座里面; |
+ | - 把iCore3(USB OTG)通过Micro USB线与计算机相连,为iCore3供电; | ||
- 打开Keil MDK 开发环境,并打开本实验工程; | - 打开Keil MDK 开发环境,并打开本实验工程; | ||
- 烧写程序到iCore3上; | - 烧写程序到iCore3上; | ||
- | - 打开Commix串口终端(注:Commix使用方法见附录); | + | - 即可在电脑上操作磁盘。 |
- | - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 | + | |
==== 六、 实验现象 ==== | ==== 六、 实验现象 ==== | ||
- | * 终端发送“Hello,I’M iCore3.”,STM32将接收到的消息又直接返回给终端,现象如下: | + | * 下载程序到iCore3上之后,可以从设备管理器内查看到USB大容量存储设备。同时可以看到设备和驱动器里面多了一个磁盘。可以对该磁盘进行操作,即可读写Micro SD卡。 |
- | {{ :icore3:icore3_arm_hal_21_3.png?direct |}} | + | {{ :icore3:icore3_arm_hal_22_2.png?direct |}} |
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | + | ||
- | **附录:** | + | |
- | * 1、驱动安装:打开本实验文件夹中的驱动文件夹,双击VCP_V1.4.0_Setup.exe,开始安装虚拟串口驱动。按照顺序安装即可。 | + | |
- | {{ :icore3:icore3_arm_hal_21_4.png?direct |}} | + | |
- | * 2、安装完成后并下载程序设备上将会显示相应的端口 | + | |
- | {{ :icore3:icore3_arm_hal_21_5.png?direct |}} | + | |