【STM32H7教程】第80章 STM32H7的QSPI 匯流排應用之QSPI Flash的MDK下載算法制作
完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980
第80章 STM32H7的QSPI 匯流排應用之QSPI Flash的MDK下載算法制作
本章節為大家講解MDK下載算法制作方法。
80.1 初學者重要提示
80.2 MDK下載演算法基礎知識
80.3 建立MDK下載演算法通用流程
80.4 QSPI Flash的MDK下載算法制作
80.5 QSPI Flash的MDK下載演算法使用方法
80.6 實驗例程說明
80.7 總結
80.1 初學者重要提示
- QSPI Flash的相關知識點可以看第78章和79章。
- QSPI Flash下載演算法檔案直接採用HAL庫製作,方便大家自己修改。
80.2 MDK下載演算法基礎知識
Flash程式設計演算法是一種用於擦除應用程式或將應用程式下載到Flash的程式程式碼。MDK本身支援的各種器件都自帶下載演算法,存放在MDK各種器件的軟體包裡面,以STM32H7為例,演算法存放在\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(軟體包版本不同,數值2.6.0不同),但不支援的需要我們自己製作,本章教程為此而生。
80.2.1 程式能夠通過下載演算法下載到晶片的核心思想
認識到這點很重要:通過MDK建立一批與地址資訊無關的函式,實現的功能主要有初始化,擦除,程式設計,讀取,校驗等,然後MDK除錯下載階段,會將演算法檔案載入到晶片的內部RAM裡面(載入地址可以通過MDK設定),然後MDK通過與這個演算法檔案的互動,實現程式下載,除錯階段資料讀取等操作。
80.2.2 演算法程式中擦除操作執行流程
擦除操作大致流程:
- 載入演算法到晶片RAM。
- 執行初始化函式Init。
- 執行擦除操作,根據使用者的MDK配置,這裡可以選擇整個晶片擦除或者扇區擦除。
- 執行Uinit函式。
- 操作完畢。
80.2.3 演算法程式中程式設計操作執行流程
程式設計操作大致流程:
- 針對MDK生成的axf可執行檔案做Init初始化,這個axf檔案是指的大家自己建立應用程式生成的。
- 檢視Flash演算法是否在FLM檔案。如果沒有在,操作失敗。如果在:
- 載入演算法到RAM。
- 執行Init函式。
- 載入使用者到RAM緩衝。
- 執行Program Page頁程式設計函式。
- 執行Uninit函式。
- 操作完畢。
80.2.4 演算法程式中校驗操作執行流程
校驗操作大致流程:
- 校驗要用到MDK生成的axf可執行檔案。校驗就是axf檔案中下載到晶片的程式和實際下載的程式讀出來做比較。
- 檢視Flash演算法是否在FLM檔案。如果沒有在,操作失敗。如果在:
- 載入演算法到RAM。
- 執行Init函式。
- 檢視校驗演算法是否存在
- 如果有,載入應用程式到RAM並執行校驗。
- 如果沒有,計算CRC,將晶片中讀取出來的資料和RAM中載入應用計算輸出的CRC值做比較。
- 執行Uninit函式。
- 替換BKPT(BreakPoint斷點指令)為 B. 死迴圈指令。
- 執行RecoverySupportStop,恢復支援停止。
- 執行DebugCoreStop,除錯核心停止。
- 執行應用:
- 執行失敗。
- 執行成功,再執行硬體復位。
- 操作完畢,停止除錯埠。
80.3 建立MDK下載演算法通用流程
下面是MDK給的一種大致操作流程,不限制必須採用這種方法,自己建立也可以的。
80.3.1 第1步,使用MDK提供好的程式模板
位於路徑:\Keil\ARM\Pack\ARM\CMSIS\version\Device\_Template_Flash。
效果如下:
80.3.2 第2步,修改工程名
MDK提供的工程模板原始名字是NewDevice.uvprojx,大家可以根據自己的需要做修改。比如修改為MyDevice.uvprojx。
80.3.3 第3步,修改使用的器件
在MDK的Option選項裡面設定使用的器件。
80.3.4 第4步,修改輸出演算法檔案的名字
這個名字是方便使用者檢視的,比如設定為stm32h7,那麼輸出的演算法檔案就是stm32h7.flm。
注:MDK這裡設定的名字與下面位置識別出來的演算法名無關:
這個名字是在FlashDev.c裡面定義的。
80.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 }
80.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選項裡面會識別出這個名字。
80.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同理,表示的可讀可寫資料段。
80.3.8 第8步,將程式可執行檔案axf修改為flm格式
通過下面的命令就可以將生成的axf可執行檔案修改為flm。
80.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型別警告資訊。
特別注意,設定了分散載入後,此處的配置就不再起作用了:
80.4 QSPI Flash的MDK下載算法制作
下面將QSPI Flash算法制作過程中的幾個關鍵點為大家做個說明。
80.4.1 第1步,製作前重要提示
這兩點非常重要:
- 程式裡面不要開啟任何中斷,全部查詢方式。
- HAL庫裡面各種時間基準相關的API全部處理掉。簡單省事些,我們這裡是直接註釋,採用死等即可。無需做超時等待,因為超時後,已經意味著操作失敗了,跟死等沒有區別。
80.4.2 第2步,準備一個工程模板
推薦大家直接使用我們本章工程準備好的模板即可,如果大家自己製作,注意一點,請使用當前最新的HAL庫。
80.4.3 第3步,修改HAL庫
這一步比較重要,主要修改了以下三個檔案:
主要是修改了HAL庫時間基準相關的幾個API,並註釋掉了一批無關的API。具體修改內容,大家可以找個比較軟體,對比修改後的這個檔案和CubeH7軟體包V1.8.0(軟體包裡面的HAL庫版本是V1.9.0)的差異即可。
80.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; }
80.4.5 第5步,配置檔案FlashDev.c的實現
配置如下:
struct FlashDevice const FlashDevice = { FLASH_DRV_VERS, /* 驅動版本,勿修改,這個是MDK定的 */ "ARMFLY_STM32H7x_QSPI_W25Q256", /* 演算法名,新增演算法到MDK安裝目錄會顯示此名字 */ EXTSPI, /* 裝置型別 */ 0x90000000, /* Flash起始地址 */ 32 * 1024 * 1024, /* Flash大小,32MB */ 4 * 1024, /* 程式設計頁大小 */ 0, /* 保留,必須為0 */ 0xFF, /* 擦除後的數值 */ 1000, /* 頁程式設計等待時間 */ 6000, /* 扇區擦除等待時間 */ 64 * 1024, 0x000000, /* 扇區大小,扇區地址 */ SECTOR_END };
註釋已經比較詳細,大家根據自己的需要做修改即可。注意一點,演算法名ARMFLY_STM32H7x_QSPI_W25Q256會反饋到這個地方:
80.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; } /* W25Q256初始化 */ result = bsp_InitQSPI_W25Q256(); if (result != 0) { return 1; } /* 記憶體對映 */ result = QSPI_MemoryMapped(); if (result != 0) { return 1; } return 0; }
初始化完畢後將其設定為記憶體對映模式。
- 復位初始化函式Uinit
擦除,程式設計和校驗函式後都會呼叫此函式。
/* ********************************************************************************************************* * 函 數 名: UnInit * 功能說明: 復位初始化 * 形 參: fnc 函式程式碼,1 - Erase, 2 - Program, 3 - Verify * 返 回 值: 0 表示成功, 1表示失敗 ********************************************************************************************************* */ int UnInit (unsigned long fnc) { int result = 0; /* W25Q256初始化 */ result = bsp_InitQSPI_W25Q256(); if (result != 0) { return 1; } /* 記憶體對映 */ result = QSPI_MemoryMapped(); if (result != 0) { return 1; } return (0); }
復位初始化這裡,直接將其設定為記憶體對映模式。
- 整個晶片擦除函式EraseChip
如果大家配置勾選了MDK Option選項中此處的配置,會呼叫的整個晶片擦除:
實際應用中不推薦大家勾選這裡,因為整個晶片擦除太耽誤時間,比如32MB QSPI Flash整個晶片擦除需要300秒左右。
另外,如果大家的演算法工程裡面沒有新增此函式,MDK會呼叫扇區擦除函式來實現,直到所有扇區擦除完畢。
/* ********************************************************************************************************* * 函 數 名: UnInit * 功能說明: 復位初始化 * 形 參: fnc 函式程式碼,1 - Erase, 2 - Program, 3 - Verify * 返 回 值: 0 表示成功, 1表示失敗 ********************************************************************************************************* */ int UnInit (unsigned long fnc) { int result = 0; /* W25Q256初始化 */ result = bsp_InitQSPI_W25Q256(); if (result != 0) { return 1; } /* 記憶體對映 */ result = QSPI_MemoryMapped(); if (result != 0) { return 1; } return (0); }
- 扇區擦除函式EraseSector
如果大家配置勾選了MDK Option選項中此處的配置,會呼叫扇區擦除:
/* ********************************************************************************************************* * 函 數 名: EraseSector * 功能說明: 扇區擦除 * 形 參: adr 擦除地址 * 返 回 值: 無 ********************************************************************************************************* */ int EraseSector (unsigned long adr) { int result = 0; /* 地址要在操作的晶片範圍內 */ if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES) { return 1; } adr -= QSPI_FLASH_MEM_ADDR; /* W25Q256初始化 */ result = bsp_InitQSPI_W25Q256(); if (result != 0) { return 1; } /* 扇區擦除 */ result = QSPI_EraseSector(adr); if (result != 0) { return 1; } /* 記憶體對映 */ result = QSPI_MemoryMapped(); if (result != 0) { return 1; } return 0; }
這裡要注意兩點:
(1) 程式裡面的操作adr -= QSPI_FLASH_MEM_ADDR,實際傳遞進來的地址是帶了首地址的,即0x90000000。
(2) 這裡執行的擦除大小要前面FlashDev.c檔案中配置的扇區大小一致,這裡是執行的64KB為扇區進行擦除。
- 頁程式設計函式ProgramPage
頁程式設計函式實現如下:
/* ********************************************************************************************************* * 函 數 名: ProgramPage * 功能說明: 頁程式設計 * 形 參: adr 頁起始地址 * sz 頁大小 * buf 要寫入的資料地址 * 返 回 值: 無 ********************************************************************************************************* */ int ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf) { int size; int result = 0; /* 地址要在操作的晶片範圍內 */ if (adr < QSPI_FLASH_MEM_ADDR || adr >= QSPI_FLASH_MEM_ADDR + QSPI_FLASH_SIZES) { return 1; } /* W25Q256初始化 */ result = bsp_InitQSPI_W25Q256(); if (result != 0) { return 1; } adr -= QSPI_FLASH_MEM_ADDR; size = sz; /* 頁程式設計 */ while(size > 0) { if (QSPI_WriteBuffer(buf, adr, 256) == 1) { QSPI_MemoryMapped(); return 1; } size -= 256; adr += 256; buf += 256; } /* 記憶體對映 */ result = QSPI_MemoryMapped(); if (result != 0) { return 1; } return (0); }
這裡注意兩點:
(1) W25Q256的頁大小是256位元組,前面FlashDev.c中將頁程式設計大小設定為4096位元組,所以此程式要做處理。
(2) 程式裡面的操作adr -= QSPI_FLASH_MEM_ADDR,實際傳遞進來的地址是帶了首地址的,即0x90000000。
- 讀取和校驗函式
我們程式中未做讀取和校驗函式。
(1) 如果程式中未做讀取函式,那麼MDK會以匯流排方式進行讀取,這也是為什麼每個函式執行完畢都設定為記憶體對映模式的原因。
(2) 如果程式中未做校驗函式,那麼MDK會讀取資料做CRC校驗。
80.4.7 第7步,修改QSPI Flash驅動檔案(引腳,命令等)
最後一步就是QSPI Flash(W25Q256)的驅動修改,大家可以根據自己的需求做修改。使用的引腳定義在檔案bsp_qspi_w25q256.c(做了條件編譯,包含了H7-TOOL和STM32-V7板子):
/* STM32-V7開發板接線 PG6/QUADSPI_BK1_NCS AF10 PF10/QUADSPI_CLK AF9 PF8/QUADSPI_BK1_IO0 AF10 PF9/QUADSPI_BK1_IO1 AF10 PF7/QUADSPI_BK1_IO2 AF9 PF6/QUADSPI_BK1_IO3 AF9 W25Q256JV有512塊,每塊有16個扇區,每個扇區Sector有16頁,每頁有256位元組,共計32MB H7-TOOL開發板接線 PG6/QUADSPI_BK1_NCS AF10 PB2/QUADSPI_CLK AF9 PD11/QUADSPI_BK1_IO0 AF10 PD12/QUADSPI_BK1_IO1 AF10 PF7/QUADSPI_BK1_IO2 AF9 PD13/QUADSPI_BK1_IO3 AF9 */ /* QSPI引腳和時鐘相關配置巨集定義 */ #if 0 #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE() #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE() #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE() #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOD_CLK_ENABLE() #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE() #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET() #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET() #define QSPI_CS_PIN GPIO_PIN_6 #define QSPI_CS_GPIO_PORT GPIOG #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI #define QSPI_CLK_PIN GPIO_PIN_2 #define QSPI_CLK_GPIO_PORT GPIOB #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI #define QSPI_BK1_D0_PIN GPIO_PIN_11 #define QSPI_BK1_D0_GPIO_PORT GPIOD #define QSPI_BK1_D0_GPIO_AF GPIO_AF9_QUADSPI #define QSPI_BK1_D1_PIN GPIO_PIN_12 #define QSPI_BK1_D1_GPIO_PORT GPIOD #define QSPI_BK1_D1_GPIO_AF GPIO_AF9_QUADSPI #define QSPI_BK1_D2_PIN GPIO_PIN_7 #define QSPI_BK1_D2_GPIO_PORT GPIOF #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI #define QSPI_BK1_D3_PIN GPIO_PIN_13 #define QSPI_BK1_D3_GPIO_PORT GPIOD #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI #else #define QSPI_CLK_ENABLE() __HAL_RCC_QSPI_CLK_ENABLE() #define QSPI_CLK_DISABLE() __HAL_RCC_QSPI_CLK_DISABLE() #define QSPI_CS_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE() #define QSPI_CLK_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() #define QSPI_BK1_D0_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() #define QSPI_BK1_D1_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() #define QSPI_BK1_D2_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() #define QSPI_BK1_D3_GPIO_CLK_ENABLE() __HAL_RCC_GPIOF_CLK_ENABLE() #define QSPI_MDMA_CLK_ENABLE() __HAL_RCC_MDMA_CLK_ENABLE() #define QSPI_FORCE_RESET() __HAL_RCC_QSPI_FORCE_RESET() #define QSPI_RELEASE_RESET() __HAL_RCC_QSPI_RELEASE_RESET() #define QSPI_CS_PIN GPIO_PIN_6 #define QSPI_CS_GPIO_PORT GPIOG #define QSPI_CS_GPIO_AF GPIO_AF10_QUADSPI #define QSPI_CLK_PIN GPIO_PIN_10 #define QSPI_CLK_GPIO_PORT GPIOF #define QSPI_CLK_GPIO_AF GPIO_AF9_QUADSPI #define QSPI_BK1_D0_PIN GPIO_PIN_8 #define QSPI_BK1_D0_GPIO_PORT GPIOF #define QSPI_BK1_D0_GPIO_AF GPIO_AF10_QUADSPI #define QSPI_BK1_D1_PIN GPIO_PIN_9 #define QSPI_BK1_D1_GPIO_PORT GPIOF #define QSPI_BK1_D1_GPIO_AF GPIO_AF10_QUADSPI #define QSPI_BK1_D2_PIN GPIO_PIN_7 #define QSPI_BK1_D2_GPIO_PORT GPIOF #define QSPI_BK1_D2_GPIO_AF GPIO_AF9_QUADSPI #define QSPI_BK1_D3_PIN GPIO_PIN_6 #define QSPI_BK1_D3_GPIO_PORT GPIOF #define QSPI_BK1_D3_GPIO_AF GPIO_AF9_QUADSPI #endif
硬體設定了之後,剩下就是QSPI Flash相關的幾個配置,在檔案bsp_qspi_w25q256.h:
主要是下面這幾個:
#define QSPI_FLASH_MEM_ADDR 0x90000000 /* W25Q256JV基本資訊 */ #define QSPI_FLASH_SIZE 25 /* Flash大小,2^25 = 32MB*/ #define QSPI_SECTOR_SIZE (4 * 1024) /* 扇區大小,4KB */ #define QSPI_PAGE_SIZE 256 /* 頁大小,256位元組 */ #define QSPI_END_ADDR (1 << QSPI_FLASH_SIZE) /* 末尾地址 */ #define QSPI_FLASH_SIZES 32 * 1024 * 1024 /* Flash大小,2^25 = 32MB*/ /* W25Q256JV相關命令 */ #define WRITE_ENABLE_CMD 0x06 /* 寫使能指令 */ #define READ_ID_CMD2 0x9F /* 讀取ID命令 */ #define READ_STATUS_REG_CMD 0x05 /* 讀取狀態命令 */ #define SUBSECTOR_ERASE_4_BYTE_ADDR_CMD 0x21 /* 32bit地址扇區擦除指令, 4KB */ #define QUAD_IN_FAST_PROG_4_BYTE_ADDR_CMD 0x34 /* 32bit地址的4線快速寫入命令 */ #define QUAD_INOUT_FAST_READ_4_BYTE_ADDR_CMD 0xEC /* 32bit地址的4線快速讀取命令 */ #define BLOCK_ERASE_64K_4_BYTE_ADDR_CMD 0xDC /* 4位元組地址,64K扇區 */ #define BULK_ERASE_CMD 0xC7 /* 整片擦除 */
80.5 QSPI Flash的MDK下載演算法使用方法
編譯本章教程配套的例子,生成的演算法檔案位於此路徑下:
80.5.1 下載演算法存放位置
生成演算法檔案後,需要大家將其存到MDK安裝目錄,有兩個位置可以存放,任選其一,推薦第2種:
- 第1種:存放到MDK的STM32H7軟包安裝目錄裡面:\Keil\STM32H7xx_DFP\2.6.0\CMSIS\Flash(軟包版本不同,數值2.6.0不同)。
- 第2種:MDK的安裝目錄 \ARM\Flash裡面。
80.5.2 下載配置
注意這裡一定要夠大,否則會提示演算法檔案無法載入:
我們這裡是將其加到DTCM中,即首地址為0x20000000,大家也可以儲存到任意其它RAM地址,只要空間還夠載入演算法檔案即可。推薦使用AXI SRAM(地址0x24000000),因為這塊RAM空間足夠大。
如果要下載程式到QSPI Flash裡面,需要做如下配置:
80.5.3 除錯配置
注意這裡一定要夠大,否則會提示演算法檔案無法載入:
我們這裡是將其加到DTCM中,即首地址為0x20000000,大家也可以儲存到任意其它RAM地址,只要空間還夠載入演算法檔案即可。
如果要做除錯下載,需要做如下配置:
80.5.4 驗證演算法檔案是否可以正常使用
為了驗證演算法檔案是否可以正常使用,大家可以執行本教程第82章或者83章配套的例子。
80.6 實驗例程說明
本章配套例子:V7-060_QSPI Flash的MDK下載算法制作。
編譯後,演算法檔案會存到此路徑下:
80.7 總結
本章節就為大家講解這麼多,為了熟練掌握,大家可以嘗試自己實現一個Flash下載演算法。