51单片机快速入门之 IIC 总线通信
协议:
- 空闲时 SCL/SDA 为高电平
- SCL高时 SDA下降沿 为开始信号
- 开始信号之后:
SCL高电平时 SDA不能变化 ,
SCL低电平时 SDA才可变
SDA 传数据时 从高到低按位传输 SCL一个脉冲高电平对应一位数据
4.SCL高电平时 SDA上升沿 为停止信号
数据格式:
1.单字节格式:
开始信号>数据(高到低)>应答(ACK)信号接收方SDA低电平>停止信号
2.多字节格式:
开始信号>发送设备地址和读写方向>应答(ACK)信号>数据传输>应答(ACK)信号接收方SDA低电平>停止信号
I2C 元器件 24c02(E^2PROM)
读/写操作:
1.单字节写流程:
开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>24C02回复ACK信号>发送一个字节>ACK信号>停止信号
2.页写流程(多字节):
开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>24C02回复ACK信号>发第一个字节数据地址>ACK>第一个字节数据>ACK>第二个字节数据...>一直到停止信号 或者16字节
最多可一次写入2-16字节!
超出会自动从初始位置覆盖数据 17时覆盖第一个字节数据,之后还有数据往后递增覆盖
3.立即地址读操作:
N范围(0~255)00H~FFH
开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>ack回复>读取一个字节数据(上次操作地址N+1的数据)>停止信号
N=255时 下一个跳转读取 0
4.选择读操作:
任选地址读
开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>低0伪写操作>ack>N字节地址>从ack>开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>读信号 1>从ack>获取N地址下的数据>停止信号
5.连续读操作:
立即地址读操作 连续
开始信号>7位地址+读1>读取FFH(255)为第一个数据,>主ACK>00H>主ack...直到出现主ack不回复 停下来>停止信号才结束本次读取
选择读操作 连续
开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>低0伪写操作>ack>N字节地址>从ack>开始信号>7位地址(1010固 0A 0B 0C)+读1/写0>读信号 1>从ack>获取N地址下的数据>ack>N+1数据>ack .....直到停止信号
电路图:
捣鼓了好几天,终于调试通了原来这元器件和书上的不同 所以说还是要多看看元器件数据手册才好,不然出了问题都找不到在哪! 就是这里 地址位我弄错了,书上很模糊所以我理解为
A0 A1 A2 顺序错了 实际上是A2 A1 A0
代码(简单发送):
#include <STC89C5xRC.H>//加载头文件,晶振12MHz
sbit SCL=P2^0; //时钟线
sbit SDA=P2^1; //数据线
void delay(unsigned int t); // 延时函数声明
void startI2C();//开始信号
void stopI2C();//停止信号
void SendByte(unsigned char dat);//发送字节数据
void main()
{
SendByte(0xA2);//发送从机地址
SendByte(0x00);//发送需要存入那个字节的地址 00H~FFH
SendByte(0x44); //发送需要存储的数据
P17=0;模拟环境提示操作成功用 ,实际使用可删除
while(1); // 防止程序重复运行
}
void startI2C() //开始信号
{
SDA=1; //首先拉高SDA
delay(10);//给他点时间
SCL=1; //拉高SCL
delay(10); //1us
SDA=0; //拉低SDA 触发开始信号
delay(10); //1us
SCL=0; //手动拉低确保时序准确 初始化以接受数据
delay(10); //1us
}
void stopI2C() //停止信号
{
if(SCL==1&&SDA==0) //只有当SCL=1 而且SDA=0时才触发 SDA=0保证已经接受到ACK回复信号
{
delay(10); //等一会
SDA=1; //拉高SDA ,上升沿触发停止信号
} else if(SCL==0) //当SCL=0时,这句没啥用其实 ,防止 SCL=0 触发不了停止信号
{
delay(10);
SCL=1; //把scl 拉高
if(SDA==0) //判断是否接收到ACK回复
{
delay(10);
SDA=1;
}
}
}
void SendByte(unsigned char dat)//发送字节数据
{
unsigned char bita;
startI2C();//发送开始信号
for(bita=0; bita<8; bita++) {
if((dat<<bita)&0x80) //每次根据bita 的值左移 bita位 再和 0x80(1000 0000) 与操作(只保最高位)
{
SDA=1; //如果是1,拉高sda传送一个1 过去
} else {
SDA=0;// 反之给0
}
delay(10); //让它们稳定一下
SCL=1;//上升读取 因开始信号已经拉低SCL
delay(10); //稳定时间
SCL=0; //下降写入数据 也可以说这是复位
delay(10); //稳定时间
}
SDA=1; //8位传完就拉高等待ACK触发
SCL=0; //下降写入数据 保证循环正确输出8位
SCL=1; //拉高以配合ACK
delay(10);//等待
if(SDA!=1) //sda=0 证明 ack已回复 触发停止信号
{stopI2C();
P30=0; //模拟环境提示操作成功用 ,实际使用可删除
} else {
P10=0;//模拟环境提示操作成功用 ,实际使用可删除
}
}
void delay(unsigned int t) // 简单延迟函数
{
while(t--);
}
运行效果:
程序设置了一个监视,操作成功会拉低P30
代码(简单读取):
#include <STC89C5xRC.H>
sbit SCL=P2^0; //时钟线
sbit SDA=P2^1; //数据线
sbit DQ=P2^4;//读取按键
void delay(unsigned int t); // 延时函数声明
void startI2C();//开始信号
void stopI2C();//停止信号
bit x; //用于存储ACK信息
unsigned char u;
void SendByte(unsigned char dat);//发送字节数据
void main()
{
SendByte(0xA2);//发送从机地址 最后一位置0 写入
x=0;
SendByte(0x01);//发送字节地址
x=0;
SendByte(0x66); //发送需要存储的数据
if(x==1) {
stopI2C(); //停止信号
}
P1=0;
x=0;
while(DQ==1);//阻断程序运行,当按钮按下往下执行
//按键判断读取并赋值给P1寄存器
if(DQ==0) {
SendByte(0xA3);//发送从机地址 最后一位置1 实际读取
if(x==1) { //表示读取请求被响应
unsigned char red,bint;
P33=0;
red=0;
// 在读取数据的循环之前添加SDA释放
if(SDA!=1&&SCL!=1) {
SDA = 1; // 释放SDA线
delay(0);
}
// 读取数据的循环
for(bint=0; bint<8; bint++) {
delay(20);//延时等待
SCL=0;//读取数据
delay(20);//延时等待
SCL=1;//
delay(20);
// 检查SDA状态,并更新red的值
if(SDA==1) { //如果读取到高电平
red |=1<<(7 - bint); // 设置对应位为1
} else {
red |=0<<(7-bint);
}
delay(10);//延时等待
}
SCL=0;//读取数据
delay(10);//延时等待
SDA = 1; // nack
delay(10);
P1=red;//把获取到的值直接用到P1中
stopI2C(); //停止信号
}
}
while(1); // 防止程序重复运行
}
void startI2C()
{
SDA=1;
delay(10);
SCL=1;
delay(10); //1us
SDA=0;
delay(10); //1us
SCL=0; //手动拉低确保时序准确
delay(10); //1us
}
void stopI2C()
{
if(SCL==1&&SDA==0) {
delay(10);
SDA=1;
} else if(SCL==0) {
delay(10);
SCL=1;
if(SDA==0) {
delay(10);
SDA=1;
}
}
}
void SendByte(unsigned char dat)//发送字节数据
{
unsigned char bita;
startI2C();//发送开始信号
for(bita=0; bita<8; bita++) {
if((dat<<bita)&0x80) {
SDA=1;
} else {
SDA=0;
}
delay(10);
SCL=1;//上升读取 因开始信号已经拉低SCL
delay(10);
SCL=0; //下降写入数据
delay(10);
}
SDA=1;
SCL=0; //下降写入数据 保证循环正确输出8位
SCL=1;
delay(10);
if(SDA==0) {
x=1; //有回复为1
P30=0;
} else {
x=0; //无回复为0
// P10=0;
}
}
void delay(unsigned int t) // 简单延迟函数
{
while(t--);
}
读取运行效果 :
由图可以看出,元器件根本没有回复数据(试过选择地址读取,也没效果) 所以就这样吧!
最后更新于2024/10/24 0:01