【STM32H7教程】第70章 STM32H7的內部Flash基礎知識和HAL庫API

  • 2020 年 3 月 11 日
  • 筆記

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

第70章       STM32H7的內部Flash基礎知識和HAL庫API

本章節為大家講解內部Flash的基礎知識和對應的HAL庫API。

70.1 初學者重要提示

70.2 內部Flash基礎知識

70.3 內部Flash的HAL庫用法

70.4 源文件stm32h7xx_hal_flash.c

70.5 總結

70.1 初學者重要提示

  1.   本章2.5小節裡面的Flash三級讀保護是重點,務必要掌握明白。
  2.   STM32H743XI有兩個獨立的BANK,一個BANK的編程和擦除操作對另一個BANK沒有任何影響。但是如果用戶應用程式和要擦寫的Flash扇區在同一個BANK,在執行擦寫操作時,用戶應用程式將停止運行,包括中斷服務程式。
  3.   STM32H7的兩個Flash BANK是256bit頻寬,CPU訪問是採用的兩個64bit AXI匯流排。
  4.   HAL庫的內部Flash編程函數HAL_FLASH_Program固定編寫32位元組數據。

70.2 內部Flash基礎知識

70.2.1 內部Flash的硬體框圖

認識一個外設,最好的方式就是看它的框圖,方便我們快速的了解內部Flash的基本功能,然後再看手冊了解細節。

通過這個框圖,我們可以得到如下資訊:

  •   sys_ck時鐘輸入

D1域匯流排時鐘。

  •   po_rst輸入

Power on reset 上電複位。

  •   d1_rst輸入

D1域系統複位。

  •   flash_it輸出

flash中斷請求輸出。

STM32H7的兩個Flash BANK是獨立的,讀寫和擦除互補影響,256bit頻寬,CPU訪問是採用的兩個64bit AXI匯流排。

70.2.2 內部Flash框架

關於內部Flash的框架,了解以下幾個知識點即可:

  •   256bit為單位,即32位元組,並且每個單位配10bit的ECC校驗位。正是這個原因要求大家對Flash進行編程時,必須以32位元組為單位。
  •   兩個獨立的BANK,每個BANK有1MB容量。並且每個BANK的扇區大小固定為128KB,即8個扇區。

BANK1的地址範圍:0x0800 0000到0x080F FFFF。

BANK2的地址範圍:0x0810 0000到0x081F FFFF。

70.2.3 內部Flash讀操作

STM32H7的內部Flash讀操作跟內部RAM的讀操作是一樣的,支援64bit,32bit,16bt和8bit,使用比較簡單。這裡我們重點普及一個知識點,H7的內部Flash在不同主頻下需要做的延遲參數:

對於上面的表格,大家可以看到,當延遲等待設置為0的時候,即無等待,單周期訪問,速度可以做到70MHz。增加1個Flash周期後,訪問速度可以做到140MHz。當增加到3個或4個Flash周期後,最高速度可以做到225MHz。

了解了這個知識點後,再來看下面的時序,非常具有參考意義:

註:ACLK、ARADDR、ARVALID、RDATA、RVALID 和RLAST是AXI匯流排訊號。Flash讀和Flash數據是 Flash 介面訊號。

關於這個時序要要認識到以下幾點:

  •   AXI匯流排發起讀取訊號後,Flash端等待了3個時鐘周期(注意延遲三個周期,支援的Flash速度),之後連續讀取了4個64bit數據。
  •   由於AXI匯流排是64bit的,所以1次讀取就可以讀出64bit數據,連續讀取4次後,就是256bit,即Flash介面的一組數據,因為H7的Flash介面頻寬是256bit的。
  •   如果不開Flash Cache的情況下,連續讀可以提升性能。

下面是連續讀取8個64bit數據的時序圖,跟連續讀取4個64bit數據基本是一樣的,只是多讀取了4組數據。

70.2.4 內部Flash寫入和擦除操作

