你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32H7的SPI 总线应用之SPI Flash的STM32CubeProg下载算法制作

[复制链接]
STMCU小助手 发布时间:2021-12-19 15:00
85.1 初学者重要提示
  QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
  STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
  本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。
85.2 STM32CubeProg简介
STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。

d80578d217eb2a83520c6f3d797218f4.png


85.3 STM32CubeProg下载算法基础知识

STM32CubeProg下载算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。ST自家的芯片都自带下载算法,存放在STM32CubeProg安装目录里面,但不支持的需要我们自己制作,本章教程为此而生。

85.3.1 程序能够通过下载算法下载到芯片的核心思想
认识到这点很重要:通过IDE开发环境创建一批与地址信息无关的算法文件,实现的功能主要有初始化,擦除,编程,读取,校验等,然后STM32CubeProg下载阶段,会将算法文件加载到芯片的内部RAM里面,然后STM32CubeProg通过与这个算法文件的交互,实现程序下载,数据读取等操作。

85.3.2 算法程序中擦除操作执行流程
注:下面是MDK的算法执行流程,STM32CubeProg是类似的。

擦除操作大致流程:

d75670256363bb50203bed0bbe4f9fd2.png


  加载算法到芯片RAM。
  执行初始化函数Init。
  执行擦除操作,根据用户配置,这里可以选择整个芯片擦除或者扇区擦除。
  执行Uinit函数。
  操作完毕。

85.3.3 算法程序中编程操作执行流程
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。

编程操作大致流程:

4fa96e9e33d700081f2cdbd312afb31e.png


  针对IDE生成的axf(elf)可执行文件做Init初始化,这个axf(elf)文件是指的大家自己创建应用程序生成的。
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
  加载算法到RAM。
  执行Init函数。
  加载用户到RAM缓冲。
  执行Program Page页编程函数。
  执行Uninit函数。
  操作完毕。

85.3.4 算法程序中校验操作执行流程
注:下面是MDK的算法执行流程,STM32CubeProg是类似的(没有Unint函数)。

校验操作大致流程:

7deeae4b650bd82c457492af14fc26b7.png


  校验要用到IDE生成的axf(elf)可执行文件。校验就是axf(elf)文件中下载到芯片的程序和实际下载的程序读出来做比较。
  查看Flash算法是否在FLM文件。如果没有在,操作失败。如果在:
加载算法到RAM。
执行Init函数。
查看校验算法是否存在
  如果有,加载应用程序到RAM并执行校验。
  如果没有,执行计算CRC,将芯片中读取数据出来和RAM中加载应用计算输出的CRC值做比较。
  执行Uninit函数。
  替换BKPT(BreakPoint断点指令)为 B. 死循环指令。
  执行RecoverySupportStop,回复支持停止。
  执行DebugCoreStop,调试内核停止。
  运行应用:
  执行失败
  执行成功,再执行硬件复位。
  操作完毕,停止调试端口。

85.4 创建STM32CubeProg下载算法通用流程
下面是STM32CubeProg给的一种大致操作流程,不限制必须采用这种方法,自己创建也可以的。

85.4.1 第1步,使用STM32CubeProg提供好的程序模板
位于路径:STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader

97f385e3b62a055bac9b5c0214b877e5.png


以M25P64为例(注,其余步骤就以这个为例子进行说明):

43b298f996937fe35e89697fc6479cb9.png


85.4.2 第2步,修改工程名
STM32CubeProg提供的工程模板原始名字是M25P64_STM3210E-EVAL.uvproj,大家可以根据自己的需要做修改。比如修改为MyDevice.uvproj(MDK4的后缀)或者MyDevice.uvprojx(MDK5的后缀)。

85.4.3 第3步,修改使用的器件

在MDK的Option选项里面设置使用的器件。

36026b9a6d60249610d0246eb725b246.png


85.4.4 第4步,修改输出算法文件的名字
这个名字是方便用户查看的,比如设置为stm32h7,那么输出的算法文件就是stm32h7.stldr。

52c72b3993f28a366a22eac56f8fd043.png


注:STM32CubeProg软件里面别的文件名并不采用这个,而是由用户在文件Dev_Inf.c里面定义的:

