目录
三、等待应答函数:u8 IIC_Wait_Ack(void)
四、应答函数:void IIC_Ack(void)、void IIC_NAck(void)
五、IIC发送一个字节函数:void IIC_Send_Byte(u8 txd)
六、IIC接收一个字节函数:u8 IIC_Read_Byte(unsigned char ack)
一、IIC起始信号— IIC_Start(void):
函数源码:
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总线,准备发送或接收数据
}
首先设置数据线SDA端口为输出模式;由于空闲状态时数据线SDA和时钟线SCL都处于高电平状态,所以将IIC_SDA 和 IIC_SCL都设置为高电平。根据起始信号时序—当SCL处于高电平时,SDA由高向低进行跳变,所以将IIC_SDA设置为低电平,此时IIC起始信号已经模拟完成,最后将IIC_SCL设置为低电平,钳住IIC总线,准备发送或接收数据。
二、IIC停止信号— IIC_Stop(void)
函数源码:
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);
}
如上图,数据发送完成后数据线SDA和时钟线SCL应该处于低电平状态,所以先将IIC_SDA 和 IIC_SCL都设置为低电平;根据停止信号时序,先将时钟线SCL设置为高电平,后再将数据线SDA由低电平设置为高电平,此时IIC停止信号已经模拟完成,此时数据线SDA和时钟线SCL都为高电平,IIC处于空闲状态。
三、等待应答函数:u8 IIC_Wait_Ack(void)
函数源码:
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;
}
当主机传送完一个字节从高到低的8位的数据后,将SDA设置成数据输入模式,在第9位时,主机需要接收来自从机的应答信号。按照数据的有效性(只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变),先将SDA信号拉高置1,再将SCL信号拉高置1;设置完成后等待应答信号(从设备将拉低SDA线,回传给主设备一个应答位ACK)的到来,若没有接收到拉低信号,函数会在while中一直循环,超过一定时间范围后,调用停止信号函数;若接收到拉低信号后,跳出while循环,再将SCL信号拉低,钳住IIC总线,准备发送下一个数据或者停止信号。
在写操作中,应答位由24C02来发出,单片机可以不加以判断,而默认24C02会发出肯定的应答;在读操作中,应答位由单片机发出,如果单片机不发应答,24C02就会默认有错,从而导致操作失败。
四、应答函数:void IIC_Ack(void)、void IIC_NAck(void)
函数源码
//产生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;
}
由应答信号时序可知,在产生有效应答信号ACK时,按照数据的有效性(只有当SCL线上的时钟信号为低时,数据线上的“高”或“低”状态才可以改变),在SCL信号处于低电平时,SDA信号置低电平,随后SCL置高电平期间,SDA信号保持不变,直至SCL再次置低电平。
在产生非有效应答信号NACK时,在SCL信号处于低电平时,SDA信号置高电平,随后SCL置高电平期间,SDA信号保持不变,直至SCL再次置低电平。
五、IIC发送一个字节函数:void IIC_Send_Byte(u8 txd)
函数源码:
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);
}
}
其中:
形参u8 txd表示所发送的内容;
局部变量 t 用以记录传输数据的次数;
首先将SDA设置为输出模式,将时钟线SCL拉低,钳住IIC总线,准备发送或接收数据;然后在一个for循环里面开始传输一个字节的数据,数据从高位开始传输:首先将txd与0X80相与,只保留第7位的数据,再将得到的数据右移7位,把最高位的数移到最低位,并将值赋值给数据线,最后将原始数据左移一位,将第6位的数据放到第7位上来;此时将时钟线拉高,读取此时数据线的状态,完成数据一位的传输。在for循环中将上诉步骤重复8次,完成一个字节数据的传输。
六、IIC接收一个字节函数:u8 IIC_Read_Byte(unsigned char ack)
函数源码:
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;
}
其中:
形参 ack 用作选择发送Ack还是NAck;
局部变量 i 用以记录传输数据的次数;
局部变量 receive 用以记录接收到的数据;
首先,将SDA数据线设置为输入模式,在一个for循环中,先将SCL时钟线拉低,准备接收一个字节的数据;然后将SDA拉高,此时先将receive先左移一位,在读取SDA数据线此时的电平,如果为高电平,满足 if 条件,receive最低位置1,如果为低电平,receive保持不变;在下一个循环中,receive左移一位,将记录在最低位的数据逐渐像最高位移动,达到存储数据的目的(至于为啥 receive<<1 要放在 if 语句的前面,我开始以为是程序出错了,纠结了一段时间,然后在草稿纸上一步一步的执行,运行过程中并没有问题,然后慢慢理解了:在执行第一步的时候,receive本身是0,左移一位后值是不变的,进行第二次循环的时候,将第一次读入到最低位的数值向高位移动一位,整个for循环需要移动8次,有效移动7次,因为当最后一次循环的时候,receive接收到的值已经存在最低位了,不再需要进行移动。所以左移语句放在if语句的前面是正确的);最后根据情况应答信号还是非应答信号。