stm32 flash模拟EEPROM(笔记)
主存储器用来存放我们在keil5中写的代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。从图中可以看出flash的起始地址是 0X08000000,当 B0、B1 都接 GND 的时候,就是从 0X08000000开始运行代码的。
一、简介
1、
主存储器用来存放我们在keil5中写的代码和数据常数(如 const 类型的数据)。对于大容量产品,其被划分为 256 页,每页 2K 字节。注意,小容量和中容量产品则每页只有 1K 字节。从图中可以看出flash的起始地址是 0X08000000,当 B0、B1 都接 GND 的时候,就是从 0X08000000开始运行代码的。
2、
信息块,该部分分为 2 个小部分,其中启动程序代码,是用来存储 ST 自带的启动程序,用于串口下载代码,当 B0 接 V3.3,B1 接 GND 的时候,运行的就是这部分代码。
3、
闪存存储器接口寄存器,该部分用于控制flash读写等,是整个闪存模块的控制机构。
二、FLASH操作使用
1、FLASH和EEPROM的区别
FLASH和EEPROM的最大区别:FLASH和EEPROM的最大区别是FLASH按扇区操作,EEPROM则按字节操作,二者寻址方法不同,存储单元的结构也不同。FLASH的电路结构较简单,同样容量占芯片面积较小,成本自然比EEPROM低,因而适合用作程序存储器,EEPROM则更多的用作非易失的数据存储器。当然用FLASH做数据存储器也行,但操作比EEPROM麻烦的多,所以更“人性化”的MCU设计会集成FLASH和EEPROM两种非易失性存储器,而廉价型设计往往只有FLASH,早期可电擦写型MCU则都是EEPRM结构,现在已基本上停产了。
2、读flash
cpu读取内部flash的程序相对比较简单,直接看程序。
//功能:读取指定地址的半个字
//参数:addr表示要读的地址
//返回值:data为指定地址内的数据
u16 Read_HalfWord(u32 addr)
{
u16 data;
data=*(u16*)addr;//先把地址强制装换为指针,然后再用*取出该指针所指向的数据
return data;
}
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_ReadData(u32 Readaddr,u16 *pBuffer,u16 NumToRead)
{
u16 i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=Read_HalfWord(Readaddr);
Readaddr+=2;
}
}
3、写flash
把数据写到flash里面相对比较复杂,但是这里面不涉及到通讯协议什么的,只用操作寄存器就行了。直接看flash的资料,根据资料里面写flash的流程来编写代码就可以了,而且有库函数可以调用,认真看一遍代码基本上没啥太难的地方。有几个注意点就是写flash必须写 16 位(不能单纯的写入 8 位数据!),然后擦除的话只能整页或者整片擦除,不能擦除半页或者当前页的某些区域。
//从指定地址开始写入指定长度的数据
//WriteAddr:起始地址(此地址必须为2的倍数!!)
//pBuffer:数据指针
//NumToWrite:半字(16位)数(就是要写入的16位数据的个数.)
#if STM32_FLASH_SIZE<256
#define STM_SECTOR_SIZE 1024 //字节
#else
#define STM_SECTOR_SIZE 2048
#endif
u16 STMFLASH_BUF[STM_SECTOR_SIZE/2];//最多是2K字节
void STMFLASH_Write(u32 WriteAddr,u16 *pBuffer,u16 NumToWrite)
{
u32 sec_addr;//扇区地址
u16 sec_off;//扇区内偏移地址(16位字计算)
u16 sec_remain;//扇区内剩余地址(16位字计算)
u16 i;
u32 offaddr; //去掉0X08000000后的地址
if(WriteAddr<STM32_FLASH_BASE||(WriteAddr>=(STM32_FLASH_BASE+1024*STM32_FLASH_SIZE)))return;//非法地址
FLASH_Unlock(); //解锁
offaddr=WriteAddr-STM32_FLASH_BASE; //实际偏移地址.
sec_addr=offaddr/STM_SECTOR_SIZE; //扇区地址 0~256 for STM32F103ZET6
sec_off=(offaddr%STM_SECTOR_SIZE)/2; //扇区内的偏移地址(2个字节为基本单位)
sec_remain=STM_SECTOR_SIZE/2-sec_off; //以两个字节为基本单位的话,所以一页内有(STM_SECTOR_SIZE/2)个基本单位。
//一页内剩余多少个基本单位呢?直接减去一页内的偏移地址就可以了
if(NumToWrite<=sec_remain)sec_remain=NumToWrite;//不大于该扇区范围
while(1)
{
STMFLASH_ReadData(sec_addr*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);
for(i=0;i<sec_remain;i++)
{
if(STMFLASH_BUF[sec_off+i]!=0xffff)break;//当检查到有地址内地数据不为0xffff,那么就跳出for循环;
}
if(i<sec_remain)//判断i的值是否小于该页的剩余量,如果小于,则说明要擦除整页。
{
FLASH_ErasePage(sec_addr*STM_SECTOR_SIZE+STM32_FLASH_BASE);//擦除这个扇区
for(i=0;i<sec_remain;i++)
{
STMFLASH_BUF[i+sec_off]=pBuffer[i];
}
STMFLASH_Write_NoCheck(sec_addr*STM_SECTOR_SIZE+STM32_FLASH_BASE,STMFLASH_BUF,STM_SECTOR_SIZE/2);//写入整个扇区(页)
}
else STMFLASH_Write_NoCheck(WriteAddr,pBuffer,sec_remain);//写已经擦除了的,直接写入扇区剩余区间.
if(NumToWrite==sec_remain)break;//写入结束了,跳出while循环
else//写入未结束
{
sec_addr++; //扇区地址增1
sec_off=0; //偏移位置为0
pBuffer+=sec_remain; //指针偏移
WriteAddr+=sec_remain; //写地址偏移
NumToWrite-=sec_remain; //字节(16位)数递减
if(NumToWrite>(STM_SECTOR_SIZE/2))sec_remain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
else sec_remain=NumToWrite;//下一个扇区可以写完了
}
}
FLASH_Lock();//上锁
}
//不检查的写入
//WriteAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
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;//地址增加2.
}
}
三、编译
主函数里面有个需要特别注意的,当我们编译程序后,会出现下面图片中显示的这段信息。
Code:表示程序所占用 FLASH 的大小(FLASH)。
RO-data:即 Read Only-data,表示程序定义的常量,如 const 类型(FLASH)。
RW-data:即 Read Write-data,表示已被初始化的全局变量(SRAM)
ZI-data:即 Zero Init-data,表示未被初始化的全局变量(SRAM)
有了这个就可以知道你当前程序所占用的 flash 和 sram 大小了,程序的大小不是.hex 文件的大小,而是编译后的 Code 和 RO-data 之和。
需要注意Flash_Save_Addr 这个是我们要写入的数据的首地址,该地址必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000。
我这里设置的是Flash_Save_Addr =0X08070000,而Flash_Save_Addr >(0X08000000+Code+RO-data);如果小于的话会造成程序卡死。
主函数代码如下:
//要写入到STM32 Flash的字符串数组
const u8 TEXT_Buffer[]={"stm32f103zet6 flash test"};
#define SIZE sizeof(TEXT_Buffer) //数组长度
#define Flash_Save_Addr 0X08070000 //设置Flash的保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
int main(void)
{
u8 key,i=0;
u8 datatemp[SIZE];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
LED_Init();
delay_init();
KEY_Init();
uart_init(115200);
while(1)
{
key= KEY_Scan(0);
if(key==KEY1_PRES)
{
printf("\r\n Start Write FLASH....\r\n");
STMFLASH_Write(Flash_Save_Addr,(u16*)TEXT_Buffer,SIZE);
printf("\r\nFLASH Write Finished!\r\n");//提示传送完成
}
if(key==KEY0_PRES)
{
printf("\r\nStart Read FLASH....");
STMFLASH_ReadData(Flash_Save_Addr,(u16*)datatemp,SIZE);
printf("%s \r\n",datatemp);//显示读到的字符串
}
}
}
四、验证
串口正常显示,验证成功。
更多推荐
所有评论(0)