芯片:STM32F103C8T6
Flash:64K 从0x08000000~0x08010000
SRAM:20K 从0x02000000~0x020005000
1、FLASH

Flash存储在code区,从0x08000000开始的地址。
整个Cortex-M3,系统分区如图所示:
1.1 Code
代码区,0x0000_0000 - 0x1FFF_FFFF,512MB
Code代码区主要用来存放用户代码数据和bootloader。
程序指令:代码
常量数据:由const 修饰的变量(const uint8_t table[] = {1,2,3})、const int i=3
初始化数据模板:全局变量的初始值,程序运行后会复制到SRAM中
BootLoader代码
由STM32F103RCT6为例
地址范围 | 用途 | 物理实现 |
---|---|---|
0x00000000--0x07FFFFFF | Flash 别名(通过 BOOT 引脚重映射) | 指向主 Flash 或系统存储器 |
0x08000000--0x0803FFFF | 主 Flash(用户程序存储区,256KB) | 实际物理 Flash |
0x1FFF0000--0x1FFF0FFF | 系统存储器(内置 Bootloader) | 只读 ROM |
0x1FFFF800--0x1FFFF80F | 选项字节(Flash 配置参数) | 特殊 Flash 区域 |
其他地址(如 0x08040000--0x1FFFFFFF) | 保留或未实现 | 无物理存储 |
1.2 存储器映射
0x0000 0000 - 0x0800 0000 根据Boot引脚配置,映射Flash/Sysmem/SRAM当中的128MB空间
0x0800 0000 - 0x0801 FFFF Flash Memory闪存存储空间128KB
0x1FFF F800 - 0x1FFF F7FE System Memory系统存储空间2KB
0x2000 0000 - 0x3FFF FFFF SRAM 存储区
2、FLASH操作
对于Flash操作,就是读、写。

这里的flash是指单片机自带的内部Flash,这个Flash是用来存储用户开发的程序代码。
STM32的闪存编程是由FPEC(闪存编程和查出控制器)模块处理的,这个模块包含7个32位寄存器,分别是:
-
FPEC 键寄存器(FLASH_KEYR) :负责对内置闪存的写操作
-
选择字节键寄存器(FLASH_OPTKEYR)
-
闪存控制寄存器(FLASH_CR)
-
闪存状态寄存器(FLASH_SR)
-
闪存地址寄存器(FLASH_AR)
-
选择字节寄存器(FLASH_OBR)
-
写保护寄存器(FLASH_WRPR)
FPEC键寄存器总共有三个键值:
RDPRT键=0x000000A5
KEY1=0x45670123
KEY2=0xCDEF89AB
在程序中,官方已经给封装好了,只需要调用flash的上锁和解锁函数即可
2.1 Flash编程事项
1字=32位
半字=16位
1字节=8位
-
STM32 复位后, FPEC 模块是被保护的,不能写入 FLASH_CR 寄存器;通过写入特定的序列到 FLASH_KEYR 寄存器可以打开 FPEC 模块(即写入 KEY1 和 KEY2),只有在写保护被解除后,我们才能操作相关寄存器。
-
STM32 闪存的编程每次必须写入 16 位,当 FLASH_CR 寄存器的PG位为' 1'时,在一个闪存地址写入一个半字将启动一次编程;写入任何非半字的数据,FPEC 都会产生总线错误。在编程过程中(BSY 位为' 1' ),任何读写闪存的操作都会使 CPU暂停,直到此次闪存编程结束。
-
STM32 的 FLASH 在编程的时候,也必须要求其写入地址的FLASH是被擦除了的(也就是其值必须是 0XFFFF),否则无法写入,在 FLASH_SR寄存器的PGERR位将得到一个警告。
2.2 Flash编程过程

-
检查FLASH_CR的LOCK是否解锁,如果没有先解锁
-
检测FLASH_SR寄存器的BSY位,确认没有其它正在进行的编程操作
-
设置FLASH_CR寄存器的PG位为1,在指定的地址写入要编程的半字
-
等待BSY位变为0
-
读出写入的地址并验证数据
FLASH_Status FLASH_GetStatus(void); // 获取Flash状态
FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) // 等待操作完成函数
写入半字操作
FLASH_Status FLASH_ProgramWord(uint32_t Address, uint32_t Data); // 字
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); // 半字
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data); // 字节
读出半字操作
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
return *(vu16*)faddr;
}
2.3 Flash擦除操作
Flash擦除操作:页擦除和整片擦除
页擦除

