STM32入门笔记12_04_硬件I2C读写MPU6050

文章详细介绍了STM32微控制器如何利用其内置的硬件I2C接口与MPU6050运动传感器进行通信,包括I2C的基本结构、控制寄存器、时序图以及相关配置函数。同时,展示了如何编写I2C通信的程序代码,用于读写MPU6050的寄存器并获取传感器数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

I2C通信外设

I2C外设简介

  • STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担
  • 支持多主机模型
  • 支持7位/10位地址模式
  • 支持不同的通讯速度,标准速度(100kHz),快速(400kHz)
  • 支持DMA
  • 兼容SMBus协议
  • STM32F103C8T6硬件I2C资源: I2C1、I2C2

I2C框图

在这里插入图片描述

SDA线控制

在这里插入图片描述

  • 发送模式: 当发送寄存器空时(TXE), 单片机将数据写入数据寄存器(DATA REGISTER), 当数据移位寄存器为空时, 数据从数据寄存器转入数据移位寄存器, 再通过数据移位寄存器一位位输出
  • 接收模式: 数据一位位被送到数据移位寄存器中, 移位寄存器再把数据一位位送到数据寄存器中(DATA REGISTER), 当接收寄存器非空时(RXNE), 单片机就可以将数据寄存器中的数据读出

单片机从模式控制(了解)

在这里插入图片描述

  • 报错误校验(PEC) 用于提高通信的可靠性,使用CRC-8算法
  • 当stm32主机属于从模式时,可以通过自身地址寄存器和双地址寄存器为stm32自定义地址

SCL线控制

在这里插入图片描述

  • 控制寄存器用于配置硬件电路

    • CR1

在这里插入图片描述

  • CR2

在这里插入图片描述

  • 状态寄存器可以读取电路的信息

    • SR1

在这里插入图片描述

  • SR2

    在这里插入图片描述

  • 时钟控制寄存器用于配置时钟信号

    • CCR

在这里插入图片描述

  • 具体翻阅STM32F10xxx参考手册

I2C基本结构

在这里插入图片描述

时序图

主机发送

在这里插入图片描述

主机接收

在这里插入图片描述

软件硬件波形对比(了解)

在这里插入图片描述

程序设计

根据主机发送和主机接收的时序图重写 MPU6050_WriteReg()MPU6050_ReadReg()

I2C的配置结构体意义见注释

I2C常用函数介绍

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

    • 用于配置I2C
  • void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);

    • 开启对应的I2C外设
  • void I2C_GenerateSTART(I2C_TypeDef I2Cx, FunctionalState NewState);*

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

    • 产生终止条件
  • void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);

    • 控制是否有应答
  • void I2C_SendData(I2C_TypeDef I2Cx, uint8_t Data);*

    • 发送数据
  • void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);

    • 用于寻找从机地址
  • ErrorStatus I2C_CheckEvent(I2C_TypeDef I2Cx, uint32_t I2C_EVENT)*

    • 状态检测函数,用于检查I2C通讯过程中产生的事件
  • FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);

    • 状态检测函数,用于获取状态寄存器中的标志位

main.c

#include "stm32f10x.h" 
#include "delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int main(void)
{
	OLED_Init();
	MPU6050_Init();
	OLED_ShowString(1, 1, "ID:");
	ID = MPU6050_GetID();
	OLED_ShowHexNum(1, 4, ID, 2);
	MPU6050_DataTypeDef MPU6050_DataStructure;
	while(1)
	{
		// 读取数据
		MPU6050_GetData(&MPU6050_DataStructure);
		// 显示数据
		OLED_ShowSignedNum(2, 1, MPU6050_DataStructure.AccX, 5);
		OLED_ShowSignedNum(3, 1, MPU6050_DataStructure.AccY, 5);
		OLED_ShowSignedNum(4, 1, MPU6050_DataStructure.AccZ, 5);
		OLED_ShowSignedNum(2, 8, MPU6050_DataStructure.GyroX, 5);
		OLED_ShowSignedNum(3, 8, MPU6050_DataStructure.GyroY, 5);
		OLED_ShowSignedNum(4, 8, MPU6050_DataStructure.GyroZ, 5);
	}
}

MPU6050.c

#include "stm32f10x.h"
#include "MPU6050_SEG.h"

#define MPU_ADDRESS 0xD0

/*
 *  等待事件 带超时退出机制 
 *  将while与I2C_CheckEvent封装在一起
 */
void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
	uint32_t Timeout=50000;
	while(Timeout && (I2C_CheckEvent(I2Cx, I2C_EVENT) != SUCCESS))
	{
		Timeout --;
	}
}

/*
 *  往指定地址写入指定数据
 */
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	// 起始条件
	I2C_GenerateSTART(I2C2, ENABLE);
	// 等待 EV5
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);
	// 呼叫设备(MPU6050)
	I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter);
	// 等待 EV6
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);
	// 寻找写入地址
	I2C_SendData(I2C2, RegAddress);
	// 等待EV8
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING);
	// 写入数据
	I2C_SendData(I2C2, Data);
	// 等待EV8_2
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);
	// 终止条件
	I2C_GenerateSTOP(I2C2, ENABLE);
}

