第五十六章 USB读卡器(Slave)实验
STM32H562系列芯片都自带了USB OTG FS(FS,即全速,12Mbps),支持USB Host和USB Device,所有USB相关例程,均使用USB OTG FS实现。
下面,我们将介绍如何使用USB OTG FS在Mini STM32H562开发板上实现一个USB读卡器。
本章分为如下几个小节:
56.1 USB简介
56.2 硬件设计
56.3 程序设计
56.4 下载验证
56.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版本仍是本文的重点介绍对象。
56.1.1 USB简介
USB本身的知识体系非常复杂,本节只能作一点知识点的引入。本书篇幅有限,不可能在这里详细介绍,想更系统地学习USB的知识可以参考《圈圈教你玩 USB》、塞普拉斯提供的《USB 101:通用串行总线 2.0 简介》等文献,下面我们一起来看USB的简单特性:
l USB的硬件接口
USB协议有漫长的发展历程,为的不同的场合和硬件功能而发展出不同的接口:Type-A、Type-B、Type-C,Type-C规范碰巧是跟着USB3.1的规范一起发布的。常见的接口类型列出如图56.1.1.1所示。

图56.1.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标准所规定的参数来设计。
l USB速度
USB规范已经为USB系统定义了以下四种速度模式:低速(Low-Speed)、全速(Full-Speed)、高速(Hi-Speed)和超高速(SuperSpeedUSB)。接口的速度上限与设备支持的USB协议标准和导线长度、阻抗有关,不同协议版本对硬件的传输线数量、阻抗等要求各不相同,各个版本的能达到的理论速度上限对应如图56.1.1.2。

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

图56.1.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.有专门的培训资料,资料存放路径:“光盘资料A盘→8,STM32参考资料→2,STM32 USB学习资料”中了,感兴趣的朋友自行去查阅更的USB的相关扩展知识,我们对USB的简介就到这里。
56.1.2 STM32H5的USB特性
USB发展到现在已经有USB1.0/1.1/2.0/3.0等多个版本。目前用的最多的就是USB1.1和USB2.0,USB3.0目前已经开始普及。STM32H562自带的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控制器。
STM32H562系列芯片自带有1个USB OTG FS。该USB OTG FS是一款双角色设备 (DRD) 控制器,同时支持从机功能和主机功能,完全符合USB 2.0规范的On-The-Go补充标准。此外,该控制器也可配置为“仅主机”模式或“仅从机”模式,完全符合USB 2.0规范。
STM32H562的USB OTG FS主要特性如下:
1,USB规范2.0版全速兼容
2,支持主机和设备两种模式
3,具有32位访问的2048字节专用SRAM数据缓冲存储器
4,USB时钟恢复
5,可配置的端点数量从1到8
6,CRC(循环冗余校验)生成/校验,反向不归零(NRZI)编码/解码和位填充
7,同步传输支持
8,双缓冲批量/同步端点支持
9,USB暂停/恢复操作
10, 锁帧时钟脉冲产生
11,支持USB 2.0 Link Power Management(仅限设备模式)
12,USB连接/断开功能(USB_DP线上有可编程控制的上拉电阻)
STM32H562 USB OTG FS框图如图56.1.2.1所示:

