STM32学习24--I2C-2
一、MPU6050简介
1、MOU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角(欧拉角),常用于平衡车、飞行器等需要检测自身姿态的场景。
2、3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度;
3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度;
3、16位ADC采集传感器的模拟信号,量化范围:-32768~32767;
4、加速度即满量程选择:±2、±4、±8、±16;
5、陀螺仪满量程选择:±250、±500、±1000、±2000(°/sec);
6、可配置的数字低通滤波;
7、可配置的时钟源;
8、可配置的采样分频;
二、硬件与引脚

1、VCC:3.3V,模块板载LDO可以接5V,芯片内核只能3.3V。
2、GND:必须与STM32共地,否则数据乱跳;
3、SDA、SCL:I2C通信引脚,最高400KHz,
4、XCL、XDA:主机I2C通信引脚:是扩展功能用,常外接磁力计、气压计,将外接的数据读取到MPU6050里面,通过DMP数据融合和姿态解算,也可将气压计直接接SCL、SDA上。
5、AD0:地址选择
AD0接地→I2C 7位地址0x68(0x1101000);
AD0接VCC→I2C 7位地址0x69 (0x1101001);
6、INT:中断输出。
三、常用关键寄存器
1、WHO_AM_I 0x75(只读):读出应为0x68,读不到则I2C不通;
2、PWR_MGMT_1 0x6B,电源管理1,上电默认0x40;
| 位 | 名称 | 说明 |
| 7 | DEVICE_RESET |
写1硬件复位芯片,硬件自动清0; 复位后芯片进入睡眠 |
| 6 | SLEEP | 1=睡眠模式;0=正常工作,必须清零 |
| 5 | CYCLE | 循环低功耗模式:睡眠--唤醒间歇采集加速度,关闭陀螺仪。 |
| 4 | RESERVED | 保留 |
| 3 | TEMP_DIS | 1-关闭片内温度传感器,0-开启 |
| 2-0 | CKLSEL | 时钟源选择 |
CKLSEL推荐:0-内部8M RC,精度差,不推荐长期使用;
1:PLL,X-Gyro时钟源,最常用;2:PLL Y-Gyro;3:PLL Z-Gyro;
标准唤醒配置:0x01--退出睡眠,使用X陀螺PLL时钟,稳定打开,建议不要写0x00;
3、PWR_MGMT_2 0x6C,电源管理2
bit[5-0]=分别控制AX、AY、AZ,GX,GY,GZ,写0开启该轴,写1关闭该轴;上电默认全部是0,六轴全开,全部传感器开启;注:这不是休眠,是单轴关闭,休眠是0x6B的bit6;
4、SMPLRT_DIV 0x19 采样率分频寄存器
陀螺仪输出数据率=陀螺仪内部输出速率/(1+SMPRT_DIV);
DLPF_CFG≠7,陀螺基础速率1000Hz(ADC);
DLPF_CFG=0或7,陀螺基础速率8000Hz(ADC);示例如下:
SMPRT_DIV=7→1000/(1+7) =125Hz,平衡车、倒立摆最常用;而加速度ADC固定1kHz,不受SMPRT_DIV的限制。
5、CONFIG 0x1A 配置寄存器
DLPF(Digital Low Pass Filter):硬件数字低通滤波
bit[2-0] =DLPF_CFG,硬件数字低通滤波,直接决定滤波、延迟。

[7-3]:FSYNC外部同步输入,不用必须写0;Frame Synchronization(帧同步)
[2-0]:DLPF_CFG数字低通滤波配置;

