玩转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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件测试经理工作日常随记【6】-利用python连接禅道数据库并自动统计bug数据到钉钉群

测试管理_利用python连接禅道数据库并统计bug数据到钉钉 这篇不多赘述,直接上代码文件。 另文章基础参考博文:参考博文 加以我自己的需求优化而成。 统计的前提 以下代码统计的前提是禅道的提bug流程应规范化 bug未解决不删除bug未关闭不删除 db_…

解读makefile中$(patsubst pattern,replacement,text)

在 Makefile 中,$(patsubst pattern,replacement,text) 是一个用于模式替换的函数,它可以将文本中符合指定模式的部分替换为指定的字符串。这个函数通常用于对文件名或路径进行模式匹配和替换,非常适合在 Makefile 中进行文件名的转换操作。 …

计算机毕业设计python+spark天气预测 天气可视化 天气大数据 空气质量检测 空气质量分析 气象大数据 气象分析 大数据毕业设计 大数据毕设

摘 要 近些年大数据人工智能等技术发展迅速,我国工业正努力从“制造”迈向“智造”实现新跨越。神经网络(NeuronNetwork)是一种计算模型,通过大量数据的学习,来发现数据之间的模式和规律,模仿人脑神经元的工作方式。随着算力的提…

【深度学习】Transformer梳理

零、前言 对于transformer,网上的教程使用记号、术语不一 。 最关键的一点,网上各种图的简化程度不一 (画个图怎么能这么偷懒) ,所以我打算自己手画一次图。 看到的最和善(但是不是那么靠谱,我…

使用 Spring HATEOAS 开发 REST 服务-浅显的理解

随笔,简单理解 一、restful是什么 1、第一层次(Level 0)的 Web 服务只是使用 HTTP 作为传输方式,实际上只是远程方法调用(RPC)的一种具体形式。 SOAP 和 XML-RPC 都属于此类 2、第二层次(Lev…

linux学习(六)

1.网络管理 (1)查看 ifconfig: root用户可以查看网卡状态, 普通用户: /sbin/ifconfig(需要加上命令的完整路径) (2)修改网络配置 通过命令修改网络配置 设置网卡的ip地址;禁用网卡和启用网卡了。 添加网关: (3)网络故障查询 ①ping 检测当前主机和目标主机是…

线段树例题

目录 1.Sequence 2.Peach Conference 3.Permutation Subsequence 1.Sequence 题目描述: Given an array a consisting of n integers, on which you are to perform m operations of two types. 1.Given two integers x,y, replace the number of index x with n…

Windows10(家庭版)中DockerDesktop(docker)的配置、安装、修改镜像源、使用

场景 Windows10中Docker的安装与遇到的那些坑: Windows10中Docker的安装与遇到的那些坑_在 docker.core.logging.httpclientexceptionintercept-CSDN博客 上面讲Docker Desktop在windows10非家庭版上的安装,如果是家庭版,则需要执行如下步骤。 注&am…

动态规划之买卖股票大集合

目录 引言 1.只能进行一次买卖股票(最多只能买一股股票) 2.可以进行多次股票买卖,且没有手续费(最多只能买一股股票) 3.可以进行多次股票买卖,但是有冷冻期,无手续费(最多只能买一…

Xunsearch:实现拼音搜索和中文分词功能

首先我们需要安装xunsearch扩展库,参考 1、设置分词器和拼音搜索功能 在创建Xunsearch对象后,可以设置相应的分词器和拼音搜索功能。以下代码示例演示了如何设置分词器和拼音搜索功能: $index $xunsearch->index; $index->setToken…

Cesium For Unity 在Unity中无法下载的问题

Unity 下载失败,提供百度网盘“com.cesium.unity-1.10.0.tgz”下载链接 链接:https://pan.baidu.com/s/1PybXQ8EvkRofOKD6rSN66g?pwd1234 提取码:1234 导入方法: 1.打开PackageManager;Window-PackageManager 2.在PackageMan…

Leetcode621. 任务调度器

Every day a Leetcode 题目来源:621. 任务调度器 类似题目:1953. 你可以工作的最大周数 解法1:贪心 本质上来说,我们需要构造一个尽量短的,相同元素间隔 > (n1) 的序列。 用一个数组 cnt 统计每个任务的次数。…

【论文阅读笔记】The Google File System

1 简介 Google File System (GFS) 是一个可扩展的分布式文件系统,专为快速增长的Google数据处理需求而设计。这篇论文发表于2003年,此前已在Google内部大规模应用。 GFS不仅追求性能、可伸缩性、可靠性和可用性等传统分布式文件系统的设计目标&#xf…

如何使用 Connector API 将数据提取到 Elasticsearch Serverless 中

作者:来自 Elastic Jedr Blaszyk Elasticsearch 支持一系列摄取方法。 其中之一是 Elastic Connectors,它将 SQL 数据库或 SharePoint Online 等外部数据源与 Elasticsearch 索引同步。 连接器对于在现有数据之上构建强大的搜索体验特别有用。 例如&…

新火种AI|警钟长鸣!教唆自杀,威胁人类,破坏生态,AI的“反攻”值得深思...

作者:小岩 编辑:彩云 在昨天的文章中,我们提到了谷歌的AI Overview竟然教唆情绪低迷的网友“从金门大桥跳下去”。很多人觉得,这只是AI 模型的一次错误判断,不会有人真的会因此而照做。但现实就是比小说电影中的桥段…

Linux shell编程学习笔记51: cat /proc/cpuinfo:查看CPU详细信息

0 前言 2024年的网络安全检查又开始了,对于使用基于Linux的国产电脑,我们可以编写一个脚本来收集系统的有关信息。对于中央处理器CPU比如,我们可以使用cat /proc/cpuinfo命令来收集中央处理器CPU的信息。 1. /proc/cpuinfo 保存了系统的cpu…

【学习心得】PyTorch的知识要点复习(持续更新)

PyTorch知识要点复习,目的是为了巩固PyTorch基础、快速回顾、深化理解PyTorch框架。这篇文章会持续更新。 一、本文的一些说明 知识点梳理:我将PyTorch的核心概念和高级技巧进行了系统化的整理,从基础的张量操作到复杂的模型构建与训练。这样…

拉普拉斯IPO:科技与产业深度融合,实现业务领域延展

我国拥有全球最具竞争优势的光伏产业链,基于降本增效的需求,光伏产业对于技术革新具有持续的需求。拉普拉斯新能源科技股份有限公司(以下简称“拉普拉斯”)凭借深厚的技术积累,以及对光伏产业深刻的理解,聚…

【数据结构】AVL树——平衡二叉搜索树

个人主页:东洛的克莱斯韦克-CSDN博客 祝福语:愿你拥抱自由的风 目录 二叉搜索树 AVL树概述 平衡因子 旋转情况分类 左单旋 右单旋 左右双旋 右左双旋 AVL树节点设计 AVL树设计 详解单旋 左单旋 右单旋 详解双旋 左右双旋 平衡因子情况如…

基于ViutualBox+Ubuntu(Linux)的开发环境搭建

实际在选择虚拟机的时候纠结了要用virualbox还是vmware,初步比较结果: 1.virualbox能够使用vmware的硬盘格式,因此可以自由选择。 2.都能够实现主机和宿主机之间的文件夹共享。 3.virualbox是自由软件,vmware是商业软件。 在功能上…