STM32-I2C通讯

这篇具有很好参考价值的文章主要介绍了STM32-I2C通讯。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

I2C(Inter-Integrated Circuit)是一种通用的总线协议。它是由Philips(飞利浦)公司,现NXP(恩智浦)半导体开发的一种简单的双向两线制总线协议标准。

I2C有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步。

支持同步,半双工,带数据应答,支持总线挂载多设备(一主多从、多主多从)协议)。

多主多从模式下,总线上任何一个模块都可以主动申请成为主机,若同时多个模块申请冲突时,总线就会进行仲裁,失败的一方自动 变为从机。

硬件电路

所有I2C设备的SCL连接在一起,SDA连接在一起

设备的SCL和SDA均要设置为开漏输出模式,开漏输出高电平没有驱动能力。

SCL和SDA各添加一个上拉电阻,阻值为4.7Ω左右。

从机对于SCL时钟线任何时刻只能被动读取。对于SDA数据线,从机不允许主动发起对SDA的控制,只有在主机发起读取从机或者从机发送应答的时候,从机才能获得短暂的控制权。

线与特性:只要有一个低电平,总线就处于低电平,所有设备都输出高电平总线才处于高电平。

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

软件I2C

软件I2C时序基本单元

设置软件I2C时,为保证时序拼接,尽量让起始条件和终止条件外的时序电源的SCL都是以低电平开始,低电平结束。

起始条件

SCL为高电平,SDA从高电平切换到低电平

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

终止条件

SCL为高电平,SDA从低电平切换到高电平

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

发送一个字节

SCL低电平器件,主机将数据位依次放到SDA线上,高位先行,主机发送0则拉低SDA,若发生发送1则放手SDA会被上拉电阻自动拉为高电平。放完一位数据后,释放SCL锁存住SDA线上的数据(此时SDA数据不可变),从机在SCL高电平期间读取数据位,依次循环8次即可发送一个字节。

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

接收一个字节

SCL低电平期间,从机将数据位依次放在SDA线上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,循环8次即可接收一个字节(主机在接收之前需要释放SDA给从机权限)

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

发送应答位

发送应答:主机在接收完一个字节后,在下个一时钟发送一位数据,数据0(拉低SDA线)表示应答,数据1表示非应答(什么也不做)。

接收应答:主机发送完一个字节后,在下一个时钟接收一个数据判断从机是否应答,0表示应答,1表示非应答。(接收之前主机需要释放SDA)

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

I2C通讯地址

大多数I2C器件支持7位地址模式,有一些器件还支持10位地址,而且两种类型的器件可以连接在同一个I2C总线上,目前10位地址的器件还没有被广泛使用

7位地址

在硬件手册中可查看到地址为多少,一般地址的最后一位是可以改变的,防止出现地址冲突的情况。一般来说I2C的从机地址高位都是由厂商设置的,低位可以灵活切换,具体可参考对应配置手册。一般主机地址需要向左移动一位,低位0或1为读写位。

10位地址

起始条件后的两个字节都是寻址

第一个字节:帧头:5位的标志位+2位地址+1位读写位

第二个字节:8位地址

第一个字节的2位地址和第二个字节的8位字节构成了10位地址

I2C通讯时序

I2C指定地址写数据

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

I2C指定地址读数据

I2C规定在主机寻址一旦读写标志位给1,立刻进行读的时序。

I2C在读的时候并不能指定特定寄存器进行读取,只会读取当前指针所在的位置的内容。若在0x09地址下写一个数据,接这转换为读,则会读出0x0a地址的内容。

所以为了指定读到特定及储存器的内容,可以在读之前,进行写数据的操作来进行指定地址。

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机

软件I2C协议代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/*引脚配置层*/

/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

//I2C写SDA引脚电平
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);		
    //根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);		//延时10us,防止时序频率超过要求
}

//I2C读SDA引脚电平
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);	//读取SDA电平
	Delay_us(10);											//延时10us,防止时序频率超过要求
	return BitValue;										//返回SDA电平
}


//I2C初始化
void MyI2C_Init(void)
{
	/*开启时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);				//将PB10和PB11引脚初始化为开漏输出
	
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			
    //设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}



//I2C起始
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);				//释放SDA,确保SDA为高电平
	MyI2C_W_SCL(1);				//释放SCL,确保SCL为高电平
	MyI2C_W_SDA(0);				//在SCL高电平期间,拉低SDA,产生起始信号
	MyI2C_W_SCL(0);				//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

//I2C终止
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平
	MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平
	MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}

//I2C发送一个字节
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)			//循环8次,主机依次发送数据的每一位
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);					//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);					//拉低SCL,主机开始发送下一位数据
	}
}

//I2C接收一个字节
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;	    //定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	MyI2C_W_SDA(1);				//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	for (i = 0; i < 8; i ++)	//循环8次,主机依次接收数据的每一位
	{
		MyI2C_W_SCL(1);			//释放SCL,主机机在SCL高电平期间读取SDA
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
		//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0

		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

// I2C发送应答位
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

//I2C接收应答位
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = MyI2C_R_SDA();					//将应答位存储到变量里
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}

硬件IIC外设

STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能

支持多主机模型

支持7位/10位地址模式

支持不同的通讯速度,标准速度(100kHZ),快速(400kHZ)

支持DMA转运

兼容SMBus协议

I2C框图

STM32-I2C通讯,STM32,stm32,嵌入式硬件,单片机文章来源地址https://www.toymoban.com/news/detail-839518.html

 I2C硬件时序

 硬件I2C配置

#define MPU6050_ADDRESS		0xD0		//MPU6050的I2C从机地址

//等待事件
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout;
	Timeout = 10000;									//给定超时计数时间
	while (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS)	//循环等待指定事件
	{
		Timeout --;										//等待时,计数值自减
		if (Timeout == 0)								//自减到0后,等待超时
		{
			/*超时的错误处理代码,可以添加到此处*/
			break;										//跳出等待
		}
	}
}

//写寄存器:RegAddress 寄存器地址
void WriteReg(uint8_t RegAddress, uint8_t Data)
{
	I2C_GenerateSTART(I2C2, ENABLE);								//硬件I2C生成起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);			//等待EV5
	
    //硬件I2C发送从机地址,方向为发送
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	


	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6
	
	I2C_SendData(I2C2, RegAddress);									//硬件I2C发送寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);	//等待EV8
	
	I2C_SendData(I2C2, Data);										//硬件I2C发送数据
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);		//等待EV8_2
	
	I2C_GenerateSTOP(I2C2, ENABLE);									//硬件I2C生成终止条件
}

//读寄存器
uint8_t ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	I2C_GenerateSTART(I2C2, ENABLE);								//硬件I2C生成起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);			//等待EV5
	
    //硬件I2C发送从机地址,方向为发送
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);	

	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);	//等待EV6
	
	I2C_SendData(I2C2, RegAddress);									//硬件I2C发送寄存器地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);		//等待EV8_2
	
	I2C_GenerateSTART(I2C2, ENABLE);							//硬件I2C生成重复起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);		//等待EV5
	
    //硬件I2C发送从机地址,方向为接收
	I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);	
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);		//等待EV6
	
	I2C_AcknowledgeConfig(I2C2, DISABLE);			//在接收最后一个字节之前提前将应答失能
	I2C_GenerateSTOP(I2C2, ENABLE);					//在接收最后一个字节之前提前申请停止条件
	
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);		//等待EV7
	Data = I2C_ReceiveData(I2C2);									//接收数据寄存器
	
    //将应答恢复为使能,为了不影响后续可能产生的读取多字节操作
	I2C_AcknowledgeConfig(I2C2, ENABLE);

	return Data;
}

//硬件I2C初始化
void I2C_Init(void)
{
	//开启时钟,需要根据硬件手册确定I2C对应GPIO口
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);		//开启I2C2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);		//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);		//将PB10和PB11引脚初始化为复用开漏输出
	
	/*I2C初始化*/
	I2C_InitTypeDef I2C_InitStructure;						//定义结构体变量
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;				//模式,选择为I2C模式
	I2C_InitStructure.I2C_ClockSpeed = 50000;				//时钟速度,选择为50KHz
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	//时钟占空比,选择Tlow/Thigh = 2
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;			//应答,选择使能
	
    //应答地址,选择7位,从机模式下才有效
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;	

	I2C_InitStructure.I2C_OwnAddress1 = 0x00;				//自身地址,从机模式下才有效
	I2C_Init(I2C2, &I2C_InitStructure);			//将结构体变量交给I2C_Init,配置I2C2
	
	/*I2C使能*/
	I2C_Cmd(I2C2, ENABLE);									//使能I2C2,开始运行
	
}

到了这里,关于STM32-I2C通讯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32硬件I2C通信外设

    本文主要介绍stm32自带的I2C通信外设,对比与软件模拟I2C,硬件I2C可以自动生成时序,时序的操作更加及时规范,可以实现更加高性能的IIC通信。 本文内容与I2C软件通信有诸多类似之处,I2C软件通信可见:https://blog.csdn.net/qq_53922901/article/details/136662006?spm=1001.2014.3001.5501 在8位指

    2024年04月08日
    浏览(90)
  • 【STM32】入门(七):I2C硬件控制方式

    之所以叫“I2C硬件控制方式”是与“软件控制方式”相对。I2C软件控制,就是写程序直接操作两个GPIO引脚,分别作为时钟线SCL和数据线SDA,按照I2C协议的时序要求,操作GPIO输入、输出、高电平、低电平。 听着就很复杂,好在STM32中有I2C的硬件实现,即通过简单的操作寄存器

    2023年04月08日
    浏览(41)
  • 【STM32】STM32学习笔记-硬件I2C读写MPU6050(35)

    I2C(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。 串行的 8 位双向数据传输位速率在标准模式下可

    2024年01月25日
    浏览(49)
  • STM32 HAL FreeRTOS 硬件I2C 使用

    因为某个项目想要颜色识别,去识别球的颜色,但是又不想多来个摄像头,所以想尝试一下颜色传感器的方案。但是经过尝试,HAL库生成的 FreeRTOS 硬件 I2C 读写一直在报错。 刚好手头上有九轴陀螺仪的例程代码。最后用FreeRTOS 硬件 I2C 读取数据。 这里提到了阻塞式 HAL 函数(

    2024年02月20日
    浏览(43)
  • 【STM32学习】——STM32-I2C外设&硬件读写MPU6050&软硬件读写波形对比

    目录 前言 一、I2C外设 二、硬件I2C操作流程 1.主机发送时序 3.其他时序

    2024年02月10日
    浏览(39)
  • STM32 SHT40驱动源码(使用硬件I2C)

    目录 简介: SHT40.c: SHT40.h 测试结果:         SHT40是瑞士Sensirion公司推出的第四代温湿度传感器,内部集成加热器用于去除表面微小液滴。集成I2C接口,典型的相对湿度精度1.8%RH,典型温度精度0.2℃,运行在0-100%RH和-40-125℃的环境中。 主控:STM32H7B0VBT6 平台:STM32CubeIDE SHT4

    2024年03月19日
    浏览(61)
  • STM32F407系列硬件I2C笔记

    STM32F407系列有3个硬件I2C: I2C1:该接口位于GPIOB引脚上,包括PB6(I2C1_SCL)和PB7(I2C1_SDA)。 I2C2:该接口位于GPIOB引脚上,包括PB10(I2C2_SCL)和PB11(I2C2_SDA)。 I2C3:该接口位于GPIOA和GPIOC引脚上,包括PA8(I2C3_SCL)和PC9(I2C3_SDA)。   硬件I2C的速度比软件I2C更快,硬件I2C通常可以

    2024年02月04日
    浏览(40)
  • STM32F030硬件I2C代码及解析

    刚接触STM32的时候,第一个学习的就是I2C,当时去网上学习别人写得I2C代码,虽然能用,但是当时并不理解为什么要这么配置,特别希望有人把代码掰碎了讲讲看,今天突然想起来,就把以前写的I2C代码拿出来掰碎了捋捋,希望对新手有些帮助。 先说说STM32的I2C: ST的M3系列还

    2024年02月08日
    浏览(36)
  • STM32 硬件IIC 控制OLED I2C卡死问题

    #更新通知:2023-09-06 STM32L151 固件库 使用I2C 太难了,又宕机了,建议不要在固件库版本上尝试硬件IIC 了,一般人真用不了,直接使用软件模拟的,或者不要使用固件库了,用HAL 库吧,据说HAL 库没这么多问题,不死心的我还是死心了,等有空再研究吧 3.1 I2C模式,我这里选的

    2024年02月09日
    浏览(38)
  • STM32配合cubeMX硬件I2C驱动0.96寸OLED

    目录 一、简单介绍 1.1   OLED 1.2   I2C协议 二、实战 2.1 工程配置 2.2 测试工程 2.3 波形分析 三、驱动OLED 3.1 初始化代码 3.2 清屏函数 3.3 设置坐标函数 3.4 显示字符函数 3.5 显示字符串函数 3.6 显示图片函数 附录 驱动代码文件 oled.c oled.h f6x8.h 有机发光二极管 (英语:Organic

    2024年02月08日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包