前言
就个人对I2C通信的理解,通过用图文的方式,尽量简洁的记录下此文。希望能对大家理解I2C通信协议有所帮助。
理解I2C
对于I2C协议的理解,我个人是将完整的I2C时序协议,分成六大块理解。分别是
开始条件
,结束条件
,发送字节
,发送字节
,发送应答
,接收应答
。通过对六大块的调用。拼接起来使用即可组成完整的I2C协议通信。
以下约定:"高"指高电平,"低"指低电平
- 开始条件(1):SDA由高变低,SCL高电平期间。
- 结束条件(1):SDA由低变高,SCL不变。
- 发送字节(8):将要发送的数据(0或1)置给SDA,然后SCL由"低"变"高",就完成一次一位数据的发送,循环8次,就是发送一个字节。
- 发送字节(8):释放SDA,然后SCL由"低"变"高",此时读取SDA的电平,就是从机返回的一位数据,循环8次,就能接收到一个字节。
- 发送应答(1):当主机发送完一个字节数据后,等待从机的回应。用来为主机判断从机有没有接受到数据做判断。
- 接收应答(1):当主机接收完一个字节数据后,给予从机的回应。用来为主机判断从机有没有接收到数据做判断。
上面的六大操作块后都跟(x),x为SDA和SCL操作的次数。(我目前见到的I2C协议是这样,不排除有改变或没见过的)。可能有人对于两个应答有绕,通俗一点来讲就是,一方发送数据发送(或返回)一个字节后,对方都得产生一个位的回应。
图解IIC
以下是参考江科大的I2C的图形和我个人理解,绘制出来的时序六大块,看着就能明白通信块的电平跳变。
-
IIC六大块图片解析
图中紫色线条和字眼的,代表输入权转移都从机,主机则在等待状态。
-
基础IIC通信
下图的图示就介绍的模块拼接后,完成一次收发通信的演示。
代码演示
下面是我在STM32F1中用的软件模拟I2C,只要理解了,都可以随便搬到其它单片机上,运行的。大家可以参考参考。
MyI2C.h
#ifndef __MyI2C__H__
#define __MyI2C__H__
void MyI2C_W_SCL(uint8_t BitValue);
void MyI2C_W_SDA(uint8_t BitValue);
uint8_t MyI2C_R_SDA(void);
void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);
#endif
MyI2C.c
#include "gpio.h"
#include "MyI2C.h"
// 使用时,可能还要重定向延时
// 假如是32位单片机,SDA端口,一般要设置为开漏模式,可以读取和设置电平
/* 配置I2C端口和引脚 */
#define MyI2C_SDA_PORT GPIOB
#define MyI2C_SDA_PIN GPIO_Pin_11
#define MyI2C_SCL_PORT GPIOB
#define MyI2C_SCL_PIN GPIO_Pin_10
/**
* @描述:写入I2C的SCL引脚数据
* @参数 BitValue: 0(写入低电平) 1(写入高电平)
*/
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(MyI2C_SCL_PORT,MyI2C_SCL_PIN,(BitAction)BitValue);
Delay_us(10);
}
/**
* @描述:写入I2C的SDA引脚数据
* @参数 BitValue: 0(写入低电平) 1(写入高电平)
*/
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(MyI2C_SDA_PORT,MyI2C_SDA_PIN,(BitAction)BitValue);
Delay_us(10);
}
/**
* @描述:读取I2C的SDA引脚数据
* @返回值: 0(读取到低电平) 1(读取到高电平)
*/
uint8_t MyI2C_R_SDA(void)
{
uint8_t BitValue;
BitValue=GPIO_ReadInputDataBit(MyI2C_SDA_PORT,MyI2C_SDA_PIN);
Delay_us(10);
return BitValue;
}
/**
* @描述:对使用到的I2C的SDA,SCL引脚进行初始化
*/
void MyI2C_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = MyI2C_SDA_PIN | MyI2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, MyI2C_SDA_PIN | MyI2C_SDA_PIN);
}
/**
* @描述:I2C开始条件
*/
void MyI2C_Start(void)
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
/**
* @描述:I2C结束条件
*/
void MyI2C_Stop(void)
{
//MyI2C_W_SCL(0);
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
/**
* @描述:I2C发送字节
*/
void MyI2C_SendByte(uint8_t Byte)
{
uint8_t i;
for(i = 0;i < 8;i++)
{
MyI2C_W_SDA(Byte & (0x80 >> i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
/**
* @描述:I2C接收字节
*/
uint8_t MyI2C_ReceiveByte(void)
{
uint8_t i,Byte=0x00;
MyI2C_W_SDA(1);
for(i = 0;i < 8;i++)
{
MyI2C_W_SCL(1);
if (MyI2C_R_SDA()) {Byte |= (0x80 >> i);}
MyI2C_W_SCL(0);
}
return Byte;
}
/**
* @描述:I2C发送应答
*/
void MyI2C_SendAck(uint8_t AckBit)
{
MyI2C_W_SDA(AckBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
/**
* @描述:I2C接收应答
*/
uint8_t MyI2C_ReceiveAck(void)
{
uint8_t AckBit;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
AckBit=MyI2C_R_SDA();
MyI2C_W_SCL(0);
return AckBit;
}
知识要点
- 一般情况下,通信数据高位在前。
- I2C器件两条线SDA(数据线),SCL(时钟线)。
- SDA,SCL线在I2C协议上是要默认高电平的,必要时是要加上上拉电阻。
- I2C协议的通信速度分为三种模式:1.标准模式的传输速率为100Kb/s;2.快速模式为400Kb/s;3.高速模式为3.4Mb/s。
- 本文记录中I2C通信时没有强调速率的影响,但是在实际使用时,如果引脚速率太快,就要在软件中,适当的在每个时序块操作中,添加一定的延时。