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

STM32学习笔记14-I2C硬件控制

I2C外设简介


  • STM32内部集成了硬件I2C收发电路(硬件收发器:自动生产波形,自动翻转电平等),可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担——软件只需要写入控制寄存器CR和DR,还有实时监控时序状态的状态寄存器SR
  • 支持多主机模型
  • 支持7位/10位地址模式
  • 支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)
  • 支持DMA
  • 兼容SMBus协议
  • STM32F103C8T6 硬件I2C资源:I2C1、I2C2

补:多主机模型:固定多主机(有固定的主机数和固定的从机数)和可变多主机(任何设备,都可以从空闲的状态跳出来作为主机,然后指定通信,之后又跳回来),采用10位地址的时序:起始条件后的两个字节都是寻址,其中前一个字节,是帧头:内容是5位的标志位11110+2位地址+1位读写位,后一个字节:纯粹的8位地址。

I2C框图

引脚对应关系

I2C基本结构(一主多从)

硬件I2C的操作流程:

主机发送

主机接收

软件/硬件波形对比

手册——对应24章

接线图

10-2 硬件I2C读写MPU6050


  1. 开启I2C外设和对应GPIO口的时钟
  2. 把I2C外设对应的GPIO口初始化为复用开漏模式
  3. 使用结构体,对整个I2C进行配置
  4. I2C_Cmd,使能I2C

相关函数:

void I2C_DeInit(I2C_TypeDef* I2Cx);

void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);

void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);

void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);  //生产起始条件

void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);  //生产结束条件

void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);  //配置应答ACK

void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);  //数据写入DR寄存器

uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);  //读取DR的数值

void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);  //发送7位地址的专用函数

//EV——多种监控

I2C_CheckEvent()  //基本

I2C_GetLastEvent()  //高级

I2C_GetFlagStatus()   //基于状态位的标准监控

void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);

ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);

void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT);

