EEPROM——>AT24CXX(STM32实战)(1/2)

EEPROM

定义

       电可擦可写EEPROM(Electrically Erasable Programmable Read-Only Memory)芯片是一种特殊的存储芯片,其特点在于其数据可以在通电状态下被擦除和重新编程,而且数据在断电后不会丢失。

  • EEPROM通过应用电场效应的“浮栅”工作原理(这个很重要,不清楚的朋友可以上网查查!)来实现可编程性。在EEPROM中,晶体管会被充电,然后用于判断它们是否应该代表规定的值。
  • 如果需要修改某个单元中的值,可以通过对其施加较高的电压来排出存储器中存储的电荷状态。
  • EEPROM不仅可以重写,而且可以单独、随意和多次重写,这使得它比其他类型的ROM更具灵活性和可靠性。

总结 

  1. 可编程性:EEPROM允许修改存储在其中的数据。
  2. 可擦除性:EEPROM中的数据可以被清空,从而可以将存储器用于新的应用场景。
  3. 随机访问:EEPROM支持随机访问,可以直接访问要读写的地址,而无需像磁带或磁盘那样顺序读取。
  4. 低功耗:在待机模式时,EEPROM几乎没有任何能耗,但在活动模式下仍能正常工作。

AT24CXX

AT24CXX是一系列电可擦可写EEPROM芯片。

传输协议

这些芯片支持I2C总线数据传送协议,并具有较宽的工作电压范围、写保护功能以及长使用寿命等特点。以下是对AT24CXX的详细讲解:

基本特性

  1. 宽电压范围:AT24CXX系列芯片的工作电压范围为1.8V至5.5V,适用于不同MCU平台的设计要求。
  2. 写保护功能:通过WP(写保护)引脚,可以实现写保护功能。当WP为高电平时,芯片只能进行读操作,防止数据被意外改写。
  3. 长使用寿命:AT24CXX的擦写次数可达到100万次,数据保存时间长达100年。(所以你买的很值)

引脚与设备地址

  • WP:写保护引脚,用于控制写功能。
  • A0、A1、A2:地址引脚,用于设置设备地址,实现多个设备的总线连接。
  • SCL、SDA:分别代表I2C协议的时钟线和数据线。

A0 A1 A2

  • 通过A0、A1、A2引脚的不同组合,可以设置不同的设备地址,从而实现同一I2C总线上连接多个AT24CXX设备。例如,AT24C01/02可使用全部三个地址引脚,因此最多可连接8个设备;而AT24C32/64则使用更复杂的地址方案。

IIC

IIC协议已经是老生常谈了,所以还不会的宝子可以上网查查噢。

STM32代码实战

我们采用的是AT24C02,其实是因为最便宜。

但是AT24CXX同系列有什么区别,我来给你举例。

  1. AT24C01: 有128个地址空间(从0到127)的EEPROM,因为2^7 = 128,但地址通常是从0开始计数的,所以实际的最大地址是127。
  2. AT24C02: 有256个地址空间的EEPROM(从0到255)。
  3. AT24C04: 有512个地址空间的EEPROM(从0到511)。
  4. AT24C08: 个有1024个地址空间的EEPROM(从0到1023)。
  5. 以此类推,对于更大的EEPROM容量,宏定义也相应地增加。

但是我们在定义地址空间时一定要记住我们的地址是从0开始的!所以我们的地址空间有128个,在定义最大地址时表示为127。 

初始化

void C04_IIC_Init(void)
{					     
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_10|GPIO_Pin_11); 	
}

我们这里初始化串口其实就是初始化IIC协议所需要的SCL以及SDA,所以接下来的IIC高低电平的控制函数构建不要太简单。

SCL SDA控制函数

我们的IIC在工作时,{开始工作——停止工作——等待应答——接收/不接受应答}{发送字节+读取字节}所以我们根据这些会写出7个不同的函数这里我就不详细讲原理了。

为什么SDA需要在输入输出间切换

OLED在操作IIC中的SDA时往往不需要改变SDA的输入输出模式,因为OLED屏幕通常不会主动向主机发送数据,除非是为了响应特定的命令或查询。而对于我们的EEPROM需要读更需要写(微控制器向主机发送自己内存中的信息)因此SDA输入,SDA输出变换是必须存在的!!!!!!