图56.1.2.1 USB OTG FS框图
对于USB OTG FS功能模块,STM32H562通过APB2总线访问(APB2总线频率至少为12MHz),以避免出现数据溢出/欠运行问题。另外,USB OTG的内核时钟必须是48MHz,由RCC_CCIPR4寄存器的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),这样就不会受到其它外设的影响。
要正常使用STM32H562的USB,就得编写USB驱动,而整个USB通信的详细过程是很复杂的,本书篇幅有限,不可能在这里详细介绍,有兴趣的朋友可以去看看电脑圈圈的《圈圈教你玩USB》这本书,该书对USB通信有详细讲解。如果要我们自己编写USB驱动,那是一件相当困难的事情,尤其对于从没了解过USB的人来说,基本上不花个一两年时间学习,是没法搞定的。不过,ST提供了我们一个完整的USB OTG 驱动库(包括主机和设备),通过这个库,我们可以很方便的实现我们所要的功能,而不需要详细了解USB的整个驱动,大大缩短了我们的开发时间和精力。ST加入ThreadX全家桶后,不再自己整理以前那一套HAL库版的USB驱动库,而是使用Azure RTOS usbx。初学者应该不知道什么是Azure RTOS usbx,这里有相关的链接,请大家自行学习,链接如下: https://learn.microsoft.com/zh-cn/azure/rtos/usbx/ 。在光盘资料中,也给大家下载好这份文档,路径如下:A盘→8,STM32参考资料 →2,STM32 USB 学习资料 →《azure-rtos-usbx.pdf》。
Mini STM32H562开发板的USB例程全部都使用STM32Cube_FW_H5_V1.0.0.zip里面的usbx驱动库,路径如下:A盘 →8,STM32参考资料 →1,STM32CubeH5固件包 →STM32Cube_FW_H5_V1.0.0\Middlewares\ST\usbx。
ST提供的STM32Cube_FW_H5_V1.0.0.zip固件包里面还有ST提供的USB相关例程,共有三款ST官方的STM32H5开发板:NUCLEO-H503RB、NUCLEO-H563ZI和STM32H573I-DK,其中USB例程最全的是STM32H573I-DK。关于usbx库和STM32H573I-DK开发板的USB例程截图,如图56.1.2.2所示:

图56.1.2.2 usbx库和ST提供的USB OTG例程
标号1是H5 USB驱动库,包括主机和从机库;
标号2是ST提供的H5 USB OTG例程,共9个(3个从机例程、6个主机例程);
这9个例程,虽然都是基于ST官方STM32H573I-DK板,但是也比较容易移植到我们的Mini STM32H562开发板上。
本实验我们移植:STM32Cube_FW_H5_V1.0.0\Projects\STM32H573I-DK\Applications \USBX\Ux_Device_MSC这个例程,以实现USB读卡器功能。
56.2 硬件设计
1. 例程功能
实验功能:开机的时候先检测SD卡和SPI FLASH是否存在,如果存在则获取其容量,并显示在LCD上面(如果不存在,则报错)。之后开始USB配置,在配置成功之后就可以在电脑上发现两个可移动磁盘。
LED0闪烁,提示程序运行。
2. 硬件资源
1)LED灯
LED0 - PB4
2)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
3)正点原子2.8/3.5/4.3/7寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
4)USB_SLAVE接口(D-/D+连接在PA11/PA12上)
5)OSPI(PB2/PB6/PD11/PD12/PD13/PE2)
6)norflash(QSPI FLASH芯片,连接在OSPI上)
7)SD卡,通过SDMMC1(SDMMC_D0~D4(PC8~PC11),SDMMC_SCK(PC12),
SDMMC_CMD(PD2))连接
8)TIM6
3. 原理图
开发板采用Type-C接口和电脑的USB接口相连接,Type-C接口与STM32的连接电路图,如下图所示:

图56.2.1 Tpye-C接口与STM32的连接电路图
从上图可以看出,Type-C接口是直接连接到STM32H562上面的,所以硬件上不需要我们做什么操作,可直接使用。
需要注意的是:这个Type-C接口(USB_SLAVE)和USB母座(USB_HOST)是共用D+和D-的,所以它们不能同时使用。这个在使用的时候,要特别注意!本实验测试时,USB_HOST不能插入任何USB设备!USB数据线是通过图56.2.2中框出来的Tpye-C接口(USB_SLAVE)连接到电脑的,不要连错。

图56.2.2 Type-C接口与电脑连接示意图
56.3 程序设计
56.3.1 程序流程图

图56.3.1.1 USB读卡器(Slave)实验程序流程图
56.3.2 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。
本实验,我们在:实验32 SD卡实验的基础上修改,代码移植自ST官方例程:STM32Cube_FW_H5_V1.0.0\Projects\STM32H573I-DK\Applications\USBX\Ux_Device_MSC。该目录下提供了两种开发环境的工程:MDK和STM32CubeIDE,我们使用的是MDK,打开MDK工程,然后就可以知道和USB相关的代码有哪些,如图56.3.2.1所示:

