I2C通信

article/2025/10/24 16:03:45

  I2C总线是PHLIPS公司推出的一种串行总线,它只有两根双向信号线。一根是数据线SDA(serial data I/o),另一根是时钟总线SCL(serial clock)。

如下图所示,I2C总线上可以挂接多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。

I2C总线的搭建

因为任何带有I2C硬件接口的器件都是开漏I型I/O口,不具备输出高电平的能力,所以2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连接到总线上的任一器件输出的低电平都将使总线的信号变低,即各器件的SDA及SCL都是“线与”关系,如下图所示。

 I2C总线传输协议

在I2C总线通信的过程中,参与通信的双方互相之间所传输的信息种类归纳如下。

  主机向从机发送的信息种类有:启动信号、停止信号、7位地址码、读/写控制位、10位地址码、数据字节、重启动信号、应答信号、时钟脉冲。

   从机向主机发送的信息种类有:应答信号、数据字节、时钟低电平。

1、传送数据的有效性规定

SCL为高电平期间,数据线SDA上的数据必须保持稳定,只有当SCL信号为低电平期间,SDA状态才允许变化。在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。

时序图如下图所示

I2C的起始信号与终止信号

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号,它标志着一次数据传输的开始。SCL为高电平期间,SDA线由低电平向高电平的变化表示终止信号,它标志着传输数据结束。

时序图如下图所示

I2C的字节传送与应答

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一个应答位(即一帧共有9位)。应答位即主机向从机发送数据时,

时序图如下图所示

应答位(ACK)的作用

I2C总线上的所有数据都是以8位字节传送的,主机每发送一个字节,就在时钟脉冲9期间释放数据线,由从机反馈一个应答信号。

主机在发送数据时,每次发送一字节的数据,都需要读取从机的应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其它工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”),主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据,对于反馈有效应答位ACK的要求是,从机在第9个时钟脉冲之前的低电平期间,将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号,这个信号是由对从机的“非应答”来实现的,然后,从机释放SDA线,以允许主机产生终止信号。

插入等待时间

如果从机需要延时下一个数据字节开始传送的时间,则可以通过把时钟总线SCL电平拉低并且保持,使主机进入等待状态,如果从机释放时钟总线,数据就可以被继续传输下去,这样就使得从机得到足够的时间转移已收到的数据,或者准备好即将发送的数据。

I2C写数据流程

 

 如上图所示,S代表START ,首先主机先发送一个起始信号,然后接着发送7位器件地址+第八位数据传送方向位0/1(“0”代表主机发送数据,“1”代表主机接收数据),发送完地址后,等待从机应答(灰色为主机所发送的,白色为从机负责发送的),当从机应答后,主机会发送向从机写入数据的首地址,从机应答后,主机即向从机发送数据,每发送完一位,等待一从机一次应答,数据传输完成后,主机发送一个停止信号即完成了数据的传输。

时钟总线为高电平期间,数据可以被读取,当SCL为低电平期间,可以向数据总线SDA线上写数据。

I2C读数据流程

主机线发送一个起始信号,然后发送一个从机地址代表选择读取哪个从机的数据,读写方向为写(因为是给从机发送地址信号),然后等待从机应答,应答后,主机选择要从从机的的哪个内存地址开始读,然后不管从机是否应答,主机即开始再次发送一个起始信号,然后接着发送从机地址,读写方向为读,然后从机应答后开始向主机发送数据,发送完成后主机发送一个非应答信号和停止信号,即完成了I2C读数据的流程。

使用I2C通信时通常要注意几点

下面即为软件模拟I2C时序

#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int

#define slave_address 0x00   //假设从机地址位0x00

sbit SDA = P2^0;                   //假设P2.0和P2.1为I2C的数据总线与时钟总线
sbit SCL = P2^1;

/******************************
** 函数名称:
** 函数功能:软件延时
*******************************/
void delay(uchar i)
{
  uchar j,k; 
  for(j=i;j>0;j--)
  for(k=125;k>0;k--);
}
/******************************
** 函数名称:
** 函数功能:I2C初始化
*******************************/
void I2C_Init()//I2C初始化,使总线处于空闲状态
{
        SCL = 1;  //拉高SDA、SCL,使总线处于空闲状态
        _nop_();
        SDA = 1;
        _nop_();
}
/******************************
** 函数名称:
** 函数功能:I2C起始信号
*******************************/
void I2C_Start() //SCL为高电平期间,SDA负跳变1->0
{
        SCL = 1;    
        _nop_();
        SDA = 1;
        delay(5);
        SDA = 0;
        delay(5);
}

