STM32学习(十)

news2025/1/9 13:31:25

I2C模块内部结构

I2C(Inter-Integrated Circuit)模块是一种由Philips公司开发的二线式串行总线协议,用于短距离通信,允许多个设备共享相同的总线‌。

  • 硬件连接简单‌:I2C通信仅需要两条总线,即SCL(时钟线)和SDA(数据线),大大简化了系统的硬件设计‌12。
  • 支持多设备共享‌:在I2C总线中,可以挂载多个从设备,每个设备都有一个唯一的地址,主设备通过广播地址的方式与从设备进行通信‌25。
  • 传输速率灵活‌:I2C总线传输模式具有向下兼容性,传输速率在标准模式下可达100kbps,快速模式下可达400kbps,高速模式下更是可达3.4Mbps‌34。

引脚初始化

引脚映射表

引脚实现代码

void My_I2C_Init(){
//对I2C进行重映射
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
		GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);
//对PB8和PB9进行初始化
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
		GPIO_InitTypeDef GPIO_InitStruct;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
		GPIO_Init(GPIOB,&GPIO_InitStruct)
}

连接电路

波特率

I2C的波特率指的是I2C总线上的数据传输速率,它可以根据不同的模式达到不同的速率‌。具体来说:

  • 在‌标准模式‌下,I2C的波特率为100kHz‌12。
  • 在‌快速模式‌下,I2C的波特率可以达到400kHz‌12。
  • 还有一些更高速的模式,如‌快速模式+‌,波特率可以达到1MHz‌1。

I2C总线中的波特率由主机控制,主机通过产生SCL(时钟线)信号来分配给所有从机,因此主机可以通过控制时钟信号频率来调节波特率,即控制通信速度‌。这种灵活性使得I2C总线能够适应不同的通信需求和应用场景。

占空比

在I2C总线通信中,占空比是指数据线(SDA)上的高电平持续时间与整个时钟周期(由时钟线SCL控制)的比例。这个比例决定了数据传输的稳定性和可靠性‌12。

在没有明确的情况下我们选择2/1的占空比

初始化I2C模块代码

void My_I2C_Init(){
//对I2C进行重映射
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
		GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);
//对PB8和PB9进行初始化
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
		GPIO_InitTypeDef GPIO_InitStruct;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1,ENABLE);//开启I2C的时钟
		RCC_APB1PeriphResetCmd(RCC_APB1Periph_CAN1,ENABLE);//施加复位信号
		RCC_APB1PeriphResetCmd(RCC_APB1Periph_CAN1,DISABLE);//释放复位信号
	
		I2C_InitTypeDef I2C_InitStruct;
		I2C_InitStruct.I2C_ClockSpeed = 400000;//波特率400k
		I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;//标准的I2C
		I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//占空比2:1
		I2C_Init(I2C1,&I2C_InitStruct);
		
		I2C_Cmd(I2C1,ENABLE);//闭合I2C1的总开关
}

写数据

数据发送的流程

主设备向从设备发送数据

  1. 发送起始信号‌:主设备在SCL(时钟线)为高电平时,将SDA(数据线)从高拉低,产生起始信号,通知所有从设备准备接收数据‌。
  2. 发送设备地址‌:主设备紧接着发送从设备的7位地址,以及一个写信号(通常是低电平),指示这是一个写操作‌。
  3. 等待从设备响应‌:从设备监测到自己的地址后,通过在下一个时钟周期拉低SDA线(发送ACK)来响应,确认它准备好了接收数据‌。
  4. 发送数据‌:主设备开始发送数据,每个字节数据后会跟着等待接收来自从设备的响应(ACK)。从设备在接收到每个字节后,都会发送一个ACK信号来确认‌。
  5. 发送停止信号‌:数据发送完毕后,主设备发送停止信号(SCL高时SDA从低变高),终止传输‌。

从设备向主设备发送数据

  1. 主设备初始化读取操作‌:主设备发送起始信号,然后发送从设备的地址以及一个读取位(通常是高电平),指示这是一个读取操作‌。
  2. 从设备响应‌:从设备监测到自己的地址后,通过发送ACK信号来响应‌。
  3. 主设备发送重复开始信号或停止信号‌:如果主设备计划在同一事务中连续读取多个从设备或进行连续读取,它可以发送重复开始信号来保持总线控制权。如果仅从当前从设备读取且读取操作即将结束,主设备在收到从设备的ACK后可直接发送停止信号‌。
  4. 从设备发送数据‌:在收到读取命令后,从设备开始发送数据。主设备在接收到每个字节后,都会发送一个ACK信号来确认。当接收到最后一个数据字节后,主设备可能会发送一个无效响应(NACK),然后发送停止信号来终止传输‌。

