当前位置: 首页 > news >正文

STM32 I2C通信协议说明

目录

背景

I2C协议

数据的有效性

I2C通信开始和停止条件

I2C数据传输

发送

响应

正常情况:

异常情况:

主机结束接收

写寄存器的标准流程

读寄存器的标准流程

仲裁机制

时钟同步

SDA线的仲裁

程序


背景

对单片机的三大通信中的I2C通信进行说明。

I2C协议

协议采用双线结构传输数据,包括一个数据线和一个时钟线(即 SDA 和 SCL 线),其中 SDA(Serial Data)线用于双向数据传输,而 SCL(Serial Clock)线则用于同步数据传输的时钟信号。通信始终由主设备(Master)控制,从设备(Slave)被动接收和回应。这种简单的线路连接方式使得设备之间的互连变得非常容易。

数据的有效性

SDA 线上的数据必须在时钟的高电平周期保持稳定 (PS:在SCL为高电平的时候,SDA发生发生变化是作为I2C通信开始和结束的信号)数据线的高或低电平状态只有在 SCL 线的时钟
信号是低电平时才能改变。也就是说在 SCL为高电平时,SDA上的信号保持稳定 只有在SCL为低电平时,SDA上的信号才能改变 。数据的接收方会在每个时钟周期的高电平期间读取数据(SDA),因此数据是在SCL为高电平时进行读取的。

I2C通信开始和停止条件

表示起始条件:SCL 是高电平时 SDA 线从高电平向低电平切换
表示结束条件: SCL 是高电平时 SDA 线由低电平向高电平切换表示停止条件
总结:I2C的SCL为高电平时候,SDA发生变化是作为开始/结束的条件
PS:起始和停止条件一般由主机产生 总线在起始条件后被认为处于忙的状态。在停止条件的某段时间后总线被认为再次处于空闲状态。如果产生 重复起始 Sr条件而不产生停止条件总线会一直处于忙的状态,此时的起始条件 S和重复起始 Sr条件在功能上是一样的。

I2C数据传输

发送

发送到 SDA 线上的每个字节必须为 8 位,每次传输可以发送的字节数量不受限制 每个字节后必须跟 一个响应位 ,首先传输的是数据的最高位 MSB,如果从机要完成一些其他功能后 例如一个
内部中断服务程序,才能接收或发送下一个完整的数据字节, 可以使时钟线 SCL 保持低电平迫使主机进入 等待状态. 当从机准备好接收下一个数据字节并释放时钟线 SCL 后数据传输继续.

响应

正常情况:

数据传输必须带响应。 相关的响应时钟脉冲由主机产生。 在响应的时钟脉冲期间, 发送器释放 SDA 线高 在响应的时钟脉冲期间 接收器必须将 SDA 线拉低!  使它在这个时钟脉冲的高电平期间保持稳定的低电平。

异常情况:

从机不能响应从机地址时 例如它正在执行一些实时函数不能接收或发送 从机必须使数据线保持 高电平(NACK) 主机然后产生一个停止条件终止传输或者产生重复起始条件开始新的传输。
如果从机 接收器响应了从机地址但是在传输了一段时间后不能接收更多数据字节 主机必须再一次
终止传输 这个情况用从机在第一个字节后没有产生响应来表示 从机使数据线保持高电平 主机产生一 个停止或重复起始条件。

主机结束接收

如果传输中有主机(作为接收者) 它必须通过在 从机不产生时钟的 最后一个字节不产生一个响应向从机 (发送器)通知数据结束! 从机 发送器必须释放数据线,允许主机产生一个停止或重复起始条件

写寄存器的标准流程

  1. Master发起START
  2. Master发送I2C addr(7bit)和w操作0(1bit),等待ACK
  3. Slave发送ACK
  4. Master发送reg addr(8bit),等待ACK
  5. Slave发送ACK
  6. Master发送data(8bit),即要写入寄存器中的数据,等待ACK
  7. Slave发送ACK
  8. 第6步和第7步可以重复多次,即顺序写多个寄存器
  9. Master发起STOP

读寄存器的标准流程

  1. Master发送I2C addr(7bit)和w操作1(1bit),等待ACK
  2. Slave发送ACK
  3. Master发送reg addr(8bit),等待ACK
  4. Slave发送ACK
  5. Master发起re -START
  6. Master发送I2C addr(7bit)和r操作1(1bit),等待ACK
  7. Slave发送ACK
  8. Slave发送data(8bit),即寄存器里的值
  9. Master发送ACK
  10. 第8步和第9步可以重复多次,即顺序读多个寄存器
  11. 主机想结束接收时,最后一个数据不再需要ACK应答,保持为高电平(NACK)。
  12. 主机发出 STOP 信号,停止 I2C 通信

 

