ITPub博客

首页 > 嵌入式/内核开发 > 嵌入式/内核开发 > 《STM32H562开发指南 V1.0 》第五十六章 USB读卡器(Slave)实验

《STM32H562开发指南 V1.0 》第五十六章 USB读卡器(Slave)实验

原创 嵌入式/内核开发 作者:正点原子 时间:2026-01-09 09:40:32 0 删除 编辑

第五十六章 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/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2024-08-30

  • 博文量
    588
  • 访问量
    764261