因为在切换工作模式时需要频繁修改定义所以我们将其设置为一个宏放在.h文件中。

宏定义

#define C04_SDA_IN()  {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=(u32)8<<12;}  	//上拉/下拉输入模式 
#define C04_SDA_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=(u32)3<<12;}	//通用推挽输出模式

到这里很多人已经晕了,那这串代码表示什么呢?让我来给你讲讲吧。(以第一个输入模式为例)

  • CRH控制的是GPIO口的(High)高八位B8-B15。(一个GPIO最多到15位)
  • GPIOB->CRH&=0XFFFF0FFF:(F——>1111,0——>0000)这是一个位清除操作。它将GPIOB的CRH寄存器的高4位(对应于PB12到PB15)清零。这是通过设置与操作(AND)来完成的。我们这里是修改SDA的GPIOB_Pin_11所以我们将对应的(1111)修改为(0000)。可以解释为选取对应的引脚。
  • GPIOB->CRH|=(u32)8<<12:这是一个位设置操作。它将一个值(在这里是8,即二进制1000)左移12位,然后与CRH寄存器的当前值进行或操作(OR)。可以理解为更改引脚的工作模式。(自己查STM32手册,这里的1000是上拉|下拉输入模式,0011为推挽输出模式)
#define C04_IIC_SCL    PBout(10) //SCL
#define C04_IIC_SDA    PBout(11) //SDA	 

我们这里 通过IO口控制高低电平,所以我们还需要引入一个“sys.c|h”函数

void C04_IIC_Start(void)
{
	C04_SDA_OUT();     //sda线输出
	C04_IIC_SDA=1;	  	  
	C04_IIC_SCL=1;
	Delay_us(4);
 	C04_IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	Delay_us(4);
	C04_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	  
//产生IIC停止信号
void C04_IIC_Stop(void)
{
	C04_SDA_OUT();//sda线输出
	C04_IIC_SCL=0;
	C04_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	Delay_us(4);
	C04_IIC_SCL=1; 
	C04_IIC_SDA=1;//发送I2C总线结束信号
	Delay_us(4);							   	
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 C04_IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	C04_SDA_IN();      //SDA设置为输入  
	C04_IIC_SDA=1;Delay_us(1);	   
	C04_IIC_SCL=1;Delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			C04_IIC_Stop();
			return 1;
		}
	}
	C04_IIC_SCL=0;//时钟输出0 	   
	return 0;  
} 
//产生ACK应答
void C04_IIC_Ack(void)
{
	C04_IIC_SCL=0;
	C04_SDA_OUT();
	C04_IIC_SDA=0;
	Delay_us(2);
	C04_IIC_SCL=1;
	Delay_us(2);
	C04_IIC_SCL=0;
}
//不产生ACK应答		    
void C04_IIC_NAck(void)
{
	C04_IIC_SCL=0;
	C04_SDA_OUT();
	C04_IIC_SDA=1;
	Delay_us(2);
	C04_IIC_SCL=1;
	Delay_us(2);
	C04_IIC_SCL=0;
}					 				     

为什么IIC这么写,为什么要添加延时。请直接移步到我的以前文章。

 STM32——OLED外设库的讲解(1/2)-CSDN博客

读取字节

//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址  
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    																 
    C04_IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		C04_IIC_Send_Byte(0XA0);	   //发送写命令
		C04_IIC_Wait_Ack();
		C04_IIC_Send_Byte(ReadAddr>>8);//发送高地址
		C04_IIC_Wait_Ack();		 
	}else C04_IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据 	 

	C04_IIC_Wait_Ack(); 
    C04_IIC_Send_Byte(ReadAddr%256);   //发送低地址
	C04_IIC_Wait_Ack();	    
	C04_IIC_Start();  	 	   
	C04_IIC_Send_Byte(0XA1);           //进入接收模式			   
	C04_IIC_Wait_Ack();	 
    temp=C04_IIC_Read_Byte(0);		   
    C04_IIC_Stop();//产生一个停止条件	    
	return temp;
}

