stm32之I2C通信协议

news2024/11/23 20:20:51

文章目录

  • 前言
  • 一、I2C通信协议
  • 二、I2C硬件电路
  • 三、I2C时序基本单元
    • 3.1 起始与终止信号
    • 3.2 发送与接收一个字节
    • 3.3 发送与接收应答
  • 四、I2C时序分析
    • 4.1 指定地址写
    • 4.2 当前地址读
    • 4.3 指定地址读


前言

提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者

本文主要探讨I2C通信协议。关于I2C通信的内容我主要会分为两大块来讲,第一块,就是介绍协议规则,然后用软件模拟的形式来实现协议。第二块,就是介绍stm32的I2C外设,然后用硬件来实现协议。因为I2C是同步时序,软件模拟协议也是非常方便,目前也存在很多软件模拟I2C的代码,所以我们先学习软件I2C,再学习硬件I2C。


一、I2C通信协议

I2C(Inter-Integrated Circuit)是一种广泛用于短距离通信的串行通信协议,主要用于连接低速外围设备,如传感器、EEPROM、ADC、DAC等,常见于嵌入式系统中。STM32微控制器系列中通常包含I2C接口,使得与各种外围设备进行通信变得非常方便。像下图中的外设也是支持I2C通信协议的,比如左图1的MPU6050模块,可以进行姿态测量。再比如说左图2OLED模块,可以显示字符之类的信息,也是采用I2C的通信协议。还有,左图3,AT24C02存储器模块,左图4,DS3231实时时钟模块等等。

在这里插入图片描述

二、I2C硬件电路

I2C是一种多主从(multi-master, multi-slave)同步半双工带数据应答的通讯协议,这意味着可以有多个主设备和多个从设备连接在同一条总线上。

I2C总线由两条线组成:

SCL(Serial Clock Line):时钟信号线,由主设备控制。
SDA(Serial Data Line):数据线,用于在主设备和从设备之间传输数据。

接下来我们就来详细分析一下,看看I2C的实现原理是怎样的吧!

在这里插入图片描述

如上左图所示就是I2C典型的电路模型了,这是一个一主多从的模型,CPU就是我们的单片机,作为总线的主机,主机的权力很大,包括对SCL线的完全控制,任何时候都是主机完全掌控SCL线的。另外,在空闲状态下,主机可以主动发起对SDA线的控制。只有在从机发送数据以及从机应答的时候,主机才会转交SDA线的控制权给从机,这就是主机的权力。

下面这四个都是被控的IC,也就是挂载在I2C总线上的从机。这些从机可以是姿态传感器、OLED、存储器、时钟模块等等。从机的权力比较小,对于SCL时钟线,在任何时刻都只能被动地读取,从机不允许控制SCL线。对于SDA数据线,从机不允许主动发起对SDA线的控制,只有在主机发送读取从机的命令后或者从机应答的时候,从机才能短暂地获取SDA线的控制权。

以上就是一主多从模型中协议的规定了。

我们先来看一下I2C的电路接线。如下图所示,I2C的接线要求是所有I2C设备的SCL连在一起,SDA连在一起。假设我们先忽略这两个电阻,那按照如下所示的接线方式,我们要如何规定每个设备的SCL和SDA的输入输出模式呢?

在这里插入图片描述

SCL应该好规定,因为现在是一主多从模式,主机拥有SCL的绝对控制权,所以主机的SCL可以配置为推挽输出模式,所有从机的SCL都配置成浮空输入或上拉输入。数据流向是主机发送,所有从机接收,这没问题,但是到SDA这里就比较麻烦了。

因为这是半双工的协议,所以主机的SDA在发送的时候是输出,在接收的时候是输入。同样,从机的SDA也会在输入和输出之间反复切换。如果你能协调好输入输出的切换时机那其实也没问题,但是这样做的话,如果总线时序没协调好,极有可能两个引脚同时处于输出的状态,如果这时又正好是一个输出高电平,一个输出低电平,那这个状态就是电源短路,这个状态是要极力避免的。

在这里插入图片描述

所以为了避免总线状态没协调好导致电源短路这个问题,I2C的设计是禁止所有设备输出强上拉的高电平,采用外置弱上拉电阻加开漏输出的电路结构。 具体来说就是设备的SCL和SDA均要配置成开漏输出模式,SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右。
在这里插入图片描述

如下图所示,所有的设备,包括CPU和被控IC,它引脚的内部结构都是如下图所示。左边这块是SCL的结构,这里SCLK就是SCL的意思。右边这一块是SDA的结构,这里DATA就是SDA的意思。

