EEPROM 之 AT24C16 - 备忘录

因为论坛里看到STM的I2C有点小bug,所以这里采用的是模拟I2C时序

【注】m0.6us表示的是这一段时间最小不能小于为0.6us,M0.6us表示的是这一段时间最大为0.6us

 对AT24C16的操作有读和写,读又分为CURRENT ADDRESS READ、RANDOM READ、SEQUENTIAL READ
,写又分为BYTE WRITE、PAGE WRITE。

WRITE

先研究写操作

BYTE WRITE

先研究写操作中的 Byte Write,它的时序如下

下面分步实现 BYTE WRITE。

首先是 START 和 STOP,具体时序如下

其中的时间要求如下(下图是START为例)

程序实现:

/******************************************
函数名:START
描 述 :产生起始条件,在SCL高电平期间,SDA从高到低表示通信开始
*******************************************/
static void START(void)
{
	SCL_H;
	SDA_H;	        /* 为产生 START 做铺垫 */
	I2C_delay(1);   /* tSU.STA=m0.6us */
	SDA_L;          /* SDA线从高到低,START */
	I2C_delay(1);   /* tHD.STA=m0.6us */
	SCL_L;
}

/******************************************
函数名:STOP
描 述 :产生结束命令
*******************************************/
static void STOP(void)
{
	SCL_L;
	SDA_L;          /* 为产生 STOP 做铺垫 */
	I2C_delay(1);
	SCL_H;
	I2C_delay(1);   
	SDA_H;          /* SDA线从低到高,STOP */
	I2C_delay(1);
	SCL_H;
}

接下来就是发送 DEVICE ADDRESS,因为DEVICE ADDRESS、WORD ADDRESS以及DATA的发送都是一样的操作,所以就写了一个统一的函数——SendByte,其操作具体的一个位时序如下

程序实现如下

/******************************************
函数名:SendByte
描 述 :发送一字节数据,数据位从高位到低位顺序发送
输 入 :-sendbyte:要发送的字节
*@nate:
在进入SendByte函数的时候,会先把SCL拉低
在退出SendByte函数的时候,也会把SCL拉低
*******************************************/
static ErrorStatus SendByte(u8 sendbyte) 
{
	u8 i,data;

	data=sendbyte;
	for(i=0;i<8;i++){
		SCL_L;
		I2C_delay(1); //tHD.DAT=m0us
		if((data&0x80)==0x80){//高位优先发送
			SDA_H;            //1
		}
		else{
			SDA_L;
		}
		I2C_delay(1);//tSU.DAT=m100ns

		SCL_H;
		I2C_delay(1);
		
		data=data<<1;
	}
	
/** 第9个周期,读取ACK **/
	SCL_L;
	SDA_H;//先把内部的输出置高,后读取
	I2C_delay(1); 
	
	SCL_H;//SCL=1期间是DATA STABLE,也就是数据有效期
	I2C_delay(1);
	if(READ_SDA==1){
		SCL_L;//出去前先把SCL拉低总没错
		return ERROR;
	}
	else {//ACK=0
		SCL_L;//出去前先把SCL拉低总没错
		return SUCCESS;
	}
}

所以 BYTE WRITE的程序就简单了

/******************************************
函数名:EE_ByteWrite
描 述 :写入一个字节到存储器里
输 入 :deviceAddr:器件地址,3位,0-7
        wordAddr  :数据字地址
        data      :要写入的数据
输 出 :无
返回值:是否成功写入
*@nate:tWR=M5ms,所以两次写操作之间,要间隔5ms以上
*******************************************/
ErrorStatus EE_ByteWrite(u8 deviceAddr,u8 wordAddr,u8 data)
{
	u8 deviceaddr;
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;
	
	START();             //START
	SendByte(deviceaddr);//DEVICE ADDRESS
	SendByte(wordAddr);  //WORD ADDRESS
	SendByte(data);      //DATA
	STOP();              //STOP
	
	I2C_delay(5000);     //tWR=M5ms 
	
	return SUCCESS;
}

PAGE WRITE

PAGE WRITE 时序如下

程序:

/******************************************
函数名:EE_PageWrite
描 述 :写入一页的数据(一页16个字节)
输 入 :deviceAddr:器件地址,3位,0-7
        wordAddr  :数据字地址
        *data     :要写入的数据首地址
        cnt       :要写入的数据字节数
输 出 :无
返回值:无
*@nate:The address “roll over” during write is from the last
 byte of the current page to the first byte ofthe same page.
*******************************************/
ErrorStatus EE_PageWrite(u8 deviceAddr,u8 wordAddr,u8 *data,u8 cnt)
{
	u8 deviceaddr,i=0;
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;
	
	START();
	SendByte(deviceaddr);
	SendByte(wordAddr);
	for(i=0;i<cnt;i++){
		SendByte(data[i]);
	}
	STOP();
		
	I2C_delay(5000);     //tWR=M5ms 
	
	return SUCCESS;
}

是不是有点奇怪BYTE WRITE 和 PAGE WRITE 后的“tWR=M5ms”?

Note: 1. The write cycle time tWR is the time from a valid stop condition of a write sequence to the end of the internal clear/write cycle.

READ

再研究读操作

/******************************************
函数名:EE_CurrentAddrRead
描 述 :产生起始条件,在SCL高电平期间,SDA从高到低表示通信开始
输 入 :deviceAddr:器件地址,3位,0-7
        wordAddr  :数据字地址
输 出 :无
返回值:无
*******************************************/
u8 EE_CurrentAddrRead(u8 deviceAddr)
{
	u8 deviceaddr,data;
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_READ;
	
	START();
	SendByte(deviceaddr);
	data=ReadByte(DISABLE);
	STOP();
	
	return data;
}

/******************************************
函数名:EE_RandomRead
描 述 :产生起始条件,在SCL高电平期间,SDA从高到低表示通信开始
输 入 :deviceAddr:器件地址,3位,0-7

输 出 :无
返回值:无
*******************************************/
u8 EE_RandomRead(u8 deviceAddr,u8 wordAddr)
{
	u8 deviceaddr,data;
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;
	
	START();
	SendByte(deviceaddr);
	SendByte(wordAddr);
	
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_READ;

	START();
	SendByte(deviceaddr);
	data=ReadByte(DISABLE);
	STOP();
	
	return data;
}

/******************************************
函数名:EE_SequentialRead
描 述 :顺序读取(一页16个字节)
输 入 :deviceAddr:器件地址,3位,0-7

输 出 :无
返回值:无
*@nate:The address “roll over” during read is from 
		the last byte of the last memory page to 
		the first byte of the first page
*******************************************/
u8 EE_SequentialRead(u8 deviceAddr,u8 wordAddr,u8* dataAddr,u8 cnt)
{
	u8 deviceaddr,i;
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;
	
	START();
	SendByte(deviceaddr);
	SendByte(wordAddr);
	
	deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_READ;

	START();
	SendByte(deviceaddr);
	for(i=0;i<cnt-1;i++){
		dataAddr[i]=ReadByte(ENABLE);
	}
	dataAddr[i]=ReadByte(DISABLE);
	
	STOP();
	
	return 0;
}

源代码:http://www.openedv.com/forum.php?mod=viewthread&tid=288334&page=1&extra=#pid931498

Q:STM32的引脚怎么才能既输入又输出呢,另外从SDA引脚读取数据时,需不需要先输出高电平再读取数据

A:STM32的引脚怎么才能即输入又输出呢

https://blog.csdn.net/qq_35629563/article/details/87710101

猜你喜欢

转载自blog.csdn.net/qq_35629563/article/details/87706317