STM32F767 Flash的基本用法

 本文介绍STM32F767 Flash的基本用法。

开发环境

硬件环境

  • 电脑:Windows 10 Home x64
  • Apollo STM32F767开发板(ST-LINK V2仿真器)

软件环境

  • Keil Version 5.24.1 (Pack Installer:Keil.STM32F7xx_DFP.2.9.0.pack)
  • STM32CubeMX Version 4.25.0(Packages Manager:STM32CubeF7)

Flash功能介绍

Flash构成

 Flash 具有以下主要特性:

  • 容量高达 1 MB;
  • 256 位宽数据读取;
  • 字节、半字、字和双字数据写入;
  • 扇区擦除与全部擦除;

 Flash结构如下:

  • 主存储器块:分为 4 个 32 KB 扇区、1 个 128 KB 扇区和 3 个 256 KB 扇区;
  • 信息块:系统存储器、OTP和选项字节

读访问延时

 该部分内容在系统内核与时钟设置文中已经讲解,这里不再介绍。

Flash控制器解锁

 复位后,Flash 控制寄存器 (FLASH_CR) 不允许执行写操作,以防因电气干扰等原因出现对Flash 的意外操作。此寄存器的解锁顺序如下:

  1. 在 Flash 密钥寄存器 (FLASH_KEYR) 中写入 KEY1 = 0x45670123
  2. 在 Flash 密钥寄存器 (FLASH_KEYR) 中写入 KEY2 = 0xCDEF89AB

编程/擦除并行位数

 通过 FLASH_CR 寄存器中的 PSIZE 域配置并行位数。并行位数表示每次对 Flash 进行写操作时将编程的字节数。PSIZE 受限于电源电压以及是否使用外部 VPP 电源。因此,在进行任何编程/擦除操作前,必须在 FLASH_CR 寄存器中对其进行正确配置。

Flash擦除顺序

 Flash 擦除操作可针对扇区或整个 Flash(批量擦除)执行。执行批量擦除时,不会影响 OTP扇区或配置扇区。

扇区擦除

 扇区擦除的具体步骤如下:

  1. 检查 FLASH_SR 寄存器中的 BSY 位,以确认当前未执行任何 Flash 操作;
  2. FLASH_CR 寄存器中将 SER 位置 1 并选择需要擦除的扇区 (SNB),所选扇区应为主存储器块中的 8 个扇区之一;
  3. FLASH_CR 寄存器中的 STRT 位置 1;
  4. 等待 BSY 位清零。

批量擦除

 要执行批量擦除,建议采用以下步骤:

  1. 检查 FLASH_SR 寄存器中的 BSY 位,以确认当前未执行任何 Flash 操作;
  2. FLASH_CR 寄存器中的 MER 位置 1;
  3. FLASH_CR 寄存器中的 STRT 位置 1;
  4. 等待 BSY 位清零。

Flash编程顺序

标准编程

 Flash 编程顺序如下:

  1. 检查 FLASH_SR 中的 BSY 位,以确认当前未执行任何主要 Flash 操作;
  2. FLASH_CR 寄存器中的 PG 位置 1;
  3. 针对所需存储器地址(主存储器块或 OTP 区域内)执行数据写入操作:
    – 并行位数为 x8 时按字节写入
    – 并行位数为 x16 时按半字写入
    – 并行位数为 x32 时按字写入
    – 并行位数为 x64 时按双字写入
  4. 等待 BSY 位清零。

HAL库Flash读写操作实现

 Flash擦除的最小单位为扇区,同时写入数据前必须对扇区进行擦除。因此一次Flash的写入过程为:确认写入的扇区、擦除对应扇区、写入数据。

 Flash的读取相对简单,直接利用指针读取即可。

获取内存地址对应扇区

 STM32F76x最大支持2MB Flash,本例中所用的STM32767 Flash为1MB,即DUAL_BANK未被定义。