7b48153027a44de33926c2da008716a0.png


85.4.5 第5步,修改使用的库文件和头文件路径
根据大家使用的主控芯片,添加相应的库文件和头文件路径。

85.4.6 第5步,修改编程算法文件Loader_Src.c
模板工程里面提供的是M25P64,部分代码如下:

  1. /**
  2.   * Description :
  3.   * Initilize the MCU Clock, the GPIO Pins corresponding to the
  4.   * device and initilize the FSMC with the chosen configuration
  5.   * Inputs    :
  6.   *      None
  7.   * outputs   :
  8.   *      R0             : "1"             : Operation succeeded
  9.   *               "0"             : Operation failure
  10.   * Note: Mandatory for all types of device
  11.   */
  12. int Init (void)
  13. {  
  14.   SystemInit();
  15.   sFLASH_Init();
  16.   return 1;
  17. }


  18. /**
  19.   * Description :
  20.   * Read data from the device
  21.   * Inputs    :
  22.   *      Address       : Write location
  23.   *      Size          : Length in bytes  
  24.   *      buffer        : Address where to get the data to write
  25.   * outputs   :
  26.   *      R0             : "1"             : Operation succeeded
  27.   *               "0"             : Operation failure
  28.   * Note: Mandatory for all types except SRAM and PSRAM   
  29.   */
  30. int Read (uint32_t Address, uint32_t Size, uint8_t* buffer)
  31. {
  32.   sFLASH_ReadBuffer(buffer, Address, Size);
  33.   return 1;
  34. }


  35. /**
  36.   * Description :
  37.   * Write data from the device
  38.   * Inputs    :
  39.   *      Address       : Write location
  40.   *      Size          : Length in bytes  
  41.   *      buffer        : Address where to get the data to write
  42.   * outputs   :
  43.   *      R0           : "1"             : Operation succeeded
  44.   *                     "0"             : Operation failure
  45.   * Note: Mandatory for all types except SRAM and PSRAM   
  46.   */
  47. int Write (uint32_t Address, uint32_t Size, uint8_t* buffer)
  48. {
  49.   sFLASH_WriteBuffer(buffer, Address, Size);
  50.   return 1;
  51. }


  52. /**
  53.   * Description :
  54.   * Erase a full sector in the device
  55.   * Inputs    :
  56.   *     None
  57.   * outputs   :
  58.   *     R0             : "1" : Operation succeeded
  59.   *              "0" : Operation failure
  60.   * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
  61.   */
  62. int MassErase (void)
  63. {  
  64.   sFLASH_EraseBulk();
  65.   return 1;   
  66. }
复制代码

