MCU常见通信总线串讲(三)—— I2C总线协议

2023-11-08 20:05

本文主要是介绍MCU常见通信总线串讲(三)—— I2C总线协议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🙌秋名山码民的主页
😂oi退役选手,Java、大数据、单片机、IoT均有所涉猎,热爱技术,技术无罪
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
获取源码,添加WX

目录

  • 前言
  • 一、I2C总线的概念
  • 二、 I2C的协议层
  • 三、STM32 I2C实战
  • 最后


前言

首先明确一个概念,关于MCU中通信总线和通信协议,通信总线是一种用于连接各种外设和模块的物理接口,它可以传输数据和控制信息。通信协议则是指在通信总线上传输数据时所遵循的规则和约定,以确保不同设备之间能够正确地交换信息,我们也可以把他叫做通信总线协议。

系列文章,主要讲解以下几个总线协议,读者可以按需选择:

  1. UART和USART
  2. RS232、RS485总线
  3. IIC总线
  4. SPI总线
  5. CAN总线
  6. USB总线

一、I2C总线的概念

其实站在我个人开发的角度来说,I2C使用的比SPI多,他的主要概念如下:

I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器和外部设备,例如传感器、存储器芯片、显示屏等。
在这里插入图片描述

特点:

  1. I2C是一个支持设备的总线,多个设备共用的信号线,他支持多个主机或者多个从机
  2. 一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA),一条串行时钟线(SCL)。数据线用来表示数据,时钟线用于同步数据的收发。
  3. I2C总线上的从机设备,都有一个单独的地址,主机通过这个地址来实现对不同设备的访问
  4. 总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
  5. I2C还有一个仲裁模式,即当多个从机都传输数据的时候,为了防止数据冲突,会产生仲裁,决定哪个设备来使用总线
  6. 具有3种传输模式:标准模式传输速率为100kbps,快速模式为400kbps,高速模式可达3.4Mbps,但目前大多I2C设备尚不支持高速模式。
  7. 连接到相同总线的IC数量受到总线的最大电容400p F限制。

二、 I2C的协议层

I2C协议层规定了在I2C通信中需要遵循的一些基本规则和标准,其中包括以下内容:

  1. 物理层规定
    • 定义了I2C总线的物理结构、传输介质(通常是双绞线)、电气特性(如电压电平、上拉电阻)等。
    • 规定了起始条件(Start condition)和停止条件(Stop condition)的时序和电平要求。

通信的起始和停止:

起始(S)和停止(P)信号是两种特殊的状态,SCL线是高电平时SDA线从高电平向低电平切换,表示通信的起始。当SCL是高电平时SDA线由低电平向高电平切换,表示通信的停止

在这里插入图片描述

  1. 数据链路层规定
    • 定义了数据帧的格式,包括地址帧和数据帧的组成结构。
    • 规定了主设备和从设备之间的地址识别机制,包括7位或10位地址模式的规定。

I2C总线上的每个设备都有自己的独立地址,主机发起通信时,通过SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。I2C协议规定设备地址可以是7位或10位,实际中7位的地址应用比较广泛
在这里插入图片描述

  1. 时序规定
    • 规定了时钟脉冲的频率、数据传输的时序要求,以确保通信的稳定性和可靠性。
    • 定义了如何进行数据的读取和写入,包括数据的传输顺序和速率的限制。

主机写数据到从机
在这里插入图片描述
主机读数据在从机
在这里插入图片描述
复和形式
读和写数据除了基本的读写,I2C通信更常用的是复合格式,该传输过程有两次起始信号(S)。一般在第1次传输中,主机通过SLAVE_ADDRESS寻找到从设备后,发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址(注意区分它与SLAVE_ADDRESS的区别);在第2次的传输中,对该地址的内容进行读或写。也就是说,第1次通信是告诉从机读写地址,第2次则是读写的实际内容。
在这里插入图片描述

  1. 错误处理规则
    • 规定了在通信过程中出现错误时的处理方式,例如发生冲突、丢失数据等情况应当如何处理。

SDA数据线在SCL的每个时钟周期传输一位数据。传输时,SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时表示数据“1”,为低电平时表示数据“0”。当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次表示数据做好准备。

在这里插入图片描述

  1. 多点连接规定

    • 规定了多个从设备共享同一条总线时的地址分配和冲突解决机制。
  2. 高级功能规定

    • 例如10位地址模式、扩展寄存器等高级功能的使用规范。

有了这些规定,每个厂家生产出来支持I2C的东西也可实现互通

三、STM32 I2C实战

