玩转STM32-I2C通信协议(详细-慢工出细活)

文章目录

  • 一、I2C总线原理(掌握)
    • 1.1 硬件构成
    • 1.2 传输位
    • 1.3数据传输格式
  • 二、STM32的I2C特性和结构
  • 三、STM32的I2C通信实现(硬件实现方式)
    • 3.1 I2C主模式
  • 四、应用实例

一、I2C总线原理(掌握)

1.1 硬件构成

I2C总线由串行数据线SDA和串行时钟线CL构成,总线上的每个器件都有一个唯一的地址。I2C总线规范要求SDA和SCL可双向通信,即一个器件既可以接收,也可以发送数据或时钟,因此I2C信号线SDA和SCL采用开集电极输出或开漏极输出方式。I2C总线必须通过上拉电阻或电流源才能够正确收发数据。
I2C总线接口内部等效电路包括输入缓冲电路与开集电极输出晶体管或开漏极MOS管。当总线处于空闲状态时,由于上拉电阻的作用,总线呈现高电平,如果某个芯片需要输出数据,可以通过输出驱动实现数据传输。开集电极出书电路有一个缺点:随着总线长度增加,输出等效电容也随之增加,上拉电阻将严重影响总线通信速度。原因是信号变化要通过RC充放电回路,从而降低了信号的转换速度。为了克服I2C总线这个缺点,NXP公司开了有源I2C总线终端,它采用两个互联的充电泵来等效上拉电阻,信号变化瞬间有源器件可以提供相当大的充放电电流,加快信号转换速率,降低寄生电容的影响。

1.2 传输位

  1. 数据有效性
    I2C总线以串行方式传输数据,数据传输是按照时钟节拍进行的。时钟线每产生一个时钟脉冲,数据线传输一位数据。I2C总线协议标准规定,SDA线上的数据必须在时钟线为高电平时保持稳定,数据线电平状态只能在时钟线位低电平时改变,在标准模式下,高低电平宽度必须不小于4.7us,I2C数据有效示意图如下:
    在这里插入图片描述
  2. 起始条件和停止条件
    当时钟线为高电平时,如果SDA数据为逻辑高电平,则代表数字1,如果SDA数据线为低电平时,则代表数据0.除此之外,在SCL为高电平时,还会有数据线SDA出现上升沿后下降沿等两种状态。I2C总线协议规定,SCL时钟线为高电平时且SDA为下降沿表示起始信号,SCL时钟线为高电平且SDA为上升沿表示停止信号。I2C总线数据传输必须以起始信号启动传输,以停止信号结束一次数据传输,I2C起始位和停止位如下:
    在这里插入图片描述
  3. 重复开始信号
    在I2C总线上,由主机发送一个起始位,启动一次数据传输后,在发送停止位前,主机可以再发送一次起始位,这个信号称为重复起始位。它可以帮助主机再不丧失总线控制权的前提下改变数据传输方向或切换到与其他从机通信,它的实现方法是再时钟信号为高电平时,SDA由高电平向低电平跳变,产生一个重复起始位,它本质上就是一个起始位。
  4. 应答信号与非应答信号
    I2C总线协议规定,发送器每发送一个字节(8bit)数据,接收器必须产生一个应答信号或非应答信号。实现方法是,发送器发送完8位数据后,第9个时钟信号将数据线置高电平,接收器根据通信状态可以将数据线拉低,产生一个应答信号;或保持数据线为高电平,产生一个非应答信号。

1.3数据传输格式