85.4.7 第6步,修改配置文件Dev_Inf.c
模板工程里面提供简单的配置说明:

  1. #if defined (__ICCARM__)
  2. __root struct StorageInfo const StorageInfo  =  {
  3. #else
  4. struct StorageInfo const StorageInfo  =  {
  5. #endif
  6.    "M25P64_STM3210E-EVAL",           // Device Name + version number
  7.    SPI_FLASH,                       // Device Type
  8.    0x00000000,                     // Device Start Address
  9.    0x00800000,                      // Device Size in Bytes (8MBytes/64Mbits)
  10.    0x00000100,                      // Programming Page Size 16Bytes
  11.    0xFF,                            // Initial Content of Erased Memory
  12. // Specify Size and Address of Sectors (view example below)
  13.    0x00000080, 0x00010000,          // Sector Num : 128 ,Sector Size: 64KBytes
  14.    0x00000000, 0x00000000,
  15. };
复制代码

注:名字M25P64_STM3210E-EVAL就是我们第4步所说的。STM32CubeProg会识别出这个名字。

85.4.8 第7步,保证生成的算法文件中RO和RW段的独立性,即与地址无关。

C和汇编的配置都勾选上:

afddd6e839292aa2b94b43e79544d630.png


汇编:

f639ae5b0d3c2e0e0190ec509fbc8a4d.png


如果程序的所有只读段都与位置无关,则该程序为只读位置无关(ROPI, Read-only position independence)。ROPI段通常是位置无关代码(PIC,position-independent code),但可以是只读数据,也可以是PIC和只读数据的组合。选择“ ROPI”选项,可以避免不得不将代码加载到内存中的特定位置。这对于以下例程特别有用:

(1)加载以响应运行事件。

(2)在不同情况下使用其他例程的不同组合加载到内存中。

(3)在执行期间映射到不同的地址。

使用Read-Write position independence同理,表示的可读可写数据段。

85.4.9 第8步,将程序可执行文件axf修改为stldr格式
通过下面的命令就可以将生成的axf可执行文件修改为stldr。

c683673d2481d8dce75b6fa01d624600.png


85.4.10   第9步,分散加载设置
我们这里的分散加载文件直接使用MDK模板工程里提供好的即可。

b96835a7506516319002e6b2e8db9371.png


分散加载文件中的内容如下:

  1. FLASH_LOADER 0x20000004 PI   ; FlashLoader Functions
  2. {
  3.   PrgCode +0           ; Code
  4.   {
  5.     * (+RO)
  6.   }
  7.   PrgData +0           ; Data
  8.   {
  9.     * (+RW,+ZI)
  10.   }
  11. }

  12. DEVICE_INFO +0               ; Device Info
  13. {
  14.   DevInfo +0           ; Info structure
  15.   {
  16.     dev_inf.o
  17.   }
  18. }
复制代码

这里要修改下Flash算法加载地址,将0x20000004修改为STM32H7的RAM地址,任何RAM块地址均可,只要够存储Flash算法。推荐设置为AXI SRAM地址0x24000004,因为空间够大,有512KB。

--diag_suppress L6305用于屏蔽L6503类型警告信息。

特别注意,设置了分散加载后,此处的配置就不再起作用了:

641395c374a3af02f1a92d3ff9ee4199.png


85.5 SPI Flash的STM32CubeProg下载算法制作
下面将SPI Flash算法制作过程中的几个关键点为大家做个说明。

85.5.1 第1步,制作前重要提示

这两点非常重要:

  程序里面不要开启任何中断,全部查询方式。
  HAL库里面各种时间基准相关的API全部处理掉。简单省事些,我们这里是直接注释,采用死等即可。无需做超时等待,因为超时后,已经意味着操作失败了,跟死等没有区别。

85.5.2 第2步,准备一个工程模板
推荐大家直接使用我们本章工程准备好的模板即可,如果大家自己制作,注意一点,请使用当前最新的HAL库。

80ad5e3abdd6a993c8621230401a1313.png


85.5.3 第3步,修改HAL库
这一步比较重要,主要修改了以下三个文件:

33bf89f25487547d4008f43b7cf10e03.png


主要是修改了HAL库时间基准相关的几个API,并注释掉了一批无关的API。具体修改内容,大家可以找个比较软件,对比修改后的这个文件和CubeH7软件包V1.8.0(软件包里面的HAL库版本是V1.9.0)的差异即可。

85.5.4 第4步,时钟初始化
我们已经用不到滴答定时器了,直接在bsp.c文件里面对滴答初始化函数做重定向:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: HAL_InitTick
  4. *    功能说明: 重定向,不使用
  5. *    形    参: TickPriority
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
  10. {
  11.     return HAL_OK;
  12. }
复制代码

然后就是HSE外置晶振的配置,大家根据自己的板子实际外挂晶振大小,修改stm32h7xx_hal_conf.h文件中HSE_VALUE大小,实际晶振多大,这里就修改为多大:

  1. #if !defined  (HSE_VALUE)
  2. #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
  3. #endif /* HSE_VALUE */
复制代码

最后修改PLL:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: SystemClock_Config
  4. *    功能说明: 初始化系统时钟
  5. *                System Clock source            = PLL (HSE)
  6. *                SYSCLK(Hz)                     = 400000000 (CPU Clock)
  7. *               HCLK(Hz)                       = 200000000 (AXI and AHBs Clock)
  8. *                AHB Prescaler                  = 2
  9. *                D1 APB3 Prescaler              = 2 (APB3 Clock  100MHz)
  10. *                D2 APB1 Prescaler              = 2 (APB1 Clock  100MHz)
  11. *                D2 APB2 Prescaler              = 2 (APB2 Clock  100MHz)
  12. *                D3 APB4 Prescaler              = 2 (APB4 Clock  100MHz)
  13. *                HSE Frequency(Hz)              = 25000000
  14. *               PLL_M                          = 5
  15. *                PLL_N                          = 160
  16. *                PLL_P                          = 2
  17. *                PLL_Q                          = 4
  18. *                PLL_R                          = 2
  19. *                VDD(V)                         = 3.3
  20. *                Flash Latency(WS)              = 4
  21. *    形    参: 无
  22. *    返 回 值: 1 表示失败,0 表示成功
  23. *********************************************************************************************************
  24. */
  25. int SystemClock_Config(void)
  26. {
  27.     RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  28.     RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  29.     HAL_StatusTypeDef ret = HAL_OK;

  30.     /* 锁住SCU(Supply configuration update) */
  31.     MODIFY_REG(PWR->CR3, PWR_CR3_SCUEN, 0);

  32.     /*
  33.       1、芯片内部的LDO稳压器输出的电压范围,可选VOS1,VOS2和VOS3,不同范围对应不同的Flash读速度,
  34.          详情看参考手册的Table 12的表格。
  35.       2、这里选择使用VOS1,电压范围1.15V - 1.26V。
  36.     */
  37.     __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  38.     while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}

  39.     /* 使能HSE,并选择HSE作为PLL时钟源 */
  40.     RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  41.     RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  42.     RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
  43.     RCC_OscInitStruct.CSIState = RCC_CSI_OFF;
  44.     RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  45.     RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;

  46.     RCC_OscInitStruct.PLL.PLLM = 5;
  47.     RCC_OscInitStruct.PLL.PLLN = 160;
  48.     RCC_OscInitStruct.PLL.PLLP = 2;
  49.     RCC_OscInitStruct.PLL.PLLR = 2;
  50.     RCC_OscInitStruct.PLL.PLLQ = 4;        

  51.     RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
  52.     RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;   
  53.     ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
  54.     if(ret != HAL_OK)
  55.     {
  56.         return 1;        
  57.     }

  58.     /*
  59.        选择PLL的输出作为系统时钟
  60.        配置RCC_CLOCKTYPE_SYSCLK系统时钟
  61.        配置RCC_CLOCKTYPE_HCLK 时钟,对应AHB1,AHB2,AHB3和AHB4总线
  62.        配置RCC_CLOCKTYPE_PCLK1时钟,对应APB1总线
  63.        配置RCC_CLOCKTYPE_PCLK2时钟,对应APB2总线
  64.        配置RCC_CLOCKTYPE_D1PCLK1时钟,对应APB3总线
  65.        配置RCC_CLOCKTYPE_D3PCLK1时钟,对应APB4总线     
  66.     */
  67.     RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \
  68.                                  RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1);

  69.     RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  70.     RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
  71.     RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
  72.     RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;  
  73.     RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
  74.     RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
  75.     RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;

  76.     /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
  77.     ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);
  78.     if(ret != HAL_OK)
  79.     {
  80.         return 1;
  81.     }

  82.     /*
  83.       使用IO的高速模式,要使能IO补偿,即调用下面三个函数
  84.       (1)使能CSI clock
  85.       (2)使能SYSCFG clock
  86.       (3)使能I/O补偿单元, 设置SYSCFG_CCCSR寄存器的bit0
  87.     */
  88.     __HAL_RCC_CSI_ENABLE() ;

  89.     __HAL_RCC_SYSCFG_CLK_ENABLE() ;

  90.     HAL_EnableCompensationCell();

  91.     __HAL_RCC_D2SRAM1_CLK_ENABLE();
  92.     __HAL_RCC_D2SRAM2_CLK_ENABLE();
  93.     __HAL_RCC_D2SRAM3_CLK_ENABLE();

  94.     return 0;
  95. }