Flash扇区的地址对应关系如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/* Base address of the Flash sectors */
#if defined(DUAL_BANK)
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base address of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base address of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base address of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base address of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base address of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base address of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base address of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base address of Sector 7, 128 Kbytes */
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) /* Base address of Sector 8, 128 Kbytes */
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) /* Base address of Sector 9, 128 Kbytes */
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base address of Sector 10, 128 Kbytes */
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base address of Sector 11, 128 Kbytes */
#define ADDR_FLASH_SECTOR_12 ((uint32_t)0x08100000) /* Base address of Sector 12, 16 Kbytes */
#define ADDR_FLASH_SECTOR_13 ((uint32_t)0x08104000) /* Base address of Sector 13, 16 Kbytes */
#define ADDR_FLASH_SECTOR_14 ((uint32_t)0x08108000) /* Base address of Sector 14, 16 Kbytes */
#define ADDR_FLASH_SECTOR_15 ((uint32_t)0x0810C000) /* Base address of Sector 15, 16 Kbytes */
#define ADDR_FLASH_SECTOR_16 ((uint32_t)0x08110000) /* Base address of Sector 16, 64 Kbytes */
#define ADDR_FLASH_SECTOR_17 ((uint32_t)0x08120000) /* Base address of Sector 17, 128 Kbytes */
#define ADDR_FLASH_SECTOR_18 ((uint32_t)0x08140000) /* Base address of Sector 18, 128 Kbytes */
#define ADDR_FLASH_SECTOR_19 ((uint32_t)0x08160000) /* Base address of Sector 19, 128 Kbytes */
#define ADDR_FLASH_SECTOR_20 ((uint32_t)0x08180000) /* Base address of Sector 20, 128 Kbytes */
#define ADDR_FLASH_SECTOR_21 ((uint32_t)0x081A0000) /* Base address of Sector 21, 128 Kbytes */
#define ADDR_FLASH_SECTOR_22 ((uint32_t)0x081C0000) /* Base address of Sector 22, 128 Kbytes */
#define ADDR_FLASH_SECTOR_23 ((uint32_t)0x081E0000) /* Base address of Sector 23, 128 Kbytes */
#else
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base address of Sector 0, 32 Kbytes */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08008000) /* Base address of Sector 1, 32 Kbytes */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08010000) /* Base address of Sector 2, 32 Kbytes */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x08018000) /* Base address of Sector 3, 32 Kbytes */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08020000) /* Base address of Sector 4, 128 Kbytes */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08040000) /* Base address of Sector 5, 256 Kbytes */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08080000) /* Base address of Sector 6, 256 Kbytes */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x080C0000) /* Base address of Sector 7, 256 Kbytes */
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08100000) /* Base address of Sector 8, 256 Kbytes */
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x08140000) /* Base address of Sector 9, 256 Kbytes */
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x08180000) /* Base address of Sector 10, 256 Kbytes */
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x081C0000) /* Base address of Sector 11, 256 Kbytes */
#endif /* DUAL_BANK */

 Flash扇区号的定义如下所示。对于STM32F767,其Flash只有7个扇区。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/** @defgroup FLASH_Sectors FLASH Sectors
* @{
*/
#define FLASH_SECTOR_0 ((uint32_t)0U) /*!< Sector Number 0 */
#define FLASH_SECTOR_1 ((uint32_t)1U) /*!< Sector Number 1 */
#define FLASH_SECTOR_2 ((uint32_t)2U) /*!< Sector Number 2 */
#define FLASH_SECTOR_3 ((uint32_t)3U) /*!< Sector Number 3 */
#define FLASH_SECTOR_4 ((uint32_t)4U) /*!< Sector Number 4 */
#define FLASH_SECTOR_5 ((uint32_t)5U) /*!< Sector Number 5 */
#define FLASH_SECTOR_6 ((uint32_t)6U) /*!< Sector Number 6 */
#define FLASH_SECTOR_7 ((uint32_t)7U) /*!< Sector Number 7 */
/** @defgroup FLASHEx_Sectors FLASH Sectors
* @{
*/
#if (FLASH_SECTOR_TOTAL == 24)
#define FLASH_SECTOR_8 ((uint32_t)8U) /*!< Sector Number 8 */
#define FLASH_SECTOR_9 ((uint32_t)9U) /*!< Sector Number 9 */
#define FLASH_SECTOR_10 ((uint32_t)10U) /*!< Sector Number 10 */
#define FLASH_SECTOR_11 ((uint32_t)11U) /*!< Sector Number 11 */
#define FLASH_SECTOR_12 ((uint32_t)12U) /*!< Sector Number 12 */
#define FLASH_SECTOR_13 ((uint32_t)13U) /*!< Sector Number 13 */
#define FLASH_SECTOR_14 ((uint32_t)14U) /*!< Sector Number 14 */
#define FLASH_SECTOR_15 ((uint32_t)15U) /*!< Sector Number 15 */
#define FLASH_SECTOR_16 ((uint32_t)16U) /*!< Sector Number 16 */
#define FLASH_SECTOR_17 ((uint32_t)17U) /*!< Sector Number 17 */
#define FLASH_SECTOR_18 ((uint32_t)18U) /*!< Sector Number 18 */
#define FLASH_SECTOR_19 ((uint32_t)19U) /*!< Sector Number 19 */
#define FLASH_SECTOR_20 ((uint32_t)20U) /*!< Sector Number 20 */
#define FLASH_SECTOR_21 ((uint32_t)21U) /*!< Sector Number 21 */
#define FLASH_SECTOR_22 ((uint32_t)22U) /*!< Sector Number 22 */
#define FLASH_SECTOR_23 ((uint32_t)23U) /*!< Sector Number 23 */
#endif /* FLASH_SECTOR_TOTAL == 24 */

 获取Flash对应扇区的函数如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* 说明: 获取指定flash地址所在的扇区