/******************************
** 函数名称:
** 函数功能:I2C终止信号
*******************************/
void I2C_Stop()  //SCL为高电平期间,SDA正跳变          0->1
{
        SDA = 0;
      _nop_();
        SCL = 1;
        delay(5);
        SDA = 1;
        delay(5);
}

/******************************
** 函数名称:
** 函数功能:主机发送应答
*******************************/
void Master_ACK    (bit i)        //主机发送应答
{
        SCL = 0;             //拉低时钟总线允许SDA数据总线变化
        _nop_();                //让总线稳定
        if(i)                    //若果i等于1,则拉低时钟线,表示主机应答
        {
            SDA = 0;        //表示主机应答
        }
        else
        {
            SDA = 1;        //发送非应答
        }
        _nop_();            //让总线稳定
        SCL = 1;            //拉高时钟总线,让从机从SDA数据线上读走主机的应答信号
        delay(5);
        SCL = 0;            //拉低时钟总线,占用总线,使双方继续通信
        _nop_();
        SDA = 1;            //释放数据总线,交由从机控制
        _nop_();
        
}

/******************************
** 函数名称:
** 函数功能:应答检测
*******************************/
bit Test_ACK()
{
        SCL = 1;        //SCL时钟总线为高电平时才能读到SDA数据线上的数据
        delay(5);
        if(SDA)
        {
            SCL = 0;
            _nop_();
            I2C_Stop();
            return(0);        //如果为非应答返回0
        }
        else
        {
            SCL = 0;
            _nop_();
            return(1);
        }        
}
 
/******************************
** 函数名称:
** 函数功能:发送一个字节
*******************************/
void I2C_send_byte(uchar byte)
{
    uchar i;    
    for(i = 0 ; i < 8 ; i++)
    {
        SCL = 0;
        _nop_();
        if(byte & 0x80)
        {
            SDA = 1;
            _nop_();
        }
        else
        {
            SDA = 0;
            _nop_();
        }
        SCL = 1;
        _nop_();
        byte <<= 1;
    }
        SCL = 0;        //占用时钟总线
        _nop_();
        SDA = 1;        //释放数据总线
        _nop_();
}

/******************************
** 函数名称:
** 函数功能:i2c读一个字节
*******************************/
uchar I2C_read_byte()
{
        uchar I2C_data,i;
        SCL = 0;
        _nop_();
        SDA = 0;
        for(i = 0 ; i < 8 ; i++)
        {
            SCL = 1;
            _nop_();
            I2C_data <<= 1;  //读完最后一位数据时不会再次移位
            if(SDA)
            {
                I2C_data = I2C_data | 0x01; 
            }
            else
            {
                I2C_data = I2C_data & 0xfe;
            }
            _nop_();
            SCL = 0;
            _nop_();
        }
        return(I2C_data);
}

/******************************
** 函数名称:
** 函数功能:i2c发送数据
*******************************/
bit I2C_transmit_data(uchar ADDR,DAT)
{
        I2C_Start();    //SCL为高电平期间SDA负跳变
        I2C_send_byte(slave_address + 0); //发送从机地址
        if(!Test_ACK) //检测应答,如果为非应答Test_ACK则返回0,则条件成立
        {
            return(0);
        }
        I2C_send_byte(ADDR);//发送往从机的写地址
        if(!Test_ACK) //如果为非应答
        {
            return(0);
        }
        I2C_send_byte(DAT);//传送8位数据,一位一位传送,最高位先开始
        if(!Test_ACK) //检测应答,如果为非应答
        {
            return(0);
        }
        I2C_Stop();        //停止信号,SCL为高电平期间,SDA正跳变
        return(1);
}

/******************************
** 函数名称:
** 函数功能:i2c读数据
*******************************/
uchar I2C_receive_data(uchar ADDR)
{
        uchar DAT;
        I2C_Start();
        I2C_send_byte(slave_address + 0);  
        if(!Test_ACK) //如果为非应答
        {
            return 0;
        }
        I2C_send_byte(ADDR);
        if(!Test_ACK) //如果为非应答
        {
            return 0;
        }
        Master_ACK(0);
        I2C_send_byte(slave_address + 1);
        if(!Test_ACK) //如果为非应答
        {
            return 0;
        }
        DAT = I2C_read_byte();
        Master_ACK(0);
        I2C_Stop();
        return(DAT);
}
void main()
{
        
        I2C_Init();        //初始化,使总线保持空闲状态
        I2C_transmit_data(255,0xf0);
        delay(5);  //软件延时,为了使能够完整的读取出数据
        I2C_receive_data(255);
        while(1);
        
        
}

 


