IIC通信协议
IIC是同步半双工通信,一个数据线SDA和一个时钟SCL线,可以接受和发送数据。在CPU与被控IC之间、IC与IC之间进行双向传送。
空闲状态
IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。
起始信号
当SCL为高期间,SDA由高到低的跳转
代码实现为:
//产生IIC起始信号
void IIC_Start(void)
{SDA_OUT(); //sda线输出模式IIC_SDA=1; IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
停止信号
当SCL为高期间,SDA由低到高的跳转
代码实现:
//产生IIC停止信号
void IIC_Stop(void)
{SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1; delay_us(4); IIC_SDA=1;//发送I2C总线结束信号
}
应答信号
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位,表示接收器已经成功接收到了该字节。应答信号为高电平时,规定为非应答位,一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是,接收器第9个时钟脉冲之前的低电平将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电压。
代码实现为:
//产生ACK应答
void IIC_Ack(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}//不产生ACK应答
void IIC_NAck(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{u8 ucErrTime=0;SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0 return 0;
}
数据的有效性
IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平才允许变化。即数据在SCL的上升沿到来之前就需要准备好,并且在下降沿到来之前必须保持稳定。
数据的传送
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(同步控制),即在SCL串行时钟的配合下,在SDA上逐位串行传送每一位数据,数据位的传输是边沿触发。
发送一字节代码实现为:
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{ u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t<8;t++){ IIC_SDA=(txd&0x80)>>7;txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的IIC_SCL=1;delay_us(2); IIC_SCL=0; delay_us(2);}
}
数据传输之前IIC_SCL必须等于0,即时信号为低电平,然后准备一位数据,准备好之后,IIC_SCL=1,即时钟信号为高电平,数据就传输过去了
对于下面的代码:
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
无符号类型左移丢弃最高位,低位补0,无符号类型右移丢弃最低位,低位补0,txd&0x80让txd第8位数据不变,其他为变成0(txd的值不受影响),左移7位,将最高位的数据赋值给IIC_SDA,当IIC_SCL=1时,第八位数据就传输过去了,txd<<=1让第7位数据变成第8位数据,下次循环就传输原来第7位数据了。循环8次将一字节数据传输完毕。
读取数据代码实现:
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){IIC_SCL=0; delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++; delay_us(1); } if (!ack)IIC_NAck();//发送nACKelseIIC_Ack(); //发送ACK return receive;
}
对于下面的代码:
receive<<=1;if(READ_SDA)receive++;
READ_SDA是获取SDA线上的电压,高电压说明说明传送过来的数据是1,receive++,下一次的时候, receive<<=1,receive先右移一位,然后接受数据,这样循环8次,就能保证接受的数据在原来的位置上了。
与EEPROM通信
硬件连接
EEPROM是一种掉电后数据不丢失的存储芯片,可以在电脑上或专用设备上擦除已有信息,重新编程。
24C02的总容量是256个字节,接口是IIC
模式选择
硬件连接中,A2=A1=A0=0(接地),所以,我们读数据时,只需写入0xA1,表示读模式,写数据时,只需写入0xA0,表示写模式
写时序
下面是写入一个字节数据的过程
- 一个IIC_START信号
- 写入模式,写入0xA0
- 等待一个ACK
- 发送写入数据的地址(0~255)
- 等待一个ACK
- 发送一个字节
- 等待一个ACK
- 一个STOP信号
具体代码如下
//在AT24CXX指定地址写入一个数据
//WriteAddr :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{ IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //发送写命令IIC_Wait_Ack();IIC_Send_Byte(WriteAddr>>8);//发送高地址 }else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据 IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr%256); //发送低地址IIC_Wait_Ack(); IIC_Send_Byte(DataToWrite); //发送字节 IIC_Wait_Ack(); IIC_Stop();//产生一个停止条件 delay_ms(10);
}
读数据
下面是读一个字节数据的过程
- 产生一个START信号
- 写入0xA0,表示是写模式
- 等待一个ACK
- 写入读数据的地址
- 等待一个ACK
- 产生一个START信号
- 写入0xA1,表示是读模式
- 等待一个ACK
- 读取数据
实现代码如下:
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值 :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{ u8 temp=0; IIC_Start(); if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0); //发送写命令IIC_Wait_Ack();IIC_Send_Byte(ReadAddr>>8);//发送高地址 }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr%256); //发送低地址IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(0XA1); //进入接收模式 IIC_Wait_Ack(); temp=IIC_Read_Byte(0); IIC_Stop();//产生一个停止条件 return temp;
}