* 参数: Address:指定flash地址
*
* @返回值 对应flash地址的扇区
*/
uint32_t STMFLASH_GetFlashSector(uint32 Address)
{
uint32_t sector = 0;
if((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0))
{
sector = FLASH_SECTOR_0;
}
else if((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1))
{
sector = FLASH_SECTOR_1;
}
else if((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2))
{
sector = FLASH_SECTOR_2;
}
else if((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3))
{
sector = FLASH_SECTOR_3;
}
else if((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4))
{
sector = FLASH_SECTOR_4;
}
else if((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5))
{
sector = FLASH_SECTOR_5;
}
else if((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6))
{
sector = FLASH_SECTOR_6;
}
else if((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7))
{
sector = FLASH_SECTOR_7;
}
else if((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8))
{
sector = FLASH_SECTOR_8;
}
else if((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9))
{
sector = FLASH_SECTOR_9;
}
else if((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10))
{
sector = FLASH_SECTOR_10;
}
#if defined(DUAL_BANK)
else if((Address < ADDR_FLASH_SECTOR_12) && (Address >= ADDR_FLASH_SECTOR_11))
{
sector = FLASH_SECTOR_11;
}
else if((Address < ADDR_FLASH_SECTOR_13) && (Address >= ADDR_FLASH_SECTOR_12))
{
sector = FLASH_SECTOR_12;
}
else if((Address < ADDR_FLASH_SECTOR_14) && (Address >= ADDR_FLASH_SECTOR_13))
{
sector = FLASH_SECTOR_13;
}
else if((Address < ADDR_FLASH_SECTOR_15) && (Address >= ADDR_FLASH_SECTOR_14))
{
sector = FLASH_SECTOR_14;
}
else if((Address < ADDR_FLASH_SECTOR_16) && (Address >= ADDR_FLASH_SECTOR_15))
{
sector = FLASH_SECTOR_15;
}
else if((Address < ADDR_FLASH_SECTOR_17) && (Address >= ADDR_FLASH_SECTOR_16))
{
sector = FLASH_SECTOR_16;
}
else if((Address < ADDR_FLASH_SECTOR_18) && (Address >= ADDR_FLASH_SECTOR_17))
{
sector = FLASH_SECTOR_17;
}
else if((Address < ADDR_FLASH_SECTOR_19) && (Address >= ADDR_FLASH_SECTOR_18))
{
sector = FLASH_SECTOR_18;
}
else if((Address < ADDR_FLASH_SECTOR_20) && (Address >= ADDR_FLASH_SECTOR_19))
{
sector = FLASH_SECTOR_19;
}
else if((Address < ADDR_FLASH_SECTOR_21) && (Address >= ADDR_FLASH_SECTOR_20))
{
sector = FLASH_SECTOR_20;
}
else if((Address < ADDR_FLASH_SECTOR_22) && (Address >= ADDR_FLASH_SECTOR_21))
{
sector = FLASH_SECTOR_21;
}
else if((Address < ADDR_FLASH_SECTOR_23) && (Address >= ADDR_FLASH_SECTOR_22))
{
sector = FLASH_SECTOR_22;
}
else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_23) */
{
sector = FLASH_SECTOR_23;
}
#else
else /* (Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11) */
{
sector = FLASH_SECTOR_11;
}
#endif /* DUAL_BANK */
return sector;
}