图56.3.2.1 ST官方例程USB相关代码
有了这个官方例程做指引,我们就知道具体需要哪些文件,从而实现本章例程。
首先,在本例程(即实验33 SD卡实验)工程的Middlewares文件夹下面,新建一个usbx文件夹,并拷贝官方USB驱动库相关代码到该文件夹下,即拷贝:光盘→8,STM32参考资料 →1,STM32CubeH5固件包 →STM32Cube_FW_H5_V1.0.0 →Middlewares →ST →usbx文件夹下的所有内容到该文件夹下面。
然后,在本例程User文件夹下,新建一个USBX文件夹用于存放MSC实现相关代码,即拷贝:STM32H573I-DK\Applications\USBX\Ux_Device _MSC\USBX文件夹下的App和Target文件夹的所有代码。最后USB_APP文件夹下的文件如图56.3.2.2所示:

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

图56.3.2.3 添加USB驱动等相关代码
1. USB驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。接下来我们看看USBX里面的几个.c文件:
app_usbx_device.c主要提供了USB设备初始化函数和USB PCD初始化MSP回调函数等。首先查看HAL_PCD_MspInit和USB_DRD_FS_IRQHandler函数,它们的定义如下:
/**
* @brief HAL库USB PCD初始化MSP回调函数
* @param hpcd: USB PCD句柄
* @retval 无
*/
void HAL_PCD_MspInit(PCD_HandleTypeDef* hpcd)
{
RCC_OscInitTypeDef rcc_osc_init_struct = {0};
RCC_PeriphCLKInitTypeDef rcc_periph_clk_init_struct = {0};
GPIO_InitTypeDef gpio_init_struct = {0};
if (hpcd->Instance == USB_DRD_FS)
{
/* 配置时钟 */
rcc_osc_init_struct.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
rcc_osc_init_struct.HSI48State = RCC_HSI48_ON;
HAL_RCC_OscConfig(&rcc_osc_init_struct);
rcc_periph_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_USB;
rcc_periph_clk_init_struct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;
HAL_RCCEx_PeriphCLKConfig(&rcc_periph_clk_init_struct);
/* 使能USB电源检测器 */
HAL_PWREx_EnableVddUSB();
HAL_PWREx_EnableUSBVoltageDetector();
/* 使能时钟 */
__HAL_RCC_USB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/* 初始化DM、DP引脚 */
gpio_init_struct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
gpio_init_struct.Mode = GPIO_MODE_AF_PP;
gpio_init_struct.Pull = GPIO_NOPULL;
gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
gpio_init_struct.Alternate = GPIO_AF10_USB;
HAL_GPIO_Init(GPIOA, &gpio_init_struct);
/* 配置中断 */
HAL_NVIC_SetPriority(USB_DRD_FS_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_DRD_FS_IRQn);
}
}
/**
* @brief USB中断服务函数
* @param 无
* @retval 无
*/
void USB_DRD_FS_IRQHandler(void)
{
HAL_PCD_IRQHandler(&g_pcd_handle);
}
HAL_PCD_MspInit函数,用于初始化USB时钟,初始化IO口,设置中断优先级等。该函数在HAL_PCD_Init函数里面被调用。这里我们需要注意的是:USB时钟源选择来自于HSI48。
USB_DRD_FS_IRQHandler函数,是USB的中断服务函数,通过调用HAL_PCD_IRQHandler函数,实现对USB各种事务的处理。
下面查看的是USB设备初始化函数,其定义如下:
/**
* @brief USB设备初始化
* @param 无
* @retval 初始化结果
* @arg 0: 成功
* @arg 1: 失败
*/
uint8_t app_usbx_device_init(void)
{
/* 初始化USBX内存资源 */
/* 初始化USB设备堆栈 */
/* 注册USB设备类 */
/* 上述代码已省略,基本直接参考ST官方例程即可,可查看源码 */
/* 初始化USB PCD */
g_pcd_handle.Instance = USB_DRD_FS;
g_pcd_handle.Init.dev_endpoints = 8;
g_pcd_handle.Init.speed = USBD_FS_SPEED;
g_pcd_handle.Init.phy_itface = PCD_PHY_EMBEDDED;
g_pcd_handle.Init.Sof_enable = DISABLE;
g_pcd_handle.Init.low_power_enable = DISABLE;
g_pcd_handle.Init.lpm_enable = DISABLE;
g_pcd_handle.Init.battery_charging_enable = DISABLE;
g_pcd_handle.Init.vbus_sensing_enable = DISABLE;
g_pcd_handle.Init.bulk_doublebuffer_enable = DISABLE;
g_pcd_handle.Init.iso_singlebuffer_enable = DISABLE;
if (HAL_PCD_Init(&g_pcd_handle) != HAL_OK)
{
return 1;
}
HAL_PCDEx_PMAConfig(&g_pcd_handle, 0x00, PCD_SNG_BUF, 0x14);
HAL_PCDEx_PMAConfig(&g_pcd_handle, 0x80, PCD_SNG_BUF, 0x54);
HAL_PCDEx_PMAConfig(&g_pcd_handle, USBD_MSC_EPOUT_ADDR, PCD_SNG_BUF, 0x94);
HAL_PCDEx_PMAConfig(&g_pcd_handle, USBD_MSC_EPIN_ADDR, PCD_SNG_BUF, 0xD4);
ux_dcd_stm32_initialize((ULONG)USB_DRD_FS, (ULONG)&g_pcd_handle);
HAL_PCD_Start(&g_pcd_handle);
return 0;
}
app_usbx_device_init函数,用于初始化USB内部资源、设备栈、注册USB设备类等,以及初始化USB底层设置等,比如:使用内部PHY,使用全速模式,不使能VBUS检测等,详见以上代码。该函数在USBD_Init函数里面被调用。
ux_device_descriptors.c提供了USB设备类的描述符相关函数,如:构建设备框架、注册一个在构建器中的类、向配置描述符添加一个新类、找到第一个可用的接口槽位、向配置描述符添加一个新类、分配和端点、配置并追加MSC描述符等函数。这个里面的内容,我们一般不用修改,直接用官方的即可。注意,这里:usbd_desc.c里面的:usbd即device类,同样:usbh即host类,所以通过函数名我们就可以很容易区分该文件是用在device还是host。
ux_device_msc.c提供一些磁盘操作函数,包括支持的磁盘个数,以及每个磁盘的初始化和读写等函数。本实验我们设置了2个磁盘:SD卡和SPI FLASH。ux_device_msc.c我们重点介绍2个函数,首先是读磁盘函数,其定义如下:
/**
* @brief 读磁盘
* @param storage: MSC类实例
* @param lun: 磁盘编号
* @param data_pointer: 数据指针
* @param number_blocks: 块数量
* @param lba: 逻辑块地址
* @param media_status: 磁盘状态
* @retval 状态机状态
*/
UINT ux_device_msc_storage_media_read(VOID *storage, ULONG lun, UCHAR
*data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status)
{
UX_PARAMETER_NOT_USED(storage);
UX_PARAMETER_NOT_USED(media_status);
switch (lun)
{
case NORFLASH_LUN:
{
norflash_read(data_pointer, (lba + NORFLASH_LUN_BLOCK_BASE)
* NORFLASH_LUN_BLOCK_LEN, number_blocks * NORFLASH_LUN_BLOCK_LEN);
break;
}
case SDCARD_LUN:
{
sd_read_disk(data_pointer, lba, number_blocks);
break;
}
default:
{
break;
}
}
return UX_STATE_NEXT;
}
ux_device_msc_storage_media_read函数,用于从存储设备读取数据,同样是根据存储设备(lun)的不同,调用不同的读取函数,完成数据读取。
下面要介绍的是读磁盘函数。其定义如下:
/**
* @brief 写磁盘
* @param storage: MSC类实例
* @param lun: 磁盘编号
* @param data_pointer: 数据指针
* @param number_blocks: 块数量
* @param lba: 逻辑块地址
* @param media_status: 磁盘状态
* @retval 状态机状态
*/
UINT ux_device_msc_storage_media_write(VOID *storage, ULONG lun, UCHAR
*data_pointer, ULONG number_blocks, ULONG lba, ULONG *media_status)
{
UX_PARAMETER_NOT_USED(storage);
UX_PARAMETER_NOT_USED(media_status);
switch (lun)
{
case NORFLASH_LUN:
{
norflash_write(data_pointer, (lba + NORFLASH_LUN_BLOCK_BASE)
* NORFLASH_LUN_BLOCK_LEN, number_blocks * NORFLASH_LUN_BLOCK_LEN);
break;
}
case SDCARD_LUN:
{
sd_write_disk(data_pointer, lba, number_blocks);
break;
}
default:
{
break;
}
}
return UX_STATE_NEXT;
}
ux_device_msc_storage_media_write函数,用于往存储设备写入数据,也是根据存储设备(lun)的不同,调用不同的写入函数,完成数据写入。
以上3个.c文件和对应.h文件的详细代码和修改方法,我们就不详细介绍了,请大家参考光盘本例程源码。
下面提几个重点地方讲解下:
1、要使用USB OTG FS,我们需要在MDK编译器的全局宏定义里面,添加宏定义:UX_INCLUDE_USER_DEFINE_FILE,具体添加情况如图56.3.2.4所示:

图56.3.2.4 定义全局宏UX_INCLUDE_USER_DEFINE_FILE
2、通过修改ux_device_msc.h里面的STORAGE_LUN_NUM宏定义的值大小,可以修改支持的磁盘个数,对于本开发板来说是支持2个磁盘。
2. main.c代码
下面是main.c的代码,具体如下:
int main(void)
{
uint8_t t = 0;
uint8_t res;
uint16_t id;
HAL_Init(); /* HAL库初始化 */
HAL_ICACHE_Enable(); /* 使能指令缓存 */
sys_stm32_clock_init(250, 4, 2, 2); /* 配置时钟,250MHz */
delay_init(250); /* 初始化延时 */
usart_init(115200); /* 初始化串口 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);
lcd_show_string(30, 70, 200, 16, 16, "USB Card Reader TEST", RED);
lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);
/* 初始化NOR Flash */
norflash_init();
id = norflash_read_id();
if ((id == 0) || (id == 0XFFFF))
{
lcd_show_string(30, 110, 200, 16, 16, "NOR Flash Error!", RED);
}
else
{
lcd_show_string(30, 110, 200, 16, 16, "NOR Flash Size: 16MB", RED);
}
/* 初始化SD卡 */
if (sd_init() != 0)
{
lcd_show_string(30, 130, 200, 16, 16, "SD Card Error!", RED);
}
else
{
lcd_show_string(30, 130, 200, 16, 16, "SD Card Size: MB", RED);
lcd_show_num(142, 130, (uint32_t)(((uint64_t)g_sd_card_info.BlockNbr
* g_sd_card_info.BlockSize) >> 20), 5, 16, RED);
}
/* USB设备初始化 */
res = app_usbx_device_init();
while (res != 0)
{
lcd_show_string(30, 150, 200, 16, 16, "USB Device Init Failed!", RED);
delay_ms(500);
lcd_show_string(30, 150, 200, 16, 16, "Please Check! ", RED);
delay_ms(500);
}
/* 每一毫秒运行一次独立模式下的USBX系统任务 */
btim_timx_int_init(1000 - 1, 250 - 1);
while (1)
{
if (++t == 20)
{
t = 0;
LED0_TOGGLE();
}
delay_ms(10);
}
}
该部分代码功能,先进行系统和外设初始化,然后判断NOR Flash、SD卡和USB初始化是否成功。最终通过定时器中断,每一毫秒运行一次独立模式下的USBX系统任务。
56.4 下载验证
将程序下载到开发板后,LCD显示的内容(假设已经插入SD卡,注意:USB数据线,要插在USB_SLAVE口!而不是USB_UART端口!),如图56.4.1所示:

图56.4.1 USB读卡器实验显示界面
此时,LED0闪烁,表示程序在运行中。电脑提示发现新硬件,并开始自动安装驱动,USB配置成功后,在电脑上可以看到磁盘,如图56.4.2所示:

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

图56.4.3 通过设备管理器查看磁盘驱动器
此时,我们就可以通过电脑读写SD卡和NOR FLASH里面的内容了。注意,在对NOR FLASH操作的时候,最好不要频繁的往里面写数据,否则很容易将NOR FLASH写爆!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70040860/viewspace-3107522/ ,如需转载,请注明出处,否则将追究法律责任。