等待总线空闲

发送数据前要监控总线是否繁忙,从BUSY标志位来判断总线是否空闲,I2C_GetFlagStatus函数用来获取BUSY标志。I2C_GetFlagStatus 函数是一个在 STM32 微控制器的 I2C(Inter-Integrated Circuit)库函数中常用的函数,用于检查 I2C 接口的状态标志。这个函数通常用于轮询(polling)方式,以确定 I2C 总线上的特定事件或状态是否已经发生,例如数据传输完成、接收到起始信号、检测到错误等。

FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
  • I2Cx:指向要检查的 I2C 接口的指针。例如,对于 STM32F103 系列,可能是 I2C1 或 I2C2
  • I2C_FLAG:要检查的特定 I2C 状态标志。这些标志在 STM32 的 I2C 库头文件中定义,通常是以 I2C_FLAG_ 开头的宏。
  • 返回值是 FlagStatus 枚举类型,它通常有两个可能的值:SET(标志已设置)和 RESET(标志未设置)。

发送起始位

发送起始位是向START寄存器内写数值1,使用函数I2C_GenerateStart完成。


I2C_GenerateStart 函数是用于生成 I2C 通信起始条件(START condition)的函数。在 I2C 通信中,起始条件是一个重要的信号,用于通知所有连接到总线的设备即将开始数据传输。当 NewState 参数为 ENABLE 时,I2C_GenerateStart 函数会设置相应的寄存器位,从而在 I2C 总线上生成一个起始条件。起始条件是一个在 SCL(时钟线)为高电平时,SDA(数据线)由高电平变为低电平的边沿。这个边沿会被所有连接到总线的 I2C 设备检测到,并通知它们即将开始数据传输。

void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);
  • I2Cx:指向要操作的 I2C 接口的指针。在 STM32 微控制器中,这通常是 I2C1I2C2 等。
  • NewState:这是一个 FunctionalState 枚举类型的值,用于指定是否生成起始条件。它可以是 ENABLE(生成起始条件)或 DISABLE(不生成起始条件)。

在发送起止位后我们需要确定起止位是否发送完毕,我们通过SB标志来判断。

while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB) == RESET);

发送地址

AF标志位是ACK应答标志位,当AF为1时ACK答应失败未收到答应,ADDR寻址成功标志位,当寻址成功值为1,失败值为0。在发送地址前我们需要清理AF标志位的值然后发送地址。

I2C_ClearFlag(I2Cx,I2C_FLAG_AF);//清除AF
I2C_SendData(I2Cx,Addr & 0xfe);//发送地址和RW

在发送的过程中需要持续判断AF和ADDR标识符的状态

while(1){
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR) == SET){
		break;
	}
	if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF) == SET){
		I2C_GenerateSTOP(I2Cx,ENABLE);
		return -1;
	}
}

后续我们继续清除ADDR状态标示符

I2C_ReadRegister(I2Cx,I2C_Register_SR1);
I2C_ReadRegister(I2Cx,I2C_Register_SR2);

发送数据

发送数据过程中我们要持续监控ACK和发送数据寄存器的状态,AF为1标示为响应ACK,停止发送数据,BTF负责监控发送数据寄存器内是否有数据,保证在其空的情况下推送数据进入。

发送停止位

代码

int main(){
	My_I2C_Init();
	
	uint8_t commands[] = {0x00,0x8d,0x14,0xaf,0xa5};
	
	My_I2C_SendBytes(I2C1,0x78,commands,5);
}

void My_I2C_Init(){
//对I2C进行重映射
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	
		GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);
//对PB8和PB9进行初始化
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
		GPIO_InitTypeDef GPIO_InitStruct;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
		GPIO_Init(GPIOB,&GPIO_InitStruct);
	
	
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);//开启I2C的时钟
		RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,ENABLE);//施加复位信号
		RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1,DISABLE);//释放复位信号
	
		I2C_InitTypeDef I2C_InitStruct;
		I2C_InitStruct.I2C_ClockSpeed = 400000;//波特率400k
		I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;//标准的I2C
		I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2;//占空比2:1
		I2C_Init(I2C1,&I2C_InitStruct);
		
		I2C_Cmd(I2C1,ENABLE);//闭合I2C1的总开关
}