最重要的知識點放在開頭說:STM32H7內部Flash的寫操作地址必須是32位元組對齊(此地址對32求餘數為0),寫入的數據量也必須是32位元組整數倍,不足32位元組整數倍,補0也要是整數倍。

這裡我們重點了解Flash的寫入和擦除流程。Flash的寫入扇區流程如下:

  1. 先保證這塊扇區空間之前已經擦除過了。
  2. 解鎖Flash,通過HAL庫的函數HAL_FLASH_Unlock實現。
  3. 檢查是否防寫,使能Flash可以編程,然後對其進行編程操作,編程完畢後,等待編程完成,然後禁止Flash編程位。具體操作可以通過HAL庫的函數HAL_FLASH_Program實現。

Flash的擦除流程如下:

  1. 解鎖Flash,通過HAL庫的函數HAL_FLASH_Unlock實現。
  2. 如果是BANK1或者BANK2需要擦除,調用函數FLASH_MassErase,然後等待擦除完成,完成之後關閉BANK1和BANK2的擦除請求位BER1/BER2
  3. 如果是扇區擦除,調用函數FLASH_Erase_Sector,然後等待擦除完成,完成之後關閉扇區的擦除請求位SER。

70.2.5 內部Flash讀保護

內部Flash支援三級讀保護RDP(read out protection)。

  •   Level 0(無保護)

默認設置,所有讀寫和擦除操作都可以正常支援。

  •   Level 1 (Flash連接保護)
    •   可以防止連接調試器時讀取Flash內容,或者RAM中存有惡意獲取程式碼,也是禁止的。因此只要調試器連接晶片,或者從內部RAM啟動運行程式碼,都是禁止訪問內部Flash的。
    •   如果沒有檢測到從內部RAM啟動和系統bootloader啟動且沒有連接調試器,對用戶Flash的讀寫和擦除操作都是允許的,並且其它安全存儲區也是可以訪問的。否則是禁止訪問的,一旦檢測到對Flash的讀請求,將產生匯流排錯誤。
    •   如果將Level 1切換到Level 0時,用戶Flash區和安全區域將被擦除。
  •   Level 2(設備保護和自舉保護)
    •   所有調試特性被關閉。
    •   禁止從RAM啟動。
    •   除了選項位元組裡面的SWAP位可以配置,其它位都無法更改。
    •   禁止了調試功能,且禁止了從RAM和系統bootloader啟動,用戶Flash區是可以執行讀寫和擦除操作的,訪問其它安全存儲區也是可以的。

特別注意:Level2修改是永久性的,一旦配置為Level2將不再支援被修改。

如果大家要設置讀保護的話,使用HAL的API可以設置,也可以使用STM32CubeProg設置:

70.2.6 內部Flash選項位元組

Flash選項位元組主要用於boot地址設置,安全保護,Flash扇區保護等,涉及到的選項比較多。如果大家打算了解這一部分的話,使用STM32CubeProg進行設置即可,也比較方便。

70.2.7 內部Flash的ECC校驗

這裡先說下為什麼內部Flash要帶ECC校驗,因為隨著晶片的製造製程水平越高,帶電粒子產生的位翻轉就越多,此時的ECC是必須要有的,一般可以糾正1-2個bit,安全等級高的Flash類存儲器和RAM類都是必須要帶ECC的。

對於STM32H7帶的ECC校驗,一般不需要用戶去管理。

  • ECC相關知識

關於ECC方面的知識,專門整理了一個帖子:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=86777

70.3 內部Flash的HAL庫用法

70.3.1 內部Flash結構體FLASH_TypeDef

內部Flash相關的暫存器是通過HAL庫中的結構體FLASH_TypeDef定義的,在stm32h743xx.h中可以找到這個類型定義:

typedef struct  {    __IO uint32_t ACR;    __IO uint32_t KEYR1;    __IO uint32_t OPTKEYR;    __IO uint32_t CR1;    __IO uint32_t SR1;    __IO uint32_t CCR1;    __IO uint32_t OPTCR;    __IO uint32_t OPTSR_CUR;    __IO uint32_t OPTSR_PRG;    __IO uint32_t OPTCCR;    __IO uint32_t PRAR_CUR1;    __IO uint32_t PRAR_PRG1;    __IO uint32_t SCAR_CUR1;    __IO uint32_t SCAR_PRG1;    __IO uint32_t WPSN_CUR1;    __IO uint32_t WPSN_PRG1;    __IO uint32_t BOOT_CUR;    __IO uint32_t BOOT_PRG;    uint32_t      RESERVED0[2];    __IO uint32_t CRCCR1;    __IO uint32_t CRCSADD1;    __IO uint32_t CRCEADD1;    __IO uint32_t CRCDATA;    __IO uint32_t ECC_FA1;    uint32_t      RESERVED1[40];    __IO uint32_t KEYR2;    uint32_t      RESERVED2;    __IO uint32_t CR2;    __IO uint32_t SR2;    __IO uint32_t CCR2;    uint32_t      RESERVED3[4];    __IO uint32_t PRAR_CUR2;    __IO uint32_t PRAR_PRG2;    __IO uint32_t SCAR_CUR2;    __IO uint32_t SCAR_PRG2;    __IO uint32_t WPSN_CUR2;    __IO uint32_t WPSN_PRG2;    uint32_t      RESERVED4[4];    __IO uint32_t CRCCR2;    __IO uint32_t CRCSADD2;    __IO uint32_t CRCEADD2;    __IO uint32_t CRCDATA2;    __IO uint32_t ECC_FA2;  } FLASH_TypeDef;

這個結構體的成員名稱和排列次序和CPU的暫存器是一 一對應的。

__IO表示volatile, 這是標準C語言中的一個修飾字,表示這個變數是非易失性的,編譯器不要將其優化掉。core_m7.h 文件定義了這個宏:

#define     __O     volatile             /*!< Defines 'write only' permissions */  #define     __IO    volatile             /*!< Defines 'read / write' permissions */

下面我們看下Flash的定義,在stm32h743xx.h文件。

#define PERIPH_BASE              (0x40000000UL)  #define D1_AHB1PERIPH_BASE       (PERIPH_BASE + 0x12000000UL)  #define FLASH_R_BASE             (D1_AHB1PERIPH_BASE + 0x2000UL)  #define FLASH                    ((FLASH_TypeDef *) FLASH_R_BASE) <----- 展開這個宏,(FLASH_TypeDef *)0x52002000

我們訪問Flash的CR1暫存器可以採用這種形式:FLASH->CR1 = 0。

70.3.2 內部Flash擦除結構體FLASH_EraseInitTypeDef

下面是做內部Flash擦除的結構體,用到的地方比較多:

typedef struct  {    uint32_t TypeErase;    uint32_t Banks;    uint32_t Sector;    uint32_t NbSectors;    uint32_t VoltageRange;  } FLASH_EraseInitTypeDef;

下面將結構體成員逐一做個說明:

  •   TypeErase

用於選擇BANK擦除還是扇區擦除,H743有兩個BANK,每個BANK有個8個扇區,每個扇區128KB。具體支援的參數如下:

#define FLASH_TYPEERASE_SECTORS      0x00U  /* 扇區方式擦除 */  #define FLASH_TYPEERASE_MASSERASE    0x01U  /* BANK方式擦除 */
  •   Banks

用於選擇要擦除的BANK,或者兩個BANK都選擇:

#define FLASH_BANK_1             0x01U                         /* Bank 1 */  #define FLASH_BANK_2             0x02U                         /* Bank 2 */  #define FLASH_BANK_BOTH          (FLASH_BANK_1 | FLASH_BANK_2) /* Bank1 和 Bank2 */
  •   Sector

用於選擇要擦除的扇區:

#define FLASH_SECTOR_0             0U       /* Sector Number 0   */  #define FLASH_SECTOR_1             1U       /* Sector Number 1   */  #define FLASH_SECTOR_2             2U       /* Sector Number 2   */  #define FLASH_SECTOR_3             3U       /* Sector Number 3   */  #define FLASH_SECTOR_4             4U       /* Sector Number 4   */  #define FLASH_SECTOR_5             5U       /* Sector Number 5   */  #define FLASH_SECTOR_6             6U       /* Sector Number 6   */  #define FLASH_SECTOR_7             7U       /* Sector Number 7   */
  •   NbSectors

用於設置要擦除的扇區個數,對於STM32H743來說,範圍1到8。

  •   VoltageRange

用於設置編程的並行位數,電壓不同,位數不同:

#define FLASH_VOLTAGE_RANGE_1        0x00000000U       /* Flash program/erase by 8 bits  */  #define FLASH_VOLTAGE_RANGE_2        FLASH_CR_PSIZE_0  /* Flash program/erase by 16 bits */  #define FLASH_VOLTAGE_RANGE_3        FLASH_CR_PSIZE_1  /* Flash program/erase by 32 bits */  #define FLASH_VOLTAGE_RANGE_4        FLASH_CR_PSIZE    /* Flash program/erase by 64 bits */

70.3.3 內部Flash的操作總結

使用方法由HAL庫提供:

  •   Flash編程函數操作流程
    •   Flash解鎖函數HAL_FLASH_Unlock。
    •   Flash查詢方式編程HAL_FLASH_Program。
    •   Flash中斷方式編程HAL_FLASH_Program_IT。
    •   Flash上鎖函數HAL_FLASH_Lock。
  •   選項位元組編程流程
    •   選項位元組解鎖函數HAL_FLASH_OB_Unlock。
    •   選項位元組載入函數HAL_FLASH_OB_Launch。
    •   選項位元組編程函數HAL_FLASHEx_OBProgram。
    •   選項位元組加鎖函數HAL_FLASH_OB_Lock。
  •   Flash擦除流程
    •   Flash解鎖函數HAL_FLASH_Unlock。
    •   Flash查詢方式擦除HAL_FLASHEx_Erase。
    •   Flash中斷方式擦除HAL_FLASHEx_Erase_IT。
    •   Flash上鎖函數HAL_FLASH_Lock。

70.4 內部Flash源文件stm32h7xx_hal_flash.c

此文件涉及到的函數較多,這裡把幾個常用的函數做個說明:

  •   HAL_FLASH_Unlock
  •   HAL_FLASH_Lock
  •   HAL_FLASHEx_Erase
  •   HAL_FLASH_Program

70.4.1 函數HAL_FLASH_Lock

函數原型:

HAL_StatusTypeDef HAL_FLASH_Lock(void)  {  /* 設置FLASH Bank1控制暫存器Lock位,即禁止訪問 */    SET_BIT(FLASH->CR1, FLASH_CR_LOCK);    /* 驗證Flash Bank1是否已經被鎖住 */    if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) == 0U)    {      return HAL_ERROR;    }    /* 設置FLASH Bank2控制暫存器Lock位,即禁止訪問 */    SET_BIT(FLASH->CR2, FLASH_CR_LOCK);     /* 驗證Flash Bank2是否已經被鎖住 */    if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) == 0U)    {      return HAL_ERROR;    }      return HAL_OK;  }

函數描述:

用於Flash加鎖,加鎖後將不能對Flash進行編程和擦除。

70.4.2 函數HAL_FLASH_Unlock

函數原型:

HAL_StatusTypeDef HAL_FLASH_Unlock(void)  {    if(READ_BIT(FLASH->CR1, FLASH_CR_LOCK) != 0U)    {      /* 允許訪問Flash Bank1 */      WRITE_REG(FLASH->KEYR1, FLASH_KEY1);      WRITE_REG(FLASH->KEYR1, FLASH_KEY2);        /* 驗證是否已經解鎖 */      if (READ_BIT(FLASH->CR1, FLASH_CR_LOCK) != 0U)      {        return HAL_ERROR;      }    }      if(READ_BIT(FLASH->CR2, FLASH_CR_LOCK) != 0U)    {      /* 允許訪問Flash Bank2 */      WRITE_REG(FLASH->KEYR2, FLASH_KEY1);      WRITE_REG(FLASH->KEYR2, FLASH_KEY2);        /* 驗證是否已經解鎖 */      if (READ_BIT(FLASH->CR2, FLASH_CR_LOCK) != 0U)      {        return HAL_ERROR;      }    }      return HAL_OK;  }

