【STM32H7教程】第84章 STM32H7的SPI 总线应用之SPI Flash的MDK下载算法制作

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

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

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

84.1 初学者重要提示

84.2 MDK下载算法基础知识

84.3 创建MDK下载算法通用流程

84.4 SPI Flash的MDK下载算法制作

84.5 SPI Flash的MDK下载算法使用方法

84.6 实验例程说明

84.7 总结

 

 

84.1 初学者重要提示

  1.   SPI Flash的相关知识点可以看第78章和79章。
  2.   SPI Flash下载算法文件直接采用HAL库制作,方便大家自己修改。

84.2 MDK下载算法基础知识

Flash编程算法是一种用于擦除应用程序或将应用程序下载到Flash的程序代码。MDK本身支持的各种器件都自带下载算法,存放在MDK各种器件的软件包里面,以STM32H7为例,算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软件包版本不同,数值2.6.0不同),但不支持的需要我们自己制作,本章教程为此而生。

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

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

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

擦除操作大致流程:

 

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

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

编程操作大致流程:

 

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

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

校验操作大致流程:

 

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

84.3 创建MDK下载算法通用流程

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

84.3.1 第1步,使用MDK提供好的程序模板

位于路径:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。

效果如下:

 

84.3.2 第2步,修改工程名

MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根据自己的需要做修改。比如修改为MyDevice.uvprojx。

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

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

 

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

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

 

注:MDK这里设置的名字与下面位置识别出来的算法名无关:

 

这个名字是在FlashDev.c里面定义的。

84.3.5 第5步,修改编程算法文件FlashPrg.c

模板工程里面仅提供了接口函数,内容需要用户自己填。

 

/* 
   Mandatory Flash Programming Functions (Called by FlashOS):
                int Init        (unsigned long adr,   // Initialize Flash
                                 unsigned long clk,
                                 unsigned long fnc);
                int UnInit      (unsigned long fnc);  // De-initialize Flash
                int EraseSector (unsigned long adr);  // Erase Sector Function
                int ProgramPage (unsigned long adr,   // Program Page Function
                                 unsigned long sz,
                                 unsigned char *buf);

   Optional  Flash Programming Functions (Called by FlashOS):
                int BlankCheck  (unsigned long adr,   // Blank Check
                                 unsigned long sz,
                                 unsigned char pat);
                int EraseChip   (void);               // Erase complete Device
      unsigned long Verify      (unsigned long adr,   // Verify Function
                                 unsigned long sz,
                                 unsigned char *buf);

       - BlanckCheck  is necessary if Flash space is not mapped into CPU memory space
       - Verify       is necessary if Flash space is not mapped into CPU memory space
       - if EraseChip is not provided than EraseSector for all sectors is called
*/

/*
 *  Initialize Flash Programming Functions
 *    Parameter:      adr:  Device Base Address
 *                    clk:  Clock Frequency (Hz)
 *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {

  /* Add your Code */
  return (0);                                  // Finished without Errors
}


/*
 *  De-Initialize Flash Programming Functions
 *    Parameter:      fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)
 *    Return Value:   0 - OK,  1 - Failed
 */

int UnInit (unsigned long fnc) {

  /* Add your Code */
  return (0);                                  // Finished without Errors
}


/*
 *  Erase complete Flash Memory
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseChip (void) {

  /* Add your Code */
  return (0);                                  // Finished without Errors
}


/*
 *  Erase Sector in Flash Memory
 *    Parameter:      adr:  Sector Address
 *    Return Value:   0 - OK,  1 - Failed
 */

int EraseSector (unsigned long adr) {

  /* Add your Code */
  return (0);                                  // Finished without Errors
}


/*
 *  Program Page in Flash Memory
 *    Parameter:      adr:  Page Start Address
 *                    sz:   Page Size
 *                    buf:  Page Data
 *    Return Value:   0 - OK,  1 - Failed
 */

int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) {

  /* Add your Code */
  return (0);                                  // Finished without Errors
}

84.3.6 第6步,修改配置文件FlashDev.c

模板工程里面提供简单的配置说明:

struct FlashDevice const FlashDevice  =  {
   FLASH_DRV_VERS,             // Driver Version, do not modify!
   "New Device 256kB Flash",   // Device Name 
   ONCHIP,                     // Device Type
   0x00000000,                 // Device Start Address
   0x00040000,                 // Device Size in Bytes (256kB)
   1024,                       // Programming Page Size
   0,                          // Reserved, must be 0
   0xFF,                       // Initial Content of Erased Memory
   100,                        // Program Page Timeout 100 mSec
   3000,                       // Erase Sector Timeout 3000 mSec

// Specify Size and Address of Sectors
   0x002000, 0x000000,         // Sector Size  8kB (8 Sectors)
   0x010000, 0x010000,         // Sector Size 64kB (2 Sectors) 
   0x002000, 0x030000,         // Sector Size  8kB (8 Sectors)
   SECTOR_END
};

