51单片机快速入门之 IIC I2C通信

51单片机快速入门之 IIC 总线通信

协议:

  1. 空闲时 SCL/SDA 为高电平
  2. SCL高时 SDA下降沿 为开始信号
  3. 开始信号之后:

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

相关推荐
安科瑞刘鸿鹏4 小时前
校园建筑用电安全监测装置 电气火灾监测预防设备功能介绍
运维·服务器·网络·嵌入式硬件·安全·能源
Wx120不知道取啥名4 小时前
C语言之长整型有符号数与短整型有符号数转换
c语言·开发语言·单片机·mcu·算法·1024程序员节
Cici_ovo7 小时前
摄像头点击器常见问题——摄像头视窗打开慢
人工智能·单片机·嵌入式硬件·物联网·计算机视觉·硬件工程
无际单片机项目实战7 小时前
为什么STM32的HAL库那么难用,ST还是要硬推HAL库?
c语言·stm32·单片机·嵌入式硬件·物联网
正在努力的小立10 小时前
STM32 HAL 点灯
stm32·单片机·嵌入式硬件
TeYiToKu11 小时前
笔记整理—linux驱动开发部分(1)驱动梗概
linux·c语言·arm开发·驱动开发·嵌入式硬件
Whappy00112 小时前
3. STM32之TIM实验--输出比较(PWM输出,电机,四轴飞行器,智能车,机器人)--(实验1:PWM驱动LED呼吸灯)
stm32·嵌入式硬件·机器人
Java Fans14 小时前
嵌入式软件在电子烟开发中的应用
嵌入式硬件
环能jvav大师16 小时前
使用Ubuntu系统+VS Code开发STC51单片机
linux·c语言·开发语言·单片机·嵌入式硬件·ubuntu
q47259945117 小时前
UART通过DMA接收和发送,使用环形缓冲区,状态机的使用
stm32·单片机·嵌入式硬件