STM32模拟IIC读写EEPROM

本人在最近的项目中,需要读写EEPROM里面的内容并分析。该EEPROM芯片为Microchip Technology公司的24LC02B系列。用STM32F103芯片做主机通过IIC接口去和它通信。

首先介绍芯片的基本特性,容量为 :256 x 8bit ,2Kbit共有256字节 ;地址和数据都是8bit;电源供电2.5V-5.5V;IIC支持最高速率400K;支持单字节写和按页写两种,写数据后最大需要延时5ms;支持连续读,单字节读和随机读取3种读方式。总的说来这颗IC很大众,操作简单。

主机STM32F1本来想用硬件IIC,找了例程,网上资料一大堆,也没成功,所以用的模拟IIC。波形虽然不太好看,但还是挺好用。

 

主机模拟IIC用的PB10和PB11,SCL时钟的周期为7us.代码如下:

#define sEE_I2C_CLK                      RCC_APB1Periph_I2C2
#define sEE_I2C_SCL_PIN                  GPIO_Pin_10                 /* PB.10 */
#define sEE_I2C_SCL_GPIO_PORT            GPIOB                       /* GPIOB */
#define sEE_I2C_SCL_GPIO_CLK             RCC_APB2Periph_GPIOB
#define sEE_I2C_SDA_PIN                  GPIO_Pin_11                  /* PB.11 */
#define sEE_I2C_SDA_GPIO_PORT            GPIOB                       /* GPIOB */
#define sEE_I2C_SDA_GPIO_CLK             RCC_APB2Periph_GPIOB


#define SDA_IN()  	{sEE_I2C_SCL_GPIO_PORT->CRH&=0XFFFF0FFF;sEE_I2C_SCL_GPIO_PORT->CRH|=(u32)8<<12;}
#define SDA_OUT() 	{sEE_I2C_SCL_GPIO_PORT->CRH&=0XFFFF0FFF;sEE_I2C_SCL_GPIO_PORT->CRH|=(u32)3<<12;} //IO操作函数	 
#define READ_SDA   	GPIO_ReadInputDataBit(sEE_I2C_SCL_GPIO_PORT,sEE_I2C_SDA_PIN)//输入SDA 
#define IIC_SDA_UP      GPIO_SetBits(sEE_I2C_SCL_GPIO_PORT,sEE_I2C_SDA_PIN)
#define IIC_SDA_DOWN    GPIO_ResetBits(sEE_I2C_SCL_GPIO_PORT,sEE_I2C_SDA_PIN)
#define IIC_SCL_UP      GPIO_SetBits(sEE_I2C_SCL_GPIO_PORT,sEE_I2C_SCL_PIN)
#define IIC_SCL_DOWN    GPIO_ResetBits(sEE_I2C_SCL_GPIO_PORT,sEE_I2C_SCL_PIN)

#define EEPROM_ADDR 	0xA0            //EEPROM的器件地址
#define PAGE_SIZE 	8		//按页写时,一页大小为8字节
#define ROM_SIZE        256		//整个ROM大小是256字节

#define I2C_BUF_LEN 256


uint8_t I2C_Rec[I2C_BUF_LEN];


static void delay_us(u32 t)
{
	u8 i = 5;

	while(t--)
	{
		i = 5;
		while(i--);
	}
}
static void delay_ms(u32 t)
{
	u16 i = 8000;

	while(t--)
	{
		i = 8000;
		while(i--);
	}
}


void IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(	sEE_I2C_SCL_GPIO_CLK, ENABLE );

	GPIO_InitStructure.GPIO_Pin = sEE_I2C_SCL_PIN|sEE_I2C_SDA_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   //推挽输出	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;//GPIO_Speed_50MHz;

	GPIO_Init(sEE_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);

	IIC_SCL_UP;
	IIC_SDA_UP;
}

//产生IIC起始信号
void IIC_Start(void)
{	
	SDA_OUT();     //sda线输出
	IIC_SDA_UP;
	IIC_SCL_UP;
	delay_us(4);
	IIC_SDA_DOWN;//START:when CLK is high,DATA change form high to low
	delay_us(4);
	IIC_SCL_DOWN; //钳住I2C总线,准备发送或接收数据 
}	
void IIC_Stop(void)
{	
	IIC_SCL_DOWN;
	delay_us(1);
	SDA_OUT();//sda线输出
	IIC_SDA_DOWN;//STOP:when CLK is high DATA change form low to high 
	delay_us(4);	
	IIC_SCL_UP;
	delay_us(2);
	IIC_SDA_UP;//发送I2C总线结束信号	
	delay_us(4);
}

//等待应答信号到来//返回值:1,接收应答失败//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{	
	u16 ucErrTime=0;

	IIC_SCL_DOWN;
	SDA_IN();	   //SDA设置为输入
	delay_us(4);
	IIC_SCL_UP;
	delay_us(4);		
	while(READ_SDA) 
	{		
	    ucErrTime++;		
	    if(ucErrTime>250)//180us		
	    {			
		IIC_Stop();
		return 1;
	    }
	}
	IIC_SCL_DOWN; //时钟输出0 
	SDA_OUT();
	return 0;
} 

//产生ACK应答
void IIC_Ack(void)
{	
	IIC_SCL_DOWN; 
	SDA_OUT();
	IIC_SDA_DOWN;	
	delay_us(2);
	IIC_SCL_UP;
	delay_us(2);	
	IIC_SCL_DOWN; 
}
//不产生ACK应答		    
void IIC_NAck(void)
{	
	IIC_SCL_DOWN; 	
	SDA_OUT();	
	IIC_SDA_UP;
	delay_us(2);	
	IIC_SCL_UP;
	delay_us(2);
	IIC_SCL_DOWN;
}		

void IIC_Send_Byte(u8 txd)
{  
	u8 t;   	
	//SDA_OUT(); //此处打开,会有毛刺       
	IIC_SCL_DOWN; //拉低时钟开始数据传输  
	SDA_OUT();
	delay_us(2);
	for(t=0;t<8;t++)   
	{                   
        if((txd&0x80)>>7)
        {
	    IIC_SDA_UP;
        }
        else
        {
	    IIC_SDA_DOWN;
        }
		txd<<=1; 	  		
		delay_us(1);   //对TEA5767这三个延时都是必须的	
		IIC_SCL_UP;		
		delay_us(4);//delay_us(2); 	
		IIC_SCL_DOWN;			
		delay_us(2);
	}	
} 	

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK 
u8 IIC_Read_Byte(unsigned char ack)
{	
#if 1
	unsigned char i,receive=0;	
	SDA_IN();//SDA设置为输入 
	for(i=0;i<8;i++ )	
	{   
		IIC_SCL_DOWN;  
		delay_us(2);		
		IIC_SCL_UP;
		receive<<=1;   
		if(READ_SDA)
		receive++;   		
		delay_us(1);  
	}					
	if (!ack)			
		IIC_NAck();//发送nACK	
	else			
		IIC_Ack(); //发送ACK  
	return receive;
#endif	
} 

void I2C_WriteByte(uint8_t word_addr, uint8_t data)
{	
	IIC_Start(); 
			
	IIC_Send_Byte(EEPROM_ADDR);	    //发器件地址	
	IIC_Wait_Ack(); 

	delay_us(6);//add ok

	IIC_Send_Byte(word_addr);   //发送低地址	

	IIC_Wait_Ack(); 
	delay_us(6);//add ok

	IIC_Send_Byte(data);	   //发送字节	

	IIC_Wait_Ack(); 

	IIC_Stop();//产生一个停止条件

	delay_ms(5);//写操作时,最后的延时不得小于3ms
}

void I2C_WriteBytePage(uint8_t word_addr,const uint8_t *pdata)
{	
	uint8_t i=0;
	uint8_t txdata;
	IIC_Start(); 
			
	IIC_Send_Byte(EEPROM_ADDR);	    //发器件地址	

	IIC_Wait_Ack(); 
	delay_us(6);//add ok
	IIC_Send_Byte(word_addr);   //发送寄存器地址
	IIC_Wait_Ack(); 
	delay_us(6);

	while(i < PAGE_SIZE)
	{
		txdata = pdata[i];
		IIC_Send_Byte(txdata);     //发送字节	
		IIC_Wait_Ack(); 
        i++;
		delay_us(4); 
	}

	IIC_Stop();//产生一个停止条件

	delay_ms(5);//写操作时,最后的延时不得小于3ms
}


void ReadLenByte(u8 *pbuf,u16 len)
{
	u16 t;

	IIC_Start(); 

	IIC_Send_Byte(EEPROM_ADDR);	   //发送写命令	
	IIC_Wait_Ack();
	delay_us(5);
	IIC_Send_Byte(0x0);    //发送寄存器地址
	IIC_Wait_Ack();	
	delay_us(5);

	IIC_Start();  	 	  
	IIC_Send_Byte(0XA1);           //进入接收模式	

	IIC_Wait_Ack();	        
	
	
	for(t=0;t<len-1;t++)
	{		
		*(pbuf)=IIC_Read_Byte(1);	    //读一个字节 
		pbuf++;
		//delay_us(200);
	}
	*(pbuf)=IIC_Read_Byte(0);	    //读一个字节 

	IIC_Stop(); 
	
}

这是引用网上的基础上,我自己稍作改动。大家可以参考。如果要使用我的代码要注意

这两个函数要根据自己pin脚去修改。

 

采集的波形如下

 

 

最后总结遇到的问题:

1.刚开始没有注意到写操作后延时,只给了200us,造成写完一笔数据后,IIC的读写操作都失败,后来给了10ms的延时就OK了;

2.网上找的代码不够严谨,很多毛刺,需要自己再调试;

3.STM32F1XX的硬件IIC比较复杂,我以前只用过STM32F0的,刚开始不知道,调了2天也没搞定硬件IIC通讯,建议对IIC或STM32F1不熟悉,谨慎使用其硬件IIC。