/*
 *  从指定地址读取数据
 */
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	// 起始条件
	I2C_GenerateSTART(I2C2, ENABLE);	// 起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);	// 等待 EV5
	
	I2C_Send7bitAddress(I2C2, MPU_ADDRESS, I2C_Direction_Transmitter); //呼叫设备 写
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); // 等待 EV6
	
	I2C_SendData(I2C2, RegAddress); // 寻找读取地址
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); // 等待EV8_2
	
	I2C_GenerateSTART(I2C2, ENABLE); // 再次产生起始条件
	MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); // 等待 EV5
	
	I2C_Send7bitAddress(I2C2, MPU_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;
}

/*
 *  配置MPU6050
 */
void MPU6050_Init(void)
{
	// I2C初始化
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	// 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);
	// I2C
	I2C_InitTypeDef I2C_InitStructure;
	I2C_InitStructure.I2C_Ack=I2C_Ack_Enable;  // 有应答
	I2C_InitStructure.I2C_ClockSpeed=50000;  // 400 kHz以下  100 kHz以上为快速通讯
	I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2;  // 低电平比高电平2:1 仅在快速通讯生效  
	I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
	I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; 
    // 从模式下 stm32作为从机的地址所占位数	
	I2C_InitStructure.I2C_OwnAddress1=0x00;  // 从模式下stm32作为从机的地址
	I2C_Init(I2C2, &I2C_InitStructure);
	// 开启I2C2
	I2C_Cmd(I2C2, ENABLE);
	
	MPU6050_WriteReg(PWR_MGMT_1, 0x01);
	MPU6050_WriteReg(PWR_MGMT_2, 0x00);
	MPU6050_WriteReg(SMPLRT_DIV, 0x09);
	MPU6050_WriteReg(CONFIG, 0x06);
	MPU6050_WriteReg(GYRO_CONFIG, 0x18);
	MPU6050_WriteReg(ACCEL_CONFIG, 0x18);
}
/*
 * 获取ID号
 */
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(WHO_AM_I);
}
/*
 *  获取MPU6050数据
 */
void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure)
{
	uint16_t Data_H, Data_L;  // 高位数据和低位数据
	Data_H = MPU6050_ReadReg(ACCEL_XOUT_H);
	Data_L = MPU6050_ReadReg(ACCEL_XOUT_L);
	MPU_DataStructure->AccX=(Data_H<<8) | Data_L;
	
	Data_H = MPU6050_ReadReg(ACCEL_YOUT_H);
	Data_L = MPU6050_ReadReg(ACCEL_YOUT_L);
	MPU_DataStructure->AccY=(Data_H<<8) | Data_L;
	
	Data_H = MPU6050_ReadReg(ACCEL_ZOUT_H);
	Data_L = MPU6050_ReadReg(ACCEL_ZOUT_L);
	MPU_DataStructure->AccZ=(Data_H<<8) | Data_L;
	
	Data_H = MPU6050_ReadReg(GYRO_XOUT_H);
	Data_L = MPU6050_ReadReg(GYRO_XOUT_L);
	MPU_DataStructure->GyroX=(Data_H<<8) | Data_L;
	
	Data_H = MPU6050_ReadReg(GYRO_YOUT_H);
	Data_L = MPU6050_ReadReg(GYRO_YOUT_L);
	MPU_DataStructure->GyroY=(Data_H<<8) | Data_L;
	
	Data_H = MPU6050_ReadReg(GYRO_ZOUT_H);
	Data_L = MPU6050_ReadReg(GYRO_ZOUT_L);
	MPU_DataStructure->GyroZ=(Data_H<<8) | Data_L;
}

MPU6050.h

#ifndef __MPU6050_H
#define __MPU6050_H
#include "MPU6050_REG.h"
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);
void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(MPU6050_DataTypeDef * MPU_DataStructure);
#endif

MPU6050_REG.h

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H
// 寄存器地址
#define SMPLRT_DIV 				0x19
#define CONFIG					0x1A
#define GYRO_CONFIG 			0x1B
#define ACCEL_CONFIG 			0x1C
#define ACCEL_XOUT_H 			0x3B
#define ACCEL_XOUT_L 			0x3C
#define ACCEL_YOUT_H 			0x3D
#define ACCEL_YOUT_L 			0x3E
#define ACCEL_ZOUT_H 			0x3F
#define ACCEL_ZOUT_L 			0x40
#define TEMP_OUT_H 				0x41
#define	TEMP_OUT_L				0x42
#define	GYRO_XOUT_H				0x43
#define	GYRO_XOUT_L				0x44
#define	GYRO_YOUT_H				0x45
#define	GYRO_YOUT_L				0x46
#define	GYRO_ZOUT_H				0x47
#define	GYRO_ZOUT_L				0x48

#define	PWR_MGMT_1				0x6B
#define	PWR_MGMT_2				0x6C
#define	WHO_AM_I				0x75

/*
 *  MPU6050数据结构体
 */
typedef struct
{
	int16_t AccX;
	int16_t AccY;
	int16_t AccZ;
	int16_t GyroX;
	int16_t GyroY;
	int16_t GyroZ;
} MPU6050_DataTypeDef;

#endif

参考资料

【STM32入门教程-2023持续更新中】

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值