主机想结束接收时,最后一个数据不再需要ACK应答,保持为高电平(NACK)。

仲裁机制

如果存在多主机的情况下,才要考虑仲裁机制。I2C总线上的仲裁分两部分:SCL线的同步和SDA线的仲裁。

时钟同步

在 I2C 总线上传送信息时的时钟同步信号是由挂接在 SCL 线上的所有器件的 逻辑“与” (线与,所以需要SCL也是开漏输出)完成的。即如果有多个主机同时产生时钟,那么只有所有主机都发送高电平时,SCL 上才表现为高电平,否则 SCL 都表现为低电平。

SDA线的仲裁

总线仲裁是为了解决多设备同时竞争中线控制权的问题,通过一定的裸机来决定哪个设备能够获得最终的总线控制权。

SDA线的仲裁也是建立在总线具有线“与”逻辑功能的原理上的。节点在发送1位数据后,比较总线上所呈现的数据与自己发送的是否一致(回读机制)。

  • 是,继续发送;
  • 否则,退出竞争;

I2C总线的控制逻辑:低电平优先

SDA线的仲裁可以保证I2C总线系统在多个主节点同时企图控制总线时通信正常进行并且数据不丢失,总线系统通过仲裁只允许一个主节点可以继续占据总线

程序

void I2C_GPIO_Init(void){ //I2C接口初始化GPIO_InitTypeDef  GPIO_InitStructure; 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);       RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //启动I2C功能 GPIO_InitStructure.GPIO_Pin = I2C_SCL | I2C_SDA; //选择端口号                      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //选择IO接口工作方式       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //设置IO接口速度(2/10/50MHz)    GPIO_Init(I2CPORT, &GPIO_InitStructure);
}void I2C_Configuration(void){ //I2C初始化I2C_InitTypeDef  I2C_InitStructure;I2C_GPIO_Init(); //先设置GPIO接口的状态I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置为I2C模式I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;I2C_InitStructure.I2C_OwnAddress1 = HostAddress; //主机地址(从机不得用此地址)I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//允许应答I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位地址模式I2C_InitStructure.I2C_ClockSpeed = BusSpeed; //总线速度设置 	I2C_Init(I2C1,&I2C_InitStructure);I2C_Cmd(I2C1,ENABLE);//开启I2C					
}void I2C_SAND_BUFFER(u8 SlaveAddr,u8 WriteAddr,u8* pBuffer,u16 NumByteToWrite){ //I2C发送数据串(器件地址,寄存器,内部地址,数量)I2C_GenerateSTART(I2C1,ENABLE);//产生起始位while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除EV5I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Transmitter);//发送器件地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除EV6I2C_SendData(I2C1,WriteAddr); //内部功能地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄存器非空,数据寄存器已空,产生EV8,发送数据到DR既清除该事件while(NumByteToWrite--){ //循环发送数据	I2C_SendData(I2C1,*pBuffer); //发送数据pBuffer++; //数据指针移位while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除EV8}I2C_GenerateSTOP(I2C1,ENABLE);//产生停止信号
}
void I2C_SAND_BYTE(u8 SlaveAddr,u8 writeAddr,u8 pBuffer){ //I2C发送一个字节(从地址,内部地址,内容)I2C_GenerateSTART(I2C1,ENABLE); //发送开始信号while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //等待完成	I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //发送从器件地址及状态(写入)while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待完成	I2C_SendData(I2C1,writeAddr); //发送从器件内部寄存器地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	I2C_SendData(I2C1,pBuffer); //发送要写入的内容while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //等待完成	I2C_GenerateSTOP(I2C1,ENABLE); //发送结束信号
}
void I2C_READ_BUFFER(u8 SlaveAddr,u8 readAddr,u8* pBuffer,u16 NumByteToRead){ //I2C读取数据串(器件地址,寄存器,内部地址,数量)while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));I2C_GenerateSTART(I2C1,ENABLE);//开启信号while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));	//清除 EV5I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); //写入器件地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清除 EV6I2C_Cmd(I2C1,ENABLE);I2C_SendData(I2C1,readAddr); //发送读的地址while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //清除 EV8I2C_GenerateSTART(I2C1,ENABLE); //开启信号while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5I2C_Send7bitAddress(I2C1,SlaveAddr,I2C_Direction_Receiver); //将器件地址传出,主机为读while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //清除EV6while(NumByteToRead){if(NumByteToRead == 1){ //只剩下最后一个数据时进入 if 语句I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位}if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)){ //读取数据*pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBufferpBuffer++; //指针移位NumByteToRead--; //字节数减 1 }}I2C_AcknowledgeConfig(I2C1,ENABLE);
}
u8 I2C_READ_BYTE(u8 SlaveAddr,u8 readAddr){ //I2C读取一个字节u8 a;while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY));I2C_GenerateSTART(I2C1,ENABLE);while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));I2C_Cmd(I2C1,ENABLE);I2C_SendData(I2C1,readAddr);while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));I2C_GenerateSTART(I2C1,ENABLE);while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));I2C_Send7bitAddress(I2C1,SlaveAddr, I2C_Direction_Receiver);while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));I2C_AcknowledgeConfig(I2C1,DISABLE); //最后有一个数据时关闭应答位I2C_GenerateSTOP(I2C1,ENABLE);	//最后一个数据时使能停止位a = I2C_ReceiveData(I2C1);return a;
}

