一、EEPROM和FLASH总体差异
1、EEPROM的发展
(1)ROM(Read Only Memory):真正意义上的只读存储器,在出厂的时候数据就写在了芯片里,只能读不能写;
(2)PROM(Program Rom):可编程只读存储器,可编程的ROM芯片,出厂的时候芯片是空白的,自己可以写入一次数据,一旦写进去芯片也是只能读不能写;
(3)EPROM(Erasable Programmable Rom):可擦除可编程只读存储器,芯片上方有一个窗口,可以用紫外线透过芯片的窗口去擦除程序,擦除完再写入,平时要把窗口遮住,防止平时使用时有光线进入芯片,导致数据受损。
(4)EEPROM(Electrically Erasable Programmable Read-Only Memory):电可擦除可编程只读存储器”,可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。
2、FLASH的分类
flash分为Norflash和Nandflash:Norflash容量小价格高,可以上电使用,通过总线与CPU连接,也可以片内执行,可用于设备启动;NandFlash容量大,价格便宜,必须初始化才能使用,通过NandFlash控制器与CPU通信。更详细的区别可以参考《嵌入式开发——常见的存储器分类和特性介绍》。
3、flash和EEPROM的异同
(1)两者都是属于ROM,并且都是电学原理进行存储
(2)FLASH比EEPROM的电路简单,同样面积flash可以存储更多数据,数据密度更高;
(3)FLASH是按块进行访问,EEPROM按字节进行访问;
(4)EEPROM的可擦写次数比FLASH多;
(5)EEPROM的单位容量价格比FLASH更高,EEPROM一般容量都很小;
(6)EEPROM一般都是用作存储程序运行时要掉电不丢失的数据,FLASH一般是用来存储程序的;
二、EEPROM的学习和使用方法
1、EEPROM写数据流程
(1)第一步,首先写IIC的起始信号,紧接着写上首字节,即我们前边讲的IIC的器件地址,并且在读写方向上选择“写”操作。
(2)第二步,发送数据的存储地址。例如24C02芯片一共有256个字节的存储空间,地址从0x00~0xFF,我们想把数据存储在芯片的哪个位置,此刻写入的就是对应的那个地址。
(3)第三步,发送要存储的数据第一个字节、第二个字节……,注意在写数据的过程中,EEPROM每个字节都会回应一个“应答位0”,来告诉我们在EEPROM芯片中写入数据成功,如果没有回应答位,说明写入数据不成功。
在写数据的过程中,每成功写入一个字节数据,EEPROM芯片的存储地址就会自动加1,当加到0xFF后,再写一个字节数据,地址就会溢出又变成了0x00。
(1)第一步,首先写IIC的起始信号,紧接着写上首字节,即我们前边讲的IIC的器件地址,并且在读写方向上选择“写”操作。
这个地方可能有人会感到很诧异,我们明明是读数据为何方向也要选“写”呢?刚才说过了,24C02芯片一共有256个地址,我们选择写操作,是为了把所要读的数据的存储地址先写进去,告诉EEPROM我们要读取哪个地址的数据。这就如同我们打电话,先拨总机号码(EEPROM的器件地址),而后还要继续拨分机号码(EEPROM的数据地址),而拨分机号码这个动作,主机仍然是发送方,方向依然是“写”。
(2)第二步,发送要读取的数据的地址,注意是地址而非存储在EEPROM中的数据,通知EEPROM我要读取哪个分机的信息。
(3)第三步,重新发送IIC的起始信号和器件地址,并且在读写方向位上选择“读”操作。
这三步当中,每一个字节实际上都是在“写”,所以每一个字节EEPROM都会回应一个“应答位0”。
(4)第四步,读取从器件发回的数据,读一个字节,如果还想继续读下一个字节,就发送一个“应答位ACK(0)”,如果不想读了,告诉EEPROM,我不想要数据了,别再发数据了,那就发送一个“非应答位NAK(1)”。
和写操作规则一样,每成功读取一个字节数据,EEPROM芯片的存储地址就会自动加1,那如果我们想继续往下读,给EEPROM一个ACK(0)低电平,然后再继续给SCL时钟线完整的时序,EEPROM会继续往外送数据。如果我们不想读了,要告诉EEPROM不要数据了,那我们直接给一个NAK(1)高电平即可。这个地方大家要从逻辑上理解透彻,不能简单的靠死记硬背了,一定要理解明白。
3、总结
(1)在通常的EEPROM应用中,单片机是主机,24C02是从机。
(2)无论是读操作还是写操作,SCL时钟线始终都是由主机控制的。
(3)写数据的时候应答信号由从机给出,表示从机是否正确接收了数据。
(4)读数据的时候应答信号则由主机给出,表示是否继续读下去。
//读取指定地址的半字(16位数据)
//faddr:读地址(此地址必须为2的倍数!!)
//返回值:对应数据.
u16 STMFLASH_ReadHalfWord(u32 faddr)
{
return *(vu16*)faddr;
}
#if STM32_FLASH_WREN //如果使能了写
//不检查的写入
//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.
}
}
//从指定地址开始写入指定长度的数据
//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 secpos; //扇区地址
u16 secoff; //扇区内偏移地址(16位字计算)
u16 secremain; //扇区内剩余地址(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; //实际偏移地址.
secpos=offaddr/STM_SECTOR_SIZE; //扇区地址 0~127 for STM32F103RBT6
secoff=(offaddr%STM_SECTOR_SIZE)/2; //在扇区内的偏移(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++; //扇区地址增1
secoff=0; //偏移位置为0
pBuffer+=secremain; //指针偏移
WriteAddr+=secremain; //写地址偏移
NumToWrite-=secremain; //字节(16位)数递减
if(NumToWrite>(STM_SECTOR_SIZE/2))secremain=STM_SECTOR_SIZE/2;//下一个扇区还是写不完
else secremain=NumToWrite;//下一个扇区可以写完了
}
};
FLASH_Lock();//上锁
}
#endif
三、EEPROM多字节读写操作时序
我们读取EEPROM数据的时候很简单,EEPROM根据我们所送的时序,直接就把数据送出来了,但是写入EEPROM数据却没有这么简单了。给EEPROM发送数据后,先保存在了EEPROM的缓存,EEPROM必须要把缓存中的数据搬移到“非易失”的区域,才能达到数据掉电不丢失的效果。而往非易失区域写入数据需要一定的时间,每种元件还不完全一样,例如ATMEL公司的AT24C02芯片的这个写入时间最高不超过5ms。在往非易失区域写的过程,EEPROM是不会再响应我们的访问的,不仅接收不到我们的数据,而且我们使用IIC标准的寻址模式去寻址,EEPROM都不会应答,就如同这个总线上没有这个器件一样。数据写入非易失区域完毕后,EEPROM再次恢复正常,可以正常读写数据了。
//从指定地址开始读出指定长度的数据
//ReadAddr:起始地址
//pBuffer:数据指针
//NumToWrite:半字(16位)数
void STMFLASH_Read(u32 ReadAddr,u16 *pBuffer,u16 NumToRead)
{
u16 i;
for(i=0;i<NumToRead;i++)
{
pBuffer[i]=STMFLASH_ReadHalfWord(ReadAddr);//读取2个字节.
ReadAddr+=2;//偏移2个字节.
}
}
//
//WriteAddr:起始地址
//WriteData:要写入的数据
void Test_Write(u32 WriteAddr,u16 WriteData)
{
STMFLASH_Write(WriteAddr,&WriteData,1);//写入一个字
}
//要写入到STM32 FLASH的字符串数组
const u8 TEXT_Buffer[]={"STM32F103 FLASH TEST"};
#define SIZE sizeof(TEXT_Buffer) //数组长度
#define FLASH_SAVE_ADDR 0X08070000 //设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000)
int main(void)
{
u8 key;
u16 i=0;
u8 datatemp[SIZE];
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
KEY_Init(); //初始化按键
LCD_Init(); //初始化LCD
POINT_COLOR=RED; //设置字体为红色
LCD_ShowString(30,50,200,16,16,"ELITE STM32");
LCD_ShowString(30,70,200,16,16,"FLASH EEPROM TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(30,110,200,16,16,"2015/1/18");
LCD_ShowString(30,130,200,16,16,"KEY1:Write KEY0:Read");
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES) //KEY1按下,写入STM32 FLASH
{
LCD_Fill(0,170,239,319,WHITE);//清除半屏
LCD_ShowString(30,170,200,16,16,"Start Write FLASH....");
STMFLASH_Write(FLASH_SAVE_ADDR,(u16*)TEXT_Buffer,SIZE);
LCD_ShowString(30,170,200,16,16,"FLASH Write Finished!");//提示传送完成
}
if(key==KEY0_PRES) //KEY0按下,读取字符串并显示
{
LCD_ShowString(30,170,200,16,16,"Start Read FLASH.... ");
delay_ms(20000);
STMFLASH_Read(FLASH_SAVE_ADDR,(u16*)datatemp,SIZE);
LCD_ShowString(30,170,200,16,16,"The Data Readed Is: ");//提示传送完成
LCD_ShowString(30,190,200,16,16,datatemp);//显示读到的字符串
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系统正在运行
i=0;
}
}
}
四、EEPROM的页写入时序
在向EEPROM连续写入多个字节的数据时,如果每写一个字节都要等待几个ms时间的话,整体上的写入效率就太低了。因此EEPROM的厂商就想了一个办法,把EEPROM分页管理。24C01和24C02这两个型号的芯片是8个字节一个页,而24C04、24C08和24C16芯片是16个字节一页。我们通常使用的EEPROM的型号是24C02,一共有256个字节,8个字节一页,那么就一共有32页。
分配好页之后,如果我们在同一个页内连续写入几个字节后,最后再发送停止位的时序。EEPROM检测到这个停止位后,就会一次性把这一页的数据写到非易失区域,就不需要像上述那样每写一个字节检测一次了,并且页写入的时间也不会超过5ms。如果我们写入的数据跨页了,那么写完了一页之后,我们要发送一个停止位,然后等待并且检测EEPROM的空闲模式,一直等到把上一页数据完全写到非易失区域后,再进行下一页的写入,这样就可以在很大程度上提高数据的写入效率。
今天的文章学习笔记:EEPROM 与 FLASH 存储器[通俗易懂]分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/87421.html