Flash擦除函数

 Flash擦除函数的实现如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* @brief Perform a mass erase or erase the specified FLASH memory sectors
* @param[in] pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
* contains the configuration information for the erasing.
*
* @param[out] SectorError pointer to variable that
* contains the configuration information on faulty sector in case of error
* (0xFFFFFFFF means that all the sectors have been correctly erased)
*
* @retval HAL Status
*/
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError)
{
HAL_StatusTypeDef status = HAL_ERROR;
uint32_t index = 0;
/* Process Locked */
__HAL_LOCK(&pFlash);
/* Check the parameters */
assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
if(status == HAL_OK)
{
/*Initialization of SectorError variable*/
*SectorError = 0xFFFFFFFFU;
if(pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)
{
/*Mass erase to be done*/
#if defined (FLASH_OPTCR_nDBANK)
FLASH_MassErase((uint8_t) pEraseInit->VoltageRange, pEraseInit->Banks);
#else
FLASH_MassErase((uint8_t) pEraseInit->VoltageRange);
#endif /* FLASH_OPTCR_nDBANK */
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* if the erase operation is completed, disable the MER Bit */
FLASH->CR &= (~FLASH_MER_BIT);
}
else
{
/* Check the parameters */
assert_param(IS_FLASH_NBSECTORS(pEraseInit->NbSectors + pEraseInit->Sector));
/* Erase by sector by sector to be done*/
for(index = pEraseInit->Sector; index < (pEraseInit->NbSectors + pEraseInit->Sector); index++)
{
FLASH_Erase_Sector(index, (uint8_t) pEraseInit->VoltageRange);
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* If the erase operation is completed, disable the SER Bit and SNB Bits */
CLEAR_BIT(FLASH->CR, (FLASH_CR_SER | FLASH_CR_SNB));
if(status != HAL_OK)
{
/* In case of error, stop erase procedure and return the faulty sector*/
*SectorError = index;
break;
}
}
}
}
/* Process Unlocked */
__HAL_UNLOCK(&pFlash);
return status;
}

FLASH_WaitForLastOperation()函数

FLASH_WaitForLastOperation()函数用于等待Flash操作完成。

 该函数首先对Flash状态进行读取,等待Flash操作完成。读取寄存器FLASH_SRBSY位,直到确认当前未执行任何Flash操作;读取寄存器FLASH_SR所有错误位,判断有无错误;读取寄存器FLASH_SREOP位,确认完成当前Flash操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/**
* @brief Wait for a FLASH operation to complete.
* @param Timeout maximum flash operationtimeout
* @retval HAL Status
*/
HAL_StatusTypeDef FLASH_WaitForLastOperation(uint32_t Timeout)
{
uint32_t tickstart = 0;
/* Clear Error Code */
pFlash.ErrorCode = HAL_FLASH_ERROR_NONE;
/* Wait for the FLASH operation to complete by polling on BUSY flag to be reset.
Even if the FLASH operation fails, the BUSY flag will be reset and an error
flag will be set */
/* Get tick */
tickstart = HAL_GetTick();
while(__HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY) != RESET)
{
if(Timeout != HAL_MAX_DELAY)
{
if((Timeout == 0)||((HAL_GetTick() - tickstart ) > Timeout))
{
return HAL_TIMEOUT;
}
}
}
if(__HAL_FLASH_GET_FLAG(FLASH_FLAG_ALL_ERRORS) != RESET)
{
/*Save the error code*/
FLASH_SetErrorCode();
return HAL_ERROR;
}
/* Check FLASH End of Operation flag */
if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_EOP) != RESET)
{
/* Clear FLASH End of Operation pending bit */
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP);
}
/* If there is an error flag set */
return HAL_OK;
}
#define __HAL_FLASH_GET_FLAG(__FLAG__) ((FLASH->SR & (__FLAG__)))

FLASH_MassErase()函数