PS:因为总线的线与特性,SCL和SDA都要设置为开漏输出

相关文章:

STM32 I2C通信协议说明

目录 背景 I2C协议 数据的有效性 I2C通信开始和停止条件 I2C数据传输 发送 响应 正常情况: 异常情况: 主机结束接收 写寄存器的标准流程 读寄存器的标准流程 仲裁机制 时钟同步 SDA线的仲裁 程序 背景 对单片机的三大通信中的I2C通信进…...

DeepSeek v3 技术报告阅读笔记

注 本文参考 DeepSeek-v3 / v2 / v1 Technical Report 及相关参考模型论文本文不包括基础的知识点讲解,为笔记/大纲性质而非教程,建议阅读技术报告原文交流可发送至邮箱 henryhua0721foxmail.com 架构核心 核心: MLA 高效推理DeepSeekMOE 更…...

HCIA项目实践(网络)---NAT地址转化技术

十三 NAT网络地址转换技术 13.1 什么是NAT NAT(Network Address Translation)地址转换技术,是一种将内部网络的私有 IP 地址转换为外部网络的公有 IP 地址的技术。其主要作用是实现多个内部网络设备通过一个公有 IP 地址访问外部网络&#x…...

VS studio报错cmake version 3.29.5-msvc4,但是没有其他信息问题解决

背景: windows电脑用VS studio 2022打开一个cmake项目,编译cmake通过,但是没有产生exe文件,IDE也没有打印其他错误信息提示,只有下图: cmake version 3.29.5-msvc4 一开始以为是编译器等问题,…...

免费deepseek的API获取教程及将API接入word或WPS中

免费deepseek的API获取教程: 1 https://cloud.siliconflow.cn/中注册时填写邀请码:GAejkK6X即可获取2000 万 Tokens; 2 按照图中步骤进行操作 将API接入word或WPS中 1 打开一个word,文件-选项-自定义功能区-勾选开发工具-左侧的信任中心-信任中心设置…...

langchain学习笔记之小样本提示词Few-shot Prompt Template

langchain学习笔记之小样本提示词 引言 Few-shot Prompt Templates \text{Few-shot Prompt Templates} Few-shot Prompt Templates简单介绍示例集创建创建 ExamplePrompt \text{ExamplePrompt} ExamplePrompt与 ExampleSelector \text{ExampleSelector} ExampleSelector创建 Fe…...

【CS.SE】优化 Redis 商户号池分配设计:高并发与内存管理

优化 Redis 商户号池分配设计:高并发与内存管理 背景 在分布式交易系统中,商户号池管理是核心模块之一。传统的商户号生成方式,依赖数据库预分配号段,导致大量号段浪费,并且在高并发请求下,性能难以满足需…...

5、《Spring Boot自动配置黑魔法:原理深度剖析》

Spring Boot自动配置黑魔法:原理深度剖析 一、引言:为什么Spring Boot能“开箱即用”? Spring Boot的核心理念是**“约定优于配置”,开发者只需引入一个spring-boot-starter-web依赖,就能直接编写RESTful API&#xf…...

稀土抑烟剂——为纺织品安全加持,保护您的每一寸触感

