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
    评论
STM32F103系列微控制器内置了SPI(串行外设接口)模块,可以用于与EEPROM(电可擦除可编程只读存储器)进行读写操作。以下是一个简单的例子来说明如何使用STM32F103的SPI模块读写EEPROM。 首先,我们需要配置STM32F103的SPI模块。在SPI模块的寄存器中,我们需要设置一些参数,如SPI时钟的分频系数、数据位长度、数据传输模式等。具体配置要根据EEPROM的规格来确定。然后,使能SPI模块并设置好CS引脚和其他相关引脚。 接下来,在主程序中,我们可以使用SPI发送一些特定的指令给EEPROM,如读指令,指令等。然后通过SPI发送具体的数据到EEPROM,完成读写操作。 以下是一个示例代码,用于演示如何使用STM32F103的SPI模块读写EEPROM: #include "stm32f10x.h" void SPI_Config(void) { // 配置SPI的时钟、数据位长度、数据传输模式等参数 // 设置CS引脚和其他相关引脚 } void EEPROM_Read(uint16_t address, uint8_t *data, uint16_t length) { // 发送读指令给EEPROM // 发送数据到EEPROM // 接收EEPROM返回的数据 // 将数据存储到指定的缓冲区中 } void EEPROM_Write(uint16_t address, uint8_t *data, uint16_t length) { // 发送指令给EEPROM // 发送数据到EEPROM // 等待操作完成 } int main(void) { uint8_t data[16]; SPI_Config(); // 配置SPI EEPROM_Read(0x10, data, sizeof(data)); // 读取EEPROM数据 // 对读取到的数据进行处理 EEPROM_Write(0x20, data, sizeof(data)); // EEPROM数据 while (1) { // 主程序持续执行的代码 } } 需要注意的是,具体读写EEPROM的指令和数据传输协议要根据你使用的EEPROM型号来确定。以上代码仅提供一个基本的框架,并不是完整的、可直接使用的代码。你需要根据实际情况进行相应的修改和补充。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值