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

news2024/11/26 0:47:36

文章目录

  • 一、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.coloradmin.cn/o/1711966.html

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

相关文章

【C++ QT项目实战-02】---- C++ QT系统实现基于QT调用RESTful接口访问JSON文件中数据

🎩 欢迎来到技术探索的奇幻世界👨‍💻 📜 个人主页:一伦明悦-CSDN博客 ✍🏻 作者简介:C软件开发、Python机器学习爱好者 🗣️ 互动与支持:💬评论 &#…

股票交易vip快速通道有什么门槛?vip交易通道的开通流程!

证券公司的VIP通道通常是为了满足高端客户或高频交易客户的需求而设立的,提供更快速、更便捷的交易服务。证券公司VIP通道适用于有追涨停板需求的投资者,以及一些喜爱高频交易的投资者,总的来说就是快速,在交易主机排队靠前。 VI…

视频集中存储LntonCVS视频监控汇聚平台智慧园区应用方案

智慧园区,作为现代化城市发展的重要组成部分,承载着产业升级的使命,是智慧城市建设的重要体现。在当前产业园区竞争日益激烈的情况下,越来越多的用户关注如何将项目打造成完善的智慧园区。 在智慧园区的建设过程中,各类…

离线deb安装下载及安装实例

1、使用apt download下载deb安装包(不包括依赖包) 1.1仅下载deb安装包 sudo apt download lrzsz 1.2安装载deb安装包 sudo dpkg -i lrzsz_0.12.21-10kylin0k2_arm64.deb 注:dpkg安装deb包,部分存在depends关系,需要使用apt-get -f instal…

【赠书第25期】C#项目开发实战(微视频版)

文章目录 前言 1 项目构思与需求分析 1.1 项目构思 1.2 需求分析 2 系统设计 2.1 系统架构设计 2.2 数据库设计 2.3 接口设计 3 编码实现 3.1 环境搭建 3.2 编码规范 3.3 编码实现 4 测试与部署 4.1 单元测试 4.2 系统测试 4.3 部署与上线 5 总结与展望 6 推…

信息安全法规和标准

《全国人民代表大会常务委员会关于维护互联网安全的决定》规定,威胁互联网运行安全的行为:(1)侵入国家事务、国防建设、尖端科学技术领域的计算机信息系统,(2)故意制作、传播计算机病毒等破坏性…

气膜建筑的运行保障:应对停电的解决方案—轻空间

气膜建筑作为一种现代化的建筑形式,以其独特的结构和多样的应用赢得了广泛关注。这种建筑依靠风机不断往内部吹气来维持其结构形态,那么如果遇到停电的情况,该如何确保其正常运行呢? 气膜建筑的供风系统 气膜建筑内部的气压维持依…

使用`War`包部署`Jenkins`(超级详细)

使用War包部署Jenkins(超级详细) 别着急,你看这年复一年,春光不必趁早,冬霜不会迟到。过去的都会过去,该来的都在路上,一切都是刚刚好。 网站说明 https://get.jenkins.io/war-stable/ war包下载地址 https://www.jenk…

Metasploit渗透测试工具使用

Metasploit Framework(MSF) 是一款开源安全漏洞检测工具,附带数千个已知的软件漏洞,并保持持 续更新。Metasploit可以用来信息收集、漏洞探测、漏洞利用等渗透测试的全流程,被安全社区冠以“可 以黑掉整个宇宙”之名。刚开始的Metasploit是采…

Steamdeck使用Windows系统游玩雪地奔驰时闪退问题解决方法

我非常喜欢雪地奔驰这款游戏,买sd的一部分也是为了它。可在我打开这个游戏时,游戏发生闪退问题。查阅了网络各个途径,基本没有解决方法。因此我自己分析终于解决该问题。以下是我解决问题的思路,仅供记录参考: 游戏在崩…

uniapp - 填充页面

在上一篇文章中,创建了一个空白的文章模块页面。在这一篇文章,让我们来向页面中填充内容。 目录 页面效果涉及uniapp组件1.view2.swiper3.scroll-view4.属性解读1) class"style1 style2 .."2) circular单属性无赋值3) :autoplay"autoplay…

大型跨境商城系统平台的技术架构分析

随着全球化的深入发展,大型跨境电商平台在如今的商业环境中扮演着越来越重要的角色。这些平台不仅仅是为了提供商品和服务,它们更是连接不同国家和地区消费者与供应商之间的桥梁。在这篇博客中,我们将深入探讨大型跨境商城系统平台的技术架构…

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

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

7 Series FPGAs Integrated Block for PCI Express IP核设计中的物理层控制核状态接口

物理层控制和状态允许用户应用程序根据数据吞吐量和电源需求来更改链路的宽度和速度。 1 Design Considerations for a Directed Link Change 在Directed Link Change(定向链接更改)期间需要注意的事项有: 链接更改操作(Link c…

加氢站压缩液驱比例泵放大器

加氢站压缩液驱液压系统的要求是实现换向和速度控制,对液压动力机构而言,按原理可区分为开式(阀控)- 节流控制系统和闭式(泵控)- 容积控制系统: 阀控系统 – 节流调速系统:由BEUEC比…

dp秒杀优惠券

1、全局id生成器 当用户抢购时,就会生成订单并保存到tb_voucher_order这张表中,而订单表如果使用数据库自增ID就存在一些问题: id的规律性太明显受单表数据量的限制 场景分析:如果我们的id具有太明显的规则,用户或者…

【机器学习】解锁AI密码:神经网络算法详解与前沿探索

👀传送门👀 🔍引言🍀神经网络的基本原理🚀神经网络的结构📕神经网络的训练过程🚆神经网络的应用实例💖未来发展趋势💖结语 🔍引言 随着人工智能技术的飞速发…

设计模式六大原则之依赖倒置原则

文章目录 概念逻辑关系 小结 概念 依赖倒置原则指在设计代码架构时,高层模块不应该依赖底层模块,二者都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。 逻辑关系 如上图所示,逻辑应该就是这样,高层依赖于…

深度学习-语言模型

深度学习-语言模型 统计语言模型神经网络语言模型语言模型的应用序列模型(Sequence Model)语言模型(Language Model)序列模型和语言模型的区别 语言模型(Language Model)是自然语言处理(NLP&…

web自动化-数据驱动与失败用例截图、失败重新运行

因为只有失败的用例需要截图,那么问题就是: 什么时候用例会失败? 数据驱动测试 我们前面覆盖到的用例都是正常的用例,如果要测试异常的用例呢? 我们来写一下登录的异常 场景:【login_page】 # 用户输入框…