今天我们来看MPU6050与水平仪这些功能代码如何实现?

那MPU6050与STM32是由什么通信的呢?

当然是I2C通信模块,在I2C这里我将PB10设置为SCL,将PB11设置为SDA

一.I2C通信.C

#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引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue为1时,需要置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引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
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初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,实现SCL和SDA引脚的初始化
  */
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发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位
	{
		/*两个!可以对数据进行两次逻辑取反,作用是把非0值统一转换为1,即:!!(0) = 0,!!(非0) = 1*/
		MyI2C_W_SDA(!!(Byte & (0x80 >> i)));//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据
	}
}

/**
  * 函    数:I2C接收一个字节
  * 参    数:无
  * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  */
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()){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
													//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

/**
  * 函    数:I2C发送应答位
  * 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  * 返 回 值:无
  */
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

/**
  * 函    数:I2C接收应答位
  * 参    数:无
  * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  */
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;							//返回定义应答位变量
}

这里I2C严格遵循I2C通信协议,在SCL高电平时,SDA由高电平变为低电平时产生起始信号,SDA由低电平变为高电平时产生终止信号。

I2C准备好了,那么该MPU6050用了

二.MPU6050

#include "stm32f10x.h"                  // Device header
#include "MyI2C.h"
#include "MPU6050_Reg.h"

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

/**
  * 函    数:MPU6050写寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 参    数:Data 要写入寄存器的数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data)
{
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(Data);				//发送要写入寄存器的数据
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_Stop();						//I2C终止
}

/**
  * 函    数:MPU6050读寄存器
  * 参    数:RegAddress 寄存器地址,范围:参考MPU6050手册的寄存器描述
  * 返 回 值:读取寄存器的数据,范围:0x00~0xFF
  */
uint8_t MPU6050_ReadReg(uint8_t RegAddress)
{
	uint8_t Data;
	
	MyI2C_Start();						//I2C起始
	MyI2C_SendByte(MPU6050_ADDRESS);	//发送从机地址,读写位为0,表示即将写入
	MyI2C_ReceiveAck();					//接收应答
	MyI2C_SendByte(RegAddress);			//发送寄存器地址
	MyI2C_ReceiveAck();					//接收应答
	
	MyI2C_Start();						//I2C重复起始
	MyI2C_SendByte(MPU6050_ADDRESS | 0x01);	//发送从机地址,读写位为1,表示即将读取
	MyI2C_ReceiveAck();					//接收应答
	Data = MyI2C_ReceiveByte();			//接收指定寄存器的数据
	MyI2C_SendAck(1);					//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_Stop();						//I2C终止
	
	return Data;
}

/**
  * 函    数:MPU6050初始化
  * 参    数:无
  * 返 回 值:无
  */
void MPU6050_Init(void)
{
	MyI2C_Init();									//先初始化底层的I2C
	
	/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);		//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);		//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x04);		//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);			//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);	//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);	//加速度计配置寄存器,选择满量程为±16g
}

/**
  * 函    数:MPU6050获取ID号
  * 参    数:无
  * 返 回 值:MPU6050的ID号
  */
uint8_t MPU6050_GetID(void)
{
	return MPU6050_ReadReg(MPU6050_WHO_AM_I);		//返回WHO_AM_I寄存器的值
}

/**
  * 函    数:MPU6050获取数据
  * 参    数:AccX AccY AccZ 加速度计X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 参    数:GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据,使用输出参数的形式返回,范围:-32768~32767
  * 返 回 值:无
  */
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

2.1首先给MPU6050写入读写(write,read)功能

I2C写寄存器流程 I2C读寄存器流程

1. 产生I2C起始信号 1. 发送重起始信号

2. 发送从机写地址 (最低位0=写) 2. 发送从机读地址 (最低位1=读)

3. 等待从机应答ACK 3. 等待从机应答ACK

4. 发送目标寄存器地址 4. 读取寄存器单字节数据

5. 等待从机应答ACK 5. 主机发送NACK(读完不再继续读)

6. 发送要写入寄存器的数据 6. 产生I2C停止信号

7. 等待从机应答ACK —

8. 产生I2C停止信号 —

补充:

写地址: 0xD0 ,读地址 =  0xD0 | 0x01 = 0xD1 ;

读完单个字节主机发NACK,多字节连续读最后一字节才NACK。

2.2MPU6050初始化(MPU6050_Init());

MyI2C_Init();									//先初始化底层的I2C
	
	/*MPU6050寄存器初始化,需要对照MPU6050手册的寄存器描述配置,此处仅配置了部分重要的寄存器*/
	MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);		//电源管理寄存器1,取消睡眠模式,选择时钟源为X轴陀螺仪
	MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);		//电源管理寄存器2,保持默认值0,所有轴均不待机
	MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x04);		//采样率分频寄存器,配置采样率
	MPU6050_WriteReg(MPU6050_CONFIG, 0x06);			//配置寄存器,配置DLPF
	MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18);	//陀螺仪配置寄存器,选择满量程为±2000°/s
	MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18);	//加速度计配置寄存器,选择满量程为±16g
}