http://chatgpt.dhexx.cn/article/Nnspv2Dk.shtml

相关文章

Linux 文件编程 read函数

read函数的功能是向以打开的文件读取数据。 read函数需要包含头文件 &#xff1a;#include <unistd.h>。 read函数的原型为&#xff1a; ssize_t read(int fd, void *buf, size_t count); 其中&#xff0c;fd为文件描述符&#xff1b;buf表示读出数据缓冲区地址&…

c语言read有什么作用,c语言read函数读到什么结束

linux下c语言编程read()函数的问题 返回-1的时候&#xff0c;要根据错误码来判断原因&#xff0c;请看下面的函数说明&#xff1a; 表头文件 #include 定义函数 ssize_t read(int fd,void * buf ,size_t count); 函数说明 read()会把参数fd 所指的文件传送count个字节到buf指针…

深入了解C read/write函数和cat命令实现

1&#xff0e;文件描述符 文件描述符&#xff08;file descriptor&#xff09;通常是一个小的非负整数&#xff0c;内核用以 标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一 个新文件时&#xff0c;它都返回一个文件描述符。在读、写文件时&#xff0c;可以使…

极路由最新刷机教程

本文参考&#xff1a;点击打开链接 前提条件 极路由1s、2、3、4&#xff0c;新老版本都可以&#xff0c;固件版本要求9006~9018。但实际上9019以及最新的1.0.xxx也都支持&#xff0c;只不过必须使用ss插件直达页面去操作ss&#xff0c;ss功能完美&#xff0c;就是界面有点简陋。…

极路由(极壹S)HC5661安装OpenWrt及救砖过程

极路由(极壹S)HC5661安装OpenWrt及救砖过程 文章目录 极路由(极壹S)HC5661安装OpenWrt及救砖过程1.免插件离线Root1.1 点击 获取local_token1.2 点击 获取uuid1.3 获取cloud token1.4 开启ssh1.5 永久开启ssh 2.刷不死uboot(breed)2.1 核对路由器型号2.2 下载breed固件2.3 刷入…

ssh登录极路由后台_OpenWrt刷机详细流程(极路由)

要想深入理解某个软件,首先熟悉其安装步骤,亲自感受软件的强大。OpenWrt是Linux的一个发行版,是一个独立的操作系统,主要运行在嵌入式设备中,由于嵌入式硬件种类繁多,并且稍微不慎就可能导致板子变砖,这给开发者带来了很大麻烦。 本篇文章以极路由为例,详细记录了OpenW…

浙大玉泉校区路由器L2TP设置(极路由1s)

我的宿舍里的ZJUWLAN信号太不稳定了&#xff0c;所以打算用有线。房间里只有一个有线口&#xff0c;还离的很远&#xff0c;所以打算连个路由器&#xff0c;然后用wifi连接。 步骤如下&#xff1a; 申请IP 浙江大学学生公寓服务网&#xff1a; http://service.chinasinew.co…

极路由云平台倒闭,路由器免插件离线ROOT方法

极路由云平台停服&#xff0c;离线root方法 免插件离线ROOT方法 访问&#xff1a;http://www.hiwifi.wtf/ 步骤 1. 点击 获取local_token 注意该网页不要关闭&#xff0c;后续获得 cloud token&#xff0c;填入并提交&#xff0c;即可临时获得ssh权限 即&#xff1a;http…

极路由1s HC5661 编程器救砖教程

说明&#xff1a;极1s如果刷错了固件&#xff0c;比如刷了HC5661A之类的&#xff0c;那么&#xff0c;会出现亮不灯的死机情况&#xff0c;这个时候说明固件引导已经刷坏了&#xff0c;只能拆机用编程器救砖或者返厂维修。如果采用编程器的话&#xff0c;因为没有备份&#xff…

极路由器1S,HC5661A刷breed再刷老毛子华硕

极路由器1S-HC5661A刷breed再刷华硕老毛子&#xff0c;再次编辑补充HC5661不带A的教程 特别提醒&#xff1a; 本教程是教授大家如何对【极路由器1S&#xff0c;HC5661A】以及【hc5661不带A的】进行刷机&#xff0c;载入breed并刷入华硕老毛子固件&#xff08;以下简称老毛子&a…