MPU6050.c//27硬件I2C读写MPU6050
//与软件的区别就是MyI2C.c这个文件,硬件是不需要的
//意思是:底层的逻辑会有不同,其他是一样的
#include "stm32f10x.h"                  // Device header#define MPU_ADD   0xD0
#include "MPU_Reg.h"
//封装指定地址写和指定地址读的时序
//优化:在代码中,存在很多死循环的地方——超时退出void CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT){uint32_t TimeOut;TimeOut=100;	while(I2C_CheckEvent(I2Cx, I2C_EVENT)!=SUCCESS){TimeOut--;if(TimeOut==0){break;//错误处理}}
}void MPU_WriteReg(uint8_t RegAddress,uint8_t Data){//用此函数,则会一直传输数据,所以我们需要用标志位去确定它是否操作成功了,这里就要用EV5事件来确定I2C_GenerateSTART(I2C2, ENABLE);//while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//封装
//		while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS){
//			TimeOut--;
//			if(TimeOut==0){
//				return;
//			}
//		}CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//发送函数会自带应答位,所以我们不需要考虑应答,只需要考虑发送后的事件EV6即可I2C_Send7bitAddress(I2C2,MPU_ADD, I2C_Direction_Transmitter); CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//EV8_1的事件是提醒应该写入DR数据,不需要等待I2C_SendData(I2C2, RegAddress); //同理,等待对应的事件EV8CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); I2C_SendData(I2C2, Data);//当发送为最后一个数据时,就需要等待EV8_2事件CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);I2C_GenerateSTOP(I2C2, ENABLE); }uint8_t MPU_ReadingReg(uint8_t RegAddress){uint8_t Data;I2C_GenerateSTART(I2C2, ENABLE);CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //发送函数会自带应答位,所以我们不需要考虑应答,只需要考虑发送后的事件EV6即可I2C_Send7bitAddress(I2C2,MPU_ADD, I2C_Direction_Transmitter); CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV8_1的事件是提醒应该写入DR数据,不需要等待I2C_SendData(I2C2, RegAddress); //在最后一个数据,用EV8_2while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS); I2C_GenerateSTART(I2C2, ENABLE);while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS); //主机接收I2C_Send7bitAddress(I2C2,MPU_ADD, I2C_Direction_Receiver); while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS); //接收从机的数据:规定:在接收数据之前,需要把ACK置0,同时设置停止位STOP//如果读取多个字节,那直接等待EV7事件,读取DR,就能收到数据,在接收最后一个字节之前EV7_1事件,需要把ACK置0,同时设置停止位STOP//如果读取一个字节,那在EV6事件之后,需要把ACK置0,同时设置停止位STOP,在等待EV7事件,不然会多一个字节I2C_AcknowledgeConfig(I2C2, DISABLE);I2C_GenerateSTOP(I2C2,ENABLE);while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS); //读取DRData=I2C_ReceiveData(I2C2); I2C_AcknowledgeConfig(I2C2, ENABLE);  //应答值设为1,给从机应答,这样可以使指定地址收多个字节return Data;}void MPU6050_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_11;GPIO_Init(GPIOB, &GPIO_InitStructure);I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode =I2C_Mode_I2C;  //I2C的模式I2C_InitStructure.I2C_ClockSpeed=50000;  //时钟频率,要低于400KHzI2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;//时钟占空比,只有在时钟频率大于100KHz才有用,小于则固定的1:1;——能更快的传输I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;  //应答配置I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;  //STM做从机,可以被响应几位地址I2C_InitStructure.I2C_OwnAddress1=0x00;  //自身寄存器,当STM32做从机时,指定STM32的自身地址,方便主机呼叫I2C_Init(I2C2,&I2C_InitStructure);I2C_Cmd(I2C2,ENABLE);MPU_WriteReg(MPU6050_PWR_MGMT_1,0x01);  //解除睡眠,选择陀螺仪时钟MPU_WriteReg(MPU6050_PWR_MGMT_2,0x00);	//6个轴均不待机MPU_WriteReg(MPU6050_SMPLRT_DIV,0x09);	//采样分频为10MPU_WriteReg(MPU6050_CONFIG,0x06);	//滤波参数最大MPU_WriteReg(MPU6050_GYRO_CONFIG,0x18);	//陀螺仪和加速度选择最大MPU_WriteReg(MPU6050_ACCEL_CONFIG,0x18);
//	//此时的MPU就在进行大量的数据转换,数据存放在其他的寄存器里
}void MPU_Getdata(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)  
{//读取加速度寄存器XYZ轴的高8位和低8位uint8_t DataH, DataL;								//定义数据高8位和低8位的变量DataH = MPU_ReadingReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据DataL = MPU_ReadingReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU_ReadingReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据DataL = MPU_ReadingReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU_ReadingReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据DataL = MPU_ReadingReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU_ReadingReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据DataL = MPU_ReadingReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU_ReadingReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据DataL = MPU_ReadingReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回DataH = MPU_ReadingReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据DataL = MPU_ReadingReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回}

http://www.dtcms.com/a/337387.html

相关文章:

  • 【音视频】瑞芯微、全志芯片在运动相机和行车记录仪产品分析
  • keil错误:Error: failed to execute ‘D:\Keil\C51\BIN\BIN\A51.EXE‘
  • MongoDB 查询方法与高级查询表(Python版)
  • Redis 客户端接口介绍
  • 微信小程序通过uni.chooseLocation打开地图选择位置,相关设置及可能出现的问题
  • Apache Shiro550 漏洞(CVE-2016-4437):原理剖析与实战 SOP
  • 【Linux开发】错误更改bash.sh导致PATH环境变量被破坏所有命令不可用的解决方法
  • 【Axure高保真原型】时间轴缩放面积图
  • CMIP6 气候模式核心特性解析
  • 学习游戏制作记录(各种独特物品效果)8.18
  • 代码随想录-数组练习
  • 矿物识别案例(数据处理:六种填充方法)
  • 深度剖析PyTorch分布式训练:从原理到工程实践
  • Centos7使用lamp架构部署wordpress
  • 安全基础DAY6-服务器安全检测和防御技术
  • 网站服务器使用免费SSL证书安全吗?
  • 计算机网络技术学习-day3《交换机配置》
  • ⭐CVPR2025 RigGS:从 2D 视频到可编辑 3D 关节物体的建模新范式
  • 一个基于前端开发的经典飞机大战游戏,具有现代化的UI设计和流畅的游戏体验。
  • OpenAL技术详解:跨平台3D音频API的设计与实践
  • 飞机起落架轮轴深孔中间段电解扩孔内轮廓检测 - 激光频率梳 3D 轮廓检测
  • 【verge3d】如何在项目里调用接口
  • Gateway中Forward配置+源码观赏
  • Pandas 核心数据结构详解(精简版)
  • Drawnix:一款免费开源的白板工具,支持思维导图、流程图、类图和手绘图
  • mybatisplus oracle 数据库OracleKeyGenerator使用序列生成主键原理
  • Redis-缓存-穿透-布隆过滤器
  • Linux 系统(如 Ubuntu / CentOS)阿里云虚拟机(ECS)上部署 Bitnami LAMP
  • 用随机森林填补缺失值:原理、实现与实战
  • 大型语言模型(LLM)存在演示位置偏差:相同示例在提示中位置不同会导致模型预测结果和准确率显著变化