FLASH_MassErase()函数用于擦除Flash全部扇区,即按照bank进行擦除。首先通过寄存器FLASH_CRMER位选择需要批量擦除的bank。STM32F767仅有一个bank,故此处只能选择bank1。之后向FLASH_CR寄存器PSIZE位写入编程大小值,向STRT位写入1启动擦除操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#if defined (FLASH_OPTCR_nDBANK)
/**
* @brief Full erase of FLASH memory sectors
* @param VoltageRange The device voltage range which defines the erase parallelism.
* This parameter can be one of the following values:
* @arg VOLTAGE_RANGE_1: when the device voltage range is 1.8V to 2.1V,
* the operation will be done by byte (8-bit)
* @arg VOLTAGE_RANGE_2: when the device voltage range is 2.1V to 2.7V,
* the operation will be done by half word (16-bit)
* @arg VOLTAGE_RANGE_3: when the device voltage range is 2.7V to 3.6V,
* the operation will be done by word (32-bit)
* @arg VOLTAGE_RANGE_4: when the device voltage range is 2.7V to 3.6V + External Vpp,
* the operation will be done by double word (64-bit)
* @param Banks Banks to be erased
* This parameter can be one of the following values:
* @arg FLASH_BANK_1: Bank1 to be erased
* @arg FLASH_BANK_2: Bank2 to be erased
* @arg FLASH_BANK_BOTH: Bank1 and Bank2 to be erased
*
* @retval HAL Status
*/
static void FLASH_MassErase(uint8_t VoltageRange, uint32_t Banks)
{
/* Check the parameters */
assert_param(IS_VOLTAGERANGE(VoltageRange));
assert_param(IS_FLASH_BANK(Banks));
/* if the previous operation is completed, proceed to erase all sectors */
FLASH->CR &= CR_PSIZE_MASK;
if(Banks == FLASH_BANK_BOTH)
{
/* bank1 & bank2 will be erased*/
FLASH->CR |= FLASH_MER_BIT;
}
else if(Banks == FLASH_BANK_2)
{
/*Only bank2 will be erased*/
FLASH->CR |= FLASH_CR_MER2;
}
else
{
/*Only bank1 will be erased*/
FLASH->CR |= FLASH_CR_MER1;
}
FLASH->CR |= FLASH_CR_STRT | ((uint32_t)VoltageRange <<8);
/* Data synchronous Barrier (DSB) Just after the write operation
This will force the CPU to respect the sequence of instruction (no optimization).*/
__DSB();
}

FLASH_Erase_Sector()函数

FLASH_Erase_Sector()函数用于擦除特定的Flash扇区。

  • FLASH_CR寄存器PSIZE位写入编程大小值;
  • FLASH_CR寄存器SNB位写入待擦除的扇区编号;向SER位写入1使能扇区擦除模式;
  • FLASH_CR寄存器STRT位写入1启动擦除操作;
  • 执行__DSB()指令:Data synchronous Barrier,即数据同步屏障。只有当此指令执行完毕后,才会执行程序中位于此指令后的指令。
  • A Data Synchronization Barrier (DSB) completes when all instructions before this instruction complete.
  • A Data Memory Barrier (DMB) ensures that all explicit memory accesses before the DMB instruction complete before any explicit memory accesses after the DMB instruction start.
  • An Instruction Synchronization Barrier (ISB) flushes the pipeline in the processor, so that all instructions following the ISB are fetched from cache or memory, after the ISB has been completed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* @brief Erase the specified FLASH memory sector
* @param Sector FLASH sector to erase
* The value of this parameter depend on device used within the same series
* @param VoltageRange The device voltage range which defines the erase parallelism.
* This parameter can be one of the following values:
* @arg FLASH_VOLTAGE_RANGE_1: when the device voltage range is 1.8V to 2.1V,
* the operation will be done by byte (8-bit)
* @arg FLASH_VOLTAGE_RANGE_2: when the device voltage range is 2.1V to 2.7V,
* the operation will be done by half word (16-bit)
* @arg FLASH_VOLTAGE_RANGE_3: when the device voltage range is 2.7V to 3.6V,
* the operation will be done by word (32-bit)
* @arg FLASH_VOLTAGE_RANGE_4: when the device voltage range is 2.7V to 3.6V + External Vpp,
* the operation will be done by double word (64-bit)
*
* @retval None
*/
void FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)
{
uint32_t tmp_psize = 0;
/* Check the parameters */
assert_param(IS_FLASH_SECTOR(Sector));
assert_param(IS_VOLTAGERANGE(VoltageRange));
if(VoltageRange == FLASH_VOLTAGE_RANGE_1)
{
tmp_psize = FLASH_PSIZE_BYTE;
}
else if(VoltageRange == FLASH_VOLTAGE_RANGE_2)
{
tmp_psize = FLASH_PSIZE_HALF_WORD;
}
else if(VoltageRange == FLASH_VOLTAGE_RANGE_3)
{
tmp_psize = FLASH_PSIZE_WORD;
}
else
{
tmp_psize = FLASH_PSIZE_DOUBLE_WORD;
}
/* Need to add offset of 4 when sector higher than FLASH_SECTOR_11 */
if(Sector > FLASH_SECTOR_11)
{
Sector += 4;
}
/* If the previous operation is completed, proceed to erase the sector */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= tmp_psize;
CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);
FLASH->CR |= FLASH_CR_SER | (Sector << FLASH_CR_SNB_Pos);
FLASH->CR |= FLASH_CR_STRT;
/* Data synchronous Barrier (DSB) Just after the write operation
This will force the CPU to respect the sequence of instruction (no optimization).*/
__DSB();
}

