| **银杏科技有限公司旗下技术文档发布平台** ||||
|技术支持电话|**0379-69926675-801**|||
|技术支持邮件|Gingko@vip.163.com|||
^ 版本 ^ 日期 ^ 作者 ^ 修改内容 ^
| V1.0 | 2020-07-29 | gingko | 初次建立 |
\\
\\
\\
\\
\\
\\
\\
===== STM32CubeMX教程三十七——USBD_MSC实验(FS) =====
\\
1.在主界面选择File-->New Project 或者直接点击ACCEE TO MCU SELECTOR
{{ :icore4tx:icore4tx_cube_37_1.png?direct |}}
2.出现芯片型号选择,搜索自己芯片的型号,双击型号,或者点击Start Project进入配置
在搜索栏的下面,提供的各 种查找方式,可以选择芯片内核,型号,等等,可以帮助你查找芯片。本实验选取的芯片型号为:STM32H750IBKx。
{{ :icore4tx:icore4tx_cube_37_2.png?direct |}}
3.配置RCC,使用外部时钟源
{{ :icore4tx:icore4tx_cube_37_3.png?direct |}}
4.时基源选择SysTick
{{ :icore4tx:icore4tx_cube_37_4.png?direct |}}
5.将PA10,PB7,PB8设置为GPIO_Output
{{ :icore4tx:icore4tx_cube_37_5.png?direct |}}
6.引脚模式配置
{{ :icore4tx:icore4tx_cube_37_6.png?direct |}}
{{ :icore4tx:icore4tx_cube_37_7.png?direct |}}
7.配置USB_OTG_FS
{{ :icore4tx:icore4tx_cube_37_8.png?direct |}}
8.配置USB_DEVICE
{{ :icore4tx:icore4tx_cube_37_9.png?direct |}}
9.配置QUADAPI
{{ :icore4tx:icore4tx_cube_37_10.png?direct |}}
引脚配置
{{ :icore4tx:icore4tx_cube_37_11.png?direct |}}
10.时钟源设置,选择外部高速时钟源,配置为最大主频
{{ :icore4tx:icore4tx_cube_37_12.png?direct |}}
{{ :icore4tx:icore4tx_cube_37_13.png?direct |}}
11.工程文件的设置, 这里就是工程的各种配置 我们只用到有限几个,其他的默认即可 IDE我们使用的是 MDK V5.27
{{ :icore4tx:icore4tx_cube_37_14.png?direct |}}
12.点击Code Generator,进行进一步配置
{{ :icore4tx:icore4tx_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 创建工程
{{ :icore4tx:icore4tx_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 集成开发环境使用方法。
==== 二、 实验设备及平台 ====
- iCore4TX 双核心板[[https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22598974120.3.29da532fLkazHH&id=614919247574|点击购买]]。
- iCore4TX 扩展底板。
- 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功能框图 ===
{{ :icore4tx:icore4tx_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.原理图 ==
{{ :icore4tx:icore4tx_arm_hal_37_3.png?direct&700 |}}
{{ :icore4tx:icore4tx_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]
axp152.set_dcdc2(1200);//[FPGA INT]
axp152.set_dcdc3(3300);//[DCOUT3]
axp152.set_dcdc4(3300);//[DCOUT4]
axp152.set_aldo1(3300);//[BK3]
axp152.set_aldo2(3300);//[ALDOOUT2]
axp152.set_dldo1(3300);//[BK0]
axp152.set_dldo2(3300);//[BK1]
HAL_Delay(200);
/* 初始化所有已配置的外围设备 */
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);
}
==== 五、 实验步骤 ====
- 把仿真器与iCore4TX的SWD调试口相连(直接相连或者通过转接器相连);
- 把iCore4TX(USB DEVICE)通过Micro USB线与计算机相连,为iCore4TX供电;
- 打开Keil MDK 开发环境,并打开本实验工程;
- 烧写程序到iCore4TX上;
- 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。
==== 六、 实验现象 ====
* 在我的电脑中可以虚拟出一个磁盘进行文件操作,如下图:
{{ :icore4tx:icore4tx_arm_hal_37_5.png?direct |}}