在这里插入图片描述

简而言之就是:

  • 所有I2C设备的SCL连在一起,SDA连在一起
  • 设备的SCL和SDA均要配置成开漏输出模式
  • SCL和SDA各添加一个上拉电阻,阻值一般为4.7KΩ左右

三、I2C时序基本单元

3.1 起始与终止信号

在这里插入图片描述

起始信号:SCL高电平期间,SDA从高电平切换到低电平
终止信号:SCL高电平期间,SDA从低电平切换到高电平

起始信号产生:

一开始,I2C总线处于空闲状态,SCL和SDA都被拉至高电平(SCL和SDA由外挂的上拉电阻拉高至高电平,总线处于平静的高电平状态)

当主机需要进行数据收发时首先就要打破总线的宁静产生一个起始信号。这个起始信号就是先把SDA电平拉低产生一个下降沿,此时,当从机捕获到SCL高电平时就会进行自身的复位,等待主机的召唤。然后在SDA产生下降沿后的一小段时间以后主机也把SCL拉低。拉低SCL一方面是为了占用这个总线,另一方面是为了方便基本单元的拼接。就是之后我们会保证除了起始和终止信号,每个时序单元的SCL都是以低电平开始,低电平结束,这样这些单元拼接起来SCL才能续得上。

终止信号产生:

终止条件是SCL高电平期间,SDA从低电平切换到高电平。也就是SCL先放手回弹到高电平,SDA再放手回弹到高电平同时产生一个上升沿信号,这个上升沿信号触发终止条件。同时终止条件之后SCL和SDA都是高电平,回归到最初平静的状态。

这个起始条件和终止条件就类似串口时序里的起始位和停止位。一个完整的数据帧总是以起始条件开始、终止条件结束的。另外,起始和终止都是由主机产生的,从机不允许产生起始和终止。所以在总线状态空闲时,从机必须始终双手放开不允许主动跳出来去碰总线。

3.2 发送与接收一个字节

发送一个字节:

在这里插入图片描述
SCL低电平期间,主机将数据位依次放到SDA线上(高位先行),然后释放SCL,从机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。

注意:SDA这里有两根实线表示的是主机控制的两根高低电平线,如果主机想要让这一位的数据置1就拉高,置0就拉低。

另外,由于这里有时钟线进行同步,所以如果主机的一个字节发送到一半突然进中断不操作SCL和SDA了,那时序就会在中断的位置不断拉长(此时SCL处于低电平状态,从机等待SCL切换到高电平才能读取SDA的数据),SCL和SDA电平都暂停变化,传输也完全停止了。等中断结束后主机回来继续操作传输仍然不会出问题,这就是同步时序的好处。

最后就是,由于这整个时序是主机发送一个字节,所以在这个单元里SCL和SDA全程都由主机掌控,从机只能被动读取。

接收一个字节:

在这里插入图片描述

SCL低电平期间,从机将数据位依次放到SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

注意:SDA这里的实线部分表示主机控制的电平,虚线部分表示从机控制的电平。SCL全程由主机控制,SDA主机在接收前要释放交由从机控制。

3.3 发送与接收应答

发送应答:

在这里插入图片描述

主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

意思大概就是主机在接收完一个字节之后(此时SCL处于高电平)把SCL拉低,然后再将SDA拉低表示应答,最后再把SCL拉高,从机检测到SCL产生上升沿后(SCL高电平)立刻读取SDA的数据来获取主机的应答状态。

接收应答:

在这里插入图片描述

主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

意思大概就是主机在发送完一个字节之后(此时SCL处于高电平)再把SCL拉低,把SDA的控制权交给从机,当从机收到SCL的下降沿后,若将SDA至低电平则表示应答,若什么也不做也就默认被外挂的上拉电阻拉高至高电平。最后主机再把SCL拉至高电平去读取SDA的数据去判断从机是否有应答。

那到这里,I2C的6块拼图就已经集齐了。分别是起始信号、终止信号、发送一个字节、接收一个字节、发送应答和接收应答。接下来我们就来拼接这些基本单元,组成一个完整的数据帧吧。

四、I2C时序分析

以下时序为在示波器下实际抓取的时序波形 ,其中黄色线表示为SCL,蓝色线表示为SDA,绿色线表示为读取数据的数据线,红色线为每个阶段时序的分隔线。(注意:不知道是什么原因部分图片有点失真,SCL和SDA均是连续的实线,大家知道一下就行了)

4.1 指定地址写

