因为论坛里看到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=m0usif((data&0x80)==0x80){//高位优先发送SDA_H; //1}else{SDA_L;}I2C_delay(1);//tSU.DAT=m100nsSCL_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=0SCL_L;//出去前先把SCL拉低总没错return SUCCESS;}
}
所以 BYTE WRITE的程序就简单了
/******************************************
函数名:EE_ByteWrite
描 述 :写入一个字节到存储器里
输 入 :deviceAddr:器件地址,3位,0-7wordAddr :数据字地址data :要写入的数据
输 出 :无
返回值:是否成功写入
*@nate:tWR=M5ms,所以两次写操作之间,要间隔5ms以上
*******************************************/
ErrorStatus EE_ByteWrite(u8 deviceAddr,u8 wordAddr,u8 data)
{u8 deviceaddr;deviceaddr=0xa0+(deviceAddr<<1)+EEPROM_WRITE;START(); //STARTSendByte(deviceaddr);//DEVICE ADDRESSSendByte(wordAddr); //WORD ADDRESSSendByte(data); //DATASTOP(); //STOPI2C_delay(5000); //tWR=M5ms return SUCCESS;
}
PAGE WRITE
PAGE WRITE 时序如下
程序:
/******************************************
函数名:EE_PageWrite
描 述 :写入一页的数据(一页16个字节)
输 入 :deviceAddr:器件地址,3位,0-7wordAddr :数据字地址*data :要写入的数据首地址cnt :要写入的数据字节数
输 出 :无
返回值:无
*@nate:The address “roll over” during write is from the lastbyte 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-7wordAddr :数据字地址
输 出 :无
返回值:无
*******************************************/
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
Q:1K的存储器能存多少东西,怎么算的?
A:首先:1k= 2^10 bit
芯片手册上这样写的:
The AT24C01A provides 1024 bits of serial electrically erasable and rogrammable read-only memory (EEPROM) organized as 128 words of 8 bits each.
AT24C01A, 1K SERIAL EEPROM: Internally organized with 16 pages of 8 bytes each,the 1K requires a 7-bit data word address for random word addressing.
计算过程:
16页8字节
16= 2^4, 8= 2^3, 1 byte= 2^3 bit
16页8字节 = 2^7字节 = 2^10 bit
所以芯片上的1K是1k个bit,1K能存放128个字节
Q:page write先写的是低字节还是高字节,Sequential Read先读的是低字节还是高字节?
A:都是先对低字节的数据进行操作的