参考优秀资料:
IIC,中文名:集成电路总线;
![[Pasted image 20240909170412.png]]IIC是串行的,半双工的,多主多从的,同步通信总线;“同步”体现在IIC的主从设备共用一条SCL时钟线。
IIC总线一般有两根信号线:SDA数据线,SCL时钟线。
如上图所示,IIC有一个SDA总线(最上面那根),也有一个SCL总线,所有设备的SDA端都接到SDA总线上,所有设备的SCL端都接到SCL总线上。
IIC支持的传输速率如下:有5种
模式 | 速率 |
---|---|
标准模式(Standard Mode) | 100kb/s |
快速模式(Fast Mode) | 400kb/s |
增强快速模式(Fast Mode Plus | 1Mb/s |
高速模式(High Speed Mode) | 3.4Mb/s |
极速模式(Ultra-FastMode) | 5Mb/s |
一般单片机是用标准模式。 |
IIC通信涉及到四种信号:
起始信号、数据信号、应答信号、终止信号;对应的是:起始位、数据传输位(固定是8bit)、应答位(ACK或者NACK)、终止位。
- 双高状态:SDA和SCL由于上拉电阻的作用都处于高电平状态
- 起始位:SCL为高电平,SDA由高电平转为低电平;启动信号是一种电平跳变时序信号,而不是一个电平信号。重点是时序,就是这个变化的过程
- 终止位:SCL为高电平,SDA由低电平转为高电平,终止信号是一种电平跳变时序信号,而不是一个电平信号。重点是时序,就是这个变化的过程
- 数据传输位:输出到SDA上的每个字节必须是8bit,先传输字节高位,后传输字节低位;这里SCL和SDA的时序需要重点注意:只有在SCL为低电平时,SDA才能允许发生高低电平的变化(数据变化),SCL为高电平时,SDA要稳定在一个状态
- 应答位:发送器每发送8bit的数据,就会在第9个时钟脉冲前释放SDA数据线(这个时候SDA不由发送器写入数据),由接收器反馈一个信号;如果要正确反馈ACK(表示接收到数据)的信号,就要:接收器在第9个时钟脉冲前的那个低电平期间,将数据线SDA拉低(低电平),在SCL为高电平期间,SDA稳定为低电平,这时就能正确反馈一个ACK应答信号;
- NACK信号:非应答信号,这时第九个时钟脉冲期间SDA为高电平,表示接收器没有成功接收该字节。
如果接收器是主控器(决定是否继续收发数据的器件),则它在收到最后一个字节数据后,会发送一个NACK信号,用来通知发送器结束数据的发送,并释放SDA线,以便后续终止信号的发送。
搭配代码理解:
#include "bsp_iic.h"
#define DELAY_TIME 5 // 这是用于延时的,会影响IIC的传输速率,默认就好
//
static void I2C_Delay(unsigned char n)
{
do
{
// _nop_()等效于汇编里面的NOP指令,也就是空一个机器周期
// 对于STC15F2K60S2单片机,一个机器周期为12个时钟周期,该单片机主频为12M
// 可以换算得1个机器周期就是1us;IIC通信要求读取一个有效位的时间要大于4us,
// 这里是满足的,注意看DELAY_TIME;2us*5=10us
_nop_();_nop_();
}
while(n--);
}
// 起始信号
void I2CStart(void)
{
// 初始时,scl,sda均为高电平
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
// scl保持高电平,sda变为低电平:高变低
sda = 0;
I2C_Delay(DELAY_TIME);
// scl变为低电平,准备数据传输
scl = 0;
}
//
void I2CStop(void)
{
// 终止时,scl为高,sda由低变高
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
// 数据传输,8bit,先传高再传低
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);
}
// 传完后scl变0,等待ACK
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
// 设备接收数据,注意这里的SCL是要与发送端同步的;不能更改sda的值
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
// 等待应答信号,此时sda控制权不在自己手上,所以不能更改sda的值
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
// 发送响应位,和数据传输位思路类似
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}