注:名字New Device 256kB Flash就是我们第4步所说的。MDK的Option选项里面会识别出这个名字。

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

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

 

汇编:

 

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

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

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

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

 

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

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

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

 

84.3.9 第9步,分散加载设置

我们这里的分散加载文件直接使用MDK模板工程里提供好的即可,无需任何修改。

 

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

; Linker Control File (scatter-loading)
;

PRG 0 PI               ; Programming Functions
{
  PrgCode +0           ; Code
  {
    * (+RO)
  }
  PrgData +0           ; Data
  {
    * (+RW,+ZI)
  }
}

DSCR +0                ; Device Description
{
  DevDscr +0
  {
    FlashDev.o
  }
}

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

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

 

84.4 SPI Flash的MDK下载算法制作

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

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

这两点非常重要:

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

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

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

 

84.4.3 第3步,修改HAL库

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

 

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

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

84.4.5 第5步,配置文件FlashDev.c的实现

配置如下:

struct FlashDevice const FlashDevice  =  {
    FLASH_DRV_VERS,               /* 驱动版本,勿修改,这个是MDK定的 */
    "ARMFLY_STM32H743_SPI_25Q64", /* 算法名,添加算法到MDK安装目录会显示此名字 */
    EXTSPI,                       /* 设备类型 */
    0xC0000000,                   /* Flash起始地址 */
    8 * 1024 * 1024,              /* Flash大小,8MB */
    4096,                         /* 编程页大小 */
    0,                            /* 保留,必须为0 */
    0xFF,                         /* 擦除后的数值 */
    6000,                         /* 页编程等待时间 */
    6000,                         /* 扇区擦除等待时间 */
    4 * 1024, 0x000000,          /* 扇区大小,扇区地址 */
    SECTOR_END    
};

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

 

84.4.6 第6步,编程文件FlashPrg.c的实现

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

  •   初始化函数Init
/*
*********************************************************************************************************
*    函 数 名: Init
*    功能说明: Flash编程初始化
*    形    参: adr Flash基地址,芯片首地址。
*             clk 时钟频率
*             fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
*    返 回 值: 0 表示成功, 1表示失败
*********************************************************************************************************
*/
int Init (unsigned long adr, unsigned long clk, unsigned long fnc) 
{
    int result = 0;
 
    /* 系统初始化 */
    SystemInit(); 

    /* 时钟初始化 */
    result = SystemClock_Config();
    if (result  != 0)
    {
        return 1;        
    }

    bsp_InitSPIBus();
    bsp_InitSFlash();

    return 0;
}
  •   复位初始化函数Uinit

擦除,编程和校验函数后都会调用此函数,我们这里未使用。

/*
*********************************************************************************************************
*    函 数 名: UnInit
*    功能说明: 复位初始化
*    形    参: fnc 函数代码,1 - Erase, 2 - Program, 3 - Verify
*    返 回 值: 0 表示成功, 1表示失败
*********************************************************************************************************
*/
int UnInit (unsigned long fnc) 
{ 
    return (0);
}

复位初始化这里,直接将其设置为内存映射模式。

  •   整个芯片擦除函数EraseChip

如果大家配置勾选了MDK Option选项中此处的配置,会调用的整个芯片擦除:

 

实际应用中不推荐大家勾选这里,因为整个芯片擦除太耽误时间。

另外,如果大家的算法工程里面没有添加此函数,MDK会调用扇区擦除函数来实现,直到所有扇区擦除完毕。

/*
*********************************************************************************************************
*    函 数 名: EraseChip
*    功能说明: 整个芯片擦除
*    形    参: 无
*    返 回 值: 0 表示成功, 1表示失败
*********************************************************************************************************
*/
int EraseChip (void) 
{    
    sf_EraseChip();
    return 0;      
}
  •   扇区擦除函数EraseSector

如果大家配置勾选了MDK Option选项中此处的配置,会调用扇区擦除:

 

/*
*********************************************************************************************************
*    函 数 名: EraseSector
*    功能说明: 扇区擦除
*    形    参: adr 擦除地址
*    返 回 值: 无
*********************************************************************************************************
*/
int EraseSector (unsigned long adr) 
{    
    adr -= SPI_FLASH_MEM_ADDR;

    sf_EraseSector(adr);
    
    return 0;     
}

这里要注意两点:

(1)     程序里面的操作adr -= SPI_FLASH_MEM_ADDR,实际传递进来的地址是带了首地址的,即0xC0000000。特别注意,我们这里的0xC0000000是随意设置的,因为STM32H7的标准SPI外设并不支持内存映射。

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

  •   页编程函数ProgramPage

页编程函数实现如下:

/*
*********************************************************************************************************
*    函 数 名: ProgramPage
*    功能说明: 页编程
*    形    参: adr 页起始地址
*             sz  页大小
*             buf 要写入的数据地址
*    返 回 值: 无
*********************************************************************************************************
*/
int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) 
{
    adr -= SPI_FLASH_MEM_ADDR;

    sf_WriteBuffer(buf, adr, sz);
    
    return (0);                      
}

这里注意两点:

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

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

  •   读取和校验函数

校验函数实现如下:

/*
*********************************************************************************************************
*    函 数 名: Verify
*    功能说明: 校验
*    形    参: adr 起始地址
*             sz  数据大小
*             buf 要校验的数据缓冲地址
*    返 回 值: -
*********************************************************************************************************
*/
unsigned char aux_buf[4096];
unsigned long Verify (unsigned long adr, unsigned long sz, unsigned char *buf)
{
    int i;

    adr -= SPI_FLASH_MEM_ADDR;

    sf_ReadBuffer(aux_buf, adr, sz);

    for (i = 0; i< sz; i++) 
    {
        if (aux_buf[i] != buf[i]) 
        return (adr+i);              /* 校验失败 */     
    }

    adr += SPI_FLASH_MEM_ADDR;
    return (adr+sz);                 /* 校验成功 */    
}

84.4.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) */

84.5 SPI Flash的MDK下载算法使用方法

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

 

84.5.1 下载算法存放位置

生成算法文件后,需要大家将其存到MDK安装目录,有两个位置可以存放,任选其一,推荐第2种:

  •   第1种:存放到MDK的STM32H7软包安装目录里面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(软包版本不同,数值2.6.0不同)。
  •   第2种:MDK的安装目录 \ARM\Flash里面。

 

84.5.2 下载配置

注意这里一定要够大,否则会提示算法文件无法加载:

 

我们这里是将其加到DTCM中,即首地址为0x20000000,大家也可以存储到任意其它RAM地址,只要空间还够加载算法文件即可。推荐使用AXI SRAM(地址0x24000000),因为这块RAM空间足够大。

如果要下载程序到QSPI Flash里面,需要做如下配置:

 

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

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

84.6 实验例程说明

本章配套例子:V7-065_SPI Flash的MDK下载算法制作

编译后,算法文件会存到此路径下:

 

84.7 总结

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

 