在这里插入图片描述

上图所示时序为对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)
具体就是对于指定从机地址为1101 000的设备,在指定地址为0x19下的寄存器中,写入数据0xAA。

空闲状态下SCL和SDA都处于高电平,当主机需要给从机写入数据时,首先SCL高电平期间拉低SDA,产生一个起始信号S(Start)。在起始信号之后紧跟着的时序必须是发送一个字节的时序,字节的内容必须是从机地址(7位)+读写位(1位)。具体就是SCL低电平期间,主机往SDA写入一位数据,然后再把SCL拉高,从机读取SDA上的一位数据,如此循环8次为写入一帧数据。然后根据协议规定,紧跟着的时序就是接收从机的应答位RA (Receive Ack),此时主机拉低SCL释放SDA,在主机释放SDA的瞬间从机将SDA拉低表示为应答,然后主机再把SCL拉高读取SDA获取从机应答的状态,若从机无应答则读取的SDA为高电平。

在这里插入图片描述

应答结束后,主机继续发送一个字节数据。跟上面发送一个字节数据的时序一样,同样的时序再来一遍,第二个字节就可以送到指定设备的内部了。从机设备可以自己定义第二个字节和后续字节的用途,一般第二个字节可以是寄存器地址或者是指令控制字等。比如MPU6050定义的第二个字节就是寄存器地址,比如AD转换器第二个字节可能定义的就是指令控制字,再比如存储器第二个字节可能定义的就是存储器地址。在我们这里第二个字节表示为寄存器地址,意思就是我要操作你0x19地址下的寄存器了,最后同样是从机应答把SDA拉低表示ok。

在这里插入图片描述

同样的流程再来一遍,主机再发送一个字节数据,这个字节就是主机写入到0x19地址下寄存器的内容了,然后就是从机接收应答。

如果主机不再需要继续传输数据了就可以产生一个停止信号 P(Stop) 。在产生停止信号之前先拉低SDA为后续SDA的上升沿作准备,然后释放SCL,再释放SDA,这样就产生了SCL高电平期间SDA的上升沿了。

在这里插入图片描述

到这里一个完整的写数据帧就完成了,这个数据帧的目的就是对于指定从机地址为1101 000的设备,在指定地址为0x19下的寄存器中,写入数据为0xAA。

4.2 当前地址读

在这里插入图片描述

上图所示时序为对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)
具体来说就是对于指定从机地址为1101 000的设备,在当前地址指针指示的地址下,读取到的从机数据为0x0F

一开始,主机在SCL高电平期间拉低SDA产生一个起始信号,然后主机发送一个字节数据来进行从机寻址和指定读写标志位。上图所示波形为主机寻址从机地址为1101 000的设备,同时最后一位读写标志为1,表示主机接下来想要读取数据。紧跟着,在发送一个字节后接收一个从机应答位。从机应答后主机释放SDA,从机往SDA写入一位数据,主机在高电平期间读取一位SDA数据,如此循环8次为读取一个字节数据,最后的结果就是主机读取到的数据为0x0F。

那现在问题就来了,这个0x0F的数据是从机哪个寄存器的数据呢?

在读的时序中I2C规定的是,主机进行寻址时一旦读写标志位给1,下一个字节就要立即转为读的时序,所以主机还来不及指定要读取的寄存器地址就得开始接收了,所以这里就没有指定地址这个环节。那主机并没有指定寄存器的地址,从机到底该发哪个寄存器的数据呢?这就需要用到上面我们说的当前地址指针了。

在从机中,所有的寄存器被分配到了一个线性区域中并且会有一个单独的指针变量指示着其中一个寄存器,这个指针上电默认一般指向0地址并且每写入和读出一个字节后这个指针就会自动自增一次移动到下一个位置。

那么在调用当前地址读的时序时,主机没有指定要读哪个地址,从机就会返回当前指针指向的寄存器的值。那按照这个特性,结合上面我们讲过的写的时序我们可以想一下,假设我刚刚在调用了指定地址写的时序,在0x19这个位置写入了0xAA这个数据,完了之后指针就会加1移动到了0x1A这个位置,此时我再调用这个当前地址读的时序,从机返回的就是0x1A这个当前地址,那我是不是就能读取到0x1A这个位置的数据了?

那我这样操作,假如我指定从机地址(1101 000)+写(0),收到从机应答后,再发送第二个字节寄存器地址(0x1A),完了之后不写数据了直接重新发起信号Sr(Start Repeat),然后再调用当前地址读的时序,这样是不是就能读到从机地址(1101 000)0x1A这个位置的数据了?

