目录
I2C总线
I2C总线介绍
I2C电路规范
I2C时序结构
起始与终止
代码理解
发送字节
代码理解
接收字节
代码理解
数据应答
代码理解
I2C的数据据帧
发送数据帧
接收数据帧
发送接收数据帧
AT24C02芯片
AT24C02介绍
引脚及应用电路
内部结构图
AT24C02数据帧
字节写
代码理解
随机读
代码理解
注意:
仿真案例
电路图
keil文件
I2C总线
I2C总线介绍
- I2C(inter IC BUS)是由Philips公司开发的一种通用数据总线
- 两根通信线:SCL(Serial Clock)SDK(Serial Data)
- 同步(有同步时钟线)、半双工(一根串行数据线),带数据应答
- 通用的I2C总线可以使各种设备的通信标准统一,对于厂家来说,使用成熟的方案可以缩短芯片的设计周期、提高稳定性,对于应用者来说使用通用的通信协议可以避免学习各种各样的自定义协议,降低了学习和应用的难度
I2C电路规范
- 所有的I2C设备的SCL连在一起,SDA连在一起
- 设备的SCL和SDA均要配置成开漏输出模式
- SCL和SDA各添加一个上拉电阻,阻值一般为4.7K欧左右
- 开漏输出和上拉电阻共同作用实现了“线与的功能”,同时此设计主要是为了解决多机通信互相干扰的问题
理解:
- CPU让引脚想发0的时候,就把他拉到低电平,CPU让引脚想发1的时候,就让NOMS关闭,那么引脚就会被外部的电源自动拉到高电平。
- 若所有设备都开漏(都不接地)那么就由两个电阻承担上拉的能力。
I2C时序结构
起始与终止
- 起始条件:SCL高电平期间,SDA从高电平切换到低电平。
- 终止条件:SCL高电平期间,SDA从低电平切换到高电平。
代码理解
//I2C的start阶段
void I2C_Start(){
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}
//I2C的stop阶段
void I2C_Stop(){
SDA=0;
SCL=1;
SDA=1;
}
发送字节
理解:SCL低电平期间,主机将数据位依次放到SDA线上(高位在前)然后拉高SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。
注意:时序中SDA为数据,所以才可以看到电平的两种状态(电平可高可低)
代码理解
//I2C的发送一个字节s数据(先发高位)
void I2C_SendByte(unsigned char byte){
unsigned char i=0;
SCL=0;
for(i=0;i<8;i++){
SDA=byte&(0x80>>i);
SCL=1;
SCL=0;
}
}
接收字节
理解:SCL低电平期间,从机将数据位依次放到SDA线上(高位在前)然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL在高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接受之前,需要释放SDA)
注意:紫色的线代表是从机控制的。
代码理解
//I2C的接收数据(先接收高位)
unsigned char I2C_ReceiveByte(){
unsigned char byte=0,i=0;
SCL=0;
SDA=1; //主机释放SDA总线
for(i=0;i<8;i++){
SCL=1;
if(SDA==1){
byte=byte|(0x80>>i);
}
SCL=0;
}
return byte;
}
数据应答
- 发送应答:主机在接收完一个字节后,其将在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
- 接收应答:主机在发送完一个字节之后,其在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前需要释放SDA)
代码理解
//I2C发送应答
void I2C_SendAck(unsigned char ackBit){
SDA=ackBit;
SCL=1; //写入数据时拉高SCL
SCL=0;
}
//I2C接收应答
unsigned char I2C_ReceiveAck(){
unsigned char ackBit=0;
SDA=1; //主机释放SDA总线
SCL=1; //scl高电平期间就可以读取数据位了
ackBit=SDA;
SCL=0;
return ackBit;
}
I2C的数据据帧
发送数据帧
前言:发送数据帧描述了要向谁发送什么。
理解:首先发送start起始,然后发送从机地址和读写位(确定是写)之后是从机的接收应答,再者就是主机发送的数据直到终止位。
注意:从机地址为前7位中,前4位是固定的,其他三位可配置,配置方式取决于AT24C02的对应三个引脚的接法;R/W中写数据该位为0,读数据为1。
接收数据帧
前言:接收数据帧描述了向谁收什么。
理解:首先发送start起始,然后发送从机地址和读写位(确定是读)之后是从机的接收应答,从机知道是读后就向主机发送数据了(主机释放SDA),然后就是主机接收数据后发送应答,直到终止位
发送接收数据帧
前言:发送接收数据帧描述了向谁收指定的什么。
理解:首先主机发送起始位,然后发送从机地址和读写位(确定是写)之后是从机的接收应答,接收应答后主机发送指定数据并接收应答,发送完毕后,主机发送起始位开始读,直到最后的终止位
AT24C02芯片
AT24C02介绍
- AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息
- 存储介质:E2PROM
- 通讯接口:I2C总线
- 容量:256字节
引脚及应用电路
内部结构图
理解:右边的最大模块就是我们的E2PROM,就是上面介绍的存储网格,左边跟个地址译码器(对应的线是地址线)下面就是数据的输入输出端(serial mux串行数据选择端数据通过Y译码器将数据一位一位输出出去)而E2PROM上面模块就是数据擦除模块;DATA WORD ADDR/COUNTER(数据字地址计数器)用来设置具体地址的,里面有存储地址的寄存器,我们每写入或读出一个数据,该寄存器会自动加1;左上三个分别为开始结束逻辑,器件地址比较器,串行控制逻辑。
AT24C02数据帧
字节写
前言:在word address处写入数据data。
代码理解
#define AT24C02_ADDRESS 0xA0
//字节写
void AT24C02_WriteByte(unsigned char wordAddress,unsigned char dat){
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(wordAddress);
I2C_ReceiveAck();
I2C_SendByte(dat);
I2C_ReceiveAck();
I2C_Stop();
}
随机读
前言:读出在word address处的数据data
代码理解
#define AT24C02_ADDRESS 0xA0
//随机读
unsigned char AT24C02_ReadByte(unsigned char wordAddress){
unsigned char dat;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(wordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
dat=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return dat;
}
注意:
- AT24C02的slave address中前4位固定为1010,可配置地址在开发板上为000,所以slave address+w的地址为0xA0;slave address+r地址为0xA1。
- word address为要发送或读取的芯片内部地址。
仿真案例
需求:将236存储在AT24C02中地址1处,并读出来。
电路图
注意:当在某个地址写入数据后,掉电以后再次开启单片机还可以读出该地址的数据(掉电以后数据不会丢失)
keil文件
#include "reg51.h"
sbit RS=P3^0;
sbit RW=P3^1;
sbit E=P3^2;
void delay(unsigned int n){
unsigned int i=0,j=0;
for(i=0;i<n;i++){
for(j=0;j<120;j++);
}
}
//写指令
void writecom(unsigned char com){
//写指令
RS=0;
RW=0;
E=0;
P2=com;
delay(5);
E=1;
E=0;
}
//写数据
void writedat(unsigned char dat){
//写指令
RS=1;
RW=0;
E=0;
P2=dat;
delay(5);
E=1;
E=0;
}
//初始化液晶屏
void initlcd(){
writecom(0x38);
writecom(0x0c);
writecom(0x06);
writecom(0x01);
}
int LCD_Pow(int X,int Y){
unsigned char i;
int Result=1;
for(i=0;i<Y;i++){
Result*=X;
}
return Result;
}
//展示数字
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){
unsigned char i;
if(Line==1){
writecom(0x80|(Column-1));
}else{
writecom(0x80|(Column-1)+0x40);
}
for(i=Length;i>0;i--){
writedat('0'+Number/LCD_Pow(10,i-1)%10);
}
}
//I2C部分
sbit SCL=P3^3;
sbit SDA=P3^4;
//I2C的start阶段
void I2C_Start(){
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}
//I2C的stop阶段
void I2C_Stop(){
SDA=0;
SCL=1;
SDA=1;
}
//I2C的发送一个字节s数据(先发高位)
void I2C_SendByte(unsigned char byte){
unsigned char i=0;
SCL=0;
for(i=0;i<8;i++){
SDA=byte&(0x80>>i);
SCL=1;
SCL=0;
}
}
//I2C的接收数据(先接收高位)
unsigned char I2C_ReceiveByte(){
unsigned char byte=0,i=0;
SCL=0;
SDA=1; //主机释放SDA总线
for(i=0;i<8;i++){
SCL=1;
if(SDA==1){
byte=byte|(0x80>>i);
}
SCL=0;
}
return byte;
}
//I2C发送应答
void I2C_SendAck(unsigned char ackBit){
SDA=ackBit;
SCL=1;
SCL=0;
}
//I2C接收应答
unsigned char I2C_ReceiveAck(){
unsigned char ackBit=0;
SDA=1; //主机释放SDA总线
SCL=1; //scl高电平期间就可以读取数据位了
ackBit=SDA;
SCL=0;
return ackBit;
}
//AT24C02部分
#define AT24C02_ADDRESS 0xA0
//字节写
void AT24C02_WriteByte(unsigned char wordAddress,unsigned char dat){
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(wordAddress);
I2C_ReceiveAck();
I2C_SendByte(dat);
I2C_ReceiveAck();
I2C_Stop();
}
//随机读
unsigned char AT24C02_ReadByte(unsigned char wordAddress){
unsigned char dat;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(wordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
dat=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return dat;
}
unsigned char redat=0;
void main(){
initlcd();
AT24C02_WriteByte(1,236);
delay(5); //每次写完后需要延时5ms
redat=AT24C02_ReadByte(1);
LCD_ShowNum(2,1,redat,3);
while(1);
}