函數描述:

此函數用於Flash解鎖,解鎖後可以對Flash進行擦除和編程。

70.4.3 函數HAL_FLASH_Program

函數原型:

HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t FlashAddress, uint32_t DataAddress)  {    HAL_StatusTypeDef status;    __IO uint32_t *dest_addr = (__IO uint32_t *)FlashAddress;    __IO uint32_t *src_addr = (__IO uint32_t*)DataAddress;    uint32_t bank;    uint8_t row_index = FLASH_NB_32BITWORD_IN_FLASHWORD;      /* 檢測參數 */    assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));    assert_param(IS_FLASH_PROGRAM_ADDRESS(FlashAddress));      /* 上鎖 */    __HAL_LOCK(&pFlash);    #if defined (FLASH_OPTCR_PG_OTP)    if((IS_FLASH_PROGRAM_ADDRESS_BANK1(FlashAddress)) || (IS_FLASH_PROGRAM_ADDRESS_OTP(FlashAddress)))  #else    if(IS_FLASH_PROGRAM_ADDRESS_BANK1(FlashAddress))  #endif    {      bank = FLASH_BANK_1;    }    else    {      bank = FLASH_BANK_2;    }      /* 錯誤標識,無錯誤 */    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;      /* 等待操作完成 */    status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, bank);      if(status == HAL_OK)    {      if(bank == FLASH_BANK_1)      {  #if defined (FLASH_OPTCR_PG_OTP)        if (TypeProgram == FLASH_TYPEPROGRAM_OTPWORD)        {          /* 設置OTP暫存器的PG位,使能可以編程 */          SET_BIT(FLASH->OPTCR, FLASH_OPTCR_PG_OTP);        }        else  #endif        {          /* 設置PG位,使能可編程 */          SET_BIT(FLASH->CR1, FLASH_CR_PG);        }      }      else      {        /* 設置PG位 */        SET_BIT(FLASH->CR2, FLASH_CR_PG);      }        __ISB();      __DSB();    #if defined (FLASH_OPTCR_PG_OTP)      if (TypeProgram == FLASH_TYPEPROGRAM_OTPWORD)      {        /* 編程OTP(16 bits) */        *(__IO uint16_t *)FlashAddress = *(__IO uint16_t*)DataAddress;      }      else  #endif      {        /* 對Flash進行編程 */        do        {          *dest_addr = *src_addr;          dest_addr++;          src_addr++;          row_index--;       } while (row_index != 0U);      }        __ISB();      __DSB();        /* 等待最後一次操作完成 */      status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, bank);    #if defined (FLASH_OPTCR_PG_OTP)      if (TypeProgram == FLASH_TYPEPROGRAM_OTPWORD)      {        /* 如果編程操作完成,關閉OTP PG位 */        CLEAR_BIT(FLASH->OPTCR, FLASH_OPTCR_PG_OTP);      }      else  #endif      {        if(bank == FLASH_BANK_1)        {          /* 如果操作完成,關閉PG位 */          CLEAR_BIT(FLASH->CR1, FLASH_CR_PG);        }        else        {          /* 如果操作完成,關閉PG位 */          CLEAR_BIT(FLASH->CR2, FLASH_CR_PG);        }      }    }      /* 解鎖 */    __HAL_UNLOCK(&pFlash);      return status;  }

函數描述:

此函數主要用於Flash編程,固定編程32個位元組數據。

