stm32内部Flash基础知识

  由于嵌入式IAP升级时,制作的Bootloader往往会涉及到Stm32的Flash的一些操作(用于写入App程序),因此这里讲解一下Stm32的Flash相关操作

一、Stm32的Flash

1.1 Flash的基础原理

  此处内容可以看该篇博文:嵌入式存储器

1.2 Stm32的Flash特性、操作

  • 已知Flash写入前需要擦除的特性
  • Nor Flash接口与RAM接口相同,因此往往MCU内部采用Nor Flash;即Stm32的Flash为 Nor Flash
    • 因此如果是擦除大块区域时,会相对较慢
  • Stm32的Flash使用前后,需要 解锁 、 锁定
    • 在实际发布的产品中,在STM32芯片的内部FLASH存储了控制程序,如果不作任何保护措施的话,可以使用下载器直接把内部FLASH的内容读取回来,得到bin或hex文件格式的代码拷贝,别有用心的厂商即可利用该代码文件山寨产品。

  对STM32 内部FLASH进行编程操作,一般需要遵循以下流程:

  1. Flash解锁
  2. 清除相关标志位
  3. 擦除Flash
  4. 写入Flash
  5. 锁定Flash

1.3 Flash的底层操作函数(仅限Stm32)

1
2
3
4
FLASH_Unlock(); //Flash解锁函数
FLASH_Lock(); //Flash锁定函数
FLASH_Status FLASH_EraseSector(uint32_t FLASH_Sector, uint8_t VoltageRange); //Flash擦除函数
FLASH_Status FLASH_ProgramWord(uint32_r Address, uint32_t Data); //Flash写入函数

1.4 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
extern void FLASH_If_Init(void);
extern void FLASH_If_Finish(void);
extern uint32_t FLASH_If_Erase(uint32_t StartSector);
extern uint32_t FLASH_If_Write(__IO uint32_t* FlashAddress, uint32_t* Data ,uint32_t DataLength);
extern void FLASH_IF_Read(uint32_t ReadAddr, uint32_t *pBuffer, uint32_t NumToRead);


//例子:Stm32F407VGT6的Flash
//FLASH 扇区的起始地址
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) //扇区0起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) //扇区1起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) //扇区2起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) //扇区3起始地址, 16 Kbytes
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) //扇区4起始地址, 64 Kbytes
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) //扇区5起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) //扇区6起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) //扇区7起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000) //扇区8起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000) //扇区9起始地址, 128 Kbytes
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) //扇区10起始地址,128 Kbytes
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) //扇区11起始地址,128 Kbytes


/********************************************************************************
* 函数名 : GetSector
* 功 能 : 获取某个地址所在的flash扇区
* 说 明 : Stm32F407VGT6的Flash扇区分配
* 入 参 : fu32_Addr:flash地址
* 返 回 : 0~11,即addr所在的扇区
* 设 计 : Shatang 时 间 : 2020.06.18
* 修 改 : none 时 间 : none
********************************************************************************/
static uint16_t GetSector(uint32_t fu32_Addr)
{
if(fu32_Addr<ADDR_FLASH_SECTOR_1)return FLASH_Sector_0;
else if(fu32_Addr<ADDR_FLASH_SECTOR_2)return FLASH_Sector_1;
else if(fu32_Addr<ADDR_FLASH_SECTOR_3)return FLASH_Sector_2;
else if(fu32_Addr<ADDR_FLASH_SECTOR_4)return FLASH_Sector_3;
else if(fu32_Addr<ADDR_FLASH_SECTOR_5)return FLASH_Sector_4;
else if(fu32_Addr<ADDR_FLASH_SECTOR_6)return FLASH_Sector_5;
else if(fu32_Addr<ADDR_FLASH_SECTOR_7)return FLASH_Sector_6;
else if(fu32_Addr<ADDR_FLASH_SECTOR_8)return FLASH_Sector_7;
else if(fu32_Addr<ADDR_FLASH_SECTOR_9)return FLASH_Sector_8;
else if(fu32_Addr<ADDR_FLASH_SECTOR_10)return FLASH_Sector_9;
else if(fu32_Addr<ADDR_FLASH_SECTOR_11)return FLASH_Sector_10;
return FLASH_Sector_11;
}
/********************************************************************************
* 函数名 : FLASH_If_Init
* 功 能 : stm32的内部flash初始化(解锁)
* 说 明 : none
* 入 参 : none
* 返 回 : none
* 设 计 : Shatang 时 间 : 2020.06.18
* 修 改 : none 时 间 : none
********************************************************************************/
void FLASH_If_Init(void)
{
FLASH_Unlock();

/* Clear pending flags (if any) */
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
}
/********************************************************************************
* 函数名 : FLASH_If_Finish
* 功 能 : stm32的内部flash关闭(上锁)
* 说 明 : none
* 入 参 : none
* 返 回 : none
* 设 计 : Shatang 时 间 : 2020.07.29
* 修 改 : none 时 间 : none
********************************************************************************/
void FLASH_If_Finish(void)
{
/* Clear pending flags (if any) */
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);