先初始化I2C,接着给MPU6050进行写命令,第一步叫醒它,

第二步电源寄存器2可以不写

第三步给分频寄存器设置采样率

第四步配置低通滤波器,可以滤波

第五步配置陀螺仪寄存器,选择量程

第六步配置加速度寄存器,选择量程

2.3读取MPU6050ID号

配置MPU6050_WHO_AM_I这个寄存器,对这个寄存器进行读

2.4六轴数据采回函数

void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

读取不同轴的寄存器的数据,进行返回

2.5这是MPU6050寄存器(已整理)

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H

#define	MPU6050_SMPLRT_DIV		0x19
#define	MPU6050_CONFIG			0x1A
#define	MPU6050_GYRO_CONFIG		0x1B
#define	MPU6050_ACCEL_CONFIG	0x1C

#define	MPU6050_ACCEL_XOUT_H	0x3B
#define	MPU6050_ACCEL_XOUT_L	0x3C
#define	MPU6050_ACCEL_YOUT_H	0x3D
#define	MPU6050_ACCEL_YOUT_L	0x3E
#define	MPU6050_ACCEL_ZOUT_H	0x3F
#define	MPU6050_ACCEL_ZOUT_L	0x40
#define	MPU6050_TEMP_OUT_H		0x41
#define	MPU6050_TEMP_OUT_L		0x42
#define	MPU6050_GYRO_XOUT_H		0x43
#define	MPU6050_GYRO_XOUT_L		0x44
#define	MPU6050_GYRO_YOUT_H		0x45
#define	MPU6050_GYRO_YOUT_L		0x46
#define	MPU6050_GYRO_ZOUT_H		0x47
#define	MPU6050_GYRO_ZOUT_L		0x48

#define	MPU6050_PWR_MGMT_1		0x6B
#define	MPU6050_PWR_MGMT_2		0x6C
#define	MPU6050_WHO_AM_I		0x75

#endif

MPU6050寄存器地址直接可以用

三.MPU6050菜单函数显示

/*----------------------------------MPU6050-----------------------------------*/
// 定义六轴原始数据变量:ax,ay,az是加速度计原始值;gx,gy,gz是陀螺仪原始值
int16_t ax,ay,az,gx,gy,gz;

// 陀螺仪通过积分单独解算出来的欧拉角(会漂移,不稳定)
float roll_g,pitch_g,yaw_g;

// 加速度计通过重力向量单独解算出来的欧拉角(静止准,动起来抖)
float roll_a,pitch_a;

// 最终经过互补滤波融合后的稳定欧拉角(给界面显示用)
float Roll,Pitch,Yaw;

// 互补滤波系数 a=0.9,表示信任陀螺仪90%,信任加速度计10%
float a=0.9;

// 圆周率,用于弧度和角度之间的转换
double pi=3.1415927;

// 采样周期 0.005秒(5ms),用于陀螺仪积分计算角度
float Delta_t=0.005;

/**
  * 函    数:MPU6050_Calculation
  * 功    能:MPU6050姿态解算核心函数(读取数据+陀螺仪积分+加速度解算+互补滤波)
  */
void MPU6050_Calculation()
{
	// 延时5ms,控制数据采样频率,保证数据稳定
	Delay_ms(5);
	
	// 调用驱动函数,读取MPU6050的六轴原始数据
	MPU6050_GetData(&ax,&ay,&az,&gx,&gy,&gz);	
	
	
	//==================== 陀螺仪积分解算欧拉角 ====================
	// 原理:角度 = 上一次角度 + 角速度 × 时间
	// 优点:动态响应快
	// 缺点:长时间会漂移
	roll_g  = Roll  + (float)gx * Delta_t;	// 横滚角积分	
	pitch_g = Pitch + (float)gy * Delta_t;	// 俯仰角积分	
	yaw_g   = Yaw   + (float)gz * Delta_t;	// 偏航角积分	
	
	
	//==================== 加速度计解算倾斜角 ====================
	// 原理:利用重力加速度在三轴上的分量,通过反三角函数计算倾角
	// 优点:静态无漂移
	// 缺点:运动时抖动大,无法解算偏航角
	pitch_a = atan2((-1)*ax, az) * 180 / pi;	// 计算俯仰角Pitch
	roll_a  = atan2(ay, az) * 180 / pi;	    	// 计算横滚角Roll

	
	//==================== 互补滤波数据融合 ====================
	// 核心思想:取两者优点,避其缺点
	// 陀螺仪占比90% → 保证动态响应快
	// 加速度计占比10% → 修正陀螺仪漂移
	Roll  = a * roll_g  +  (1-a) * roll_a;	// 融合横滚角
	Pitch = a * pitch_g +  (1-a) * pitch_a;	// 融合俯仰角
	
	// 偏航角Yaw无法通过加速度计修正,只能用陀螺仪积分
	Yaw   = a * yaw_g;	
}