示例:写0x06,即DLPF_CFG=6,带宽5Hz,强抑制振动噪声,但延迟大,适合平衡车,高速项目选3或4;
DLPF是硬件模拟之后数字滤波,优先用硬件DLPF,不能全靠软件滤波。
加速度计带宽好陀螺带宽不是严格相等,是2套独立的数字滤波器硬件。
DLPF带宽 VS SMPLRT_DIV(0x19)输出采样频率
DLPF_CFG:硬件滤波器截止带宽,抗混叠、过滤噪声,发送在ADC之后,输出寄存器之前。
SMPLRT_DIV:输出分频,决定MCU读到数据的输出频率ODR(只改变输出多少Hz给MCU,不改变滤波器截止频率)。
经验设计原则:硬件DLPF带宽必须小于ODR/2,否则发发送信号混叠。比如:ODR=125Hz,ODR/2=62.5,那么DLPF_CFG只能选4(21Hz)、5(10Hz)、6(5Hz),不能选0/1/2/3。
6、GYRO_CONFIG 0x1B 陀螺仪配置寄存器

[7-5]是陀螺仪X、Y、Z的自检位;[4-3]是FS_SEL陀螺仪满量程选择;[2-0]保留,为0;

示例:0x18:FS_SEL=3,±2000°/s;最大量程,分辨率最低,适合高速旋转。普通平衡小车推荐0x08,±500°/s。
7、ACCEL_CONFIG 0x1C 加速度计配置寄存器

[7-5]是加速度计X、Y、Z的自检位;[4-3]是AFS_SEL加速度计量程选择;[2-0]写0;

示例:0x18:AFS_SEL=3,±16g;量程越大,分辨率越低。静止、倾角检测选±2g,冲击、剧烈运动选±8g。
8、数据寄存器组

只读,0x3B开始,连续14个字节,全部为16位二进制补码,高字节在前,低字节在后。读取后必须存入int16_t有符号16位整型,不能用uint16_t直接运算。
示例:0x3B-0x3C:ACCEL_XOUT_H/L,X加速度;以此类推……

0x41-0x42 :TEMP_OUT_H/L:片内温度原始值,温度计算公式:
int16_t Temp_raw;
float Temp_c =Temp_raw/340.0f+36.53f;
这是芯片硅片内部温度,不是环境温度,32-38℃属于正常,不能当环境温度计。

