| **银杏科技有限公司旗下技术文档发布平台** |||| |技术支持电话|**0379-69926675-801**||| |技术支持邮件|Gingko@vip.163.com||| ^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^ | V1.0 | 2020-03-23 | gingko | 初次建立 | \\ \\ \\ \\ \\ \\ \\ ===== STM32CubeMX教程三十七——USBD_MSC实验(FS) ===== \\ 1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR {{ :icore4t:icore4t_cube_37_1.png?direct |}} 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 {{ :icore4t:icore4t_cube_37_2.png?direct |}} 3.配置RCC,使用外部时钟源 {{ :icore4t:icore4t_cube_37_3.png?direct |}} 4.时基源选择SysTick {{ :icore4t:icore4t_cube_37_4.png?direct |}} 5.将PA10,PB7,PB8设置为GPIO_Output {{ :icore4t:icore4t_cube_37_5.png?direct |}} 6.引脚模式配置 {{ :icore4t:icore4t_cube_37_6.png?direct |}} {{ :icore4t:icore4t_cube_37_7.png?direct |}} 7.配置USB_OTG_FS {{ :icore4t:icore4t_cube_37_8.png?direct |}} 8.配置USB_DEVICE {{ :icore4t:icore4t_cube_37_9.png?direct |}} 9.配置QUADAPI {{ :icore4t:icore4t_cube_37_10.png?direct |}} 引脚配置 {{ :icore4t:icore4t_cube_37_11.png?direct |}} 10.时钟源设置,选择外部高速时钟源,配置为最大主频 {{ :icore4t:icore4t_cube_37_12.png?direct |}} {{ :icore4t:icore4t_cube_37_13.png?direct |}} 11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 {{ :icore4t:icore4t_cube_37_14.png?direct |}} 12.点击Code Generator,进行进一步配置 {{ :icore4t:icore4t_cube_37_15.png?direct |}} * **Copy all used libraries into the project folder** * 将HAL库的所有.C和.H都复制到所建工程中 * 优点:这样如果后续需要新增其他外设又可能不再用STM32CubeMX的时候便会很方便 * 缺点:体积大,编译时间很长 * **Copy only the necessary library files** * 只复制所需要的.C和.H(推荐) * 优点:体积相对小,编译时间短,并且工程可复制拷贝 * 缺点:新增外设时需要重新用STM32CubeMX导入 * **Add necessary library files as reference in the toolchain project configuration file** * 不复制文件,直接从软件包存放位置导入.C和.H * 优点:体积小,比较节约硬盘空间 * 缺点:复制到其他电脑上或者软件包位置改变,就需要修改相对应的路径 * 自行选择方式即可 13.然后点击GENERATE CODE 创建工程 {{ :icore4t:icore4t_cube_37_16.png?direct |}} 创建成功,打开工程。 \\ \\ \\ ===== 实验三十七:USBD_MSC实验(FS)——虚拟U盘(SPI FLASH) ===== ==== 一、 实验目的与意义 ==== - 了解STM32 USB SLAVE结构。 - 了解STM32 USB SLAVE特征。 - 掌握USB SLAVE MSC的使用方法。 - 掌握STM32 HAL库中QUADSPI属性的配置方法。 - 掌握KEIL MDK 集成开发环境使用方法。 ==== 二、 实验设备及平台 ==== - iCore4T 双核心板。[[https://item.taobao.com/item.htm?spm=a1z10.1-c.w137644-251734891.3.5923532fDrMDOe&id=610595120319|点击购买]] - iCore4T 扩展底板。 - JLINK(或相同功能)仿真器。[[https://item.taobao.com/item.htm?id=554869837940|点击购买]] - Micro USB线缆。 - Keil MDK 开发平台。 - STM32CubeMX开发平台。 - 装有WIN XP(及更高版本)系统的计算机。 ==== 三、 实验原理 ==== === 1.SPI FLASH简介 === * 串行接口设备spi flash就是通过串行的接口进行操作的flash存储设备。flash按照内部存储结构不同,分为两种:nor flash和nand flash。这里spi flash 属于 nor flash。SPI一种通信接口。那么严格的来说SPI Flash是一种使用SPI通信的Flash,即,可能指NOR也可能是NAND。但现在大部分情况默认下人们说的SPI Flash指的是SPI Nor Flash。早期Norflash的接口是parallel的形式,即把数据线和地址线并排与IC的管脚连接。但是后来发现不同容量的Nor flash不能硬件上兼容(数据线和地址线的数量不一样),并且封装比较大,占用了较大的PCB板位置,所以后来逐渐被SPI(串行接口)Nor flash所取代。同时不同容量的SPI Nor flash管脚也兼容封装也更小。,至于现在很多人说起NOR flash直接都以SPI flash来代称。 * Nor Flash根据数据传输的位数可以分为并行(Parallel,即地址线和数据线直接和处理器相连)Nor Flash和串行(SPI,即通过SPI接口和处理器相连)Nor Flash;区别主要就是: * 1、SPI Nor Flash每次传输一bit位的数据,parallel连接的Nor Flash每次传输多个bit位的数据(有x8和x16bit两种); * 2、SPI Nor Flash比parallel便宜,接口简单点,但速度慢。 * Nand Flash是地址数据线复用的方式,接口标准统一(x8bit和x16bit),所以不同容量在兼容性上基本没什么问题。但是目前对产品的需求越来越小型化以及成本要求也越来越高,所以SPI Nand Flash渐渐成为主流,并且采用SPI NAND Flash方案,主控也可以不需要传统NAND控制器,只需要有SPI接口接口操作访问,从而降低成本。另外SPI Nand Flash封装比传统的封装也小很多,故节省了PCB板的空间。 === 2.QSPI协议简介 === * QSPI是Queued SPI的简写,是Motorola公司推出的SPI接口的扩展,比SPI应用更加广泛。在SPI协议的基础上,Motorola公司对其功能进行了增强,增加了队列传输机制,推出了队列串行外围接口协议(即QSPI协议)。QSPI是一种专用的通信接口,连接单、双或四(条数据线)SPIF lash存储介质。 * 该接口可以在以下三种模式下工作: * 间接模式:使用QSPI寄存器执行全部操作 * 状态轮询模式:周期性读取外部Flash状态寄存器,而且标志位置1时会产生中断(如擦除或烧写完成,会产生中断) * 内存映射模式:外部Flash映射到微控制器地址空间,从而系统将其视作内部存储器采用双闪存模式时,将同时访问两个Quad-SPI Flash,吞吐量和容量均可提高二倍。 === 3.QSPI功能框图 === {{ :icore4t:icore4t_arm_hal_37_1.png?direct&800 |}} === 4.QUADSPI 信号接口协议模式 === == (1) 单线 SPI 模式 == * 传统SPI模式允许串行发送/接收单独的1位。在此模式下,数据通过SO信号(其I/O与IO0共享)发送到Flash。从Flash接收到的数据通过SI(其I/O与IO1共享)送达。通过将(QUADSPI_CCR中的)IMODE/ADMODE/ABMODE/DMODE字段设置为01,可对不同的命令阶段分别进行配置,以使用此单个位模式。在每个已配置为单线模式的阶段中: * IO0 (SO)处于输出模式 * IO1 (SI)处于输入模式(高阻抗) * IO2 处于输出模式并强制置“0”(以禁止“写保护”功能) * IO3 处于输出模式并强制置“1”(以禁止“保持”功能) * 若DMODE=01,这对于空指令阶段也同样如此。 == (2) 双线 SPI 模式 == * 在双线模式下,通过IO0/IO1信号同时发送/接收两位。通过将QUADSPI_CCR寄存器的IMODE/ADMODE/ABMODE/DMODE字段设置为10,可对不同的命令阶段分别进行配置,以使用双线SPI模式。在每个已配置为单线模式的阶段中: * IO0/IO1在数据阶段进行读取操作时处于高阻态(输入),在其他情况下为输出 * IO2处于输出模式并强制置“0” * IO3处于输出模式并强制置“1” * 在空指令阶段,若DMODE=01,则IO0/IO1始终保持高阻态。 == (3) 四线 SPI 模式 == * 在四线模式下,通过IO0/IO1/IO2/IO3信号同时发送/接收四位。通过将QUADSPI_CCR寄存器的IMODE/ADMODE/ABMODE/DMODE字段设置为11,可对不同的命令阶段分别进行配置,以使用四线SPI模式。在每个已配置为四线模式的阶段中,IO0/IO1/IO2/IO3在数据阶段进行读取操作时均处于高阻态(输入),在其他情况下为输出。在空指令阶段中,若DMODE=11,则IO0/IO1/IO2/IO3均为高阻态。IO2和IO3仅用于Quad SPI模式如果未配置任何阶段使用四线SPI模式,即使UADSPI激活,对应IO2和IO3的引脚也可用于其他功能。本实验使用的即为四线模式。 == (4) SDR 模式 == * 默认情况下,DDRM位(QUADSPI_CCR[31])为0,QUADSPI在单倍数据速率(SDR)模式下工作。在SDR模式下,当QUADSPI驱动IO0/SO、IO1、IO2、IO3信号时,这些信号仅在CLK的下降沿发生转变。在SDR模式下接收数据时,QUADSPI假定Flash也通过CLK的下降沿发送数据。默认情况下(SSHIFT=0时),将使用CLK后续的边沿(上升沿)对信号进行采样。 == (5) DDR 模式 == * 若DDRM位(QUADSPI_CCR[31])置1,则QUADSPI在双倍数据速率(DDR)模式下工作。在DDR模式下,当QUADSPI在地址/交替字节/数据阶段驱动IO0/SO、IO1、IO2、IO3信号时,将在CLK的每个上升沿和下降沿发送1位。指令阶段不受DDRM的影响。始终通过CLK的下降沿发送指令。在DDR模式下接收数据时,QUADSPI假定Flash通过CLK的上升沿和下降沿均发送数据。若DDRM=1,固件必须清零SSHIFT位(QUADSPI_CR[4])。因此,在半个CLK周期后(下一个反向边沿)对信号采样。四线模式下DDR命令时序如图所示: {{ :icore4t:icore4t_arm_hal_37_2.png?direct |}} == (6) 双闪存模式 == * 若DFM位(QUADSPI_CR[6])为1,QUADSPI处于双闪存模式。QUADSPI使用两个外部四线SPI Flash(FLASH1和FLASH2),在每个周期中发送/接收8位(在DDR模式下为16位),能够有效地将吞吐量和容量扩大一倍。每个Flash使用同一个CLK并可选择使用同一个nCS信号,但其IO0、IO1、IO2和IO3信号是各自独立的。双闪存模式可与单比特模式、双比特模式以及四比特模式结合使用,也可与SDR或DDR模 * 式相结合。Flash的大小在FSIZE[4:0](QUADSPI_DCR[20:16])中指定,指定的值应能够反映Flash的总容量,即单个组件容量的2倍。如果地址X为偶数,QUADSPI赋给地址X的字节是存放于FLASH1的地址X/2中的字节,QUADSPI赋给地址X+1的字节是存放于FLASH2的地址X/2中的字节。也就是说,偶地址中的字节存储于FLASH1,奇地址中的字节存储于FLASH2。 * 在双闪存模式下读取Flash状态寄存器时,需要读取的字节数是单闪存模式下的2倍。这意味着在状态寄存器获取指令到达后,如果每个Flash给出8个有效位,则QUADSPI必须配置为2个字节(16位)的数据长度,它将从每个Flash接收1个字节。如果每个Flash给出一个16位的状态,则QUADSPI必须配置为读取4字节,以在双闪存模式下可获取两个Flash的所有状态位。结果(在数据寄存器中)的最低有效字节是FLASH1状态寄存器的最低有效字节,而下一个字节是FLASH2状态寄存器的最低有效字节。数据寄存器的第三个字节是FLASH1的第二个字节,第四个字节是FLASH2的第二个字节(Flash具有16位状态寄存器时)。 * 偶数个字节必须始终在双闪存模式下访问。因此,若DRM=1,则数据长度字段(QUADSPI_DLR[0])的位0始终保持为1。 * 在双闪存模式下,FLASH1接口信号的行为基本上与正常模式下相同。在指令、地址、交替字节以及空指令周期阶段,FLASH2接口信号具有与FLASH1接口信号完全相同的波形。也就是说,每个Flash总是接收相同的指令与地址。然后,在数据阶段,BK1_IOx和BK2_IOx总线并行传输数据,但发送到FLASH1(或从其接收)的数据与FLASH2中的不同。 == 5.MSC简介 == * MSC是一种计算机和移动设备之间的传输协议,它允许一个通用串行总线(USB)设备来访问主机的计算设备,使两者之间进行文件传输。 * MSC的通用性和操作简单使他成为移动设备上最常见的文件系统,USB MSC并不需要任何特定的文件系统, 相反,它提供了一个简单的界面来读写接口用于访问任何硬盘驱动器。操作系统可以把MSC像本地硬盘一样格式化,并可以与他们喜欢的任何文件系统格式它,当然也可以创建多个分区。 == 6.原理图 == {{ :icore4t:icore4t_arm_hal_37_3.png?direct&700 |}} {{ :icore4t:icore4t_arm_hal_37_4.png?direct&600 |}} * 本实验使用STM32H750的USB OTG FS利用SPI FLASH虚拟U盘,用USB线连接PC机与开发板,在电脑上就可以像操作普通U盘那样来操作开发板的FLASH存储设备。 ==== 四、 实验程序 ==== 1.主函数 int main(void) { /* MCU配置*/ /* 重置所有外围设备,初始化Flash接口和Systick */ HAL_Init(); /* 配置系统时钟*/ SystemClock_Config(); i2c.initialize(); axp152.initialize(); axp152.set_dcdc1(3500);//[ARM & FPGA BK1/2/6 &OTHER] axp152.set_dcdc2(1200);//[FPGA INT & PLL D] axp152.set_aldo1(2500);//[FPGA PLL A] axp152.set_dcdc4(3300);//[POWER_OUTPUT] axp152.set_dcdc3(3300);//[FPGA BK4][Adjustable] axp152.set_aldo2(3300);//[FPGA BK3][Adjustable] axp152.set_dldo1(3300);//[FPGA BK7][Adjustable] axp152.set_dldo2(3300);//[FPGA BK5][Adjustable] /* 初始化所有已配置的外围设备 */ MX_GPIO_Init(); MX_QUADSPI_Init(); MX_USB_DEVICE_Init(); LED_ON; BSP_QSPI_Init(); /* 无限循环 */ while (1) { } } 2.QUADSPI初始化函数 本实验中QSPI我们只是用到了配置的IO,参数没有用到。 uint8_t BSP_QSPI_Init(void) { QSPIHandle.Instance = QUADSPI; /* 调用DeInit函数重置驱动程序 */ if (HAL_QSPI_DeInit(&QSPIHandle) != HAL_OK) { return QSPI_ERROR; } /* 系统级初始化 */ BSP_QSPI_MspInit(&QSPIHandle, NULL); /* QSPI初始化 */ /* 时钟预分频器设置为1,因此QSPI时钟= 240MHz /(1 + 1)= 120MHz */ QSPIHandle.Init.ClockPrescaler = 1; //时钟预分频器 QSPIHandle.Init.FifoThreshold = POSITION_VAL(W25Q64_FLASH_SIZE) - 1; //指定FIFO中的阈值字节数 QSPIHandle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE; //指定样本移位 QSPIHandle.Init.FlashSize = POSITION_VAL(W25Q64_FLASH_SIZE) - 1; //闪存尺寸 QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_6_CYCLE; //指定片选高电平时间 QSPIHandle.Init.ClockMode = QSPI_CLOCK_MODE_0; //指定时钟模式 QSPIHandle.Init.FlashID = QSPI_FLASH_ID_1; //指定要使用的Flash QSPIHandle.Init.DualFlash = QSPI_DUALFLASH_DISABLE; //指定双闪存模式状态 if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK) { } return QSPI_OK; } void HAL_QSPI_MspInit(QSPI_HandleTypeDef* qspiHandle) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(qspiHandle->Instance==QUADSPI) { /* QUADSPI时钟使能*/ __HAL_RCC_QSPI_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); /**QUADSPI GPIO 配置 PB6 ------> QUADSPI_BK1_NCS PF7 ------> QUADSPI_BK1_IO2 PB2 ------> QUADSPI_CLK PD13 ------> QUADSPI_BK1_IO3 PD12 ------> QUADSPI_BK1_IO1 PD11 ------> QUADSPI_BK1_IO0 */ GPIO_InitStruct.Pin = GPIO_PIN_6; //要配置的GPIO引脚 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; //所选引脚的操作模式 GPIO_InitStruct.Pull = GPIO_NOPULL; //所选引脚的上拉或下拉激活 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//引脚的速度 GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;//外设要连接到选定的引脚 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);//根据GPIO_Init中的指定参数初始化GPIOx外设 GPIO_InitStruct.Pin = GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_12|GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_QUADSPI; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); } } 3.STORAGE_Read_FS函数 #define BLK_NBR 0x800 //块数量 #define BLK_SIZ 0x1000 //块大小 int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { BSP_QSPI_Read(buf, blk_addr * BLK_SIZ, blk_len * BLK_SIZ); return (USBD_OK); } 4.STORAGE_Write_FS函数 int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { BSP_QSPI_Write(buf, blk_addr * BLK_SIZ, blk_len * BLK_SIZ); return (USBD_OK); } 5.STORAGE_GetCapacity_FS函数 int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size) //获取存储容量 { *block_num = BLK_NBR; *block_size = BLK_SIZ; return (USBD_OK); } ==== 五、 实验步骤 ==== - 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连); - 把iCore4T(USB DEVICE)通过Micro USB线与计算机相连,为iCore4T供电; - 打开Keil MDK 开发环境,并打开本实验工程; - 烧写程序到iCore4T上; - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 ==== 六、 实验现象 ==== * 在我的电脑中可以虚拟出一个磁盘进行文件操作,如下图: {{ :icore4t:icore4t_arm_hal_37_5.png?direct |}}