IIC介绍
IIC即Inter-IntegratedCircuit(集成电路总线),是一种多向控制总线,由飞利浦半导体公司在八十年代初设计,主要是用来连接整体电路(ICS)。在IIC中,多个设备可以连接到同一总线结构下,同时每个设备都可以作为实施数据传输的控制源,这种方式简化了信号传输总线。
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。IIC时钟频率: 不高于400K
上拉电阻一般在4.7k~10k之间
通信方式简介
1、主机发送开始信号
2、主机发送地址数据
3、从机接收后与自身地址匹配,若符合则发送ACK响应表示建立连接,否则不响应
4、主机收到ACK后可以发送数据
5、数据发送完成后,从机(接收方)发送ACK表示接收成功
6、数据发送完成,主机发送结束信号
信号介绍
总线信号 :
SDA :串行数据线
SCL :串行时钟
总线空闲状态 :
SDA :高电平
SCL :高电平
起始位: SCL为高电平期间 SDA出现下降沿
终止位: SCL为高电平期间 SDA出现上升沿
应答ACK: 当IIC主机(不一定是发送端还是接受端)将8位数据或命令传出后,会将SDA信号设置为输入,等待从机应答(等待SDA由高电平拉为低电平)若从机正确应答,表明数据或者命令传输成功,否则传输失败,注意,应答信号是数据接收方发送给数据发送方的。
数据传输 : SDA的数据在SCL高电平期间被写入从机。所以SDA的数据变化要发生在SCL低电平期间。
IIC器件地址: 每一个IIC器件都有一个器件地址,有的器件地址在出厂时地址就设定好了,用户不可以更改,比如OV7670的地址为0x42。有的器件例如EEPROM,前四个地址已经确定为1010,后三个地址是由硬件链接确定的,所以一个IIC总线最多能连8个EEPROM芯片。
数据传输介绍
开始信号传输之后,七位地址代表器件地址,第八位代表读或者写,0为写,1代表读
写入过程:
进行写操作时,首先发送该器件的7位地址码和写方向位”0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为响应,单片机收到应答后就可以传送数据了。传送数据时,单片机首先发送一个字节的被写入存储器的首地址,收到存储器器件的应答后,单片机就逐个发送数据字节,但每发送一个字节后都要等待应答。从机在接收到每一个数据字节地址后自动加1。装载字节数超过从机的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。
两次写之间要有一个10ms的间隔
字节写:
页写:
读入过程:
单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。
然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
当前地址读:
字节读:
页读:
以上时序的寄存器地址都是8位的,下面的是8位设备地址,16位寄存器地址,16位数据的传输时序
16位设备地址的读写时序没找到,但是有一个读的程序可以参看,其实就是发送设备地址时,先发高位地址,再发低位地址,之后再进行其他操作
u8 IIC_Read(u8 addr,u16 iaddr)
{
u8 temp;
IIC_Start();
IIC_SendByte(addr);//写模式
IIC_Wait_Ack();
IIC_SendByte((u8)(iaddr>> 8));//高位地址
IIC_Wait_Ack();
IIC_SendByte((u8)aiaddrdr);//低位地址
IIC_Wait_Ack();
IIC_Start();
IIC_SendByte(addr+1);//进入读模式
IIC_Wait_Ack();
temp = IIC_ReadByte();
IIC_NAck();//发送nACK
IIC_Stop();
return temp;
}
IIC程序
//初始化IIC接口
void IIC_Init(void)
{
RCC->APB2ENR|=1<<3; //使能外设IO时钟
GPIOB->CRL&=0X00FFFFFF; //PB6/7 推挽输出
GPIOB->CRL|=0X33000000;
GPIOB->ODR|=3<<6; //PB6,7 输出高
}
//产生起始信号
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;//钳住IIC总线,准备发送或接收数据
}
//产生停止信号
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;//发送IIC总线结束信号
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)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();//SDA引脚设置为输入
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//产生NACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();//SDA引脚设置为输入
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();//SDA引脚设置为输出
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读一个字节
//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();//发送NACK
else
IIC_Ack(); //发送ACK
return receive;
}
//读目标器件的寄存器数据,可单读可连续读
//addr:器件地址
//regist:寄存器地址
//len:读取数据长度,单位 字节
void IIC_ReadOneByte(u8 addr,u16 regist,u8 len)
{
u8 i=0;
IIC_Start();
IIC_Send_Byte(addr);//发送器件地址+写命令0
IIC_Wait_Ack();
IIC_Send_Byte(regist);
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(addr+1);//发送器件地址+读命令1
IIC_Wait_Ack();
while(len)
{
if(len==1)buf[i]=IIC_Read_Byte(0);//读数据,发送NACK
else buf[i]=IIC_Read_Byte(1);//读数据,发送ACK,可以连读
len--;
i++;
}
IIC_Stop();
}
//写一个字节
//addr:器件地址
//regist:寄存器地址
//data:数据
void IIC_WriteOneByte(u8 addr,u8 regist,u8 data)
{
IIC_Start();
IIC_Send_Byte(addr);//发送器件地址+写命令0
IIC_Wait_Ack();
IIC_Send_Byte(regist);
IIC_Wait_Ack();
IIC_Send_Byte(data);
IIC_Wait_Ack();
IIC_Stop();
}
//写多个字节
//addr:器件地址
//regist:寄存器地址
//len:写数据长度,单位 字节
//buf:数据区
//返回值:0,正常
// 其他,错误
u8 IIC_WritelenByte(u8 addr,u8 regist,u8 len,u8 *buf)
{
u8 i;
IIC_Start();
IIC_Send_Byte(addr);//发送器件地址+写命令0
IIC_Wait_Ack();
IIC_Send_Byte(regist);
IIC_Wait_Ack();
for(i=0;i<len;i++)
{
IIC_Send_Byte(buf[i]);//发送数据
if(IIC_Wait_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
}
IIC_Stop();
return 0;
}