#include "I2C.h"#define SCL_H         GPIOB->BSRR = GPIO_Pin_8
#define SCL_L         GPIOB->BRR  = GPIO_Pin_8 #define SDA_H         GPIOB->BSRR = GPIO_Pin_9
#define SDA_L         GPIOB->BRR  = GPIO_Pin_9 #define SCL_read      GPIOB->IDR  & GPIO_Pin_8
#define SDA_read      GPIOB->IDR  & GPIO_Pin_9 static void I2C_delay(void)
{volatile int i = 7;while (i)i--;
}static bool I2C_Start(void)
{SDA_H;SCL_H;I2C_delay();if (!SDA_read)return false;SDA_L;I2C_delay();if (SDA_read)return false;SDA_L;I2C_delay();return true;
}static void I2C_Stop(void)
{SCL_L;I2C_delay();SDA_L;I2C_delay();SCL_H;I2C_delay();SDA_H;I2C_delay();
}static void I2C_Ack(void)
{SCL_L;I2C_delay();SDA_L;I2C_delay();SCL_H;I2C_delay();SCL_L;I2C_delay();
}static void I2C_NoAck(void)
{SCL_L;I2C_delay();SDA_H;I2C_delay();SCL_H;I2C_delay();SCL_L;I2C_delay();
}static bool I2C_WaitAck(void)
{SCL_L;I2C_delay();SDA_H;I2C_delay();SCL_H;I2C_delay();if (SDA_read) {SCL_L;return false;}SCL_L;return true;
}static void I2C_SendByte(uint8_t byte)
{uint8_t i = 8;while (i--) {SCL_L;I2C_delay();if (byte & 0x80)SDA_H;elseSDA_L;byte <<= 1;I2C_delay();SCL_H;I2C_delay();}SCL_L;
}static uint8_t I2C_ReceiveByte(void)
{uint8_t i = 8;uint8_t byte = 0;SDA_H;while (i--) {byte <<= 1;SCL_L;I2C_delay();SCL_H;I2C_delay();if (SDA_read) {byte |= 0x01;}}SCL_L;return byte;
}void i2cInit(void)
{GPIO_InitTypeDef gpio;//已更改RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);    gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;gpio.GPIO_Speed = GPIO_Speed_2MHz;gpio.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_Init(GPIOB, &gpio);}bool i2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t len, uint8_t * data)
{int i;if (!I2C_Start())return false;I2C_SendByte(addr << 1 | I2C_Direction_Transmitter);if (!I2C_WaitAck()) {I2C_Stop();return false;}I2C_SendByte(reg);I2C_WaitAck();for (i = 0; i < len; i++) {I2C_SendByte(data[i]);if (!I2C_WaitAck()) {I2C_Stop();return false;}}I2C_Stop();return true;
}
/
int8_t i2cwrite(uint8_t addr, uint8_t reg, uint8_t len, uint8_t * data)
{if(i2cWriteBuffer(addr,reg,len,data)){return TRUE;}else{return FALSE;}//return FALSE;
}
int8_t i2cread(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{if(i2cRead(addr,reg,len,buf)){return TRUE;}else{return FALSE;}//return FALSE;
}
//
bool i2cWrite(uint8_t addr, uint8_t reg, uint8_t data)
{if (!I2C_Start())return false;I2C_SendByte(addr << 1 | I2C_Direction_Transmitter);if (!I2C_WaitAck()) {I2C_Stop();return false;}I2C_SendByte(reg);I2C_WaitAck();I2C_SendByte(data);I2C_WaitAck();I2C_Stop();return true;
}bool i2cRead(uint8_t addr, uint8_t reg, uint8_t len, uint8_t *buf)
{if (!I2C_Start())return false;I2C_SendByte(addr << 1 | I2C_Direction_Transmitter);if (!I2C_WaitAck()) {I2C_Stop();return false;}I2C_SendByte(reg);I2C_WaitAck();I2C_Start();I2C_SendByte(addr << 1 | I2C_Direction_Receiver);I2C_WaitAck();while (len) {*buf = I2C_ReceiveByte();if (len == 1)I2C_NoAck();elseI2C_Ack();buf++;len--;}I2C_Stop();return true;
}uint16_t i2cGetErrorCounter(void)
{// TODO maybe fix this, but since this is test code, doesn't matter.return 0;
}

最后

如果本文对你有所帮助,还请三连支持一下博主!
请添加图片描述

这篇关于MCU常见通信总线串讲(三)—— I2C总线协议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/372184

相关文章

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

java常见报错及解决方案总结

《java常见报错及解决方案总结》:本文主要介绍Java编程中常见错误类型及示例,包括语法错误、空指针异常、数组下标越界、类型转换异常、文件未找到异常、除以零异常、非法线程操作异常、方法未定义异常... 目录1. 语法错误 (Syntax Errors)示例 1:解决方案:2. 空指针异常 (NullPoi

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

MySQL常见的存储引擎和区别说明

《MySQL常见的存储引擎和区别说明》MySQL支持多种存储引擎,如InnoDB、MyISAM、MEMORY、Archive、CSV和Blackhole,每种引擎有其特点和适用场景,选择存储引擎时需根... 目录mysql常见的存储引擎和区别说明1. InnoDB2. MyISAM3. MEMORY4. A

前端bug调试的方法技巧及常见错误

《前端bug调试的方法技巧及常见错误》:本文主要介绍编程中常见的报错和Bug,以及调试的重要性,调试的基本流程是通过缩小范围来定位问题,并给出了推测法、删除代码法、console调试和debugg... 目录调试基本流程调试方法排查bug的两大技巧如何看控制台报错前端常见错误取值调用报错资源引入错误解析错误

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I