用户工具

站点工具


icore4t_37
银杏科技有限公司旗下技术文档发布平台
技术支持电话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 2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置 在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。 3.配置RCC,使用外部时钟源 4.时基源选择SysTick 5.将PA10,PB7,PB8设置为GPIO_Output 6.引脚模式配置 7.配置USB_OTG_FS 8.配置USB_DEVICE 9.配置QUADAPI 引脚配置 10.时钟源设置,选择外部高速时钟源,配置为最大主频 11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27 12.点击Code Generator,进行进一步配置

  • 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 创建工程 创建成功,打开工程。


实验三十七:USBD_MSC实验(FS)——虚拟U盘(SPI FLASH)

一、 实验目的与意义

  1. 了解STM32 USB SLAVE结构。
  2. 了解STM32 USB SLAVE特征。
  3. 掌握USB SLAVE MSC的使用方法。
  4. 掌握STM32 HAL库中QUADSPI属性的配置方法。
  5. 掌握KEIL MDK 集成开发环境使用方法。

二、 实验设备及平台

  1. iCore4T 双核心板。点击购买
  2. iCore4T 扩展底板。
  3. JLINK(或相同功能)仿真器。点击购买
  4. Micro USB线缆。
  5. Keil MDK 开发平台。
  6. STM32CubeMX开发平台。
  7. 装有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功能框图

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命令时序如图所示:

(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.原理图

  • 本实验使用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);
}

五、 实验步骤

  1. 把仿真器与iCore4T的SWD调试口相连(直接相连或者通过转接器相连);
  2. 把iCore4T(USB DEVICE)通过Micro USB线与计算机相连,为iCore4T供电;
  3. 打开Keil MDK 开发环境,并打开本实验工程;
  4. 烧写程序到iCore4T上;
  5. 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。

六、 实验现象

  • 在我的电脑中可以虚拟出一个磁盘进行文件操作,如下图:

icore4t_37.txt · 最后更改: 2022/04/01 10:52 由 sean