81.1 初学者重要提示
QSPI Flash的相关知识点可以看第78章和79章。
QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
81.2 STM32CubeProg简介
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。
81.3 STM32CubeProg下载算法基础知识
STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。
81.3.1 程序能够通过下载算法下载到芯片的核心思想
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。
81.3.2 算法程序中擦除操作执行流程
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。
擦除操作大致流程:
加载算法到芯片RAM。
执行初始化函数Init。
执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
执行Uinit函数。
操作完毕。
81.3.3 算法程序中编程操作执行流程
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
编程操作大致流程:
针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
加载算法到RAM。
执行Init函数。
加载用户到RAM缓冲。
执行Program Page页编程函数。
执行Uninit函数。
操作完毕。
81.3.4 算法程序中校验操作执行流程
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。
校验操作大致流程:
校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
加载算法到RAM。
执行Init函数。
查看校验算法是否存在
如果有,加载应用程序到RAM并执行校验。
如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
执行Uninit函数。
替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
执行RecoverySupportStop,回复支持停止。
执行DebugCoreStop,调试内核停止。
运行应用:
执行失败
执行成功,再执行硬件复位。
操作完毕,停止调试端口。
81.4 创建STM32CubeProg下载算法通用流程
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。
81.4.1 第1步,使用STM32CubeProg提供好的程序模板
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
以M25P64为例(注,其余步骤就以这个为例子进行说明):
81.4.2 第2步,修改工程名
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。
81.4.3 第3步,修改使用的器件
在MDK的Option选项里面设置使用的器件。
81.4.4 第4步,修改输出算法文件的名字
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。
注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:
81.4.5 第5步,修改使用的库文件和头文件路径
根据大家使用的主控芯片,添加相应的库文件和头文件路径。
81.4.6 第5步,修改编程算法文件Loader_Src.c
模板工程里面提供的是M25P64,部分代码如下:
- /**
- * Description :
- * Initilize the MCU Clock, the GPIO Pins corresponding to the
- * device and initilize the FSMC with the chosen configuration
- * Inputs :
- * None
- * outputs :
- * R0 : "1" : Operation succeeded
- * "0" : Operation failure
- * Note: Mandatory for all types of device
- */
- int Init (void)
- {
- SystemInit();
- sFLASH_Init();
- return 1;
- }
- /**
- * Description :
- * Read data from the device
- * Inputs :
- * Address : Write location
- * Size : Length in bytes
- * buffer : Address where to get the data to write
- * outputs :
- * R0 : "1" : Operation succeeded
- * "0" : Operation failure
- * Note: Mandatory for all types except SRAM and PSRAM
- */
- int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
- {
- sFLASH_ReadBuffer(buffer, Address, Size);
- return 1;
- }
- /**
- * Description :
- * Write data from the device
- * Inputs :
- * Address : Write location
- * Size : Length in bytes
- * buffer : Address where to get the data to write
- * outputs :
- * R0 : "1" : Operation succeeded
- * "0" : Operation failure
- * Note: Mandatory for all types except SRAM and PSRAM
- */
- int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
- {
- sFLASH_WriteBuffer(buffer, Address, Size);
- return 1;
- }
- /**
- * Description :
- * Erase a full sector in the device
- * Inputs :
- * None
- * outputs :
- * R0 : "1" : Operation succeeded
- * "0" : Operation failure
- * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
- */
- int MassErase (void)
- {
- sFLASH_EraseBulk();
- return 1;
- }
复制代码
81.4.7 第6步,修改配置文件Dev_Inf.c
模板工程里面提供简单的配置说明:
- #if defined (__ICCARM__)
- __root struct StorageInfo const StorageInfo = {
- #else
- struct StorageInfo const StorageInfo = {
- #endif
- "M25P64_STM3210E-EVAL", // Device Name + version number
- SPI_FLASH, // Device Type
- 0x00000000, // Device Start Address
- 0x00800000, // Device Size in Bytes (8MBytes/64Mbits)
- 0x00000100, // Programming Page Size 16Bytes
- 0xFF, // Initial Content of Erased Memory
- // Specify Size and Address of Sectors (view example below)
- 0x00000080, 0x00010000, // Sector Num : 128 ,Sector Size: 64KBytes
- 0x00000000, 0x00000000,
- };
复制代码
注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。
81.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。
C和汇编的配置都勾选上:
汇编:
如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:
(1)加载以响应运行事件。
(2)在不同情况下使用其他例程的不同组合加载到内存中。
(3)在执行期间映射到不同的地址。
使用Read-Write position independence同理,表示的可读可写数据段。
81.4.9 第8步,将程序可执行文件axf修改为stldr格式
通过下面的命令就可以将生成的axf可执行文件修改为stldr。
81.4.10 第9步,分散加载设置
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。
分散加载文件中的内容如下:
- FLASH_LOADER 0x20000004 PI ; FlashLoader Functions
- {
- PrgCode +0 ; Code
- {
- * (+RO)
- }
- PrgData +0 ; Data
- {
- * (+RW,+ZI)
- }
- }
- DEVICE_INFO +0 ; Device Info
- {
- DevInfo +0 ; Info structure
- {
- dev_inf.o
- }
- }
复制代码
这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。
--diag_suppress L6305用于屏蔽L6503类型警告信息。
特别注意,设置了分散加载后,此处的配置就不再起作用了:
81.5 QSPI Flash的STM32CubeProg下载算法制作
下面将QSPI Flash算法制作过程中的几个关键点为大家做个说明。
81.5.1 第1步,制作前重要提示
这两点非常重要:
程序里面不要开启任何中断,全部查询方式。
HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。
81.5.2 第2步,准备一个工程模板
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。
81.5.3 第3步,修改HAL库
这一步比较重要,主要修改了以下三个文件:
主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。
81.5.4 第4步,时钟初始化
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:
- /*
- *********************************************************************************************************
- * 函 数 名: HAL_InitTick
- * 功能说明: 重定向,不使用
- * 形 参: TickPriority
- * 返 回 值: 无
- *********************************************************************************************************
- */
- HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
- {
- return HAL_OK;
- }
复制代码
然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:
- #if !defined (HSE_VALUE)
- #define HSE_VALUE ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
- #endif /* HSE_VALUE */
复制代码
最后修改PLL:
- /*
- *********************************************************************************************************
- * 函 数 名: SystemClock_Config
- * 功能说明: 初始化系统时钟
- * System Clock source = PLL (HSE)
- * SYSCLK(Hz) = 400000000 (CPU Clock)
- * HCLK(Hz) = 200000000 (AXI and AHBs Clock)
- * AHB Prescaler = 2
- * D1 APB3 Prescaler = 2 (APB3 Clock 100MHz)
- * D2 APB1 Prescaler = 2 (APB1 Clock 100MHz)
- * D2 APB2 Prescaler = 2 (APB2 Clock 100MHz)
- * D3 APB4 Prescaler = 2 (APB4 Clock 100MHz)
- * HSE Frequency(Hz) = 25000000
- * PLL_M = 5
- * PLL_N = 160
- * PLL_P = 2
- * PLL_Q = 4
- * PLL_R = 2
- * VDD(V) = 3.3
- * Flash Latency(WS) = 4
- * 形 参: 无
- * 返 回 值: 1 表示失败,0 表示成功
- *********************************************************************************************************
- */
- int SystemClock_Config(void)
- {
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
- RCC_OscInitTypeDef RCC_OscInitStruct = {0};
- HAL_StatusTypeDef ret = HAL_OK;
- /* 锁住SCU(Supply configuration update) */
- MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);
- /*
复制代码
1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
详情看参考手册的Table 12的表格。
2、这里选择使用VOS1,电压范围1.15V - 1.26V。
- */
- __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
- while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
- /* 使能HSE,并选择HSE作为PLL时钟源 */
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
- RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
- RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- RCC_OscInitStruct.PLL.PLLM = 5;
- RCC_OscInitStruct.PLL.PLLN = 160;
- RCC_OscInitStruct.PLL.PLLP = 2;
- RCC_OscInitStruct.PLL.PLLR = 2;
- RCC_OscInitStruct.PLL.PLLQ = 4;
- RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
- RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
- ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
- if(ret != HAL_OK)
- {
- return 1;
- }
- /*
- 选择PLL的输出作为系统时钟
- 配置RCC_CLOCKTYPE_SYSCLK系统时钟
- 配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
- 配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
- 配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
- 配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
- 配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线
- */
- RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
- RCC_CLOCKTYPE_PCLK2 | RCC_CLOCKTYPE_D3PCLK1);
- RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
- RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
- RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
- RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
- RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
- RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
- RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
- /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
- ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
- if(ret != HAL_OK)
- {
- return 1;
- }
- /*
- 使用IO的高速模式,要使能IO补偿,即调用下面三个函数
- (1)使能CSI clock
- (2)使能SYSCFG clock
- (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
- */
- __HAL_RCC_CSI_ENABLE() ;
- __HAL_RCC_SYSCFG_CLK_ENABLE() ;
- HAL_EnableCompensationCell();
- __HAL_RCC_D2SRAM1_CLK_ENABLE();
- __HAL_RCC_D2SRAM2_CLK_ENABLE();
- __HAL_RCC_D2SRAM3_CLK_ENABLE();
- return 0;
- }
复制代码
81.5.5 第5步,配置文件Dev_Inf.c的实现
配置如下:
- #if defined (__ICCARM__)
- __root struct StorageInfo const StorageInfo = {
- #else
- struct StorageInfo const StorageInfo = {
- #endif
- "ARMFLY_STM32H7x_QSPI_W25Q256", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
- NOR_FLASH, /* 设备类型 */
- 0x90000000, /* Flash起始地址 */
- 32 * 1024 * 1024, /* Flash大小,32MB */
- 4*1024, /* 编程页大小 */
- 0xFF, /* 擦除后的数值 */
- 512 , 64 * 1024, /* 块个数和块大小 */
- 0x00000000, 0x00000000,
- };
复制代码
注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H7x_QSPI_W25Q256会反馈到这个地方:
81.5.6 第6步,编程文件Loader_Src.c的实现
下面将变成文件中实现的几个函数为大家做个说明:
初始化函数Init
- /*
- *********************************************************************************************************
- * 函 数 名: Init
- * 功能说明: Flash编程初始化
- * 形 参: 无
- * 返 回 值: 0 表示失败, 1表示成功
- *********************************************************************************************************
- */
- int Init(void)
- {
- int result = 1;
- /* 系统初始化 */
- SystemInit();
- /* 时钟初始化 */
- result = SystemClock_Config();
- if (result == 1)
- {
- return 0;
- }
- /* W25Q256初始化 */
- result = bsp_InitQSPI_W25Q256();
- if (result == 1)
- {
- return 0;
- }
- /* 内存映射 */
- result = QSPI_MemoryMapped();
- if (result == 1)
- {
- return 0;
- }
- return 1;
- }
复制代码
初始化完毕后将其设置为内存映射模式。
整个芯片擦除函数MassErase
整个芯片的擦除实现如下:
- /*
- *********************************************************************************************************
- * 函 数 名: MassErase
- * 功能说明: 整个芯片擦除
- * 形 参: 无
- * 返 回 值: 1 表示成功,0表示失败
- *********************************************************************************************************
- */
- int MassErase(void)
- {
- int result = 1;
- /* W25Q256初始化 */
- result = bsp_InitQSPI_W25Q256();
- if (result == 1)
- {
- return 0;
- }
- result = QSPI_EraseChip();
- if (result == 1)
- {
- return 0;
- }
- /* 内存映射 */
- result = QSPI_MemoryMapped();
- if (result == 1)
- {
- return 0;
- }
- return result;
- }
复制代码
扇区擦除函数SectorErase
扇区擦除实现:
- /*
- *********************************************************************************************************
- * 函 数 名: SectorErase
- * 功能说明: EraseStartAddress 擦除起始地址
- * EraseEndAddress 擦除结束地址
- * 形 参: adr 擦除地址
- * 返 回 值: 1 表示成功,0表示失败
- *********************************************************************************************************
- */
- int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
- {
- uint32_t BlockAddr, result;
- EraseStartAddress -= QSPI_FLASH_MEM_ADDR;
- EraseEndAddress -= QSPI_FLASH_MEM_ADDR;
- EraseStartAddress = EraseStartAddress - EraseStartAddress % 0x10000; /* 64KB首地址 */
- /* W25Q256初始化 */
- result = bsp_InitQSPI_W25Q256();
- if (result == 1)
- {
- return 0;
- }
- while (EraseEndAddress >= EraseStartAddress)
- {
- /* 防止超出256MB空间 */
- BlockAddr = EraseStartAddress & 0x0FFFFFFF;
- result = QSPI_EraseSector(BlockAddr);
- if (result == 1)
- {
- QSPI_MemoryMapped();
- return 0;
- }
- EraseStartAddress += 0x10000;
- }
- /* 内存映射 */
- result = QSPI_MemoryMapped();
- if (result == 1)
- {
- return 0;
- }
- return 1;
- }
复制代码
这里要注意两点:
(1) 程序里面的操作EraseStartAddress-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
(2) 这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的64KB为扇区进行擦除。
页编程函数Write
页编程函数实现如下:
- int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
- {
- int result = 0;
- uint32_t end_addr, current_size, current_addr;
- if (Address < QSPI_FLASH_MEM_ADDR || Address >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES)
- {
- return 0;
- }
- Address -= QSPI_FLASH_MEM_ADDR;
- /* 计算写入地址和页末的字节大小 */
- current_size = QSPI_PAGE_SIZE - (Address % QSPI_PAGE_SIZE);
- /* 检测页剩余空间是否够用 */
- if (current_size > Size)
- {
- current_size = Size;
- }
- /* W25Q256初始化 */
- result = bsp_InitQSPI_W25Q256();
- if (result == 1)
- {
- return 0;
- }
- current_addr = Address;
- end_addr = Address + Size;
- do{
- if (QSPI_WriteBuffer(buffer, current_addr, current_size) == 1)
- {
- QSPI_MemoryMapped();
- return 0;
- }
- /* 更新地址 */
- current_addr += current_size;
- buffer += current_size;
- current_size = ((current_addr + QSPI_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : QSPI_PAGE_SIZE;
- }while(current_addr < end_addr);
- /* 内存映射 */
- result = QSPI_MemoryMapped();
- if (result == 1)
- {
- return 0;
- }
- return (1);
- }
复制代码
这里注意两点:
(1) W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,所以此程序要做处理。
(2) 程序里面的操作Address-= QSPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0x90000000。
读取和校验函数
我们程序中未做读取和校验函数。
(1) 如果程序中未做读取函数,那么STM32CubeProg会以总线方式进行读取,这也是为什么每个函数执行完毕都设置为内存映射模式的原因。
(2) 如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。
81.5.7 第7步,修改QSPI Flash驱动文件(引脚,命令等)
最后一步就是QSPI Flash(W25Q256)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_qspi_w25q256.c(做了条件编译,包含了H7-TOOL和STM32-V7板子):
- /*
- STM32-V7开发板接线
- PG6/QUADSPI_BK1_NCS AF10
- PF10/QUADSPI_CLK AF9
- PF8/QUADSPI_BK1_IO0 AF10
- PF9/QUADSPI_BK1_IO1 AF10
- PF7/QUADSPI_BK1_IO2 AF9
- PF6/QUADSPI_BK1_IO3 AF9
- W25Q256JV有512块,每块有16个扇区,每个扇区Sector有16页,每页有256字节,共计32MB
- H7-TOOL开发板接线
- PG6/QUADSPI_BK1_NCS AF10
- PB2/QUADSPI_CLK AF9
- PD11/QUADSPI_BK1_IO0 AF10
- PD12/QUADSPI_BK1_IO1 AF10
- PF7/QUADSPI_BK1_IO2 AF9
- PD13/QUADSPI_BK1_IO3 AF9
- */
- /* QSPI引脚和时钟相关配置宏定义 */
- #if 0
- #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE()
- #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE()
- #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
- #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
- #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
- #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
- #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE()
- #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE()
- #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET()
- #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET()
- #define QSPI_CS_PIN GPIO_PIN_6
- #define QSPI_CS_GPIO_PORT GPIOG
- #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI
- #define QSPI_CLK_PIN GPIO_PIN_2
- #define QSPI_CLK_GPIO_PORT GPIOB
- #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI
- #define QSPI_BK1_D0_PIN GPIO_PIN_11
- #define QSPI_BK1_D0_GPIO_PORT GPIOD
- #define QSPI_BK1_D0_GPIO_AF GPIO_AF9_QUADSPI
- #define QSPI_BK1_D1_PIN GPIO_PIN_12
- #define QSPI_BK1_D1_GPIO_PORT GPIOD
- #define QSPI_BK1_D1_GPIO_AF GPIO_AF9_QUADSPI
- #define QSPI_BK1_D2_PIN GPIO_PIN_7
- #define QSPI_BK1_D2_GPIO_PORT GPIOF
- #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI
- #define QSPI_BK1_D3_PIN GPIO_PIN_13
- #define QSPI_BK1_D3_GPIO_PORT GPIOD
- #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI
- #else
- #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE()
- #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE()
- #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
- #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE()
- #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE()
- #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET()
- #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET()
- #define QSPI_CS_PIN GPIO_PIN_6
- #define QSPI_CS_GPIO_PORT GPIOG
- #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI
- #define QSPI_CLK_PIN GPIO_PIN_10
- #define QSPI_CLK_GPIO_PORT GPIOF
- #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI
- #define QSPI_BK1_D0_PIN GPIO_PIN_8
- #define QSPI_BK1_D0_GPIO_PORT GPIOF
- #define QSPI_BK1_D0_GPIO_AF GPIO_AF10_QUADSPI
- #define QSPI_BK1_D1_PIN GPIO_PIN_9
- #define QSPI_BK1_D1_GPIO_PORT GPIOF
- #define QSPI_BK1_D1_GPIO_AF GPIO_AF10_QUADSPI
- #define QSPI_BK1_D2_PIN GPIO_PIN_7
- #define QSPI_BK1_D2_GPIO_PORT GPIOF
- #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI
- #define QSPI_BK1_D3_PIN GPIO_PIN_6
- #define QSPI_BK1_D3_GPIO_PORT GPIOF
- #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI
- #endif
复制代码
硬件设置了之后,剩下就是QSPI Flash相关的几个配置,在文件bsp_qspi_w25q256.h:
主要是下面这几个:
- #define QSPI_FLASH_MEM_ADDR 0x90000000
- /* W25Q256JV基本信息 */
- #define QSPI_FLASH_SIZE 25 /* Flash大小,2^25 = 32MB*/
- #define QSPI_SECTOR_SIZE (4 * 1024) /* 扇区大小,4KB */
- #define QSPI_PAGE_SIZE 256 /* 页大小,256字节 */
- #define QSPI_END_ADDR (1 << QSPI_FLASH_SIZE) /* 末尾地址 */
- #define QSPI_FLASH_SIZES 32 * 1024 * 1024 /* Flash大小,2^25 = 32MB*/
- /* W25Q256JV相关命令 */
- #define WRITE_ENABLE_CMD 0x06 /* 写使能指令 */
- #define READ_ID_CMD2 0x9F /* 读取ID命令 */
- #define READ_STATUS_REG_CMD 0x05 /* 读取状态命令 */
- #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD 0x21 /* 32bit地址扇区擦除指令, 4KB */
- #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD 0x34 /* 32bit地址的4线快速写入命令 */
- #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD 0xEC /* 32bit地址的4线快速读取命令 */
- #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD 0xDC /* 4字节地址,64K扇区 */
- #define BULK_ERASE_CMD 0xC7 /* 整片擦除 */
复制代码
81.6 QSPI Flash的STM32CubeProg下载算法使用方法
编译本章教程配套的例子,生成的算法文件位于此路径下:
81.6.1 下载算法存放位置
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:
\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader
81.6.2 STM32CubeProg下载配置
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。
点击Connect后效果如下:
在这里选择我们制作的下载算法:
任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming
下载完成后的效果如下:
81.6.3 验证算法文件是否可以正常使用
为了验证算法文件是否可以正常使用,大家可以运行本教程第82章或者83章配套的例子。也可以使用STM32CubeProg直接读取:
81.7 实验例程说明
本章配套例子:V7-061_QSPI Flash的STM32CubeProg下载算法制作。
编译本章教程配套的例子,生成的算法文件位于此路径下:
81.8 总结
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。
|