/**
  * 函    数:Show_MPU6050_UI
  * 功    能:在OLED屏幕上显示姿态角数据
  */
void Show_MPU6050_UI()
{
	// 显示左上角返回图标
	OLED_ShowImage(0,0,16,16,Return);	
	
	// 显示横滚角 Roll,保留两位小数
	OLED_Printf(0,16,OLED_8X16,"Roll:%.2f",Roll);	
	
	// 显示俯仰角 Pitch,保留两位小数
	OLED_Printf(0,32,OLED_8X16,"Pitch:%.2f",Pitch);
	
	// 显示偏航角 Yaw,保留两位小数
	OLED_Printf(0,48,OLED_8X16,"Yaw:%.2f",Yaw);
}

/**
  * 函    数:MPU6050
  * 功    能:MPU6050功能主函数,循环刷新数据,按键返回菜单
  */
int MPU6050()
{
	while(1)
	{
		// 读取按键键值
		KeyNum=Key_GetNum();
		
		// 如果按下KEY3,退出当前界面,返回上一级菜单
		if(KeyNum==3)
		{
			OLED_Clear();		// 清屏
			OLED_Update();		// 更新屏幕
			return 0;	        // 退出函数
		}
		
		OLED_Clear();			        // 清空屏幕缓冲区
		MPU6050_Calculation();		    // 执行姿态解算
		Show_MPU6050_UI();		        // 显示姿态数据
		OLED_ReverseArea(0,0,16,16);	// 反转返回图标区域(闪烁效果)
		OLED_Update();			        // 将缓冲区内容刷新到屏幕
	}
}

将采集的数据返回到定义的六个变量,进行姿态解算函数,主要是利用欧拉角公式计算,其余的都挺简单

四.陀螺仪菜单函数显示

/*----------------------------------水平仪-------------------------------------*/

void Show_Gradienter_UI(void)
{
	MPU6050_Calculation();
	OLED_DrawCircle(64,32,30,0);
	OLED_DrawCircle(64-Roll,32+Pitch,4,1);
}

int Gradienter(void)
{
	while(1)
	{
		KeyNum=Key_GetNum();
		if(KeyNum==3)
		{
			OLED_Clear();
			OLED_Update();
			return 0;
		}
		OLED_Clear();
		Show_Gradienter_UI();
		OLED_Update();
	}
}	

水平仪功能的实现原理: 通过 MPU6050 姿态解算得到 Roll(横滚角) 和 Pitch(俯仰角), 将角度值直接作为偏移量,实时修改小球在屏幕上的坐标。 当模块发生倾斜时,角度变化 → 小球坐标同步变化,从而实现小球随倾斜移动的效果

五.总菜单添加代码

int Menu(void)
{
    move_flag=1;
    uint8_t DirectFlag=2;                          // ? 改了
    while(1)
    {
        KeyNum=Key_GetNum();                       // ? 改了
        uint8_t menu_flag_temp=0;
        if(KeyNum==1)                              // ? 改了
        {
            DirectFlag=1;
            move_flag=1;
            menu_flag--;
            if(menu_flag<=0)menu_flag=8;
        }
        else if(KeyNum==2)                         // ? 改了
        {
            DirectFlag=2;
            move_flag=1;
            menu_flag++;
            if(menu_flag>=9)menu_flag=1;
        }
        else if(KeyNum==3)                         // ? 改了
        {
            OLED_Clear();
            OLED_Update();
            menu_flag_temp=menu_flag;
        }

        if(menu_flag_temp==1){return 0;}
        else if(menu_flag_temp==2){MenuToFunction();StopWatch();    move_flag=1;}
        else if(menu_flag_temp==3){ LED();move_flag=1;}
        else if(menu_flag_temp==4){MPU6050();move_flag=1;}
        else if(menu_flag_temp==5){Game();move_flag=1;}
        else if(menu_flag_temp==6){Emoji();move_flag=1;}
        else if(menu_flag_temp==7){Gradienter();move_flag=1;}
        else if(menu_flag_temp==8){HeartRate();move_flag=1;} // 新增:心率血氧
        if(menu_flag==1)
        {
            if(DirectFlag==1)Set_Selection(move_flag,1,0);
            else if(DirectFlag==2)Set_Selection(move_flag,0,0);
        }
        else
        {
            if(DirectFlag==1)Set_Selection(move_flag,menu_flag,menu_flag-1);
            else if(DirectFlag==2)Set_Selection(move_flag,menu_flag-2,menu_flag-1);
        }
    }
}

在对应的菜单下添加对应的功能即可

六.总结

今天我们了解了MPU6050以及水平仪两个的详细代码讲解,以及该怎么写给菜单对应的功能,顺便还复习了I2C的通信方式,了解MPU6050工作需要先对寄存器进行操作,那么今天就到这里,感谢你能看到这里,感谢你们的支持,你们的支持是我最大的动力!!!

 

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