一般情况下,一个标准I2C通信由四部分组成:起始信号、从机地址、数据传输、停止信号。I2C通信由主机发送一个起始信号来启动,然后由主机对从机寻址并决定数据传输方向。I2C总线上传输数据的最小单位是一个字节,首先发送数据位最高位,每传送完一个字节,接收器必须发送一个应答位,如果数据接收器来不及处理数据,可以通过拉低时钟线SCL来通知数据发送器暂停传输;每次通信的数据字节数是没有限制的;全部数据传送结束后,由主机发送停止信号,结束通信。I2C通信时序如下:
数据手册

  1. I2C总线寻址约定
    I2C总线采用软件方法实现从机寻址来简化总线连接,I2C总线采用了独特的寻址约定,规定了起始信号后的第一个字节位寻址字节,用来寻址被控器件,并规定数据传输方向。目前I2C支持7位寻址方式和10位寻址方式,为了使读者更容易理解I2C操作方式,重点解释7位寻址模式,再掌握7位寻址模式后,可以很容易的理解10位寻址模式。
    在7位寻址模式中,寻址字节由从机的7位地址位(D7~D1)和1位读写位(D0)组成。当读写位D0=1时,表示从下一个字节开始主机从从机读取数据;当读写位D0=0时,表示从下一个字节开始主机将数据传输给从机。主机发送起始信号后立即传送寻址字节,总线上的所有器件都将寻址字节中的7位地址与自己的地址比较,如果两者相同,则该器件认为被主机寻址,并发送应答信号,寻址字节中的读写位决定了主机和从机时发送器还是接收器。
    主机作为被控器时,其7位地址在I2C总线地址寄存器中给出,为软件地址,而非单片机类型的外围器件地址,完全由器件类型与引脚电平给定。在I2C总线中,不允许有两个地址相同的器件,否则就会造成传输错误。
  2. 数据传世模式
    1)主机从从机读取N个字节
    主机首先产生起始信号,然后发送寻址字节,寻址字节传输完毕,主机释放数据线(数据线拉高),并产生一个时钟信号,等待被寻址器件应答信号。
    被寻址器件一旦检测到寻址地址与自己的地址相同则产生一个应答信号,从机发送完应答信号后,开始发送数据。从机每发送完一个字节数据,主机产生一个应答信号。
    当数据传送完毕后,主机产生一个非应答信号结束数据传输,然后主机产生一个停止信号结束通信或产生一个重复起始信号进入下一次数据传输。
    在数据传输过程中,主机随时可以产生非应答信号来提前结束本次数据传输。
    2)主机向从机写N个字节
    主机首先产生起始信号,然后发送寻址字节,寻址自己传输完成后,根据D0为判断时读取还是发送数据,主机产生一个时钟信号,等待从记得应答信号。
    被寻址器件一旦检测到寻址地址与自身的地址相同,则产生一个应答信号,主机收到应答信号后,开始发送数据。主机没发送一个字节的数据,从机产生一个应答信号。
    当数据传送完毕后,主机产生一个停止信号结束数据传输或产生一个重复起始信号进入下一次数据传输。
    3)重复起始位
    当主机在访问类似存储器器件时,主机除了发送寻址地址字节来确定从机外,还要发送存储单元地址内容;如果需要读取存储单元数据,存在着先写后读的情况,为了解决这个问题,可以利用重复起始信号来实现这个过程:
    主机首先按照(2)中的主机向从机写入多字节数据,将存储单元地址写入从机,数据传输结束后并不产生停止信号而是产生一个重复起始位,然后发送寻址字节。寻址字节中,读写位D0=1,然后等待从机应答,从机发完应答位后,开始将数据传输给主机,然后执行过程和(1)中相同。
    重复起始位开可以让主机在不丧失总线控制权的情况下,寻址下一个器件,与另外一个从机进行通信。
    4)冲裁与同步
    所有主机在SCL线上产生自己的时钟来传输,I2C总线上的数据只有适中的高电平周期有限,因此需要一个确定的时钟进行逐位仲裁。

二、STM32的I2C特性和结构

STM32的I2C模块具有4中工作模式,即主发送器模式、主接收器模式、从发送器模式、从接收器模式。下图为I2C内部结构:
来自数据手册

三、STM32的I2C通信实现(硬件实现方式)

3.1 I2C主模式