-
检查 FLASH_CR 的 LOCK 是否解锁,如果没有则先解锁
-
检查 FLASH_SR 寄存器的 BSY 位,以确认没有其他正在进行的闪存操作
-
设置 FLASH_CR 寄存器的 PER 位为' 1'
Flash擦除操作:
FLASH_Status FLASH_ErasePage(uint32_t Page_Address); // 页
FLASH_Status FLASH_EraseAllPages(void); // 所有页
FLASH_Status FLASH_EraseOptionBytes(void); // 字节数据擦除
3、 STM32实现对Flash操作
3.1 从Flash读数据
前面描述了,对于读flash操作来说,就是简单传入要读数据的地址,然后要读取数据的大小即可
FlashResult STMFLASH_ReadHalfWord(u32 ReadAddr, u16 *pData)
{
if(ReadAddr & 0x01) {
return FLASH_ERROR_INVALID_ADDR;
}
*pData = *(vu16*)ReadAddr;
return FLASH_SUCCESS;
}
FlashResult MyFlash_Read(u32 ReadAddr, u16 *pBuffer, u16 NumToRead)
{
u16 i;
FlashResult result;
if((ReadAddr < STM32_FLASH_BASE) || (ReadAddr >= (STM32_FLASH_BASE + 1024 * STM32_FLASH_SIZE))) {
return FLASH_ERROR_INVALID_ADDR;
}
for(i = 0; i < NumToRead;i++) {
result = STMFLASH_ReadHalfWord(ReadAddr, &pBuffer[i]);
if(result != FLASH_SUCCESS)
return result;
ReadAddr += 2;
}
return FLASH_SUCCESS;
}
传入的地址是有效的
3.2 向Flash写数据
从Flash写数据是一个复杂的过程,对Flash的操作,写也是最重要的部分。
由于Flash物理特性决定了向flash某个区域写数据,必须将其所在的页擦除才可以操作,所有就要涉及到判断,传入的地址是在那一页,那个位置,前面有多少数据需要保存,后面有多少空间支持写入,如果写入的数据太大,当前页装不完,就需要考虑写入下一页。
比如:
图中,黑色为flash的起始地址,红色为要写入数据的起始地址,写入数据大小为size,刚好需要存到第三页的中间位置(蓝色为写入数据的结束地址)。
1、offset = size-0x08000000 得到内存偏移地址
2、secpos = offset / 1024 得到在哪一页
3、secoff = offset%1024 得到在该页的起始地址
4、secremain = 1024/2 - secoff 得到内存剩余空间
要写入的数据地址为:0x08000804 写入数据为:数据[1044]个1、内存偏移地址为 = 0x08000804 - 0x08000000 = 0x804
2、得到那一页 = 0x804==2052/1024 = 2页
3、得到页内偏移地址 = 0x804==2052%1024 = 4
写数据:
得到要写入数据的页数和页内偏移地址,将该页起始到写入数据的首地址的数据读出
擦除该页
将要写入数据和之前的数据存入从新写入到该页
如果该页存不完数据,就增加页数,继续存储数据
FlashResult STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite){
u16 i;
for(i = 0; i < NumToWrite; i++) {
if(FLASH_ProgramHalfWord(WriteAddr, pBuffer[i]) != FLASH_COMPLETE) {
return FLASH_ERROR_PROGRAM_FAILED;
}
WriteAddr+=2;
}
return FLASH_SUCCESS;
}
FlashResult MyFlash_Write(u32 WriteAddr, u16 *pBuffer, u16 NumToWrite)
{
u32 offaddr; //去掉0X08000000后的地址(偏移地址)
u32 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(16位字计算)
u16 i;
FlashResult result;
if((WriteAddr < STM32_FLASH_BASE)||(WriteAddr >= (STM32_FLASH_BASE + 1024*STM32_FLASH_SIZE))) {
return FLASH_ERROR_INVALID_ADDR;
}
FLASH_Unlock(); // 解锁
offaddr = WriteAddr - STM32_FLASH_BASE;
secpos = offaddr/STM_SECTOR_SIZE;
secoff = (offaddr % STM_SECTOR_SIZE)/2;
secremain = STM_SECTOR_SIZE/2 - secoff;
if(NumToWrite <= secremain) secremain = NumToWrite;
while(1) {
result = MyFlash_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE, flashread_buffer.STMFLASH_BUF, STM_SECTOR_SIZE/2);
if(result != FLASH_SUCCESS) {
FLASH_Lock();
return result;
}
for(i = 0; i < secremain; i++) {
if(flashread_buffer.STMFLASH_BUF[secoff + i] != 0xFFFF) break;
}
if(i < secremain) {
if(FLASH_ErasePage(secpos * STM_SECTOR_SIZE + STM32_FLASH_BASE) != FLASH_COMPLETE) {
FLASH_Lock();
return FLASH_ERROR_ERASE_FAILED;
}
for(i = 0; i < secremain; i++) {
flashread_buffer.STMFLASH_BUF[i+secoff] = pBuffer[i];
}
result = STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,flashread_buffer.STMFLASH_BUF, STM_SECTOR_SIZE/2);
if(result != FLASH_SUCCESS) {
FLASH_Lock();
return result;
}
} else {
result = STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);//写已经擦除了的,直接写入扇区剩余区间.
if(result != FLASH_SUCCESS) {
FLASH_Lock();
return result;
}
}
if(NumToWrite == secremain) break;
else {
secpos++;
secoff=0;
pBuffer += secremain;
WriteAddr+= secremain; //写地址偏移
NumToWrite -= secremain;
if(NumToWrite > (STM_SECTOR_SIZE/2)) secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
else secremain = NumToWrite;
}
}
FLASH_Lock();
return FLASH_SUCCESS;
}
向flash写入数据的流程为:1、拿到偏移地址,和页地址,页内地址
2、解锁
3、从内存中读出数据
4、擦除该页
5、向该页写入数据(如果该页写不完就修改页地址,指向下一页)
6、上锁
3.3 结果显示

在main函数中打印输出
printf("Data to write: %s\r\n", TEXT_Buffer);
write_res = MyFlash_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,(SIZE + 1)/2);
if (write_res != FLASH_SUCCESS) {
printf("Write failed! Error: %d\r\n", write_res);
}
printf("write success\r\n");
delay_ms(20);
MyFlash_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);
printf("Data read from flash: %s\r\n", datatemp);
printf("read success\r\n");
write_res = MyFlash_Write(FLASH_SAVE_ADDR2,(u16*)TEXT2_Buffer,20/2);
if (write_res != FLASH_SUCCESS) {
printf("Write failed! Error: %d\r\n", write_res);
}
printf("write success\r\n");
delay_ms(20);
MyFlash_Read(FLASH_SAVE_ADDR2,(u16*)datatemp,SIZE);
printf("Data read from flash: %s\r\n", datatemp);
printf("read success\r\n");
使用专业工具,查看stm32f103c8t6中flash存入的数据