这个时序也就是接下来要讲的指定地址读的时序了。

4.3 指定地址读

在这里插入图片描述

上图所示时序为对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)
具体就是对于指定从机地址为1101 000的设备,在指定地址为0x19下的寄存器中,读取到的数据为0xAA。

上面我们已经讲了该时序的基本思路了,现在就来简单地分析一下。

首先最开始依然是主机先产生一个起始信号,然后发送第一个字节数据进行从机寻址(1101 000)+读写标志位(0表示为写),收到从机应答之后再发送第二个字节数据指定寄存器地址0x19,此时从机接收到这个时序后指针就指向了这个0x19地址的寄存器了,然后主机继续收取从机的应答信号。

在这里插入图片描述

然后我们不写入数据而是直接重新发起起始信号Sr(Start Repeat),然后主机再重新进行从机寻址(1101 000)+读写标志位(1表示为读),接着主机发起接收一个字节的时序,这个接收的字节数据就是地址为0x19的寄存器数据了。最后主机回复了一个非应答 SA(Send Ack) 也就是SDA为1表示停止接收数据了,从机收到主机的非应答信号之后释放SDA控制权,主机拿到SDA控制权后先把SDA拉低为产生上升沿作准备,最后主机先释放SCL回弹到高电平,然后再释放SDA回弹到高电平,同时产生一个上升沿信号触发结束信号。

注意:如果主机只想读一个字节就停止的话,那么在读完一个字节之后一定要给从机发个非应答SA(Send Ack)。非应答就是该主机应答的时候主机不把SDA拉低(在外挂的上拉电阻作用下拉高至高电平),SCL高电平期间从机读到SDA为1代表主机没有应答,从机收到非应答之后知道主机不想再继续了,从机就会释放SDA把控制权交还给主机。如果主机想连续读取多个字节就需要在最后一个字节给非应答,而在这之前的所有字节都要给应答。

在这里插入图片描述

还有另外一种方法,就是你可以不重新发起起始信号而是直接发停止信号P(Stop) ,然后再发起起始信号,这样也是可以的,只不过这样是两条时序而已。但是I2C协议官方规定的复合格式是一整个数据帧,就是先起始,再重复起始,再停止,相当于把两条时序拼接成一条。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2077073.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

刷机维修进阶教程-----紫光展讯芯片修改参数 修复基带 信号的一种步骤解析【二】

上期解析了一款紫光芯片机型 改写参数的步骤。今天来讲解另外一款紫光新机型修改参数的具体步骤。同类紫光展讯 展锐芯片机型可以参考尝试 通过博文了解; 1---紫光芯片机型新款机型改写参数的步骤解析 2----了解同类芯片修改参数的常用步骤 3----列举一些紫光芯片机型开启…

OpenStack常见模块详解

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

“NoSQL数据库技术及其应用”写作框架,软考高级,系统架构设计师

论文真题 随着互联网web2.0网站的兴起,传统关系数据库在应对web2.0 网站,特别是超大规模和高并发的web2.0纯动态SNS网站上已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展…

Zookeeper的watch机制是如何工作的?

ZooKeeper Watch 概述 ZooKeeper Watch 机制类似于 Java 设计模式中的观察者模式或者监听模式,唯一的不同是不再基于线程间通信,而是基于进程间通信。 ZooKeeper Watch 机制是指,客户端在所有的读命令上告知服务端:这个节点或者…

windows 提权方式汇总

windows 提权 一、土豆(potato)家族提权 原理 土豆提权就是通过 windows 的 COM(Component Object Model,组件对象模型)类。向指定的服务器端口发送 NTLM 认证,捕获认证数据包,并修改数据包内…

【C++】—— 内存管理

【C】—— 内存管理 1 C/C 的内存划分 1.1 C/C 的内存分布1.2 C/C 的内存分布练习 2 C语言 中动态内存管理方式:malloc/calloc/realloc/free3 C 内存管理方式3.1 new / delete 操作内置类型3.2 new 和 delete 操作自定义类型3.2.1 new 和 delete 操作自定义类型基础…

layui栅格布局设置列间距不起作用

layui栅格布局支持设置列间距,只需使用预置类layui-col-space*即可。不过实际使用时却始终看不到效果。   根据layui官网文档的说明,只需要在行所在div元素的class属性中增加layui-col-space*即可出现列间距。如下图所示:   但是实际使用…

【MySQL】MySQL 表的增删改查(进阶)

欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 目录 约束类型 not null 非空 unique 唯一 default 指定默认值 primary key 主键 foreign key 外键 check字句 检查 表设计 确定实体 实体之间的关系 聚合查询 聚合函数…

基于SSM的学生信息管理系统的设计与实现 (含源码+sql+视频导入教程+文档+VISIO图)

👉文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的学生信息管理系统12拥有三种角色:学生、教师、管理员 学生:选课、查看已选课程、查看成绩 教师:成绩管理 管理员:课程管理、学生…

ai变声:视频怎么变音?分享6个语音变声器,视频变声不再难!

想过如何让自己的直播内容更吸引人吗?你是否希望通过变声器来打造独特的声音效果?或者,如何用创意声音提升观众的互动体验呢?随着直播行业的不断发展,每位主播都在努力寻找吸引观众的独特方式,而变声器正是…

【电脑使用耳机录音注意事项】

文章目录 电脑音设置 电脑音设置 打开声音设置:右键小喇叭 → 选择“声音(S)”→选择 “录制”: 选择 “阵列麦克风” 调整声音大小: 音频增强设置

AI大模型编写多线程并发框架(六十二):限流和并发度优化

系列文章目录 文章目录 系列文章目录前言一、项目背景二、第三轮对话-补充异步执行代码三、第四轮对话-增加限流器四、第五轮对话-抽取限流器接口五、第六轮对话-修改并发度三、参考文章 前言 在这个充满技术创新的时代,AI大模型正成为开发者们的新宠。它们可以帮助…

何为MethodHandles?

最近在梳理ThreadPoolExecutor,无意间看到其内部类Worker实现了一个名字叫做AbstractQueuedSynchronizer的抽象类。看到它,我便想起当年为了面试而疯狂学习这个知识点的场景。不过这种临时抱佛脚的行为,并未给我带来即时的收益。也是这次的疯…

基于Java的高校学生工作系统的设计与实现(论文+源码)_kaic

基于Java的高校学生工作系统的设计与实现(论文源码)_kaic 摘 要 本系统为高校学生工作管理系统,系统能够为高校提供便捷的学生信息管理功能。该系统采用 Java 语言编写,系统采用MVC架构进行设计,通过Servlet和JSP等技术实现前后端数据交互和…

【漏洞复现】SuiteCRM responseEntryPoint Sql注入漏洞

免责声明: 本文内容旨在提供有关特定漏洞或安全漏洞的信息,以帮助用户更好地了解可能存在的风险。公布此类信息的目的在于促进网络安全意识和技术进步,并非出于任何恶意目的。阅读者应该明白,在利用本文提到的漏洞信息或进行相关测…

基于UDS的Flash 刷写——BootLoad刷写流程详解

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者,时光不负有心人。 目录 流程概述UDS流程详解释前编程①诊断会话控制 - 切换到扩展会话(10 03)②例程控制-预编程条件检查(31 01 02 03)③DTC…

ClickHouse分布式部署搭建单分片二副本集群

搭建单分片二副本集群,使用MergeTree引擎测试数据同步 服务器: 127.0.0.1 clickhouse 127.0.0.2 clickhouse + keeper 结构图 1.修改hosts vi /etc/hosts 添加需要部署的ip和名字 127.0.0.1 node1 127.0.0.2 node2 2. node1配置文件修改 2.1 修改/etc/clickhouse-se…

Excel中使用VBS自定义函数将中文转为拼音首字母

1、在“开发工具”中&#xff0c;点击“Visual Basic”。如果没有“开发工具”&#xff0c;则添加。 2、添加“模块”&#xff0c;在窗口中添加自定义函数。 Function MyGetPYChar(char) MyCodeNumber 65536 Asc(char) If (MyCodeNumber > 45217 And MyCodeNumber <…

【网络安全】缓存配置错误导致授权绕过

未经许可,不得转载。 文章目录 正文复现正文 一个电子商务网站,它有 2 个资产:target.com和admin.target.com target.com是面向用户的门户,用户可以去那里购买物品。admin.target.com是卖家的管理门户,卖家可以在其中列出他们的物品,跟踪订单、客户信息等。 我正在测试…

有希带你深入理解指针(3)

前言 本篇文章是对指针知识的进一步讲解&#xff0c;如果对部分知识有不了解的地方可以移步前文进行学习&#xff01; 1.字符指针变量 该内容我们在前面的文章中已提到过&#xff0c;想必大家对它应该不陌生吧&#xff01;这里我们会对它进行详细的介绍。 一般情况下&#xf…