zoukankan      html  css  js  c++  java
  • 【STM32H7教程】第85章 STM32H7的SPI 总线应用之SPI Flash的STM32CubeProg下载算法制作

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

    第85章       STM32H7的SPI 总线应用之SPI Flash的STM32CubeProg下载算法制作

    本章节为大家讲解STM32CubeProg下载算法制作方法。

    85.1 初学者重要提示

    85.2 STM32CubeProg简介

    85.3 STM32CubeProg下载算法基础知识

    85.4 创建STM32CubeProg下载算法通用流程

    85.4 SPI Flash的STM32CubeProg下载算法制作

    85.5 SPI Flash的STM32CubeProg下载算法使用方法

    85.6 实验例程说明

    85.7 总结

    85.1 初学者重要提示

    1.   QSPI Flash的相关知识点可以看第78章和79章。
    2.   QSPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。
    3.   STM32CubeProg下载算法制作和MDK下载算法制作基本是一样
    4.   本教程的第68章USB DFU和第69章串口IAP章节为大家介绍过STM32CubeProg的用法。STM32CubeProg下载http://www.armbbs.cn/forum.php?mod=viewthread&tid=97370

    85.2 STM32CubeProg简介

    STM32CubeProg,此软件实现了之前的 DfuSe,STLINK 小软件和 Flashloader 三合一,并且支持外部 EEPROM,NOR Flash,SPI Flash,NAND Flash 等烧写,也支持 OTA 编程。

     

    85.3 STM32CubeProg下载算法基础知识

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

    85.3.1 程序能够通过下载算法下载到芯片的核心思想

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

    85.3.2 算法程序中擦除操作执行流程

    注:下面是MDK的算法执行流程,STM32CubeProg是类似的。

    擦除操作大致流程:

     

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

    85.3.3 算法程序中编程操作执行流程

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

    编程操作大致流程:

     

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

    85.3.4 算法程序中校验操作执行流程

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

    校验操作大致流程:

     

    •   校验要用到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提供好的程序模板

    位于路径:STMicroelectronicsSTM32CubeSTM32CubeProgrammerinExternalLoader

     

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

     

    85.4.2 第2步,修改工程名

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

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

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

     

    85.4.4 第4步,修改输出算法文件的名字

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

     

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

     

    85.4.5 第5步,修改使用的库文件和头文件路径

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

    85.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;    
    }

    85.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会识别出这个名字。

    85.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同理,表示的可读可写数据段。

    85.4.9 第8步,将程序可执行文件axf修改为stldr格式

    通过下面的命令就可以将生成的axf可执行文件修改为stldr。

     

    85.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类型警告信息。

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

     

    85.5 SPI Flash的STM32CubeProg下载算法制作

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

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

    这两点非常重要:

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

    85.5.2 第2步,准备一个工程模板

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

     

    85.5.3 第3步,修改HAL库

    这一步比较重要,主要修改了以下三个文件:

     

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

    85.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;
    }

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

    配置如下:

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

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

     

    85.5.6 第6步,编程文件Loader_Src.c的实现

    下面将变成文件中实现的几个函数为大家做个说明:

    •   初始化函数Init
    /*
    *********************************************************************************************************
    *    函 数 名: Init
    *    功能说明: Flash编程初始化
    *    形    参: 无
    *    返 回 值: 0 表示失败, 1表示成功
    *********************************************************************************************************
    */
    int Init(void)
    {   
        int result = 0;
    
        /* 系统初始化 */
        SystemInit(); 
    
        /* 时钟初始化 */
        result = SystemClock_Config();
        if (result == 1)
        {
            return 0;
        }
    
        /* SPI Flash初始化 */
        bsp_InitSPIBus();
        bsp_InitSFlash();
    
        return 1;
    }
    •   整个芯片擦除函数MassErase

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

    /*
    *********************************************************************************************************
    *    函 数 名: MassErase
    *    功能说明: 整个芯片擦除
    *    形    参: 无
    *    返 回 值: 1 表示成功,0表示失败
    *********************************************************************************************************
    */
    int MassErase(void)
    {
        sf_EraseChip();
    
        return 1;   
    }
    •   扇区擦除函数SectorErase
    /*
    *********************************************************************************************************
    *    函 数 名: SectorErase
    *    功能说明: EraseStartAddress 擦除起始地址
    *             EraseEndAddress   擦除结束地址
    *    形    参: adr 擦除地址
    *    返 回 值: 1 表示成功,0表示失败
    *********************************************************************************************************
    */
    int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress)
    {
        uint32_t BlockAddr;
        
        EraseStartAddress -= SPI_FLASH_MEM_ADDR;
        EraseEndAddress -= SPI_FLASH_MEM_ADDR;
        EraseStartAddress = EraseStartAddress -  EraseStartAddress % 0x1000; /* 4KB首地址 */
        
        while (EraseEndAddress >= EraseStartAddress)
        {
            BlockAddr = EraseStartAddress & 0x0FFFFFFF;
    
            sf_EraseSector(BlockAddr);    
    
            EraseStartAddress += 0x1000;
        }
    
        return 1;
    }

    这里要注意两点:

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

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

    •   页编程函数Write

    页编程函数实现如下:

    /*
    *********************************************************************************************************
    *    函 数 名: Write
    *    功能说明: 写数据到Device
    *    形    参: Address 写入地址
    *             Size   写入大小,单位字节
    *             buffer 要写入的数据地址
    *    返 回 值: 1 表示成功,0表示失败
    *********************************************************************************************************
    */
    int Write(uint32_t Address, uint32_t Size, uint8_t* buffer)
    {  
        Address -= SPI_FLASH_MEM_ADDR;
    
        sf_WriteBuffer(buffer, Address, Size);
        
        return 1; 
    }

    这里注意两点:

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

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

    •   读取函数
    /*
    *********************************************************************************************************
    *    函 数 名: Read
    *    功能说明: 从SPI Flash读取数据
    *    形    参: Address 读取地址
    *             Size   读取大小,单位字节
    *             buffer 读取存放的数据缓冲
    *    返 回 值: 1 表示成功,0表示失败
    *********************************************************************************************************
    */
    int Read(uint32_t Address, uint32_t Size, uint8_t* Buffer)
    { 
        Address -= SPI_FLASH_MEM_ADDR;
        sf_ReadBuffer(Buffer, Address, Size);
    
        return 1;
    }
    •   读取和校验函数

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

    85.5.7 第7步,修改SPI Flash驱动文件(引脚,命令等)

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

    /*
    *********************************************************************************************************
    *                                时钟,引脚,DMA,中断等宏定义
    *********************************************************************************************************
    */
    #define SPIx                            SPI1
    #define SPIx_CLK_ENABLE()                __HAL_RCC_SPI1_CLK_ENABLE()
    #define DMAx_CLK_ENABLE()                __HAL_RCC_DMA2_CLK_ENABLE()
    
    #define SPIx_FORCE_RESET()                __HAL_RCC_SPI1_FORCE_RESET()
    #define SPIx_RELEASE_RESET()            __HAL_RCC_SPI1_RELEASE_RESET()
    
    #define SPIx_SCK_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    #define SPIx_SCK_GPIO                    GPIOB
    #define SPIx_SCK_PIN                    GPIO_PIN_3
    #define SPIx_SCK_AF                        GPIO_AF5_SPI1
    
    #define SPIx_MISO_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    #define SPIx_MISO_GPIO                    GPIOB
    #define SPIx_MISO_PIN                     GPIO_PIN_4
    #define SPIx_MISO_AF                    GPIO_AF5_SPI1
    
    #define SPIx_MOSI_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE()
    #define SPIx_MOSI_GPIO                    GPIOB
    #define SPIx_MOSI_PIN                     GPIO_PIN_5
    #define SPIx_MOSI_AF                    GPIO_AF5_SPI1

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

    主要是下面这几个:

    /* 串行Flash的片选GPIO端口, PD13  */
    #define SF_CS_CLK_ENABLE()             __HAL_RCC_GPIOD_CLK_ENABLE()
    #define SF_CS_GPIO                    GPIOD
    #define SF_CS_PIN                    GPIO_PIN_13
    
    #define SF_CS_0()                    SF_CS_GPIO->BSRR = ((uint32_t)SF_CS_PIN << 16U) 
    #define SF_CS_1()                    SF_CS_GPIO->BSRR = SF_CS_PIN
        
    #define CMD_AAI       0xAD      /* AAI 连续编程指令(FOR SST25VF016B) */
    #define CMD_DISWR      0x04        /* 禁止写, 退出AAI状态 */
    #define CMD_EWRSR      0x50        /* 允许写状态寄存器的命令 */
    #define CMD_WRSR      0x01      /* 写状态寄存器命令 */
    #define CMD_WREN      0x06        /* 写使能命令 */
    #define CMD_READ      0x03      /* 读数据区命令 */
    #define CMD_RDSR      0x05        /* 读状态寄存器命令 */
    #define CMD_RDID      0x9F        /* 读器件ID命令 */
    #define CMD_SE        0x20        /* 擦除扇区命令 */
    #define CMD_BE        0xC7        /* 批量擦除命令 */
    #define DUMMY_BYTE    0xA5        /* 哑命令,可以为任意值,用于读操作 */
    
    #define WIP_FLAG      0x01        /* 状态寄存器中的正在编程标志(WIP) */

    85.6 QSPI Flash的STM32CubeProg下载算法使用方法

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

     

    85.6.1 下载算法存放位置

    生成此文件后,需要大家将其存放到STM32CubeProg安装目录路径:

    STMicroelectronicsSTM32CubeSTM32CubeProgrammerinExternalLoader

     

    85.6.2 STM32CubeProg下载配置

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

     

    点击Connect后效果如下:

     

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

     

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

     

    下载完成后的效果如下:

     

    85.6.3 验证算法文件是否可以正常使用

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

     

    85.7 实验例程说明

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

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

     

    85.8 总结

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

  • 相关阅读:
    SparkSql初级编程实践
    云时代架构之苏宁安全架构演进及实践
    云时代架构之知乎网站架构变迁史
    质量属性的六个常见属性场景之《淘宝网》
    云时代架构之游戏服务器的架构演进
    《架构漫谈阅读心得》
    转换后缀表达式
    约瑟夫环(改进3.0)
    栈结构之后缀表达式
    约瑟夫环(改进2.0)
  • 原文地址:https://www.cnblogs.com/armfly/p/14036568.html
Copyright © 2011-2022 走看看