读取指的是主机从EEPROM上读取一个字节。

  • u16 ReadAddr: 表示要读取数据的地址。
  • temp:用于存储从EEPROM中读取的数据。

1.打开IIC。 

2.if(EE_TYPE>AT24C16)
EE_TYPE指的是我们使用的EEPROM类型,如果EEPROM的类型大于AT24C16,则执行大容量的EEPROM的读取操作;否则,执行小容量的EEPROM的读取操作。 

3.IIC发送一个字节

  • 对于大容量的EEPROM,需要发送两个地址字节(高地址和低地址)。
  • 对于小容量的EEPROM,地址被转换为与AT24C16兼容的格式发送(因为AT24C16只有16KB的容量,所以只需要一个地址字节)。

我来详细点讲把 !C04_IIC_Send_Byte 函数被用来通过I2C总线发送一个字节的数据。在读取AT24CXX系列EEPROM数据时,如果EEPROM的容量超过256字节(即需要超过一个字节的地址来寻址),则需要发送两个地址字节:一个高地址字节和一个低地址字节。如果EEPROM的容量较小,例如AT24C01(128字节)或AT24C02(256字节),则只需要一个地址字节。

4.            C04_IIC_Send_Byte(0XA1); //进入接收模式   

等待应答确认

               C04_IIC_Send_Byte(0XA0); //进入发送模式  

等待应答确认

5.C04_IIC_Send_Byte(ReadAddr>>8);发送地址的高字节。

由于我们可能正在使用一个大于2KB的EEPROM,我们需要两个字节的地址(一个高字节和一个低字节)。通过将我们所需要读取的内容ReadAddr右移8位,我们得到地址的高字节。

6.     else C04_IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));

对于小容量的EEPROM(例如,256字节或更小),我们通常只需要发送一个地址字节。这里的表达式0XA0+((ReadAddr/256)<<1)并不直接发送地址字节。由于ReadAddr是一个16位的地址,而我们的EEPROM只有256字节,所以ReadAddr/256总是等于0(对于任何有效的地址)。这意味着我们实际上只是在发送0XA00XA2(取决于位移操作的结果,但在这里它总是0)。

这个else部分应该简化为直接发送器件地址(对于读操作,通常是0xA1)和地址字节。

7.发送低地址位。

C04_IIC_Send_Byte(ReadAddr%256);   发送低地址  ,

  • 无论EEPROM的容量如何,我们都需要发送地址的低字节。通过ReadAddr%256,我们得到地址的低字节。

8.C04_IIC_Start(); C04_IIC_Send_Byte(0XA1); //进入接收模式 

   temp=C04_IIC_Read_Byte(0);

   C04_IIC_Stop(); 

发送读命令0XA1,读取一个字节的数据并存储在temp变量中。

发送字节

//在AT24CXX指定地址写入一个数据
//WriteAddr  :写入数据的目的地址    
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{				   	  	    																 
    C04_IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		C04_IIC_Send_Byte(0XA0);	    //发送写命令
		C04_IIC_Wait_Ack();
		C04_IIC_Send_Byte(WriteAddr>>8);//发送高地址
 	}else
	{
		C04_IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据 
	}	 
	C04_IIC_Wait_Ack();	   
    C04_IIC_Send_Byte(WriteAddr%256);   //发送低地址
	C04_IIC_Wait_Ack(); 	 										  		   
	C04_IIC_Send_Byte(DataToWrite);     //发送字节							   
	C04_IIC_Wait_Ack();  		    	   
    C04_IIC_Stop();//产生一个停止条件 
	Delay_ms(10);	 
}

 

 就是和读取字节换一下部分代码的位置,无非就是多一个延迟!这个延时加不加看自己单片机的运行情况!

总结一下吧:(我以最复杂的EEPROM情况来讲)先判断EEPROM大小,如果为大内存,那我们先选择EEPROM读取或者发送命令,完成动作后等待应答,应答成功后,发送或接受高八位,等待应答,再发送或接收低八位数据,再次等待应答,等待我们的微控制器或者主机发送或读取我们刚刚读取或发送的数据后,又一次开始的等待应答,最后停止EEPROM的IIC传输。

今天先学一半,不要贪杯哦!

明天接着来哈!咱们继续讲! 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值