【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下載算法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM