首页 > 其他分享 >玩转STM32-I2C通信协议(详细-慢工出细活)

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

时间:2024-05-29 22:59:34浏览次数:14  
标签:慢工 字节 总线 通信协议 STM32 OLED 寻址 主机 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);
}

标签:慢工,字节,总线,通信协议,STM32,OLED,寻址,主机,I2C
From: https://blog.csdn.net/2301_81355360/article/details/139255015

相关文章

  • Proteus8.0仿真应用设计(九十三)基于STM32CubeMX、STM32F103C8T6 、FREERTOS、MCP4151(SP
    一、简介:        MCP4151是一款SPI接口的7位数字电位器。二、主要特性:电阻值(欧姆)5kΩ、10kΩ、50kΩ、100kΩ抽头数257接口类型SPI通道数1供电电压1.8V~5.5V精度±20%温度系数(典型值)150ppm/℃工作温度-40℃~+125℃三、引脚定义:四、内部逻辑框图:五、时序图......
  • 基于STM32的GPS远程矿山矿场监控系统
    **单片机设计介绍,基于STM32的GPS远程矿山矿场监控系统文章目录一概要二、功能设计设计思路三、软件设计原理图五、程序六、文章目录一概要  基于STM32的GPS远程矿山矿场监控系统概要如下:一、系统概述基于STM32的GPS远程矿山矿场监控系统旨在实现对矿山......
  • STM32软件重启失败
    现象描述stm32使用NVIC_SystemReset()函数软件重启失败解决方法找到NVIC_SystemReset()的定义,继续跳转至__NVIC_SystemReset函数,将其中的SCB_AIRCR_SYSRESETREQ_Msk修改为SCB_AIRCR_VECTRESET_Msk即可。__NO_RETURN__STATIC_INLINEvoid__NVIC_SystemReset(void){......
  • STM32简介
    STM32单片机是由意法半导体公司(ST,STMicroelectronics)研发的32位基于ARM(AdvancedRISCMachine)内核的微控制器(MCU,MicrocontrollerUnit)。STM32系列分为不同的系列,有STM32F,STM32G,STM32H,STM32L,STM32U等这几个系列,如图1所示。图1STM32不同系列展示在STM32的每个系列中又......
  • STM32 IIC读取Eeprom失败问题
    现象描述异常断开产品的电源,会出现产品无法读取eeprom数据,并且iic返回繁忙警告。原因iic再读取eeprom时,出现电源抖动的情况。而该电源能维持芯片不进入关机状态,但是会影响iic通讯问题。解决办法在出现iic忙碌时,将iic复位即可。voidi2c_reset(void){ /*复位I2C*/ /*Res......
  • 基于STM32的智能个人健康助手
    设计摘要:基于单片机的智能个人健康助手是一种集成了传感器、数据处理和智能算法的智能设备,旨在帮助用户监测和改善个人健康状况。该助手可以通过采集用户的体温、心率和血氧等,进行数据分析和健康评估,并提供个性化的健康建议和提醒。该系统的核心部件是单片机,它可以连接各种传......
  • STM32CubeMX导入新版本的固件包
    检查用的芯片的固件版本2.在STM32Cube\Repository目录下检查有没有对应的库文件,我这里是更新之后在ST官网下载对应版本的固件包,注意下载的固件包不能放在STM32Cube\Repository目录下在CubeMx到导入下载好的固件包导入之后等待解压之后,就导入完成。不可简单的把固......
  • STM32F103 IAP 在线升级OTA
    一:前言MCU设备上的OTA升级可理解为IAP(InApplicationProgramming)技术,MCU通过外设接口(如UART、IIC、SPI、CAN、USB等接口),连接具备联网能力的模块、器件、设备(以下统称上位机)。上位机从服务器上拉取固件包,再将固件包以约定的通讯协议,经由通讯接口发送至MC......
  • 关于STM32单片机的一些事情-1.1
    1.1.1回顾:上一篇博客我们一起了解了一下:stm32工程文件的建立,以及使用hal库函数去操作单片机。本篇我们将继续探讨STM32的一些事情。1.1.2GPIO简介GPIO(GeneralPurposeInputOutput)通用输入输出口可配置为8种输入输出模式引脚电平:0V~3.3V,部分引脚可容忍5V输出模式下......
  • STM32_HAL_串口IAP
    1.IAP简介        IAP(InApplicationProgramming)即在应用编程。STM32可以通过设置MSP的方式从不同的地址启动:包括Flash地址、RAM地址等,在默认方式下,我们的嵌入式程序是以连续二进制的方式烧录到STM32的可寻址Flash区域上的。如果我们用的Flash容量大......