int My_I2C_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size){
		//等待总线空闲
		while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BUSY) == SET){}
			
		//发送起止位
		I2C_GenerateSTART(I2C1,ENABLE);
		//确定起止位是否发送完毕	
		while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB) == RESET){}
			
	  //发送地址
		//清除AF
			I2C_ClearFlag(I2Cx,I2C_FLAG_AF);
			
			I2C_SendData(I2Cx,Addr & 0xfe);
			
			while(1){
				if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR) == SET){
					break;
				}
				if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF) == SET){
					I2C_GenerateSTOP(I2Cx,ENABLE);
					return -1;//寻址失败
				}
			}
			//清除ADDR
			I2C_ReadRegister(I2Cx,I2C_Register_SR1);
			I2C_ReadRegister(I2Cx,I2C_Register_SR2);
			
			//发送数据 
			for(uint8_t i = 0;i<Size;i++){
				while(1){
					if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF) == SET){
						I2C_GenerateSTOP(I2Cx,ENABLE);
						return -2;
					}
					if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_TXE) == SET){
						break;
					}
				}
				
				I2C_SendData(I2Cx,pData[i]);
			
			}
			while(1){
					if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF) == SET){
						I2C_GenerateSTOP(I2Cx,ENABLE);
						return -2;
					}
					if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BTF) == SET){
						break;
					}
			}
			
			//发送停止位
			I2C_GenerateSTOP(I2Cx,ENABLE);
			return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2273809.html

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

相关文章

Flutter:吸顶效果

在分页中&#xff0c;实现tab吸顶。 TDNavBar的screenAdaptation: true, 开启屏幕适配。 该属性已自动对不同手机状态栏高度进行适配。我们只需关注如何实现吸顶。 view import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import p…

数据结构基础之《(13)—前缀树》

一、前缀树 1、前缀树&#xff08;Trie&#xff09;&#xff0c;又称字典树或单词查找树&#xff0c;是一种用于存储字符串集合的数据结构。 2、前缀树的操作 &#xff08;1&#xff09;单个字符串中&#xff0c;字符从前到后的加到一棵多叉树上 &#xff08;2&#xff09;字…

道品科技智慧农业与云平台:未来农业的变革之路

随着全球人口的不断增长&#xff0c;农业面临着前所未有的挑战。如何在有限的土地和资源上提高农业生产效率&#xff0c;成为了各国政府和农业从业者亟待解决的问题。智慧农业的兴起&#xff0c;结合云平台的应用&#xff0c;为农业的可持续发展提供了新的解决方案。 ## 一、智…

【Linux基础指令】第一期

一、Linux的介绍 Linux是一个开源的操作系统&#xff0c;性能、稳定性、安全性方面上都是很优秀的&#xff0c;所以它一直是企业后端系统的首选。所以其图形化界面并不是Linux的必需品&#xff0c;所以我们避免不了要使用命令行的形式来使用Linux&#xff0c;也就离不开…

​​​​​​芯盾时代以数据为核心的车联网业务安全解决方案

芯盾时代车联网业务安全建设聚焦智能网联业务运行过程产生的多维度、多模态、多视角数据以及因业务需求产生的过程数据和业务衍生数据&#xff0c;以网络安全等级保护为基础&#xff0c;坚持网络安全管理体系和技术体系并重的原则&#xff0c;加强网络安全体系化、实战化、常态…

剖析 Claim-Check 模式:以小传大,赋能分布式系统与微服务

1. 前言 1.1 写作背景与目的 在当今分布式系统与微服务架构盛行的时代&#xff0c;服务间的消息传递与数据交换越来越频繁。传统的消息传输在面对海量数据时&#xff0c;往往会遇到以下痛点&#xff1a; 消息体过大&#xff1a;直接通过消息队列或服务间接口发送大体量数据&…

VS2022引入sqlite数据库交互

法一&#xff1a;用官网编译好的动态库(推荐) 下载所需文件 sqlite官网地址&#xff1a;https://www.sqlite.org/howtocompile.html 下载以下的2个压缩包 第一个压缩包 sqlite-amalgamation-xxxx.zip&#xff0c;xxxx是版本号,保持一致即可&#xff0c;这里面有sqite3.h 第…

计算机的错误计算(二百零五)

摘要 基于一位读者的问题&#xff0c;提出题目&#xff1a;能用数值计算证明 吗&#xff1f;请选用不同的点&#xff08;即差别大的数&#xff09;与不同的精度。实验表明&#xff0c;大模型理解了题意。但是&#xff0c;其推理能力值得商榷。 例1. 就摘要中问题&#xff0…

设计形成从业务特点到设计模式的关联