FLASH_Lock();
}
/********************************************************************************
* 函数名 : FLASH_If_Erase
* 功 能 : stm32的内部flash擦除
* 说 明 : 只针对APP文件地址的存储内容进行擦除
* 入 参 : none
* 返 回 : none
* 设 计 : Shatang 时 间 : 2020.06.18
* 修 改 : none 时 间 : none
********************************************************************************/
uint32_t FLASH_If_Erase(uint32_t StartSector)
{
uint32_t UserStartSector;
uint32_t i = 0;

/* Get the sector where start the user flash area */
UserStartSector = GetSector(StartSector);

for(i = UserStartSector; i <= GetSector(APPLICATION_END_ADDRESS); i += 8)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if (FLASH_EraseSector(i, VoltageRange_3) != FLASH_COMPLETE)
{
/* Error occurred while page erase */
return (1);
}
}

return (0);
}
/********************************************************************************
* 函数名 : FLASH_If_Write
* 功 能 : stm32的内部flash写操作
* 说 明 : none
* 入 参 : none
* 返 回 : none
* 设 计 : Shatang 时 间 : 2020.06.18
* 修 改 : none 时 间 : none
********************************************************************************/
uint32_t FLASH_If_Write(__IO uint32_t* FlashAddress, uint32_t* Data ,uint32_t DataLength)
{
uint32_t i = 0;

for (i = 0; (i < DataLength) && (*FlashAddress <= (APPLICATION_END_ADDRESS-4)); i++)
{
/* Device voltage range supposed to be [2.7V to 3.6V], the operation will
be done by word */
if (FLASH_ProgramWord(*FlashAddress, *(uint32_t*)(Data+i)) == FLASH_COMPLETE)
{
/* Check the written value */
if (*(uint32_t*)*FlashAddress != *(uint32_t*)(Data+i))
{
/* Flash content doesn't match SRAM content */
return(2);
}
/* Increment FLASH destination address */
*FlashAddress += 4;
}
else
{
/* Error occurred while writing data in Flash memory */
return (1);
}
}

return (0);
}

/********************************************************************************
* 函数名 : STMFLASH_ReadWord
* 功 能 : 读取指定地址的半字(16位数据)
* 说 明 : none
* 入 参 : fu32_Addr:读地址
* 返 回 : 对应数据
* 设 计 : Shatang 时 间 : 2020.06.18
* 修 改 : none 时 间 : none
********************************************************************************/
static uint32_t STMFLASH_ReadWord(uint32_t fu32_Addr)
{
return *(volatile uint32_t*)fu32_Addr;
}
/********************************************************************************
* 函数名 : STMFLASH_ReadWord
* 功 能 : 从指定地址开始读出指定长度的数据
* 说 明 : none
* 入 参 : ReadAddr:起始地址
pBuffer:数据指针
NumToRead:读的数据个数
* 返 回 : 对应数据
* 设 计 : Shatang 时 间 : 2020.06.18
* 修 改 : none 时 间 : none
********************************************************************************/
void FLASH_IF_Read(uint32_t ReadAddr, uint32_t *pBuffer, uint32_t NumToRead)
{
uint32_t i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i] = STMFLASH_ReadWord(ReadAddr);//读取4个字节.
ReadAddr+=4;//偏移4个字节.
}
}

二、详细应用实例

  具体实际应用:基于Xmodem协议的IAP升级,可以看这篇文章:IAP升级 & Bootloader制作

-------------本文结束感谢您的阅读-------------