更新极路由器1S,HC5661A刷breed再刷老毛子华硕教程

更新极路由器1S-HC5661A刷breed再刷老毛子老毛子教程&#xff0c;再次编辑附加HC5661不带A的 特别提醒&#xff1a; 2021年1月1日起&#xff0c;极路由器官网www.hiwifi.com域名到期&#xff0c;云平台正式关闭&#xff0c;之前我发布的极1S刷机教程是基于云平台获取root权限的…

极路由刷个人编译的openwrt固件全过程

说明 以下是极1s刷机的整个流程&#xff0c;包括openwrt 极路由固件的编译和升级流程&#xff0c;主要是为了方便发烧友或者开发人员。 准备工作 极壹S(型号HC5661)一台 串口板和串口线(如果是高级玩家) 电脑、网线等软件(vmware、tftpd、linux编译环境) 网络配置 极路由为…

极1s刷Padavan改华硕[N14U N54U]固件

打开极路由的开发者模式 打开开发者模式 登陆极路由的后台管理界面&#xff0c;点击云插件&#xff0c;进入到极路由云插件管理后点击路由信息&#xff0c;然后在最下面点击开通开发者模式&#xff0c;这里需要你放弃极路由的保修以及安装APP等一系列的工作&#xff0c;大家照…

在极路由极1S上使用entware

目录 打开ssh功能安装entware使用感想备份和还原entware注意程序开启启动参考文章 本想在极1S上安装些程序&#xff0c;结果登录ssh后发现系统自带的opkg有限制&#xff0c;即使更改为openWRT的源也不能安装&#xff0c;说各种不兼容。于是打算装entware。 entware是面向 Linux…

Drcom下如何使用路由器上校园网并开启WIFI(以广东工业大学、极路由1S HC5661A为例)

免责声明&#xff1a; 在根据本教程进行实际操作时&#xff0c;如因您操作失误导致出现的一切意外&#xff0c;包括但不限于路由器变砖、故障、数据丢失等情况&#xff0c;概不负责&#xff1b;该技术仅供学习交流&#xff0c;请勿将此技术应用于任何商业行为&#xff0c;所产生…

极路由1S HC5661A 刷入不死u-boot(breed)加刷潘多拉固件教程

原文来自&#xff1a;新版极路由1S&#xff08;HC5661A&#xff09;刷潘多拉加SS配置教程 不死uboot(breed)如何使用 下文是以原文为基础&#xff0c;更详细也更适合小白的整合版。 注意&#xff1a;本贴是新版的白盒极路由1S&#xff08;HC5661A&#xff09;教程&#xff…

极路由s1有wds_极路由1、1s等机型刷OpenWrt--成为真正的极客

0x00&#xff1a;我为什么买极路由 买极路由的时候&#xff0c;他们说这是个给极客打造的智能路由&#xff0c;一开始还因为国际版&#xff0c;被gov直接请去喝茶了。 当时觉得&#xff0c;这玩意能给APPStore加速&#xff0c;还能屏蔽广告&#xff0c;简直NB。那时候也没啥钱&…

极路由1S 开启临时ssh 并刷breed成功

手上有一台别人给的 极路由1s&#xff0c;刷的是不知名版本的openwrt版本&#xff0c;也无法远程ssh&#xff0c;也就无法刷新的固件玩玩。 以前没接触过软路由和路由系统&#xff0c;找了很多资料&#xff0c;都没法正常获取ssh权限。 折腾了一两天&#xff0c;结果发现我刷的…

极路由 刷linux,记一次 极路由1S HC5661 TTL root 刷 U-BOOT 不死固件 及 爱快固件-20200320更新...

18.png (15.32 KB, 下载次数: 8) 2016-7-14 16:55 上传 2、用PUTTY的SSH类型登陆192.168.199.1,端口号:22。login as输入root,password输入admin。 备份原厂固件,需要插入SD卡(不需要刷回原厂固件的就直接略过吧):root@Hiwifi:/#cat/proc/mtd #列出路由器分区情况m…

极路由 刷linux,极路由1s刷openwrt不完全教程

极路由是基于openwrt做的二次开发。cpu是mtk7620a属于mips架构。 如果对svn和编译比较熟悉的话&#xff0c;可以获取官方的源码&#xff0c;自行编译。 如果对这些一窍不通的话&#xff0c;建议安装我下面提供的教程一步步操作。 1&#xff0c;首先看你的极路由版本。如果是开发…