HAL_FLASHEx_Erase()函数

HAL_FLASHEx_Erase()函数为对Flash进行bank擦除或者多个区块擦除的函数,其实现利用了FLASH_MassErase()函数与FLASH_Erase_Sector()函数。其实现非常简单,这里不再说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* @brief Perform a mass erase or erase the specified FLASH memory sectors
* @param[in] pEraseInit pointer to an FLASH_EraseInitTypeDef structure that
* contains the configuration information for the erasing.
*
* @param[out] SectorError pointer to variable that
* contains the configuration information on faulty sector in case of error
* (0xFFFFFFFF means that all the sectors have been correctly erased)
*
* @retval HAL Status
*/
HAL_StatusTypeDef HAL_FLASHEx_Erase(FLASH_EraseInitTypeDef *pEraseInit, uint32_t *SectorError)
{
HAL_StatusTypeDef status = HAL_ERROR;
uint32_t index = 0;
/* Process Locked */
__HAL_LOCK(&pFlash);
/* Check the parameters */
assert_param(IS_FLASH_TYPEERASE(pEraseInit->TypeErase));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
if(status == HAL_OK)
{
/*Initialization of SectorError variable*/
*SectorError = 0xFFFFFFFFU;
if(pEraseInit->TypeErase == FLASH_TYPEERASE_MASSERASE)
{
/*Mass erase to be done*/
#if defined (FLASH_OPTCR_nDBANK)
FLASH_MassErase((uint8_t) pEraseInit->VoltageRange, pEraseInit->Banks);
#else
FLASH_MassErase((uint8_t) pEraseInit->VoltageRange);
#endif /* FLASH_OPTCR_nDBANK */
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* if the erase operation is completed, disable the MER Bit */
FLASH->CR &= (~FLASH_MER_BIT);
}
else
{
/* Check the parameters */
assert_param(IS_FLASH_NBSECTORS(pEraseInit->NbSectors + pEraseInit->Sector));
/* Erase by sector by sector to be done*/
for(index = pEraseInit->Sector; index < (pEraseInit->NbSectors + pEraseInit->Sector); index++)
{
FLASH_Erase_Sector(index, (uint8_t) pEraseInit->VoltageRange);
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* If the erase operation is completed, disable the SER Bit and SNB Bits */
CLEAR_BIT(FLASH->CR, (FLASH_CR_SER | FLASH_CR_SNB));
if(status != HAL_OK)
{
/* In case of error, stop erase procedure and return the faulty sector*/
*SectorError = index;
break;
}
}
}
}
/* Process Unlocked */
__HAL_UNLOCK(&pFlash);
return status;
}

Flash编程函数

 Flash编程函数的实现如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* @brief Program byte, halfword, word or double word at a specified address