规范和指引在应用架构、数据架构等各架构方向上形成规范性约束指导。同一个决策要点、架构单元在统一的架构原则指导下&#xff0c;会因业务特点差异有不同的实现&#xff0c;经过总结形成了最佳实践。在开展新应用的设计时&#xff0c;根据决策要点以及相关的业务特点&#xf…

深度学习blog-深刻理解线性变换和矩阵

深度学习中避免不了矩阵运算&#xff0c;或者张量&#xff08;其实是矩阵数组&#xff09;运算。卷积是矩阵加、乘法&#xff0c;注意力也是一样。本质都一样&#xff0c;所谓注意力&#xff0c;卷积、滤波&#xff0c;是对不必了解数学的人说的&#xff0c;底层都是矩阵运算&a…

C/C++程序性能测试方法综述

摘要 性能测试是软件开发中不可或缺的一部分&#xff0c;特别是在对性能要求较高的C/C程序中。本文将详细介绍多种C/C程序性能测试方法&#xff0c;包括时间复杂度分析、事后统计方法、事前分析估算方法、使用性能测试工具&#xff08;如Google Benchmark、gprof、Valgrind等&…

jmeter 中 BeanShell 预处理程序、JSR223后置处理程序使用示例

1. 各个组件如何新建的&#xff1f; 2. "http请求" 组件内容样例&#xff1a; "消息体数据" 源码&#xff1a; {"task_tag": "face_detect","image_type": "base64","extra_args": [{"model"…

电脑32位和64位之区别(Difference between 32-Bit and 64 Bit Computers)

电脑32位和64位之区别 很多小伙伴还不知道电脑32位和64位是什么意思&#xff0c;今天小编就来普及一下。 32位和64位是指电脑处理器&#xff08;CPU&#xff09;和操作系统的架构&#xff0c;决定了电脑如何处理数据、存储信息、运行程序等。 32位和64位是指电脑系统中每个处…

vue -关于浏览器localstorge数据定期清除的实现

1.实现背景 用户登录时的信息存在了localstorge中&#xff0c;但它会一直存在。一般来说&#xff0c;我们希望这个数据能够定期被清除掉&#xff0c;以下一个定时清除的实现。 2.实现原理 在用户登录时&#xff0c;将用户信息存入localstorge的同时&#xff0c;将当前时间作…

【JavaEE进阶】获取Cookie/Session

&#x1f340;Cookie简介 HTTP协议自身是属于 "⽆状态"协议. "⽆状态"的含义指的是: 默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信,和下次通信之间没有直接的联系.但是实际开发中,我们很多时候是需要知道请求之间的关联关系的. 例如登陆⽹站成…

【工具变量】统计行业锦标赛激励数据集(2008-2023年)

一、数据简介 坚持创新驱动发展&#xff0c;要强化企业创新主体地位&#xff0c;发挥企业家在技术创新中的重要作用。作为企业组织内部最具有影响力的角色&#xff0c;高级管理人员拥有企业经营管理的自由裁量权&#xff0c;对企业战略决策及由此产生的经营绩效具有举足轻重的…

UVM: TLM机制

topic overview 不建议的方法&#xff1a;假如没有TLM TLM TLM 1.0 整个TLM机制下&#xff0c;底层逻辑离不开动作发起者和被动接受者这个底层的模型基础&#xff0c;但实际上&#xff0c;在验证环境中&#xff0c;任何一个组件&#xff0c;都有可能成为动作的发起者&#xff0…

Scratch023-(沙漠变绿洲)

提示&#xff1a; 知识回顾&#xff1a; 1、画笔的各个属性 2、“将笔的颜色设为”积木 3、“将笔的颜色增加”积木 文章目录 前言一、案例展示二、功能分析三、步骤拆解1.背景角色和画笔的初始化&#xff08;1&#xff09;初始化画笔2、一起绘制一个小雨滴3、绘制多个随机的小…

游戏语音趋势解析,社交互动有助于营造沉浸式体验

语音交互的新架构出现 2024 年标志着对话语音 AI 取得了突破&#xff0c;出现了结合 STT → LLM → TTS 模型来聆听、推理和回应对话的协同语音系统。 OpenAI 的 ChatGPT 语音模式将语音转语音技术变成了现实&#xff0c;引入了基于音频和文本信息进行端到端预训练的模型&…

详细全面讲解C++中重载、隐藏、覆盖的区别

文章目录 总结1、重载示例代码特点1. 模板函数和非模板函数重载2. 重载示例与调用规则示例代码调用规则解释3. 特殊情况与注意事项二义性问题 函数特化与重载的交互 2. 函数隐藏&#xff08;Function Hiding&#xff09;概念示例代码特点 3. 函数覆盖&#xff08;重写&#xff…