Ⅰ
开始和停止条件
SCL时钟电平为高:
SDA数据线由高 -> 低 为总线开始条件;
SDA数据线由低 -> 高 为总线结束条件;
(注意:开始之后将SCL变为低电平,防止误操作SDA使其通信停止,见源代码)
时序图:
源代码程序:
/************************************************
函数名称 : I2C_Start
功 能 : I2C开始
参 数 : 无
返 回 值 : 无
作 者 : strongerHuang
*************************************************/
voidI2C_Start(void)
{
I2C_SCL_HIGH; //SCL高
I2C_Delay();
I2C_SDA_HIGH; //SDA高 -> 低
I2C_Delay();
I2C_SDA_LOW; //SDA低
I2C_Delay();
I2C_SCL_LOW; //SCL低(待写地址/数据)
I2C_Delay();
}
/************************************************
函数名称 : I2C_Stop
功 能 : I2C停止
参 数 : 无
返 回 值 : 无
作 者 : strongerHuang
*************************************************/
void I2C_Stop(void)
{
I2C_SDA_LOW; //SDA低 -> 高
I2C_Delay();
I2C_SCL_HIGH; //SCL高
I2C_Delay();
I2C_SDA_HIGH; //SDA高
I2C_Delay();
}
Ⅱ
数据位传输
SCL时钟电平为低, 可以改换SDA数据线的电平,在SCL上升沿的过程将SDA数据发送出去。
(切记:请先将SCL变为低电平,再改变SDA电平状态。 主要用于I2C读写Byte函数,这两个函数网上很多人写的不规范,引用需注意,在下面我会举例说明)
时序图:
发送一位“高”数据流程:
SCL_LOW时钟低 -> SDA_HIGH数据 -> SCL_HIGH时钟高
Ⅲ
应答位信息
I2C是以字节(8位)的方式进行传输,总线上每传输完1字节之后会有一个应答信号,主器件(主机)需要产生对应的一个额外时钟。
应答位产生及接收:
1.在(主机)写数据的时候是从机应答(给主机),主机检测;
2.在(主机)读数据的时候是主机应答(给从机),从机检测;
(我们借助I2C读写函数一起理解)
1.主机写,从机应答,主机读取应答
时序图:
源代码:
/************************************************
函数名称 : I2C_GetAck
功 能 : I2C主机读取应答(或非应答)位
参 数 : 无
返 回 值 : I2C_ACK ----- 应答
I2C_NOACK --- 非应答
作 者 : strongerHuang
*************************************************/
uint8_t I2C_GetAck(void)
{
uint8_t ack;
I2C_SCL_LOW; //SCL低 -> 高
I2C_Delay();
I2C_SDA_HIGH; //释放SDA(开漏模式有效)
I2C_Delay();
I2C_SCL_HIGH; //SCL高(读取应答位)
I2C_Delay();
if(I2C_SDA_READ)
ack = I2C_NOACK;//非应答
else
ack = I2C_ACK; //应答
I2C_SCL_LOW; //SCL低
I2C_Delay();
returnack;
}
2.主机读,主机产生应答
时序图:
源代码:
/************************************************
函数名称 : I2C_PutAck
功 能 : I2C主机产生应答(或非应答)位
参 数 : I2C_ACK ----- 应答
I2C_NOACK --- 非应答
返 回 值 : 无
作 者 : strongerHuang
*************************************************/
voidI2C_PutAck(uint8_t Ack)
{
I2C_SCL_LOW; //SCL低
I2C_Delay();
if(I2C_ACK == Ack)
I2C_SDA_LOW; //应答
else
I2C_SDA_HIGH; //非应答
I2C_Delay();
I2C_SCL_HIGH; //SCL高 -> 低
I2C_Delay();
I2C_SCL_LOW; //SCL低
I2C_Delay();
}
Ⅳ
I2C写一字节
这里说的I2C写,是主机往从机接入1Byte的数据;
“写”要求按照上面的“数据为传输”来操作:在SCL时钟为低电平时准备好,待SCL为高电平时发送出去。
写完一字节(8位)之后,读取从机的应答位:
若为0,表示从机应答,可以继续下一步操作;
若为1,表示从机非应答,不能进行下一步操作。
注意:
I2C写一字节,不是EEPROM写一字节(需要区分开来)
写一字节时序(前面8位数据 + 最后1为应答):
源代码:
/************************************************
函数名称 : I2C_WriteByte
功 能 : I2C写一字节
参 数 : Data --- 数据
返 回 值 : 无
作 者 : strongerHuang
*************************************************/
void I2C_WriteByte(uint8_t Data)
{
uint8_t cnt;
for(cnt=0; cnt<8; cnt++)
{
I2C_SCL_LOW; //SCL低(SCL为低电平时变化SDA有效)
I2C_Delay();
if(Data & 0x80)
I2C_SDA_HIGH;//SDA高
else
I2C_SDA_LOW; //SDA低
Data <<= 1;
I2C_Delay();
I2C_SCL_HIGH; //SCL高(发送数据)
I2C_Delay();
}
I2C_SCL_LOW; //SCL低(等待应答信号)
I2C_Delay();
I2C_GetAck(); //读取应答位
}
提示:
网上常见几种关于“I2C写数据函数”的不规范写法, 或许整个I2C驱动能通信成功,但各个函数之间依赖关系很强,不便理解,也不是标准的函数。
1.首先将SCL置高:
voidI2C_WriteByte(uint8_t Data)
{
uint8_t cnt;
for(cnt=0; cnt<8; cnt++)
{
I2C_SCL_HIGH;
if(Data & 0x80)
I2C_SDA_HIGH;
else
I2C_SDA_LOW;
Data <<= 1;
I2C_SCL_LOW;
}
I2C_GetAck();
}
这种程序的写法有一个致命的地方(有可能停止,或重新开始I2C通信):
首先将SCL置高:
A.若之前SDA是低电平,第一位写入高电平,将停止I2C通信。
B.若之前SDA是高电平,第一位写入低电平,将重新开始I2C通信。
2.写完8位数据之后,未将SCL置低(也就是SCL保持高电平状态)
由于写完8位数据之后,将要读取应答信号,也就是要SDA将从输出状态变为输入状态。
这个时候SCL为高,如果SDA最后一位是低且SDA是开漏模式,需要将SDA释放,也就是要将SDA置位高,那么,这个时候就进行了一个停止操作。
3.时序混乱
void I2C_WriteByte(uint8_t Data)
{
uint8_t cnt;
I2C_SCL_HIGH;
for(cnt=0; cnt<8; cnt++)
{
if(Data & 0x80)
I2C_SDA_HIGH;
else
I2C_SDA_LOW;
Data <<= 1;
I2C_SCL_LOW;
I2C_SCL_HIGH;
}
I2C_GetAck();
}
多种问题的例子,有可能产生以下问题:
A.有可能多写1位数据;
B.有可能停止I2C通信;
C.有可能重新开始I2C通信。
Ⅴ
I2C读一字节
I2C的读一字节函数,其实和“写一字节”类似,只是数据传输方向相反,应答的方向也是相反。
读完一字节(8位)之后,由主机产生应答(或非应答)位:
若产生应答,表示可以继续读下一字节操作(从设备地址指向下一字节);
若产生非应答,表示不可以继续读下一字节操作;
网上I2C读数据程序和“写数据”类似,存在很多不标准的版本,参考时请注意。
读一字节时序(主机读取前面8位数据 + 主机产生1为非应答<连续读,主机产生应答位>):
源代码:
/************************************************
函数名称 : I2C_ReadByte
功 能 : I2C读一字节
参 数 : ack --------- 产生应答(或者非应答)位
返 回 值 : data -------- 读取的一字节数据
作 者 : strongerHuang
*************************************************/
uint8_t I2C_ReadByte(uint8_t ack)
{
uint8_t cnt;
uint8_t data;
I2C_SCL_LOW; //SCL低
I2C_Delay();
I2C_SDA_HIGH; //释放SDA(开漏模式有效)
for(cnt=0; cnt<8; cnt++)
{
I2C_SCL_HIGH; //SCL高(读取数据)
I2C_Delay();
data <<= 1;
if(I2C_SDA_READ)
data |= 0x01; //SDA为高(数据有效)
I2C_SCL_LOW; //SCL低
I2C_Delay();
}
I2C_PutAck(ack); //产生应答(或者非应答)位
return data; //返回数据
}
-
协议
+关注
关注
2文章
598浏览量
39156 -
I2C
+关注
关注
28文章
1481浏览量
123238 -
SCL
+关注
关注
1文章
239浏览量
17032
发布评论请先 登录
相关推荐
评论