学习笔记:EEPROM 与 FLASH 存储器

一、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。
 

2、EEPROM读数据流程

(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的空闲模式,一直等到把上一页数据完全写到非易失区域后,再进行下一页的写入,这样就可以在很大程度上提高数据的写入效率。

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值