IIC
IIC:集成电路总线(Inter-Integrated Circuit)
快速:400kbit/s 高速
:3.4Mbit/s
速度由 SCL 决定,上升沿斜率受上拉电阻和等效电容影响。
物理层
两线式串行总线,可发送和接收数据。
数据线
:SDA
时钟线
:SCL
同步(带时钟)串行 半双工
(发送或接收)通信方式 。
IIC 的 GPIO 为开漏模式,支持线与功能,开漏模式无法输出高电平,所以需要外部上拉两条线上接有上拉电阻,保证空闲状态处于稳定的高电平
。
可以连接多个设备(保证设备地址不同),支持多主机多从机连接模式,上图可认为是一个主机
(启动数据传输,并产生时钟信号的设备),多个从机
(被主机寻址的数据)。
多主机则采用仲裁模式。
协议层
1、空闲状态
即:两条信号线同时处于高电平。两个线都接有上拉电阻,保证其空闲状态处于稳定高电平
。
2、起始信号(START)
SCL时钟线为高时,SDA数据线由高到低的跳变。
3、终止信号(STOP)
SCL时钟线为高时,SDA数据线由低到高的跳变。
4、数据的有效性
SCL高电平时,SDA传输的数据稳定有效。想变化(传输数据时)需要在SCL低电平时变化(数据在SCL的上升沿到来之前需要准备好,并在下降沿到来之前保持稳定)。
5、应答信号ACK
主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答。
应答信号为低电平
时为有效应答位(从机的SDA
向主机发送低电平)。
当发送高电平时,发送方会产生上述的停止信号来结束数据的发送。
SDA是一根线,线上被所有主机和从机占着,不是一个主机一直霸占着线,所以从机可以发信号。
接收器在SCL第9个时钟脉冲之前的低电平期间将SDA线拉低,确保在该时钟的高电平期间从机SDA为稳定的低电平。
图解
:SCL高电平期间,主机SDA从高到低跳变,为起始信号S
准备开始传输数据;SCL高电平,传输数据稳定,低电平期间发送变化(传输其他值)以此串行传输完一个字节后;从机ACK可以在SCL高电平
期间,发出ACK应答,表示可继续传输数据。当发送高电平(非ACK应答)时,发送方会产生的停止信号来结束数据的发送。
6、数据传输
在SCL串行时钟下,SDA上逐位串行传输每一位的数据。数据位的传输时边沿触发。
总线寻址方式
按照从机地址位数可以分为7bits或10bits
D1-D7地址位;D0控制数据方向位,0:向从机写数据,1:向从机读数据
当主机发送7位地址到数据线上后,从机将会与其进行地址配对。数据传输方向则被D0位控制。7位从机地址可由4位固定地址和3位可编程地址组成,那么因此可得到,一共可挂载寻址8个从机(3位的二进制为8)。理论上可挂载2^8=128个设备(地址0x00不用),所以是127个设备,但是由于驱动有限,不可能挂那么多。
*
数据传输(详解)
IIC在SDA线上传输的是广义的数据(包括地址数据和数据信号),起始信号后,必须发送地址数据。
1
、主机向从机发送
数据,数据传送方向在整个传送过程中不变
解释:灰色块为主机产生的信号,首先主机产生起始信号S,然后第一个数据发送从机地址,因为是发送数据,最低控制位为0;红色块为SDA(从机)发送ACK应答,则表示可继续传输数据,当主机接收到非ACK时或者即便从机发送ACK时,数已经发完,那么最后主机产生终止信号P结束传输。
2
、主机(在第一个字节即:地址后)从从机中读取数据
解释:主机产生起始信号S,第一个数据发送从机地址,最低控制位为1,表示读取数据。从机发送ACK应答,然后从机通过SDA线(SDA线是共用的)发送数据给主机,此时主机产生ACK应答,从机则继续发送数据,当主机产生非ACK应答时,主机再发送停止信号P结束传输。
3
、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读写方向位正好相反
解释:主机产生起始信号S开始通信,(默认为7位的情况)第一个字节(从机地址+0)主机向从机发送地址,从机产生ACK应答,主机发送数据,那么此时从机发送ACK应答,可以继续进行传输数据。
此时主机需要读取从机数据时,则起始信号和从机地址都被重复产生一次后,从机产生ACK应答,然后向主机发送数据,主机接收到数据后,发送非ACK,表示不需要再接收数据,发送停止信号S。
下面以正点原子STM32F407与EEPROM(24C02)进行IIC通信为例,讲解部分代码
//产生IIC起始信号 SCL时钟线为高时,SDA数据线由高到低的跳变
void IIC_Start(void)
{
SDA_OUT(); //SDA线输出 即主机输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0; //1~0跳变 起始信号
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号 SCL时钟线为高时,SDA数据线由低到高的跳变
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//0~1跳变 发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值: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) //READ_SDA这个的值是由从机,即24c02发送信号过来 ,若为1则不应答,若为0则应答 这个是实际的应答
{
ucErrTime++;
if(ucErrTime>250) //超时响应 则终止
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答 程序模拟的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;
}
//IIC发送一个字节
//返回从机有无应答 1,有应答 0,无应答
void IIC_Send_Byte(u8 txd) //txd发送的数据
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++) //以此从最高位读取数据到SDA线上
{
IIC_SDA=(txd&0x80)>>7; //以此取最高位 右移7位取出送入数据线
txd<<=1; //以此左移一位
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读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++; //读取从机SDA上的数据,该位为1则++, 依次从最低位开始获取
delay_us(1);
}
if (!ack)
IIC_NAck();//发送NACK
else
IIC_Ack(); //发送ACK
return receive;
}
//在AT24CXX指定地址写入一个数据 注意地址需要发两次
//第一次是片外寻址,第二次是片内地址
//WriteAddr :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{
IIC_Start(); //开始信号
IIC_Send_Byte(0XA0+((WriteAddr/256)<<1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack(); //等待ACK应答
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
delay_ms(10);
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值 :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(0XA0+((ReadAddr/256)<<1)); //发送器件地址0XA0,写数据
//写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。
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); //0表示不需要ACK应答,读完数据就完事儿了
IIC_Stop();//产生一个停止条件
return temp;
}
主函数 通过按键对AT24C02芯片利用IIC通信进行写或者读操作
while(1)
{
key=KEY_Scan(0);
if(key==KEY1_PRES)//KEY1按下,写入24C02
{
LCD_Fill(0,170,239,319,WHITE);//清除半屏
LCD_ShowString(30,170,200,16,16,"Start Write 24C02....");
AT24CXX_Write(0,(u8*)TEXT_Buffer,SIZE);
LCD_ShowString(30,170,200,16,16,"24C02 Write Finished!");//提示传送完成
}
if(key==KEY0_PRES)//KEY0按下,读取字符串并显示
{
LCD_ShowString(30,170,200,16,16,"Start Read 24C02.... ");
AT24CXX_Read(0,datatemp,SIZE);
LCD_ShowString(30,170,200,16,16,"The Data Readed Is: ");//提示传送完成
LCD_ShowString(30,190,400,16,16,datatemp);//显示读到的字符串
}
i++;
delay_ms(10);
if(i==20)
{
LED0=!LED0;//提示系统正在运行
i=0;
}
}
以上是我对IIC通信的粗略理解,参考了正点原子的标准库视频,这边推荐看普中51单片机视频中对IIC的经典讲解,有问题可以互相交流。谢谢!(视频均来自B站)