为什么要用Flash来模拟EEPORM
Flash更适合作为程序存储器,EEPROM更适合作为数据存储器,存储大量数据的时候就需要使用EEPROM,廉价的MCU往往只有FLASH而没有EEPROM,所以这个时候就需要外加EEPROM或者选择一些别的办法,使用Flash模拟EEPROM就是一个很好的选择,说白了就是使用stm32内部flash里面一部分空间用来保存数据,不只是保存程序代码。
FLAHS和EEPROM有什么区别
Flash是按照扇区操作,EEPROM是按字节操作
核心代码讲解
offaddr=WriteAddr-STM32_FLASH_BASE;
secpos=offaddr/STM_SECTOR_SIZE;
secoff=(offaddr%STM_SECTOR_SIZE)/2;
secremain=STM_SECTOR_SIZE/2-secoff;
S
T
M
32
_
F
L
A
S
H
_
B
A
S
E
=
0
X
8000000
STM32\_FLASH\_BASE = 0X800 0000
STM32_FLASH_BASE=0X8000000
假设写入地址为
W
r
i
t
e
A
d
d
r
=
0
X
8030000
WriteAddr=0X8030000
WriteAddr=0X8030000
那么偏移地址就是
s
e
c
p
o
s
=
0
X
8030000
−
0
X
8000000
=
0
X
0030000
secpos=0X8030000-0X8000000=0X0030000
secpos=0X8030000−0X8000000=0X0030000
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u32 offaddr;
if(WriteAddr<STM32_FLASH_BASE||
(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))
{
return;
}
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)
{
STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,
STMFLASH_BUF,STM_SECTOR_SIZE/2);
for(i=0;i<secremain;i++)
{
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;
}
if(i<secremain)
{
FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);
for(i=0;i<secremain;i++)
{
STMFLASH_BUF[i+secoff]=pBuffer[i];
}
STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);
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();
}
存储器地址映射(注意RCT6只有128页)
- 存储器大小:我们说的flash大小,指的是主存储器的大小
- 信息块:
- 启动程序代码,用于存储ST自带的启动程序,用于串口下载代码
- 用户选择字节一般用于配置读保护和写保护等功能
- 接口寄存器:用于配置flash读写等
B0和B1
在开发板上有两个跳线帽,可以用来切换B0(Boot0)和B1(Boot1),模式如下
B0 | B1 | 描述 |
---|
GND | GND | 从0x8000000运行(主存储器基地址) |
3.3 | GND | 从信息块运行 |
flash写
- 只有解除写保护才能操作相关寄存器(检查FLASH_CR的LOCK是否解锁)
- flash编程时确保没有其他正在进行的编程操作(检查FLASH_SR的BSY位)
- 每次编程必须写入半字,否则会出错(设置FLASH_CR寄存器的PG位为1)
- flash编程时,写入地址的flash必须是被擦除的,否则无法写入
flash页擦除
- 设置CR寄存器的PER为1
- 配置AR寄存器选择擦除页
- 配置CR寄存器的STRT为1
- 等待SR的BSY为0
- 读出验证
整片擦除
- 设置CR的MER为1
- 设置CR的STAT为1
- 等待SR的BSY为0
- 读出验证
寄存器(具体的参看flash编程手册)
- KEYR寄存器写入FPEC解锁键
- CR寄存器:锁,开始等
- SR寄存器:检查忙
- AR寄存器:写地址
flash操作常用库函数
stm32flash.h
#ifndef __STMFLASH_H__
#define __STMFLASH_H__
#include "sys.h"
#define STM32_FLASH_SIZE 256
#define STM32_FLASH_WREN 1
#define STM32_FLASH_BASE 0x08000000
u16 STMFLASH_ReadHalfWord(u32 faddr);
void STMFLASH_WriteLenByte(u32 WriteAddr,u32 DataToWrite,u16 Len);
u32 STMFLASH_ReadLenByte(u32 ReadAddr,u16 Len);
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite);
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead);
void Test_Write(u32 WriteAddr,u16 WriteData);
#endif
stm32flash.c
#include "stmflash.h"
#include "delay.h"
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
return *(vu16*)faddr;
}
#if STM32_FLASH_WREN
void STMFLASH_Write_NoCheck(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u16 i;
for(i=0;i<NumToWrite;i++)
{
FLASH_ProgramHalfWord(WriteAddr,pBuffer[i]);
WriteAddr+=2;
}
}
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024
#else
#define STM_SECTOR_SIZE 2048
#endif
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
u32 offaddr;
if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;
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)
{
STMFLASH_Read(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
for(i=0;i<secremain;i++)
{
if(STMFLASH_BUF[secoff+i]!=0XFFFF)break;
}
if(i<secremain)
{
FLASH_ErasePage(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE);
for(i=0;i<secremain;i++)
{
STMFLASH_BUF[i+secoff]=pBuffer[i];
}
STMFLASH_Write_NoCheck(secpos*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
}else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,secremain);
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();
}
#endif
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{
u16 i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);
ReadAddr+=2;
}
}
void Test_Write(u32 WriteAddr,u16 WriteData)
{
STMFLASH_Write(WriteAddr,&WriteData,1);
}
主函数调用
#include "delay.h"
#include "sys.h"
#include "key.h"
#include "oled.h"
#include "stmflash.h"
const u8 TEXT_Buffer[]={"STM32 FLASH TEST"};
#define SIZE sizeof(TEXT_Buffer)
#define FLASH_SAVE_ADDR 0X08020000
int main(void)
{
u8 datatemp[SIZE];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
KEY_Init();
OLED_Init();
while(1)
{
if(KEY0)
{
OLED_Refresh();
OLED_ShowString(0,0,"start write",16,1);
STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);
OLED_ShowString(0,16,"write finished",16,1);
OLED_ShowString(0,32," ",16,1);
OLED_Refresh();
}
if(!KEY0){
OLED_Refresh();
OLED_ShowString(0,0,"start read ",16,1);
STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);
OLED_ShowString(0,16,"read data is ",16,1);
OLED_ShowString(0,32,datatemp,16,1);
OLED_Refresh();
}
}
}
学习目标
作业
- 阅读flash库
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)