IIC总线
I2C总线 简介
-
IIC(Inter-Integrated Circuit)或I2C:
它是一种串行同步半双工总线,使用多主多从架构。
由PHILIPS公司在1980年代,为了让主板、嵌入式系统或手机连接低速周边设备而发展。
IIC硬件结构简单、接口连接方便、成本低,因此应用很广。 -
IIC 在 源设备与 被控外设(目标设备) 之间、外设 与 外设 之间等都可以进行双向数据通信;
-
高速 IIC 总线标准速率为100Kbps,高速通信速率可达 400kbps 及以上(1MBbsp)。
-
应用领域:
存储:eeprom外设,如Atmel的AT24CXX系列;
传感器: 温湿度传感器、IMU-六轴传感器(MPUxxx)、触摸芯片(GT9xx)等。
显示:
DSI:DS90UB9xx系列,AIM9xx,ICN6202: mipi、lvds等
CSI: TP6808、TP2855等
I2C总线 -硬件连接
(1)IIC总线硬件组成:
- SDA(串行数据线)
- SCL(串行时钟线)
- 上拉电阻
(2)IIC总线通信原理
通过对SCL和SDA两根硬线高、低电平时序的控制,来产生IIC总线协议所需要的信号来进行数据的传递。
在总线空闲 Bus idle 状态时,这两根线会被上拉电阻拉高,保持着高电平。
-
IIC总线为两线制:
即有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。
所有设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。 -
上拉电阻的作用是在空闲状态下时,保持SDA和SCL为高电平状态。一般Rp = 4.7k
(3)上拉电阻的取值范围和影响因素
-
IIC总线是具有多主多从的系统总线可裁决功能的高性能串行总线。
(多主机用的比较少,基本上都是一个主机挂载多个从机设备) -
主设备通过寻址来确定与哪个器件进行通信。
通常情况下,
把带IIC总线接口的模块作为主设备(Master):主动发起接收和发送数据;
把挂接在总线上的其他设备都作为从设备(Slave) :被动的接收和发送数据为从机。 -
IIC总线上可挂接的设备总数量受总线的最大电容400pF所限制。
如果所挂接的是相同型号的器件,则还受器件地址位个数的限制。 -
IIC总线数据传输速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。
一般通过IIC总线接口可编程时钟SCL来实现传输速率的调整,同时也跟所接的上拉电阻的阻值有关。
(4)SDA、SCL必须配置成开漏的原因
- 电平转换:实现硬件电路中,不同电压的电平匹配;
- 防止端口(pin引脚)之间的短路,避免电流倒灌注入GND;
I2C总线 -寻址
-
怎么区分主机和哪一个从机通信?
IIC总线上的每一个设备都可以作为主设备或从设备,而且每一个接到IIC总线上的器件都会分配一个唯一的地址。主机和其他从机器件在数据传输时通过对地址进行匹配后再确认通信,不区分主机、从机,发送数据的设备是发送器,接收数据的设备是接收器。 -
IIC总线上的主设备与从设备间以字节(8位 = 一帧)为单位进行双向的数据传输,主、从设备间的数据传输是建立在地址的基础上。
也就是说:主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位。然后协议规定再给地址添加一个最低位,用来表示接下来数据传输的方向:
0:主机 —> 向从机写、发送Tx 数据(从机接收数据)
1:主机 —> 向从机读、接收 Rx从机的数据(从机发送数据 ); -
设备地址的产生?
通过芯片引脚的物理接地或上拉电阻拉高。
(1)硬件引脚选择
eg:
参考 IIC器件的数据手册,如MPU6050芯片,总共有7位地址依次为bit6~bit0:101 1xxx, 低三位总共有8种地址类型选配。
如果第三位引脚全部物理接地,则该设备地址为0x58。
注:此类芯片在该主、从总线上最多可以挂载8个。
(2)时序选择
电容触摸屏GT911、GT928、GT9147的使用
I2C总线 -通信协议
首先需要默认记住I2C协议的规则:
(1)高稳定、低变化(data 数据域):
在正常数据传输交互时,主机、从机都必须遵循:sda的电平变化在scl的一个时钟周期内:
在scl电平拉低电平low 的时候,sda 可以进行高、低电平的切换;
在scl电平拉高电平high的时候,sda 必须保持 当前电平 的稳定;
- i2c stretch 功能 (时钟scl拉长)
在数据通信时,主机发送速度太快会导致从机来不及操作内部的数据交互,从机端会控制SCL时钟硬线主动拉低scl电平使主机暂时停止数据的交互,等待从机完成内部的数据操作后,会主动释放拉低的scl,从而恢复正常通信。此过程叫i2c stretch 功能。
(2)Start开始信号、Stop停止信号:
这两个信号都是由主机产生的,不属于数据域交互,在scl的一个时钟周期内,不再遵循高稳定低变化规则,
而是相反的逻辑:
在scl的高电平时,主机将sda的电平由 高–>低 是Start信号(下降沿);
在scl的高电平时,主机将sda的电平由 低–>高 是Stop信号(上升沿);
(3)应答ACK、非应答NACK
在scl的一个时钟周期内,从机在scl的高电平时,将sda的电平由高拉低(或者继续保持低电平状态) 则是ACK信号;
从机在scl的高电平时,如果sda的电平一直是 高电平 则是NACK信号;
(一)起始信号和终止信号
(1)IIC协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生,即意味着从设备不可以主动通信,所有的通信都是从主设备发起的,主机可以发出询问的command,然后等待从机设备的通信。
(2)起始和结束信号的产生条件:总线在空闲状态时,SCL和SDA默认都是保持着高电平状态。
- 当SCL为高电平时,SDA电平状态发生由高到低的跳变,产生一个下降沿:起始信号;
- 当SCL为高电平时,SDA由低到高的跳变,产生一个上升沿:终止信号。
(3)在起始条件产生后,总线处于忙状态:即本次数据传输的总线由主、从设备独占,其他IIC器件此时无法访问总线;而在终止条件产生后,本次数据传输的主、从设备将总线释放,总线再次处于空闲状态。
起始和终止信号如图所示:
(二)应答信号和非应答信号
- IIC总线数据传输是以字节为单位。SCL线上产生每个时钟脉冲的过程中,主设备将在SDA线上传输一个数据位,当一个字节的数据按数据位从高到低的顺序逐次传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位(应答信号), 此时才认为一个字节真正的被传输完成。
- 当然并不是所有的字节传输都必须有一个应答位,eg:当从设备不能在接收到主设备发送的数据时,从设备将会回传一个非应答位(即非应答信号:延时一段时间默认回传一个非应答信号)。
ACK 应答位的操作流程(同样遵循数据位的规则: SDA在SCL一个周期内 高稳定,低变化)
主机行为:
主机在第 9 个时钟周期(ACK 周期)释放 SDA 线(即 SDA 变为高电平,由上拉电阻维持)。
从机行为:
在第 9 个时钟周期(ACK 周期)期间,
在 SCL 低电平期间,从机将 SDA 线拉低(表示 ACK)。
在 SCL 高电平期间,从机保持 SDA 为低电平,主机检测到低电平即表示 ACK 有效。
应答信号和非应答信号如图所示:
(三)数据状态:SCL高读 / 低写
SCL在高电平期间,SDA数据线必须保持数据的稳定,此时才可以读取数据。
SCL在低电平时,SDA数据线上高低电平状态才允许变化,此时才可以写入数据。
(四)I2C总线 -数据的读写过程
注意:无论主机还是从机,发送或接收数据都是把数据先放入自己的移位寄存器里保存的,之后再进行数据的发送到SDA/写入到自己的内存中。
写入过程
过程描述:
- 主机先向SDA发送一个起始信号S后(表示主机要开始传输数据了),再发送 从机地址(7位) + 读/写位(0写/1读)。紧接着每一个从机开始将自己的地址和主机发送的地址进行匹配,如果发现相同,则从机会发送一个应答信号ACK(表示从机已匹配上,可以与主机进行数据传输)。主机接收到从机的ACK信号后,主机继续会再发送一个 从机内部内存的地址来指定主机要写入的内存位置。从机接收到从机发送的数据后会产生应答信号ACK返回,主机接收到从机的ACK信号后,主机开始进行数据的传输。每次从机接收到数据都会应答发送ACK信号,当主机发送完数据不想发了,会向SDA发送停止信号P。
读取过程
I2C软件模拟时序
(1)IIC时序函数
以stm32F105VCT为例:
/* --------------------- IIC 初始化 -------------------------*/
/* IIC1 初始化 */
void IIC_Dev_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能GPIOB端口时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* 配置SCL,SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* SCL、SDA初始默认状态值 */
IIC_SCL = 1;
IIC_SDA = 1;
}
/* --------------------- IIC 初始化 -------------------------*/
/* IIC1 初始化 */
void IIC_Dev_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能GPIOB端口时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
/* 配置SCL,SDA */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* SCL、SDA初始默认状态值 */
IIC_SCL = 1;
IIC_SDA = 1;
}
/*---------------------------- 软件模拟 IIC时序 ---------------------------*/
/* 产生IIC 起始信号 */
void IIC_Start(void)
{
SDA_OUT(); // 设置SDA线为输出
IIC_SDA = 1;
IIC_SCL = 1;
delay_us(4);
IIC_SDA = 0;
delay_us(4);
IIC_SCL = 0;
}
/* 产生IIC 停止信号 */
void IIC_Stop(void)
{
SDA_OUT();
IIC_SCL = 0;
IIC_SDA = 0;
delay_us(4);
IIC_SCL = 1;
IIC_SDA = 1; // 发送I2C总线结束信号
delay_us(4);
}
/* 等待应答信号到来 */
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();
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;
}
/*------------- 指令周期 约1.05us ----------------*/
void delay_us(int i)
{
while(i--);
}
可以适用于任何的stm32F系列平台的IIC时序,delay_us()使用MDK(keil5.0)可以调试仿真:一条指令的大概执行时间。
(2)IIC 发送/接收 数据帧的过程
/*---------------- IIC发送/接收一个 Byte数据 -------------------*/
/**
* @brief IIC发送一个字节
* @param
* @retval 返回从机有无应答
* 1:有应答
* 0:无应答
*/
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);
}
}
/* 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;
}
(3)iic.h 头文件
#define I2C_CLK_SPEED 100000 //100KHZ < 400KHZ
/* ------------------------ IIC的SCL、SDA 位带操作 ----------------------- */
//外设地址
#define PERIPH_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
#define GPIOB_ODR_Addr (GPIOB_BASE + 12)
#define GPIOB_IDR_Addr (GPIOB_BASE + 8)
//位带映射操作
#define BITBAND(addr, bitnum) \
((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr, n)
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr, n)
#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //read SDA
/* ------------------- 设置SDA_IO数据流传输方向--------------------*/
/* SDA数据流输入 */
#define SDA_IN() \
do \
{ \
GPIOB->CRH &= 0XFFFFFF0F; \
GPIOB->CRH |= (u32)8 << 4; \
} while (0) //上拉输入
/* SDA数据流输出 */
#define SDA_OUT() \
do \
{ \
GPIOB->CRH &= 0XFFFFFF0F; \
GPIOB->CRH |= (u32)3 << 4; \
} while (0) //50MHZ推挽输出