函數參數:

  •   第1個參數是要編程的Flash類型,支援兩種參數:
    •   FLASH_TYPEPROGRAM_FLASHWORD,用於晶片內部Flash編程。
    •   FLASH_TYPEPROGRAM_OTPWORD,用於晶片內部OTP存儲區編程,當前的H743並沒有這個區域,所以可以忽略。
  •   第2個參數是要編程的Flash地址。
  •   第3個參數是要編程到Flash的數據地址。
  •   返回值,返回HAL_TIMEOUT表示超時,HAL_ERROR表示參數錯誤,HAL_OK表示發送成功,HAL_BUSY表示串口忙,正在使用中。

注意事項:

  •   第2個參數的Flash地址要是32位元組對齊的,即此地址對32求余等於0。
  •   第3個參數務必要是32位元組的整數倍。

70.4.4 函數HAL_FLASHEx_Erase

函數原型:

HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError)  {    HAL_StatusTypeDef status = HAL_OK;    uint32_t sector_index;    /* 檢查參數 */    assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));    assert_param(IS_FLASH_BANK(pEraseInit->Banks));    /* 上鎖 */    __HAL_LOCK(&pFlash);    /* 無錯誤 */    pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;    /* 等待BANK1的操作完成 */    if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)    {      if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_1) != HAL_OK)      {        status = HAL_ERROR;      }    }    /* 等待BANK2的操作完成 */    if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)    {      if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_2) != HAL_OK)      {        status = HAL_ERROR;      }    }      if(status == HAL_OK)    {      if(pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)      {    /* 整個BANK1或者BANK2擦除 */        FLASH_MassErase(pEraseInit->VoltageRange, pEraseInit->Banks);      /* 等待操作完成 */        if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)        {          if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_1) != HAL_OK)          {            status = HAL_ERROR;          }      /* 如果擦除完成,關閉BANK1的BER位 */          FLASH->CR1 &= (~FLASH_CR_BER);        }        if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)        {          if(FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_2) != HAL_OK)          {            status = HAL_ERROR;          }      /* 如果擦除操作完成,關閉BANK2的BER位 */          FLASH->CR2 &= (~FLASH_CR_BER);        }      }      else      {    /* 初始化扇區錯誤碼 */        *SectorError = 0xFFFFFFFFU;      /* 扇區方式擦除 */        for(sector_index = pEraseInit->Sector; sector_index < (pEraseInit->NbSectors + pEraseInit->Sector);   sector_index++)        {          FLASH_Erase_Sector(sector_index, pEraseInit->Banks, pEraseInit->VoltageRange);            if((pEraseInit->Banks & FLASH_BANK_1) == FLASH_BANK_1)          {        /* 等待BANK1操作完成 */            status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_1);          /* 如果擦除操作完成,禁止SER位 */            FLASH->CR1 &= (~(FLASH_CR_SER | FLASH_CR_SNB));          }          if((pEraseInit->Banks & FLASH_BANK_2) == FLASH_BANK_2)          {        /* 等待BANK2操作完成 */            status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE, FLASH_BANK_2);          /* 如果擦除操作完成,禁止SER位 */            FLASH->CR2 &= (~(FLASH_CR_SER | FLASH_CR_SNB));          }            if(status != HAL_OK)          {        /* 如果擦除出錯,停止後續擦除,返回擦除異常的扇區號 */            *SectorError = sector_index;            break;          }        }      }    }    /* 解鎖 */    __HAL_UNLOCK(&pFlash);      return status;  }

函數描述:

用於內部Flash的批量擦除(BANK擦除)和扇區方式擦除。

函數參數:

  •   第1個參數是FLASH_EraseInitTypeDef類型結構體指針變數。
  •   第2個參數是錯誤碼返回,返回0xFFFFFFFF表示全部正確,返回其它值是擦除過程中的錯誤扇區。
  •   返回值,返回HAL_ERROR表示參數錯誤,HAL_OK表示發送成功,HAL_BUSY表示忙,正在使用中。

70.5 總結

本章節就為大家講解這麼多,對於內部Flash編程來說,掌握本章節的這些知識點就夠用了,更多的知識點可以看STM32H7的參考手冊學習。