完整教程下載地址: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 初學者重要提示
- SPI Flash的相關知識點可以看第78章和79章。
- 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下載算法。