原文地址:https://www.cnblogs.com/armfly/p/14027556.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


  译序:JWMediaPlayer是开源的网页使用的Flash播放器。本文采摘于JWPlayer的官方文档,讲解了JWPlayer对于RTMP的使用方法,我们可以从JWPlayer客户端的角度来了解RTMP协议。以下是官方原文:      简介    RTMP(RealTimeMessagingProtocol
    Flash编程原理都是只能将1写为0,而不能将0写成1.所以在Flash编程之前,必须将对应的块擦除,而擦除的过程就是将所有位都写为1的过程,块内的所有字节变为0xFF.因此可以说,编程是将相应位写0的过程,而擦除是将相应位写1的过程,两者的执行过程完全相反.一、Nor和NandFlash
 上传setenvgatewayip192.168.1.1;setenvserverip192.168.1.7;setenvipaddr192.168.1.156;mw.b0x820000000xff0x1000000sfprobe0sfread0x8200000000x1000000tftp0x82000000test.bin0x1000000 下载mw.b82000000ff1000000tftp82000000test.bi
Error:FlashDownloadFailed-"Cortex-M3"出现一般有两种情况:1.SWD模式下,Debug菜单中,Reset菜单选项(Autodetect/HWreset/sysresetReq/Vectreset)默认是AutoDetect,改成SysResetReq即可。2.Jtag模式下,主要是芯片大小选错。Flash->ConfigureFalshTools配置窗口,切换到“Utilities"
jPlayer是一个用于控制和播放mp3文件的jQuery插件。它在后台使用Flash来播放mp3文件,前台播放器外观完全可以使用XHML/CSS自定义。支持:有一点比较好的是,在支持html5的浏览器上会使用html5的标签audio或者video,而不支持的浏览器上使用swf来播放选择需要播放的Mp3文件。播放、暂停
#ifndef__FONTUPD_H__#define__FONTUPD_H__#include"sys.h" //字库信息结构体定义33字节__packedtypedefstruct{u8fontok;//字库存在标志,0XAA,字库正常;其他,字库不存在u32ugbkaddr;//unigbk的地址u32ugbksize;//unigbk的大小u32f12addr;//gbk12地址u32g
ROM(ReadOnlyMemory)和RAM(RandomAccessMemory)指的都是半导体存储器。ROM在系统停止供电的时候仍然可以保持数据,而RAM通常都是在掉电之后就丢失数据,但是访问速度快。典型的RAM就是计算机的内存。RAM有两大类,一种称为静态RAM(StaticRAM/SRAM),SRAM速度非常快,是目前读写最快的存储
JSpc端和移动端实现复制到剪贴板功能实现在网页上复制文本到剪切板,一般是使用JS+Flash结合的方法,网上有很多相关文章介绍。随着HTML5技术的发展,Flash已经在很多场合不适用了,甚至被屏蔽。本文介绍的一款JS插件,实现了纯JS方法复制文本到剪切板。插件名是Clipboard.js,该插件不依
例子:R0=1R1=1R2=10R3=e000ed10R12=0LR=fffffff9(中断返回值)PC=0PSR=60000013或60000016或60000036(Z、C、EXCEPT_NUM:RTC_WKUP_IRQn、EXTI0_IRQn、USART2_IRQn)BFAR=e000ed38(不关心)CFSR=20000(INVSTATE:Invalidstateusagefault thePCvaluestackedf
 内存接口概念首先来分析下操作GPIO控制器和操作UART控制器两者的区别如图是S3C2440是个片上系统,有GPIO控制器(接有GPIO管脚),有串口控制器(接有TXDRXD引脚)配置GPIO控制器相应的寄存器,即可让引脚输出高低电平;配置UART控制器相应的寄存器,即可让引脚输出波形。前者相对简单,类
小编导语:    近几年来,网页游戏成为了游戏界关注的焦点,由于其制作简单,成本低并且收益率较高,因此成为了众多游戏厂商追逐的对象,但是除了商家夸张的炒作宣传外,很少有页游佳作出现。然而,随着Unity3D游戏引擎的出现,网页游戏的3D化成了页游冲出重围的杀手锏,那么在flash网页游戏称
1.指定数组到特定的Flash单元#pragmalocation=0x000FFF00 __rootconstcharFlash_config[]={0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xA,0xB,0xC,0xD,0xE,0xF,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0
继续研究发现,计算机的固件真的很有趣。参考了一些重要的资料,比如http://donovan6000.blogspot.com/2013/06/insyde-bios-modding-advanced-and-power-tabs.html等,对于IDA的使用也了解了一些。最后,总结一下目前看来可行性的方案:0.基础知识储备,包括UEFIBIOS的概念,InsydeBIOS的
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>navigator对象<itle></head><body><buttononclick="checkFlash()">检测</button>
修改网上流传的flash-marker.js(function(global,factory){typeofexports==='object'&&typeofmodule!=='undefined'?module.exports=factory():typeofdefine==='function'&&define.amd?define(factory
shareObject本地缓存存储位置:win7系统用户到C:\Users\[你的用户名]\AppData\Roaming\Macromedia\FlashPlayer\#SharedObjects\XP或2003用户到:C:\DocumentsandSettings\用户名\ApplicationData\Macromedia\FlashPlayer\#SharedObjects\ ---------------------作者:iteye_
安装谷歌浏览器之后经常遇到Flash崩溃或者浏览器在浏览Flash内容时卡死的情况。在网上查找资料大多都认为应该是浏览器自带的Flash插件工作模式引起的问题,解决方法如下:首先在地址栏输入chrome://plugins/显示浏览器使用的插件。点击右上角的详细信息,可以看到Flash插件为进程外
之前一直使用的W25Q16spiflash都没问题,换了一款W25Q80后发现工作不正常,经过测试,初步定位到问题在于初始化SPI后是否将CS拉高。于是又去查看了一下原厂代码:发现原厂的代码初始化SPI接口时是专门拉高CS的。结论:网上很多代码初始化SPI接口时没有专门拉高CS,对某些型号可能确实
======================================================NANDFlash最小存储单元:写数据操作:通过对控制闸(ControlGate)施加高电压,然后允许源极(SOURCE)和汲极(RRAIN)间的N信道(N-Channel)流入电子,等到电流够强,电子获得足够能量时,便会越过浮置闸(FloatingGate)底下的二氧化硅层(S
安装CnarioPlayer3.8.1.156或其他版本时,有时会出现如下提示:Warning4154.AdobeFlashPlayer13...notcorrectlyinstalled:请前往AdobeFlash网站,并选择下图示的版本下载安装: