一、IIC通信简介
1.IIC总线有两根双向信号线,一根是时钟线SCL,另一根是数据线SDA。
2.IIC总线上所有器件的SDA、SCL引脚输出驱动都为 开漏 结构,通过外接上拉电阻实现总线上所有节点SDA、SCL信号的线与逻辑关系;
3.总线上的所有设备通过软件寻址且具有唯一的地址
4.主从关系。从设备不能主动发起传输,其传输是受到主设备控制的。所以主机读取从机时,需先由主机向从机发读命令,然后从机发送数据到主机。
二、工作时序
1.START与STOP条件
与主设备的IIC通信由主设备发送START条件启动,并由主设备发送STOP条件终止。当SCL为高电平时,SDA线上由高到低的转换定义了START条件。当SCL为高电平时,SDA线上由低到高的转换定义了STOP条件。
2.数据有效性
在SCL的每个时钟脉冲期间传输一个数据位,一个字节由SDA线上的8位组成。字节可以是器件地址,寄存器地址,也可以是写入从器件或从器件读取的数据。数据首先传输最高位(MSB)。在START和STOP条件之间,可以从主向从传输任意数量的数据字节。SDA线上的数据必须在时钟周期的高电平期间保持稳定,因为当SCL为高电平时数据线的变化被解释为控制命令(START和STOP)。其中ACK为应答位。
3. 重复开始信号
重复开始信号(ReSTART/Sr): 在结束时不给出STOP信号,而以一个时钟周期内再次给出开始信号作为替代
4.应答信号
协议规定数据传输过程必须包含应答(ACK)。接收器通过应答告知发送的字节已被成功接收,之后发送器可以进行下一个字节的传输。主机产生数据传输过程中的所有时钟,包括用于应答的第9个时钟。发送器在应答时钟周期内释放对SDA总线的控制,这样接收器可以通过将SDA线拉低告知发送器:数据已被成功接收。
应答信号分为两种:
1)当第9位(应答位)为 低电平 时,为 ACK (Acknowledge) 信号
2)当第9位(应答位)为 高电平 时,为 NACK(Not Acknowledge)信号
主机发送数据,从机接收时,ACK信号由从机发出。当在SCL第9位时钟高电平信号期间,如果SDA仍然保持高电平,则主机可以直接产生STOP条件终止以后的传输或者继续ReSTART开始一个新的传输
从机发送数据,主机读取数据时,ACK信号由主机给出。主机响应ACK表示还需要再接收数据,而当主机接收完想要的数据后,通过发送NACK告诉从机读取数据结束、释放总线。随后主机发送STOP命令,将总线释放,结束读操作。
三、一般过程
主设备想要向从设备发送数据:
1.主发送器发送START条件并寻址从接收器‘
2.主发送器将数据发送到从接收器
3.主发送器以STOP条件终止传输
主设备想要从从设备接收/读取数据:
1.主发送器发送START条件并寻址从接收器‘
2.主发送器发送请求读取的寄存器
3.主发送器从从发送器接收数据
4.主发送器以STOP条件终止传输
四、IIC模块代码
#define DELAY_TIME 5
/** 定义IIC总线时钟线和数据线 */
sbit scl = P2^0;
sbit sda = P2^1;
/**
* @brief I2C总线中一些必要的延时
*
* @param[in] i - 延时时间调整
* @return none
*/
void i2c_delay(unsigned char i)
{
do
{
_nop_();
}
while(i--);
}
/**
* @brief 产生IIC总线启动条件
*
* @param[in] none
* @param[out] none
* @return none
*/
void i2c_start(void)
{
sda = 1;
scl = 1;
i2c_delay(DELAY_TIME);
sda = 0;
i2c_delay(DELAY_TIME);
scl = 0;
}
/**
* @brief 产生IIC总线停止条件
*
* @param[in] none
* @param[out] none.
* @return none
*/
void i2c_stop(void)
{
sda = 0;
scl = 1;
i2c_delay(DELAY_TIME);
sda = 1;
i2c_delay(DELAY_TIME);
}
/**
* @brief I2C发送一个字节的数据
*
* @param[in] byt - 待发送的字节
* @return none
*/
void i2c_sendbyte(unsigned char byt)
{
unsigned char i;
//
EA = 0; //关闭中断,避免因为中断而影响总线读写的时序,导致读写失败。
for(i=0; i<8; i++){
scl = 0;
i2c_delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
i2c_delay(DELAY_TIME);
scl = 1;
byt <<= 1;
i2c_delay(DELAY_TIME);
}
EA = 1;
//
scl = 0;
}
/**
* @brief 等待应答
*
* @param[in] none
* @param[out] none
* @return none
*/
unsigned char i2c_waitack(void)
{
unsigned char ackbit;
scl = 1;
i2c_delay(DELAY_TIME);
ackbit = sda; //while(sda); //wait ack
scl = 0;
i2c_delay(DELAY_TIME);
return ackbit;
}
/**
* @brief I2C接收一个字节的数据
*
* @param[in] none
* @param[out] da
* @return da - 从IIC总线上接收得到数据
*/
unsigned char i2c_receivebyte(void)
{
unsigned char da;
unsigned char i;
//
EA = 0;
for(i=0;i<8;i++){
scl = 1;
i2c_delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
i2c_delay(DELAY_TIME);
}
EA = 1;
//
return da;
}
/**
* @brief 发送应答
*
* @param[in] ackbit - 设定是否发送应答
* @return - none
*/
void i2c_sendack(unsigned char ackbit)
{
scl = 0;
sda = ackbit; //0:发送应答信号; 1:发送非应答信号
i2c_delay(DELAY_TIME);
scl = 1;
i2c_delay(DELAY_TIME);
scl = 0;
sda = 1;
i2c_delay(DELAY_TIME);
}