在主模式时,,I2C接口启动数据传输并产生时钟信号。串行数据传输总是以起始条件开始并以停止条件结束。当通过START位在总线上产生了起始条件,设备就进入了主模式。 以下是主模式所要求的操作顺序:
● 在I2C_CR2寄存器中设定该模块的输入时钟以产生正确的时序
● 配置时钟控制寄存器
● 配置上升时间寄存器
● 编程I2C_CR1寄存器启动外设
● 置I2C_CR1寄存器中的START位为1,产生起始条件
I2C模块的输入时钟频率必须至少是:
● 标准模式下为:2MHz
● 快速模式下为:4MHz

  1. 发送起始条件
    当总线空闲(BUSY = 0)时,发送起始信号(START = 1),I2C接口将产生一个起始信号并切换至主模式。在主模式下,设置START位将在当前字节传输完成后由硬件产生一个重开始条件,起始信号一旦发出,SB位被硬件置位,如果中断未屏蔽,则会产生一个中断。然后主设备等待读状态寄存器SR1,接着将从地址写入DR寄存器。
  2. 从地址的发送
    从地址通过内部移位寄存器被送到SDA线上。下图中,在7位地址模式时,只需送出一个地址字节。一旦该地址字节被送出,ADDR位被硬件置位,如果中断允许,则产生一个中断。然后主设备等待一次读SR1寄存器,读SR2寄存器。
  3. 发送数据
    在发送地址和清除ADDR位后,将等待发送的数据写入数据寄存器DR,I2C模块通过内部移位寄存器将数据字节从DR寄存器发送到SDA线上。主设备等待发送完毕即TxE被清除,如下图EV8事件。
    当收到应答脉冲时,TxE位被硬件置位,如果允许中断,则产生一个中断。如果TxE位置位并且在上一次数据发送结束之前没有写入新的数据字节到DR寄存器,则BTF被置位,I2C模块拉长时钟线等待数据写入DR数据寄存器,数据写入后将BTF清除,I2C继续发送数据。
  4. 停止和结束
    在DR寄存器中写入最后一个字节后,通过设置STOP位产生一个停止条件,如下图EV8_2,然后I2C接口将自动回到从模式。
    下图是I2C主模式下的数据发送示意图:
    在这里插入图片描述

四、应用实例

  1. 利用I2C控制OLED显示屏
  2. 实例代码
    1)I2C硬件配置
void OledDriver_Init(void)
{OledDriver_GPIO_Configuration();OledDriver_I2C1_Configuration();
}
//=============================================================================
//文件名称:OledDriver_GPIO_Configuration
//功能概要:OLED显示屏引脚配置
//参数说明:无
//函数返回:无
//=============================================================================
void OledDriver_GPIO_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB , ENABLE);GPIO_InitStructure.GPIO_Pin = GPIO_OLED_I2C1_SCL_PIN | GPIO_OLED_I2C1_SDA_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIO_OLED_I2C1_PORT, &GPIO_InitStructure);
}
//=============================================================================
//文件名称:OledDriver_I2C2_Configuration
//功能概要:OLED显示屏I2C配置
//参数说明:无
//函数返回:无
//=============================================================================
void OledDriver_I2C1_Configuration(void)
{I2C_InitTypeDef I2C1_InitStructure;RCC_APB1PeriphClockCmd( RCC_APB1Periph_I2C1, ENABLE); I2C1_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C1_InitStructure.I2C_ClockSpeed = I2C1_SPEED;I2C1_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C1_InitStructure.I2C_OwnAddress1 = I2C1_OWN_ADDRESS1;I2C1_InitStructure.I2C_Ack = I2C_Ack_Enable;I2C1_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;I2C_Init(OLED_I2C,&I2C1_InitStructure);I2C_Cmd(OLED_I2C,ENABLE);I2C_AcknowledgeConfig(OLED_I2C, ENABLE);
}
//=============================================================================
//文件名称:I2C1_Byte_Write
//功能概要:I2C写一个字节
//参数说明:无
//函数返回:无
//=============================================================================
void I2C1_Byte_Write(uint8_t addr, uint8_t data)
{// 发送开始信号I2C_GenerateSTART(OLED_I2C,ENABLE);// 检查EV5事件while(!I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_MODE_SELECT));  // 检测是否作为主机,开始信号是否成功// 发送设备写地址I2C_Send7bitAddress(OLED_I2C,OLED_SLAVE_WRITE_ADDR,I2C_Direction_Transmitter);// 检查EV6事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);  // 检查地址是否发送成功// 发送设备内部地址I2C_SendData(OLED_I2C,addr);// 检查EV8_1事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);  // 检查移位寄存器中是否还有数据// 发送数据I2C_SendData(OLED_I2C,data);// 检查EV8_2事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);  // 检查移位寄存器中是否还有数据// 发送停止信号I2C_GenerateSTOP(OLED_I2C,ENABLE);
}
//=============================================================================
//文件名称:I2C1_Page_Write
//功能概要:I2C写多个字节
//参数说明:无
//函数返回:无
//=============================================================================
void I2C1_Page_Write(uint8_t addr, uint8_t *pdata,uint16_t Num_ByteToWite)
{
//	I2CTimeout = I2CT_LONG_TIMEOUT;// 发送开始信号I2C_GenerateSTART(OLED_I2C,ENABLE);// 检查EV5事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR); // 发送设备写地址I2C_Send7bitAddress(OLED_I2C,OLED_SLAVE_WRITE_ADDR,I2C_Direction_Transmitter);// 检查EV6事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);  // 检查地址是否发送成功// 发送设备内部地址I2C_SendData(OLED_I2C,addr);// 检查EV8_1事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);  // 检查移位寄存器中是否还有数据while(Num_ByteToWite){I2C_SendData(OLED_I2C,*pdata);Num_ByteToWite--;pdata++;}// 检查EV8_2事件while(I2C_CheckEvent(OLED_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR); // 发送停止信号I2C_GenerateSTOP(OLED_I2C,ENABLE);
}

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

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

