因IIC时序细节的理解不是一两篇文章就能描写清楚的,这里笔者只针对《手把手教你学51单片机》文档第14章内容做补充辅助理解,如果大家想彻底弄明白IIC的所有通信细节过程,那么就需要“好事多磨”了。
我们现在先贴出IIC的时序流程图
我们同样是运用宋老师的代码,这里笔者把代码和截图一起呈现出来让大家能够一句句地去理解含义,打开宋老师的代码lesson14_1。
void I2CStart() //产生总线起始信号 { I2C_SDA = 1; //首先确保SDA、SCL都是高电平 I2C_SCL = 1; I2CDelay(); //延时几微秒 I2C_SDA = 0; //先拉低SDA I2CDelay(); //延时几微秒 I2C_SCL = 0; //再拉低SCL }
void I2CStop() //产生总线停止信号 { I2C_SCL = 0; //首先确保SDA、SCL都是低电平 I2C_SDA = 0; I2CDelay(); //延时几微秒 I2C_SCL = 1; //先拉高SCL I2CDelay(); I2C_SDA = 1; //再拉高SDA I2CDelay(); }
假如我们单片机作为主机,需要传输0xA0这个字节给从机,从机其实就是芯片EEPROM,而0xA0的作用是寻址,用来查看开发板上有没有相应的从机。
我们看到“bit I2CWrite(unsigned char dat)”,如果传入的参数给dat为0xA0,那么相关执行过程为下图所示
现在我们结合上图再来对比代码
bit I2CWrite(unsigned char dat) //I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 { bit ack; //用于暂存应答位的值 unsigned char mask; //用于探测字节内某一位值的掩码变量 for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行 { if ((mask&dat) == 0) //该位的值输出到SDA上 I2C_SDA = 0; else I2C_SDA = 1; I2CDelay(); I2C_SCL = 1; //拉高SCL I2CDelay(); I2C_SCL = 0; //再拉低SCL,完成一个位周期 } I2C_SDA = 1; //8位数据发送完后,主机释放SDA,以检测从机应答 I2CDelay(); I2C_SCL = 1; //拉高SCL ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值 I2CDelay(); I2C_SCL = 0; //再拉低SCL完成应答位,并保持住总线 return ack; //返回从机应答值 }
如果存在从机,那么就会产生应答,结果演变为下图
所以“ack = I2C_SDA;”就会演变为“ack = 0;”,这样返回值就为0了。
要是我们随便寻址,发送的是0xC4,那么不存在相应地址的器件,自然不会存在拉低的现象,导致结果与上图相反
返回值就为1了。
这也就充分理解并且分析了宋老师的lesson14_1的代码了。这时我们会发现在函数“bit I2CAddressing(unsigned char addr)”里如果传入的参数为0x50,那么“ack = I2CWrite(addr<<1);”其实就是“ack = I2CWrite(0xA0);”。
如果传入的参数为0x62,那就是“ack = I2CWrite(0xC4);”。
本文固定URL:https://www.dotcpp.com/course/391
C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:
一点编程也不会写的:零基础C语言学练课程
解决困扰你多年的C语言疑难杂症特性的C语言进阶课程
从零到写出一个爬虫的Python编程课程
只会语法写不出代码?手把手带你写100个编程真题的编程百练课程
信息学奥赛或C++选手的 必学C++课程
蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程
手把手讲解近五年真题的蓝桥杯辅导课程