* @param TypeProgram Indicate the way to program at a specified address.
* This parameter can be a value of @ref FLASH_Type_Program
* @param Address specifies the address to be programmed.
* @param Data specifies the data to be programmed
*
* @retval HAL_StatusTypeDef HAL Status
*/
HAL_StatusTypeDef HAL_FLASH_Program(uint32_t TypeProgram, uint32_t Address, uint64_t Data)
{
HAL_StatusTypeDef status = HAL_ERROR;
/* Process Locked */
__HAL_LOCK(&pFlash);
/* Check the parameters */
assert_param(IS_FLASH_TYPEPROGRAM(TypeProgram));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
if(status == HAL_OK)
{
switch(TypeProgram)
{
case FLASH_TYPEPROGRAM_BYTE :
{
/*Program byte (8-bit) at a specified address.*/
FLASH_Program_Byte(Address, (uint8_t) Data);
break;
}
case FLASH_TYPEPROGRAM_HALFWORD :
{
/*Program halfword (16-bit) at a specified address.*/
FLASH_Program_HalfWord(Address, (uint16_t) Data);
break;
}
case FLASH_TYPEPROGRAM_WORD :
{
/*Program word (32-bit) at a specified address.*/
FLASH_Program_Word(Address, (uint32_t) Data);
break;
}
case FLASH_TYPEPROGRAM_DOUBLEWORD :
{
/*Program double word (64-bit) at a specified address.*/
FLASH_Program_DoubleWord(Address, Data);
break;
}
default :
break;
}
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation((uint32_t)FLASH_TIMEOUT_VALUE);
/* If the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
/* Process Unlocked */
__HAL_UNLOCK(&pFlash);
return status;
}

FLASH_Program_Byte()函数

FLASH_Program_Byte()函数用于向特定地址写入byte数据。具体功能如下:

  • FLASH_CR寄存器PSIZE位写入编程大小值:byte;
  • FLASH_CR寄存器PG位写入1,激活Flash编程;
  • 向Flash对应地址写入数值,注意指针类型的变换;
  • 通过DSB保障数据同步。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* @brief Program byte (8-bit) at a specified address.
* @note This function must be used when the device voltage range is from
* 2.7V to 3.6V.
*
* @note If an erase and a program operations are requested simultaneously,
* the erase operation is performed before the program one.
*
* @param Address specifies the address to be programmed.
* @param Data specifies the data to be programmed.
* @retval None
*/
static void FLASH_Program_Byte(uint32_t Address, uint8_t Data)
{
/* Check the parameters */
assert_param(IS_FLASH_ADDRESS(Address));
/* If the previous operation is completed, proceed to program the new data */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= FLASH_PSIZE_BYTE;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint8_t*)Address = Data;
/* Data synchronous Barrier (DSB) Just after the write operation
This will force the CPU to respect the sequence of instruction (no optimization).*/
__DSB();
}

FLASH_Program_HalfWord()FLASH_Program_Word()FLASH_Program_DoubleWord()函数实现与此类似,不再进行介绍。

当该函数执行完毕后,需要将FLASH_CR寄存器PG位复位,关闭Flash编程。

基于HAL库的Flash读写函数实现

Flash写入函数

 Flash写入函数的实现如下所示。该函数逻辑简明,这里不再说明。使用时请务必注意待写入flash的地址,本例中采用字写入方式,故写入地址必须为4的倍数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
/**
* 说明: 向指定flash区域写入数据
* @参数 WriteAddr:待写入flash的地址,本例中采用字写入方式,故写入地址必须为4的倍数;
* @参数 pBuffer:指向待写入数据的指针,按照字写入方式,所以类型为uint32;
* @参数 NumToWrite:待写入数据个数。
*
* @返回值 HAL Status
*/
HAL_StatusTypeDef STMFLASH_Write(uint32 WriteAddr,uint32 *pBuffer,uint32 NumToWrite)
{
FLASH_EraseInitTypeDef EraseInitStruct;
HAL_StatusTypeDef status = HAL_ERROR;
uint32 SECTORError = 0;
uint32_t FirstSector = 0, NbOfSectors = 0;
uint32 addrStart = WriteAddr; /* flash写入的起始地址 */
uint32 addrEnd = WriteAddr + NumToWrite * 4 - 1; /* flash写入的结束地址 */
/* 检测flash地址是否合法 */
assert_param(IS_FLASH_ADDRESS(addrStart));
assert_param(IS_FLASH_ADDRESS(addrEnd));
/* 如果写入数据量为0,则返回错误 */
if (NumToWrite == 0) return status;
/* 解锁flash */
HAL_FLASH_Unlock();
/* 获取要擦除的首个扇区 */
FirstSector = STMFLASH_GetFlashSector(addrStart);
/* 获取要擦除的扇区个数 */
NbOfSectors = STMFLASH_GetFlashSector(addrEnd) - FirstSector + 1;
/* 设置EraseInitStruct结构体信息 */
EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS; /* 扇区擦除 */
EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3; /* 电压在2.7-3.6V之间,实际为3.3V */
EraseInitStruct.Sector = FirstSector;
EraseInitStruct.NbSectors = NbOfSectors;
/* 擦除相应的flash扇区 */
status = HAL_FLASHEx_Erase(&EraseInitStruct, &SECTORError);
if (status != HAL_OK) return status;
status = FLASH_WaitForLastOperation(FLASH_WAITETIME); /* 等待flash操作完成 */
if(status == HAL_OK)
{
while(addrStart <= addrEnd)//写数据
{
/* 向flash写入数据 */
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,addrStart,*pBuffer);
if(status != HAL_OK) return status;
addrStart += 4;
pBuffer++;
}
}
/* 锁定flash */
HAL_FLASH_Lock();
return status;
}