复制代码

85.5.5 第5步,配置文件Dev_Inf.c的实现
配置如下:

  1. #if defined (__ICCARM__)
  2. __root struct StorageInfo const StorageInfo  =  {
  3. #else
  4. struct StorageInfo const StorageInfo =  {
  5. #endif
  6.     "ARMFLY_STM32H743_SPI_W25Q64", /* 算法名,添加算法到STM32CubeProg安装目录会显示此名字 */
  7.     NOR_FLASH,                      /* 设备类型 */
  8.     0xC0000000,                     /* Flash起始地址 */
  9.     8 * 1024 * 1024,                /* Flash大小,8MB */
  10.     4096,                           /* 编程页大小 */
  11.     0xFF,                           /* 擦除后的数值 */
  12.     2048 , 4 * 1024,                /* 块个数和块大小 */
  13.     0x00000000, 0x00000000,
  14. };
复制代码

注释已经比较详细,大家根据自己的需要做修改即可。注意一点,算法名ARMFLY_STM32H743_SPI_W25Q64会反馈到这个地方:



85.5.6 第6步,编程文件Loader_Src.c的实现
下面将变成文件中实现的几个函数为大家做个说明:

  初始化函数Init
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: Init
  4. *    功能说明: Flash编程初始化
  5. *    形    参: 无
  6. *    返 回 值: 0 表示失败, 1表示成功
  7. *********************************************************************************************************
  8. */
  9. int Init(void)
  10. {   
  11.     int result = 0;

  12.     /* 系统初始化 */
  13.     SystemInit();

  14.     /* 时钟初始化 */
  15.     result = SystemClock_Config();
  16.     if (result == 1)
  17.     {
  18.         return 0;
  19.     }

  20.     /* SPI Flash初始化 */
  21.     bsp_InitSPIBus();
  22.     bsp_InitSFlash();

  23.     return 1;
  24. }
复制代码

  整个芯片擦除函数MassErase
整个芯片的擦除实现如下:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: MassErase
  4. *    功能说明: 整个芯片擦除
  5. *    形    参: 无
  6. *    返 回 值: 1 表示成功,0表示失败
  7. *********************************************************************************************************
  8. */
  9. int MassErase(void)
  10. {
  11.     sf_EraseChip();

  12.     return 1;   
  13. }
复制代码

  扇区擦除函数SectorErase
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: SectorErase
  4. *    功能说明: EraseStartAddress 擦除起始地址
  5. *             EraseEndAddress   擦除结束地址
  6. *    形    参: adr 擦除地址
  7. *    返 回 值: 1 表示成功,0表示失败
  8. *********************************************************************************************************
  9. */
  10. int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
  11. {
  12.     uint32_t BlockAddr;

  13.     EraseStartAddress -= SPI_FLASH_MEM_ADDR;
  14.     EraseEndAddress -= SPI_FLASH_MEM_ADDR;
  15.     EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */

  16.     while (EraseEndAddress >= EraseStartAddress)
  17.     {
  18.         BlockAddr = EraseStartAddress & 0x0FFFFFFF;

  19.         sf_EraseSector(BlockAddr);   

  20.         EraseStartAddress += 0x1000;
  21.     }

  22.     return 1;
  23. }
复制代码

这里要注意两点:

(1)     程序里面的操作EraseStartAddress-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。

(2)     这里执行的擦除大小要前面Dev_Inf.c文件中配置的扇区大小一致,这里是执行的4KB为扇区进行擦除。

  页编程函数Write
页编程函数实现如下:

  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: Write
  4. *    功能说明: 写数据到Device
  5. *    形    参: Address 写入地址
  6. *             Size   写入大小,单位字节
  7. *             buffer 要写入的数据地址
  8. *    返 回 值: 1 表示成功,0表示失败
  9. *********************************************************************************************************
  10. */
  11. int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
  12. {  
  13.     Address -= SPI_FLASH_MEM_ADDR;

  14.     sf_WriteBuffer(buffer, Address, Size);

  15.     return 1;
  16. }
复制代码

这里注意两点:

(1)     W25Q256的页大小是256字节,前面FlashDev.c中将页编程大小设置为4096字节,主要是方便擦除操作。

(2)     程序里面的操作Address-= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。

  读取函数
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: Read
  4. *    功能说明: 从SPI Flash读取数据
  5. *    形    参: Address 读取地址
  6. *             Size   读取大小,单位字节
  7. *             buffer 读取存放的数据缓冲
  8. *    返 回 值: 1 表示成功,0表示失败
  9. *********************************************************************************************************
  10. */
  11. int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
  12. {
  13.     Address -= SPI_FLASH_MEM_ADDR;
  14.     sf_ReadBuffer(Buffer, Address, Size);

  15.     return 1;
  16. }
复制代码

  读取和校验函数
我们程序中未做校验函数。如果程序中未做校验函数,那么STM32CubeProg会读取数据做校验。

85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)
最后一步就是SPI Flash(W25Q64)的驱动修改,大家可以根据自己的需求做修改。使用的引脚定义在文件bsp_spi_bus.c:

  1. /*
  2. *********************************************************************************************************
  3. *                                时钟,引脚,DMA,中断等宏定义
  4. *********************************************************************************************************
  5. */
  6. #define SPIx                            SPI1
  7. #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
  8. #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()

  9. #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
  10. #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()

  11. #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
  12. #define SPIx_SCK_GPIO                    GPIOB
  13. #define SPIx_SCK_PIN                    GPIO_PIN_3
  14. #define SPIx_SCK_AF                        GPIO_AF5_SPI1

  15. #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
  16. #define SPIx_MISO_GPIO                    GPIOB
  17. #define SPIx_MISO_PIN                     GPIO_PIN_4
  18. #define SPIx_MISO_AF                    GPIO_AF5_SPI1

  19. #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
  20. #define SPIx_MOSI_GPIO                    GPIOB
  21. #define SPIx_MOSI_PIN                     GPIO_PIN_5
  22. #define SPIx_MOSI_AF                    GPIO_AF5_SPI1