相关文章

day19--IO流(二)

day19-IO流(二) 一、字符流 同学们,前面我们学习了字节流,使用字节流可以读取文件中的字节数据。但是如果文件中有中文使用字节流来读取,就有可能读到半个汉字的情况,这样会导致乱码。虽然使用读取全部字…

Harmony OS 开发指南——源码下载和编译

本文介绍了如何下载鸿蒙系统源码,如何一次性配置可以编译三个目标平台(Hi3516,Hi3518和Hi3861)的编译环境,以及如何将源码编译为三个目标平台的二进制文件。 坑点总结: 下载源码基本上没有太多坑&#xff…

爬虫案例-亚马逊反爬分析-验证码突破(x-amz-captcha)

总体概览:核心主要是需要突破该网站的验证码,成功后会返回我们需要的参数后再去请求一个中间页(类似在后台注册一个session),最后需要注意一下 IP 是不能随意切换的 主要难点: 1、梳理整体反爬流程 2、验证…

【C++初阶】--- C++入门(中)

目录 一、缺省参数1.1 缺省参数概念1.2 缺省参数分类 二、函数重载2.1 函数重载概念2.2 C支持函数重载的原理 --- 名字修饰 三、引用3.1 引用概念3.2 引用特性3.3 常引用3.4 使用场景3.5 引用和指针的区别 一、缺省参数 1.1 缺省参数概念 缺省参数是声明或定义函数时为函数的…

数据挖掘与机器学习——回归分析

目录 回归分析定义: 案例: 线性回归 预备知识: 定义: 一元线性回归: 如何找出最佳的一元线性回归模型: 案例: python实现: 多元线性回归 案例: 线性回归的优缺…

大疆、猛犸、西圣无线麦克风哪个牌子好?大疆、西圣麦克风测评PK

在当今这个全民参与媒体创作的时代,视频分享已经崛起为引领流行文化的关键力量。随着自媒体领域的竞争日益白热化,要想在浩瀚的内容海洋中脱颖而出,创作出卓越的作品无疑是吸引观众目光的核心要素。然而想要成功打造这样的佳作,除…

学习Uni-app开发小程序Day21

学习了评分组件、自定义导航栏 评分组件uni-rate 这是需要达到的效果图,这里先分析下效果图, 1、图片是从布局中间弹出的,那这里就要用到uni-popup ,设置type从中间弹出 2、这个弹出的顶部和上一张的顶部布局是一样的&#xff0c…

水电表远程抄表:智能化时代的能源管理新方式

