stmflash.c
9.1 KB
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/**
****************************************************************************************************
* @file stmflash.c
* @author 正点原子团队(ALIENTEK)
* @version V1.0
* @date 2020-04-26
* @brief STM32内部FLASH读写 驱动代码
* @license Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
****************************************************************************************************
* @attention
*
* 实验平台:正点原子 MiniSTM32 V4开发板
* 在线视频:www.yuanzige.com
* 技术论坛:www.openedv.com
* 公司网址:www.alientek.com
* 购买地址:openedv.taobao.com
*
* 修改说明
* V1.0 20200426
* 第一次发布
*
****************************************************************************************************
*/
#include "stmflash.h"
/**
* @brief 从指定地址读取一个半字 (16位数据)
* @param faddr : 读取地址 (此地址必须为2的倍数!!)
* @retval 读取到的数据 (16位)
*/
uint16_t stmflash_read_halfword(uint32_t faddr)
{
return *(volatile uint16_t *)faddr;
}
/**
* @brief 从指定地址开始读出指定长度的数据
* @param raddr : 起始地址
* @param pbuf : 数据指针
* @param length: 要读取的半字(16位)数,即2个字节的整数倍
* @retval 无
*/
void stmflash_read(uint32_t raddr, uint16_t *pbuf, uint16_t length)
{
uint16_t i;
for (i = 0; i < length; i++)
{
pbuf[i] = stmflash_read_halfword(raddr); /* 读取2个字节 */
raddr += 2; /* 偏移2个字节 */
}
}
/**
* @brief 不检查的写入
这个函数的假设已经把原来的扇区擦除过再写入
* @param waddr : 起始地址 (此地址必须为2的倍数!!,否则写入出错!)
* @param pbuf : 数据指针
* @param length : 要写入的 半字(16位)数
* @retval 无
*/
void stmflash_write_nocheck(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
uint16_t i;
for (i = 0; i < length; i++)
{
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, waddr, pbuf[i]);
waddr += 2; /* 指向下一个半字 */
}
}
/**
* @brief 在FLASH 指定位置, 写入指定长度的数据(自动擦除)
* @note 该函数往 STM32 内部 FLASH 指定位置写入指定长度的数据
* 该函数会先检测要写入的扇区是否是空(全0XFFFF)的?, 如果
* 不是, 则先擦除, 如果是, 则直接往扇区里面写入数据.
* 数据长度不足扇区时,自动被回擦除前的数据
* @param waddr : 起始地址 (此地址必须为2的倍数!!,否则写入出错!)
* @param pbuf : 数据指针
* @param length : 要写入的 半字(16位)数
* @retval 无
*/
uint16_t g_flashbuf[STM32_SECTOR_SIZE / 2]; /* 最多是2K字节 */
void stmflash_write(uint32_t waddr, uint16_t *pbuf, uint16_t length)
{
uint32_t secpos; /* 扇区地址 */
uint16_t secoff; /* 扇区内偏移地址(16位字计算) */
uint16_t secremain; /* 扇区内剩余地址(16位字计算) */
uint16_t i;
uint32_t offaddr; /* 去掉0X08000000后的地址 */
FLASH_EraseInitTypeDef flash_eraseop;
uint32_t erase_addr; /* 擦除错误,这个值为发生错误的扇区地址 */
if (waddr < STM32_FLASH_BASE || (waddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE)))
{
return; /* 非法地址 */
}
HAL_FLASH_Unlock(); /* FLASH解锁 */
offaddr = waddr - STM32_FLASH_BASE; /* 实际偏移地址. */
secpos = offaddr / STM32_SECTOR_SIZE; /* 扇区地址 0~127 for STM32F103RBT6 */
secoff = (offaddr % STM32_SECTOR_SIZE) / 2; /* 在扇区内的偏移(2个字节为基本单位.) */
secremain = STM32_SECTOR_SIZE / 2 - secoff; /* 扇区剩余空间大小 */
if (length <= secremain)
{
secremain = length; /* 不大于该扇区范围 */
}
while (1)
{
stmflash_read(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 读出整个扇区的内容 */
for (i = 0; i < secremain; i++) /* 校验数据 */
{
if (g_flashbuf[secoff + i] != 0XFFFF)
{
break; /* 需要擦除 */
}
}
if (i < secremain) /* 需要擦除 */
{
flash_eraseop.TypeErase = FLASH_TYPEERASE_PAGES; /* 选择面擦除 */
flash_eraseop.Banks = FLASH_BANK_1;
flash_eraseop.NbPages = 1;
flash_eraseop.PageAddress = secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE; /* 要擦除的扇区 */
HAL_FLASHEx_Erase( &flash_eraseop, &erase_addr);
for (i = 0; i < secremain; i++) /* 复制 */
{
g_flashbuf[i + secoff] = pbuf[i];
}
stmflash_write_nocheck(secpos * STM32_SECTOR_SIZE + STM32_FLASH_BASE, g_flashbuf, STM32_SECTOR_SIZE / 2); /* 写入整个扇区 */
}
else
{
stmflash_write_nocheck(waddr, pbuf, secremain); /* 写已经擦除了的,直接写入扇区剩余区间. */
}
if (length == secremain)
{
break; /* 写入结束了 */
}
else /* 写入未结束 */
{
secpos++; /* 扇区地址增1 */
secoff = 0; /* 偏移位置为0 */
pbuf += secremain; /* 指针偏移 */
waddr += secremain * 2; /* 写地址偏移(16位数据地址,需要*2) */
length -= secremain; /* 字节(16位)数递减 */
if (length > (STM32_SECTOR_SIZE / 2))
{
secremain = STM32_SECTOR_SIZE / 2; /* 下一个扇区还是写不完 */
}
else
{
secremain = length; /* 下一个扇区可以写完了 */
}
}
}
HAL_FLASH_Lock(); /* 上锁 */
}
/**
* @brief Gets the sector of a given address
* @param Address: Flash address
* @retval The sector of a given address
*/
static uint32_t GetSector(uint32_t Address)
{
uint32_t sector = 0;
if ((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)) {
sector = 0;
} else if ((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)) {
sector = 1;
} else if ((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)) {
sector = 2;
} else if ((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)) {
sector = 3;
} else if ((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)) {
sector = 4;
} else if ((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)) {
sector = 5;
} else if ((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)) {
sector = 6;
} else if ((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7)) {
sector = 7;
} else if ((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8)) {
sector = 8;
} else if ((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9)) {
sector = 9;
} else if ((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10)) {
sector = 10;
} else if ((Address < ADDR_FLASH_SECTOR_34) && (Address >= ADDR_FLASH_SECTOR_33)) {
sector = 33;
} else if ((Address < ADDR_FLASH_SECTOR_62) && (Address >= ADDR_FLASH_SECTOR_61)) {
sector = 61;
} else /*(Address < FLASH_END_ADDR) && (Address >= ADDR_FLASH_SECTOR_11))*/
{
sector = 11;
}
return sector;
}
uint32_t FLASH_If_Erase(uint32_t startAddress, uint32_t endAddress)
{
uint32_t startSector;
uint32_t endSector;
uint32_t SectorError;
FLASH_EraseInitTypeDef pEraseInit;
uint8_t sectorCount;
uint32_t ret;
HAL_FLASH_Unlock();
//
// * @brief Clear the specified FLASH flag.
// * @param __FLAG__ specifies the FLASH flags to clear.
// * This parameter can be any combination of the following values:
// * @arg @ref FLASH_FLAG_EOP FLASH End of Operation flag
// * @arg @ref FLASH_FLAG_WRPERR FLASH Write protected error flag
// * @arg @ref FLASH_FLAG_PGERR FLASH Programming error flag
// * @arg @ref FLASH_FLAG_OPTVERR Loaded OB and its complement do not match
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPTVERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGERR);
startSector = GetSector(startAddress);
endSector = GetSector(endAddress);
sectorCount = endSector - startSector + 1;
pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
pEraseInit.PageAddress = startAddress;
pEraseInit.NbPages = sectorCount;
pEraseInit.Banks = FLASH_BANK_1;
// pEraseInit.VoltageRange = VOLTAGE_RANGE_3;
if (HAL_FLASHEx_Erase(&pEraseInit, &SectorError) != HAL_OK) {
ret = FLASHIF_ERASE_ERROR;
goto out;
}
ret = FLASHIF_OK;
out:
HAL_FLASH_Lock();
return ret;
}