复制代码

硬件设置了之后,剩下就是SPI Flash相关的几个配置和片选引脚配置,在文件bsp_spi_flash.c:

主要是下面这几个:

  1. /* 串行Flash的片选GPIO端口, PD13  */
  2. #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
  3. #define SF_CS_GPIO                    GPIOD
  4. #define SF_CS_PIN                    GPIO_PIN_13

  5. #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U)
  6. #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN

  7. #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
  8. #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
  9. #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
  10. #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
  11. #define CMD_WREN      0x06        /* 写使能命令 */
  12. #define CMD_READ      0x03      /* 读数据区命令 */
  13. #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
  14. #define CMD_RDID      0x9F        /* 读器件ID命令 */
  15. #define CMD_SE        0x20        /* 擦除扇区命令 */
  16. #define CMD_BE        0xC7        /* 批量擦除命令 */
  17. #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */

  18. #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */
复制代码

85.6 QSPI Flash的STM32CubeProg下载算法使用方法
编译本章教程配套的例子,生成的算法文件位于此路径下:

5ec6d402a7646232a55f4834cd00ac0c.png


85.6.1 下载算法存放位置
生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:

\STMicroelectronics\STM32Cube\STM32CubeProgrammer\bin\ExternalLoader

2609073778af5845a869911ab36624f4.png


