|
|
我经过研究发现,IIC用软件模拟,分别写启动程序,停止程序,再写移位和位与屏蔽取高一位判断就1还是0,然后把它等于到SDA上,发送程序,再ACK应答程序。用时在组装起来。硬件这些程序已经是寄存器命令,一字节数据放入BUF发送。好多通讯由软件模拟变为硬件,写程序就简单 你可以看IIC文字很难理解,不过直接看视频IIC软件手写过程,就通了 #ifndef __IIC_H #define __IIC_H #include "config.h" // ===================== 引脚定义 ======================= sbit SCL = P1^0; // 时钟线 sbit SDA = P1^1; // 数据线 // ===================== 函数声明 ======================= void IIC_Init(void); void IIC_Start(void); void IIC_Stop(void); void IIC_Send_Byte(unsigned char txd); unsigned char IIC_Read_Byte(unsigned char ack); bit IIC_Wait_Ack(void); void IIC_Ack(void); void IIC_NAck(void); #endif // ============================== // 2. IIC起始信号 // ============================== void IIC_Start(void) { SDA = 1; // 确保SDA高 SCL = 1; // 时钟高 delay_us(5); SDA = 0; // 时钟高电平时,SDA从高变低 = 起始信号 delay_us(4); SCL = 0; // 拉低时钟,准备发送数据 }// ============================== // 3. IIC停止信号 // ============================== void IIC_Stop(void) { SDA = 0; // 确保SDA低 SCL = 1; // 时钟高 delay_us(5); SDA = 1; // 时钟高时,SDA从低变高 = 停止信号 delay_us(4); } // ============================== // 4. 发送一个字节(8位) // ============================== void IIC_Send_Byte(unsigned char txd) { unsigned char t; for(t=0;t<8;t++) // 循环8次,发8位 { SCL = 0; // 时钟拉低,才能改变SDA delay_us(2); if((txd & 0x80)>>7) // 取最高位 SDA = 1; else SDA = 0; txd <<= 1; // 左移一位 delay_us(2); SCL = 1; // 时钟拉高,从机采样数据 delay_us(2); } SCL = 0; // 8位发完,拉低时钟 delay_us(2);// ============================== // 5. 等待从机应答(ACK) // ============================== bit IIC_Wait_Ack(void) { unsigned char ucErrTime = 0; SDA = 1; delay_us(1); // 主机释放SDA SCL = 1; delay_us(1); // 时钟拉高 while(SDA) // 读取SDA:低=应答,高=不应答 { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; // 失败 } } SCL = 0; // 时钟拉低 return 0; // 成功 } }// ============================== // 6. 主机产生应答 ACK // ============================== void IIC_Ack(void) { SCL = 0; SDA = 0; // 应答:SDA=0 delay_us(2); SCL = 1; delay_us(2); SCL = 0; } // ============================== // 8. 读取一个字节 // ============================== unsigned char IIC_Read_Byte(unsigned char ack) { unsigned char i,receive = 0; for(i=0;i<8;i++) { SCL = 0; delay_us(2); SCL = 1; // 时钟拉高,读取数据 receive <<= 1; if(SDA) receive++; delay_us(1); } if(!ack) IIC_NAck(); // 最后一个字节发NACK else IIC_Ack(); // 继续读发ACK return receive; } START → 发送设备地址 → 等待ACK → 发送寄存器地址 → ACK → 发送数据 → ACK → STOP START → 设备地址 → ACK → 寄存器地址 → ACK → START → 设备地址+1 → ACK → 读取数据 → NACK → STOP 五、必须记住的 5 条规则 SCL 高电平时,SDA 不能变 SCL 高电平时,SDA 下降沿 = START SCL 高电平时,SDA 上升沿 = STOP 数据高位先发 每发 1 个字节必须等应答 六、这个代码能直接驱动哪些设备? ✅ OLED 显示屏 ✅ 24C02 EEPROM ✅ MPU6050 ✅ SHT30 / SHT20 ✅ HMC5883 ✅ 所有 IIC 设备 |
| 很给力啊! |
| 讲解非常的细致,谢谢楼主 加油 努力 |
| 来学习学习 |
| 讲解非常的细致,看其他资料都没弄太明白 |
| 讲的明白。 |
|
任何串行通讯协议,其原理其实极其简单,就一条:通讯双方事先协商好,用不同的变化组合来表达不同的意思。 剩下的,就只是规则了:谁主谁从、什么样的动作组合表示开始、什么样的动作组合表示结束、什么样的动作表示开始传递数据、什么样的动作组合表示放上去了一位数据、什么样的动作表示可以读取数据了、什么样的动作表示这一位数据是1或是0、什么样的动作表示反馈信号、传输方向怎么确定、是从高位到低位还是从低位到高位进行传输、哪些数据是命令类数据哪些数据才是本次要传送的目标内容、要不要对接收到的数据进行正确性校验、对错误数据怎么处理,等等。 你在学习任何通讯协议时,要弄清楚的就是这些规则,万变不离其宗,这些规则没多少道理可讲,设计者就是这样规定的,我们该做的就是个死记。如果你看不惯或者觉得不够合理,可以,你自己想怎么设计就怎么设计,然后当然你也可以申请专利,至于能不能得到推广?要不你自娱自乐吧。 关于动作组合,实在是乏善可陈,就三个要素:电位、时间、传输数据线。 |
dyx811 发表于 2025-12-9 12:32 谢了,你们的肯定,就是我的动力。 |
| IIC让我迷惑了好久,这里终于悟到了IIC通讯的真理,感谢楼主的分享!讲解非常的细致入微,是我学习的指路明灯,向您致敬! |