4.由于我的平台限制,STM32供电3.3V,EEPROM供电为5v。虽然读写也成功了,但是建议读者最好使二者电平匹配的。

5.IIC的通讯线要接上拉电阻,要不然没有输出。

 

  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32 I2CEEPROM跨页是指在使用STM32的I2C总线与EEPROM进行通信时,当需要入的数据跨越了EEPROM的页边界时的处理方式。EEPROM是一种可擦非易失性存储器,通常将数据按页(page)进行存储,每页包含固定数量的字节。 当需要向EEPROM中的一个页入数据时,可以使用I2C总线发送一组连续的入命令。但当需要跨页入数据时,就需要分为多个步骤来完成: 1. 发送入命令:首先,通过I2C总线发送一个入命令,指定要入的EEPROM地址。 2. 入数据至页边界:将将要入的数据按页边界切割,将数据的第一部分EEPROM的当前页。 3. 切换至下一页:如果需要入的数据跨越了页边界,就需要切换到下一页。通过I2C总线发送一个新的入命令,指定下一页的地址。 4. 入剩余数据:将剩余的数据入新的页中。 需要特别注意的是,在跨页入时,需要保持I2C总线的稳定。在切换页的过程中,可能会出现一些延迟。为了确保稳定性,可以在发送入命令之后,等待一段时间,以确保EEPROM已切换到新的页。 总结起来,STM32 I2CEEPROM跨页,需要将要入的数据按页边界切割,并在切换页时保持I2C总线的稳定性。 ### 回答2: STM32是一种微控制器,具有许多输入/输出接口(I/O接口),其中包括IIC(Inter-Integrated Circuit)接口。EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种常用的非易失性存储器。 在STM32中,使用IICEEPROM可以通过跨页的方式实现。跨页是一种技术,允许在一次传输中向EEPROM的多个页中连续入数据。具体步骤如下: 1. 配置STM32的IIC接口模块。首先,需要设置相关寄存器以启用IIC接口,并配置通信速率和其他参数。 2. 初始化EEPROM。在进行IIC之前,需要初始化EEPROM。这可能包括发送一些特定的命令和设置寄存器。 3. 选择入的页。要进行跨页,需要选择要入的起始页。可以使用特定的命令和地址选择页。在接收到页选择命令后,EEPROM将准备好接收数据。 4. 传输数据。将待入的数据传输到IIC接口,并将其发送到EEPROM。在跨页操作中,数据会自动从一个页传输到下一个页。可以通过循环迭代来传输更多数据。 5. 等待入完成。在数据传输完毕后,需要等待EEPROM完成入操作。通常,EEPROM会有一个内部标志位来指示入是否完成。可以通过查询此标志位来等待入操作的完成。 6. 关闭IIC接口。在完成操作后,需要关闭IIC接口,以便其他设备可以使用该接口。 请注意,具体的实现细节可能会因不同的STM32系列和EEPROM型号而有所不同。因此,建议参考相应的STM32和EEPROM芯片的数据手册,以获取更详细的信息和代码示例。 ### 回答3: IIC(Inter-Integrated Circuit)是一种串行通信协议,常被用来连接微控制器与外部设备。在STM32微控制器中,使用IIC总线可以实现对EEPROM(Electrically Erasable Programmable Read-Only Memory)的操作。 对于EEPROM的跨页操作,即在入数据时,需要跨越EEPROM的页边界进行入。通常,EEPROM内部的数据存储以页为单位分割,每一页包含多个字节的数据。当入数据时,如果需要跨越页边界,就需要对两个相邻页的数据进行处理。 在使用STM32的IICEEPROM进行跨页操作时,可以按照以下步骤进行: 1. 初始化IIC总线和相关的引脚设置,确保正确连接STM32和EEPROM。 2. 设置EEPROM地址,确定要入的页和具体的地址。 3. 将待入的数据按照页的边界进行分割,分为需要入的第一个页和第二个页的数据。 4. 先发送入第一个页数据的命令,同时发送第一个页数据的地址和数据。 5. 等待IIC总线的传输完成,并确认入第一个页数据是否成功。 6. 如果入第一个页数据成功,再发送入第二个页数据的命令,同时发送第二个页数据的地址和数据。 7. 等待IIC总线的传输完成,并确认入第二个页数据是否成功。 8. 在每一步操作完成后,需要根据EEPROM的ACK信号来判断是否入成功。 在进行EEPROM的跨页操作时,需要注意以下几点: - 需要保证跨页入的数据长度不超出一个页的容量。 - 入数据时需要正确设置地址,并按照页的边界进行分割。 - 在入数据后,需要通过确认ACK信号来验证入是否成功。 - 需要正确处理IIC总线的通信协议,包括起始位、地址位、数据位和停止位等。 总结来说,使用STM32的IIC总线EEPROM时,可以通过合适的设置和使用跨页操作来实现对EEPROM数据的

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值