1.行业背景与界定 水电表远程抄表,是随着物联网技术发展,完成的一种新型的能源计量管理方式。主要是通过无线传输技术,如GPRS、NB-IoT、LoRa等,将水电表的信息实时传输到云服务器,进而取代了传统人工当场抄水表。这种…

目标检测——家庭日常用品数据集

引言 亲爱的读者们,您是否在寻找某个特定的数据集,用于研究或项目实践?欢迎您在评论区留言,或者通过公众号私信告诉我,您想要的数据集的类型主题。小编会竭尽全力为您寻找,并在找到后第一时间与您分享。 …

CentOS 7如何使用systemctl管理应用

说明:本文介绍如何使用systemctl命令的方式来启动、查看、停止和重启应用,以安装后的prometheus、alertmanager为例; Step1:创建文件 在系统/etc/systemd/system/路径下,创建一个xxx.service文件,该文件内…

【最优化方法】实验二 一维搜索方法的MATLAB实现

实验二  一维搜索方法的MATLAB实现 实验的目的和要求:通过本次实验应使学生掌握如何使用MATLAB软件进行一维搜索,并学会对具体问题进行分析。 实验内容: 1、0.618法的MATLAB实现 2、Fibonacci法的MATLAB实现 学…

机器学习(五) -- 监督学习(4) -- 集成学习方法-随机森林

系列文章目录及链接 上篇:机器学习(五) -- 监督学习(3) -- 决策树 下篇:机器学习(五) -- 监督学习(5) -- 线性回归1 前言 tips:标题前有“***”…

150.二叉树:二叉树的后序遍历(力扣)

代码解决 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr, right(nullptr) {}* Tree…

Autoware 技术代码解读(三)

Tip: 如果你在进行深度学习、自动驾驶、模型推理、微调或AI绘画出图等任务,并且需要GPU资源,可以考虑使用Compshare的GPU算力云平台。他们提供高性价比的4090 GPU,按时收费每卡2.6元,月卡只需要1.7元每小时,并附带200G…

​一款开源的.NET程序集反编译、编辑和调试神器

前言 说到.NET相关的反编译工具大家脑海里第一个想到的工具是什么?ILSpy、dnSpy、还是dotPeek?咱们今天的主要内容是讲讲dnSpyEx(dnSpyEx是dnSpy项目的非官方Fork维护版本)这个开源的.NET程序集反编译、编辑和调试工具该如何使用…

Burnout Drift Multiplayer(多人赛车游戏模板)

Burnout Drift Multiplayer是一个完整的项目,已在PC、Android、iOS和WebGL上发布的Burnout Drift系列中使用。 [包括最新版本的Realistic Car Controller Pro] PC、WebGL和Android演示 包括Burnout Drift系列的所有功能。您只需几分钟即可轻松添加自己的车辆、场景和配乐。 警…

Windows 下载安装Apache

一、官网下载 1、打开Apache官网http://httpd.apache.org,点击Download。 2、选择Windows版本,点击链接。 3、选择对应版本选择下载。 二、安装、设置 1、将下载好的解压。 2、依次打开Apache24-conf-httpd.conf,用记事本打开 1)、修改路径 2)、修改…

mysql去除重复数据

需求描述 doc表有很多重复的title,想去除掉重复的记录 表结构 CREATE TABLE doc (id INT PRIMARY KEY,title VARCHAR(255),content TEXT );去重SQL -- 创建临时表 CREATE TEMPORARY TABLE temp_doc AS SELECT * FROM doc WHERE 10;-- 插入唯一的记录(每个title最…

[数据集][目标检测]红外兔子检测数据集VOC+YOLO格式96张1类别

数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):96 标注数量(xml文件个数):96 标注数量(txt文件个数):96 标注类别数…

Redis 源码学习记录:散列 (dict)

散列 Redis 源码版本:Redis-6.0.9,本篇文章的代码均在 dict.h / dict.c 文件中。 散列类型可以存储一组无需的键值对,他特别适用于存储一个对象数据。 字典 Redis 通常使用字典结构体存储用户散列数据。字典是 Redis 的重要数据结构。除了散…