85.6.2 STM32CubeProg下载配置
我们这里以STLINK连接开发板为例进行说明(USB DFU或者串口方式不支持下载外部Flash)。

d4e256633eafdbf4bcd8cce6e3b5909f.png


点击Connect后效果如下:



在这里选择我们制作的下载算法:

d4fa28f64be2de98e04f3188eb2fe09e.png


任意加载个hex或者bin文件,并按照如下配置,然后点击Start Programming

842518a7cc1dc569598d372132b2ee58.png


下载完成后的效果如下:

68a92f267829ca77f125db982d6c0479.png


85.6.3 验证算法文件是否可以正常使用
为了验证算法文件是否可以正常使用,大家可以运行本教程第86章配套的例子。也可以使用STM32CubeProg直接读取:

566d2fba421c710da70e5656c6934466.png


85.7 实验例程说明

本章配套例子:V7-066_SPI Flash的STM32CubeProg下载算法制作。

编译本章教程配套的例子,生成的算法文件位于此路径下:

6108d1ff7e349f2aa268ee31804c57ca.png


85.8 总结
本章节就为大家讲解这么多,为了熟练掌握,大家可以尝试自己实现一个Flash下载算法。



27a88335ceb606bd021c3ebc65d88e21.png
51f220556177a36712977b8d140b627f.png
收藏 评论0 发布时间:2021-12-19 15:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版