Flash读取函数

 Flash读取函数的实现如下所示。该函数逻辑简明,这里不再说明。使用时请务必注意待读取入flash的地址,本例中采用字读取方式,故读取地址必须为4的倍数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 说明: 从指定flash区域读取一个字
*
* @返回值 对应flash区域的数据
*/
uint32 STMFLASH_ReadWord(uint32 Address)
{
return *(__IO uint32_t *)Address;
}
/**
* 说明: 从指定flash区域读取数据
* @参数 ReadAddr:待读取flash的地址,本例中采用字读取方式,故读取地址必须为4的倍数;
* @参数 pBuffer:指向存储读取数据数组的指针,按照字读取方式,所以类型为uint32;
* @参数 NumToRead:待读取数据个数。
*
* @返回值 无
*/
void STMFLASH_Read(uint32 ReadAddr,uint32 *pBuffer,uint32 NumToRead)
{
uint32 i;
for(i = 0;i < NumToRead;i++)
{
pBuffer[i] = STMFLASH_ReadWord(ReadAddr); /* 读取一个字 */
ReadAddr += 4;
}
}

实验验证

 验证程序片段如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
uint32 mainloop = 0;
#define FLASH_SAVE_ADDR ADDR_FLASH_SECTOR_7 - 8
uint8 writeBuffer[200];
uint8 readBuffer[200];
#define TEXT_LENTH sizeof(writeBuffer) //数组长度
#define SIZE TEXT_LENTH/4+((TEXT_LENTH%4)?1:0)
int16 test = 0;
uint16 screenShow = 0;
/* 初始化部分省略 */
while (1)
{
mainloop++;
oled_putuint32(0, 2, mainloop);
SYS_Delay_US(1000000);
for (test = 0; test < 200; test++)
{
writeBuffer[test] = test + mainloop%50;
}
screenShow += STMFLASH_Write(FLASH_SAVE_ADDR,(uint32*)writeBuffer,SIZE);
STMFLASH_Read(FLASH_SAVE_ADDR,(uint32*)readBuffer,SIZE);
HAL_UART_Transmit(&huart1,readBuffer,200,1000);
for (test = 0; test < 200; test++)
{
if (writeBuffer[test] != readBuffer[test]) screenShow++;
}
oled_putuint32(0, 5, screenShow);
}

 程序功能如下:

  • 写入地址为扇区7首地址减去8,实际为扇区6靠近末端,则擦除、读取均进行跨扇区操作;
  • 向该地址flash写入200字节数据并读取比对,确认有无读写错误;
  • 经验证读写均正常,实验完成。
文章目录
  1. 1. 开发环境
    1. 1.1. 硬件环境
    2. 1.2. 软件环境
  2. 2. Flash功能介绍
    1. 2.1. Flash构成
    2. 2.2. 读访问延时
    3. 2.3. Flash控制器解锁
    4. 2.4. 编程/擦除并行位数
    5. 2.5. Flash擦除顺序
      1. 2.5.1. 扇区擦除
      2. 2.5.2. 批量擦除
    6. 2.6. Flash编程顺序
      1. 2.6.1. 标准编程
  3. 3. HAL库Flash读写操作实现
    1. 3.1. 获取内存地址对应扇区
    2. 3.2. Flash擦除函数
      1. 3.2.1. FLASH_WaitForLastOperation()函数
      2. 3.2.2. FLASH_MassErase()函数
      3. 3.2.3. FLASH_Erase_Sector()函数
      4. 3.2.4. HAL_FLASHEx_Erase()函数
    3. 3.3. Flash编程函数
      1. 3.3.1. FLASH_Program_Byte()函数
  4. 4. 基于HAL库的Flash读写函数实现
    1. 4.1. Flash写入函数
    2. 4.2. Flash读取函数
  5. 5. 实验验证
|