示例:0x43-0x44:ACCEL_XOUT_H/L,X陀螺仪角速度;以此类推……
四、代码部分
1、MPU6050.c文件
#include "stm32f10x.h"
#include "I2C.h"
#include "MPU6050Req.h"
#include "MPU6050.h"
#define MPU6050_ADDRESS 0xD0
void MPU6050_WriteReq(uint8_t ReqAddress,uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveACK();
MyI2C_SendByte(ReqAddress);
MyI2C_ReceiveACK();
MyI2C_SendByte(Data);
MyI2C_ReceiveACK();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReq(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveACK();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveACK();
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS|0x01);
MyI2C_ReceiveACK();
Data =MyI2C_ReceiveByte();
MyI2C_SendACK(1);
MyI2C_Stop();
return Data;
}
void MPU6050_Init()
{
MyI2C_Init();
MPU6050_WriteReq(MPU6050_PWR_MGMT_1,0x01);
MPU6050_WriteReq(MPU6050_PWR_MGMT_2,0x00);
MPU6050_WriteReq(MPU6050_SMPLRT_DIV,0x09);
MPU6050_WriteReq(MPU6050_CONFIG,0x06);
MPU6050_WriteReq(MPU6050_GYRO_CONFIG,0x18);
MPU6050_WriteReq(MPU6050_ACCEL_CONFIG,0x18);
Delay_ms(100);
}
uint8_t GetID()
{
return MPU6050_ReadReq(MPU6050_WHO_AM_I);
}
void MPU6050_GetData(MPU6050_StructureData* SensorValue)
{
uint8_t DataH,DataL;
DataH = MPU6050_ReadReq(MPU6050_ACCEL_XOUT_H);
DataL = MPU6050_ReadReq(MPU6050_ACCEL_XOUT_L);
SensorValue->AccX = (int16_t)DataH<<8| DataL;
DataH = MPU6050_ReadReq(MPU6050_ACCEL_YOUT_H);
DataL = MPU6050_ReadReq(MPU6050_ACCEL_YOUT_L);
SensorValue->AccY = (int16_t)DataH<<8| DataL;
DataH = MPU6050_ReadReq(MPU6050_ACCEL_ZOUT_H);
DataL = MPU6050_ReadReq(MPU6050_ACCEL_ZOUT_L);
SensorValue->AccZ = (int16_t)DataH<<8| DataL;
}
2、MPU6050.h文件
#ifndef __MPU6050_H
#define __MPU6050_H
#include "Delay.h"
void MPU6050_WriteReq(uint8_t ReqAddress,uint8_t Data);
uint8_t MPU6050_ReadReq(uint8_t RegAddress);
void MPU6050_Init();
uint8_t GetID();
typedef struct
{
int16_t AccX;
int16_t AccY;
int16_t AccZ;
int16_t GroX;
int16_t GroY;
int16_t GroZ;
}MPU6050_StructureData;
void MPU6050_GetData(MPU6050_StructureData* SensorValue);
#endif
3、MPU6050寄存器文件
#ifndef __MPU6050REQ_H
#define __MPU6050REQ_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 //加速度计X轴高位
#define MPU6050_ACCEL_XOUT_L 0x3C //加速度计X轴低位,以此类推……
#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 //电源管理1,配置睡眠模式和时钟源选择
#define MPU6050_PWR_MGMT_2 0x6C //电源管理2,配置X、Y、Z睡眠与否
#define MPU6050_WHO_AM_I 0x75
#endif
4、main函数
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
MPU6050_StructureData Sensor_Value;
int main()
{
OLED_Init();
MPU6050_Init();
ID= GetID();
Test =TestValue();
OLED_ShowHexNum(1,1,ID,2);
OLED_ShowHexNum(1,4,Test,2);
while(1)
{
MPU6050_GetData(&Sensor_Value);
OLED_ShowSignedNum(2,1,Sensor_Value.AccX,5);
OLED_ShowSignedNum(3,1,Sensor_Value.AccY,5);
OLED_ShowSignedNum(4,1,Sensor_Value.AccZ,5);
Delay_ms(100);
}
}
练习2:连续读
1、I2C底层修改
#include "stm32f10x.h" // Device header
#include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction)BitValue);
Delay_us(5);
}
void MyI2C_W_SDA(uint8_t BitValue)
{
GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction)BitValue);
Delay_us(5);
}
uint8_t MyI2C_R_SDA()
{
uint8_t BitValue;
BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
Delay_us(5);
return BitValue;
}
void MyI2C_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
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);
GPIO_SetBits(GPIOB,GPIO_Pin_10 |GPIO_Pin_11);
}
void MyI2C_Start()
{
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
MyI2C_W_SDA(0);
MyI2C_W_SCL(0);
}
void MyI2C_Stop()
{
MyI2C_W_SCL(0);
MyI2C_W_SDA(0);
MyI2C_W_SCL(1);
MyI2C_W_SDA(1);
}
void MyI2C_SendByte(uint8_t Byte)
{
for(uint8_t i=0;i<8;i++)
{
MyI2C_W_SDA(Byte&(0x80>>i));
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
}
uint8_t MyI2C_ReceiveByte()
{
uint8_t Byte=0x00;
uint8_t i=0;
MyI2C_W_SDA(1);
for(i=0;i<8;i++)
{
MyI2C_W_SCL(1);
if(MyI2C_R_SDA()==1)
{
Byte|=(0x80>>i);
}
// MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
return Byte;
}
void MyI2C_SendACK(uint8_t ACKBit)
{
MyI2C_W_SDA(ACKBit);
MyI2C_W_SCL(1);
MyI2C_W_SCL(0);
}
uint8_t MyI2C_ReceiveACK()
{
uint8_t ACKBit ;
MyI2C_W_SDA(1);
MyI2C_W_SCL(1);
ACKBit = MyI2C_R_SDA();
MyI2C_W_SCL(0);
return ACKBit;
}
//连续读
void MyI2C_ReadMulti(uint8_t Addr,uint8_t Reg,uint8_t*Buff,uint8_t Lenth)
{
MyI2C_Start();
MyI2C_SendByte(Addr);
MyI2C_ReceiveACK();
MyI2C_SendByte(Reg);
MyI2C_ReceiveACK();
MyI2C_Start();
MyI2C_SendByte(Addr |0x01);
MyI2C_ReceiveACK();
for(uint8_t i=0;i<Lenth;i++)
{
Buff[i]= MyI2C_ReceiveByte();
if(i ==Lenth-1)
{
MyI2C_SendACK(1);
}
else
{
MyI2C_SendACK(0);
}
}
MyI2C_Stop();
}
2、MPU6050.c文件
#include "stm32f10x.h"
#include "I2C.h"
#include "MPU6050Req.h"
#include "MPU6050.h"
#define MPU6050_ADDRESS 0xD0
void MPU6050_WriteReq(uint8_t ReqAddress,uint8_t Data)
{
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveACK();
MyI2C_SendByte(ReqAddress);
MyI2C_ReceiveACK();
MyI2C_SendByte(Data);
MyI2C_ReceiveACK();
MyI2C_Stop();
}
uint8_t MPU6050_ReadReq(uint8_t RegAddress)
{
uint8_t Data;
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS);
MyI2C_ReceiveACK();
MyI2C_SendByte(RegAddress);
MyI2C_ReceiveACK();
MyI2C_Start();
MyI2C_SendByte(MPU6050_ADDRESS|0x01);
MyI2C_ReceiveACK();
Data =MyI2C_ReceiveByte();
MyI2C_SendACK(1);
MyI2C_Stop();
return Data;
}
void MPU6050_Init()
{
MyI2C_Init();
MPU6050_WriteReq(MPU6050_PWR_MGMT_1,0x01);
MPU6050_WriteReq(MPU6050_PWR_MGMT_2,0x00);
MPU6050_WriteReq(MPU6050_SMPLRT_DIV,0x09);
MPU6050_WriteReq(MPU6050_CONFIG,0x06);
MPU6050_WriteReq(MPU6050_GYRO_CONFIG,0x18);
MPU6050_WriteReq(MPU6050_ACCEL_CONFIG,0x18);
Delay_ms(100);
}
uint8_t GetID()
{
return MPU6050_ReadReq(MPU6050_WHO_AM_I);
}
uint8_t TestValue()
{
return MPU6050_ReadReq(MPU6050_PWR_MGMT_1);
}
void MPU6050_GetData(MPU6050_StructureData* SensorValue)
{
uint8_t Buff[14];
MyI2C_ReadMulti(MPU6050_ADDRESS,MPU6050_ACCEL_XOUT_H,Buff,14);
SensorValue->AccX =(int16_t)Buff[0]<<8 |Buff[1];
SensorValue->AccY =(int16_t)Buff[2]<<8 |Buff[3];
SensorValue->AccZ =(int16_t)Buff[4]<<8 |Buff[5];
SensorValue->GroX=(int16_t)Buff[8]<<8 |Buff[9];
SensorValue->GroY =(int16_t)Buff[10]<<8 |Buff[11];
SensorValue->GroZ =(int16_t)Buff[12]<<8 |Buff[13];
}
3、main函数
#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int16_t AccX,AccY,AccZ,GroX,GroY,GroZ;
MPU6050_StructureData Sensor_Value;
int main()
{
OLED_Init();
MPU6050_Init();
ID= GetID();
Test =TestValue();
OLED_ShowHexNum(1,1,ID,2);
while(1)
{
MPU6050_GetData(&Sensor_Value);
OLED_ShowSignedNum(2,1,Sensor_Value.AccX,5);
OLED_ShowSignedNum(3,1,Sensor_Value.AccY,5);
OLED_ShowSignedNum(4,1,Sensor_Value.AccZ,5);
OLED_ShowSignedNum(2,8,Sensor_Value.GroX,5);
OLED_ShowSignedNum(3,8,Sensor_Value.GroY,5);
OLED_ShowSignedNum(4,8,Sensor_Value.GroZ,5);
Delay_ms(100);
}
}
结果:量程配置±16g,平放时,AccZ≈+2048,AccX、AccY在0附近小范围跳动。
陀螺仪静止全部接近0,手动转动模块,正负值来回变化。
本文完!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)