一、稀土抑烟剂的基本概念 稀土抑烟剂是基于稀土元素(如稀土氧化物和稀土金属化合物)研发的一类新型阻燃材料。它能够有效提高纺织品的阻燃性,抑制火灾发生时产生的烟雾和有害气体,减少火灾对人体的危害。稀土抑烟剂具有更强的稳…...

如何使用CSS画一个三角形,原理是什么?

如何用 CSS 画一个三角形?原理和实战指南 一、核心原理 CSS 画三角形的本质是利用边框(border)的叠加特性。当一个元素的宽高为 0 时,其边框会以对角线形式相交,形成四个独立的三角形区域。通过控制某一边的边框颜色为…...

Docker拉不下来镜像问题解决法案

打开docker的设置界面 配置如下: vi /etc/docker/daemon.json {"builder": {"gc": {"defaultKeepStorage": "20GB","enabled": true}},"experimental": false,"registry-mirrors": ["…...

DeepSeek 多模态大模型Janus-Pro本地部署教程

1.部署环境配置 我个人用的是Mac的m1pro 16512配置,我跑了1B的版本很流畅,7B的也可以跑起来,稍微感觉有一些卡顿。 需要安装Git-lfs,访问官网下载安装包安装,这个工具是用于下载大型文件必备的软件,这里用…...

笔记8——模式匹配 match语句(仅在Python 3.10及以上版本中可用)

文章目录 模式匹配 match语句(仅在 Python 3.10及以上版本 中可用)基本语法基本匹配操作应用场景 模式匹配 match语句(仅在 Python 3.10及以上版本 中可用) Python 3.10 及以上版本中才引入了 match 语句用于简化复杂的条件判断和数据解构;类似于其他语言中的 swit…...

maven-antrun-plugin插件的用法

maven-antrun-plugin 是 Maven 中一个非常强大的插件,它允许你在 Maven 构建过程中运行 Apache Ant 任务。通过这个插件,你可以在 Maven 构建的各个阶段(如 compile、package 等)中执行自定义的 Ant 任务,比如复制文件…...

iOS主要知识点梳理回顾-4-运行时类和实例的操作

类和实例的操作 iOS 运行时(Objective-C Runtime)提供了丰富的 API 来对类进行动态操作,包括创建类、修改类的结构、添加方法、替换方法等。这对于实现动态特性、AOP(面向切面编程)、方法拦截等功能非常重要。以下举例…...

vue2和vue3生命周期的区别通俗易懂

用最直白的对比帮你理解 Vue2 和 Vue3 生命周期的区别,就像对比手机系统的升级: 一、生命周期阶段对比表(老手机 vs 新手机) 阶段Vue2(老系统)Vue3(新系统)变化说明初始化beforeCre…...

使用 meshgrid函数绘制网格点坐标的原理与代码实现

使用 meshgrid 绘制网格点坐标的原理与代码实现 在 MATLAB 中,meshgrid 是一个常用函数,用于生成二维平面网格点的坐标矩阵。本文将详细介绍如何利用 meshgrid 函数生成的矩阵绘制网格点的坐标,并给出具体的代码实现和原理解析。 实现思路 …...

postgresql源码学习(59)—— 磁盘管理器 SMGR

一、 定义及作用 PostgreSQL 的磁盘管理器(Storage Manager,简称 SMGR)是数据库系统中负责管理底层存储的核心模块。磁盘管理器并非直接操作磁盘上的文件,而是通过VFD(虚拟文件描述符,将在后续学习&#xf…...

Spring Boot(8)深入理解 @Autowired 注解:使用场景与实战示例

搞个引言 在 Spring 框架的开发中,依赖注入(Dependency Injection,简称 DI)是它的一个核心特性,它能够让代码更加模块化、可测试,并且易于维护。而 Autowired 注解作为 Spring 实现依赖注入的关键工具&…...

UE_C++ —— Structs

目录 一,实现一个UStruct 二,Struct Specifiers 三,最佳做法与技巧 结构体(Struct)是一种帮助组织和操作相关属性的数据结构;在引擎中,结构体会被引擎反射系统识别为 UStruct,但不…...

【JVM】- 内存结构

引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

【2025年】解决Burpsuite抓不到https包的问题

环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...

网络编程(UDP编程)

思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分:机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域,衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标,自2002年由IBM的Kishore Papineni等人提出以来,…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...

LangFlow技术架构分析

🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例

目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...

数据库——redis

一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...