目录
前言
MPU6050
1.简介
2.相关参数
3.硬件电路
4.MPU6050框图
C编程实现I2C读写MPU6050步骤
1.MyI2C.c文件
(1)引脚的宏定义
(2)对SCL和SDA的操作以及初始化
(3)起始信号标志
(4)终止信号标志
(5)发送一个字节
(6)接收一个字节
(7)发送应答
(8)接收应答
2.MPU6050.c文件
MPU6050_reg.h
(1)指定地址写
(2)指定地址读
(3)获取转换后的数据
(4)初始化
项目实践
前言
上一期我发布了关于I2C通讯的原理(链接:stm32入门-----I2C通讯-CSDN博客),本期我们就来学习通过软件编程来去实现stm32与MPU6050之间的I2C读写操作(视频:[10-3] 软件I2C读写MPU6050_哔哩哔哩_bilibili)
本期项目工程代码我已上传至百度网盘,可自行下载
链接: https://pan.baidu.com/s/1-HVrt5ROsxLqMWCnUgxXCw?pwd=0721
提取码: 0721
MPU6050
(MPU6050介绍视频:[10-2] MPU6050简介_哔哩哔哩_bilibili)
1.简介
- MPU6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景
- 3轴加速度计(Accelerometer):测量X、Y、Z轴的加速度
- 3轴陀螺仪传感器(Gyroscope):测量X、Y、Z轴的角速度
下面第一张图是MPU6050元器件的芯片,上面讲到MPU6050是一种6轴姿态传感器,是可以去测量芯片自身X、Y、Z轴的加速度、角速度参数,那么我们对于加速度可以想象成第二张图这种形式,也就是中间放一个滑块,如果前后上下左右这六个面都接上一个弹簧电压传感器,然后去通过这个滑块的姿势不同的受力情况来去感应出电压的大小进而求出受力大小,其实这个就是我们高中学过的,然后通过F=ma,测出加速度a的值就行了。同样的对于角速度我们可以想象成下面第三张图的情况,就是中间有一个平衡环,当感应到旋转的时候也会感应出不同的电压进而得到角速度的值。但是实际上MPU6050中间并没有图二和图三这种东西,这只不过是一种用来理解的模型,其内部是通过自身电路去实现这些模型的。
2.相关参数
- 16位ADC采集传感器的模拟信号,量化范围:-32768~32767
- 加速度计满量程选择:±2、±4、±8、±16(g)
- 陀螺仪满量程选择: ±250、±500、±1000、±2000(°/sec)
- 可配置的数字低通滤波器
- 可配置的时钟源
- 可配置的采样分频
- I2C从机地址:1101000(AD0=0)
- 1101001(AD0=1)
3.硬件电路
引脚 | 功能 |
VCC、GND | 电源 |
SCL、SDA | I2C通信引脚 |
XCL、XDA | 主机I2C通信引脚 |
AD0 | 从机地址最低位 |
INT | 中断信号输出 |
4.MPU6050框图
下面从这个框图就可以看出,MPU6050这个芯片去测量加速度和角速度都是去通过ADC模数转换器来去实现的。
C编程实现I2C读写MPU6050步骤
如果相关原理不熟悉的可以去看看上一期的内容:stm32入门-----I2C通讯-CSDN博客
工程主要文件如下,其中MyI2C.c 和MyI2C.h 文件是用来编写I2C接口读取功能的,MPU6050的文件是用来封装前面写好的I2C模块来去读写MPU6050寄存器的操作。
下面我就对这些模块进行一一讲解。
1.MyI2C.c文件
对于这个文件我们需要实现6个功能,实际上就是我们上一期讲到的I2C通讯的六大功能,分别是开始、停止、发送、接受、发送应答、接收应答。
(1)引脚的宏定义
这里我们可以去根据接线图来去宏定义SDA与SCL的引脚,通过宏定义可以去提高代码的修改复用效率,到时候如果想修改为其他引脚,我们只需要去改这个宏定义对应的引脚就行了。下面我是定义到pin6 和pin7的引脚,跟接线图的是不一样的,注意一下。
#include "stm32f10x.h" // Device header
#include "Delay.h"#define SDA_PORT GPIOA
#define SCL_PORT GPIOA
#define SCL_PIN GPIO_Pin_6
#define SDA_PIN GPIO_Pin_7
(2)对SCL和SDA的操作以及初始化
下面我就去通过函数的形式去对GPIO口读写SCL和SDA的操作进行封装,到时候直接去拿出来使用就行了,就不需要用GPIO口的函数读写。
//对SCL和SDA进行操作
void I2C_W_SCL(uint8_t val) { //写入SCLGPIO_WriteBit(SCL_PORT, SCL_PIN, (BitAction)val);Delay_us(10);
}
void I2C_W_SDA(uint8_t val) { //写SDAGPIO_WriteBit(SDA_PORT, SDA_PIN, (BitAction)val);Delay_us(10);
}
uint8_t I2C_R_SDA() { //读取SDAuint8_t value; value = GPIO_ReadInputDataBit(SDA_PORT, SDA_PIN);Delay_us(10);return value;
}//初始化
void I2C_init() {//配置SCL和SDA RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_initstruct; GPIO_initstruct.GPIO_Mode=GPIO_Mode_Out_OD; //开漏输出GPIO_initstruct.GPIO_Pin=SCL_PIN | SDA_PIN;GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_initstruct);GPIO_SetBits(GPIOA, SCL_PIN | SDA_PIN);}
(3)起始信号标志
// 1.开始
void I2C_Start() {//这里需要先SDA再放手SCL,因为避免SDA在SCL高电平的时候变化,这是结束的意思I2C_W_SDA(1);I2C_W_SCL(1);//先拉低SDAI2C_W_SDA(0);I2C_W_SCL(0);
}
(4)终止信号标志
//2.终止
void I2C_Stop() {I2C_W_SDA(0);I2C_W_SCL(1);I2C_W_SDA(1);
}
(5)发送一个字节
// 发送一个字节
void I2C_sendbyte(uint8_t byte) {uint8_t i;for (i = 0;i < 8;i++) {//在SCL低电平期间给SDA放入这个字节的一位,高位先放I2C_W_SDA(byte & (0x80 >> i));//按位与1000 0000//下面这里走一个SCL时钟,从机就会把上面放入的数据读取走I2C_W_SCL(1);I2C_W_SCL(0);}
}
(6)接收一个字节
//接受一个字节
uint8_t I2C_recivebyte() {uint8_t result = 0x00;//存储读取到的结果uint8_t i;I2C_W_SDA(1);//主机释放SDA,这时候从机就会拿到SDA放入数据for (i = 0;i < 8;i++) {I2C_W_SCL(1);//SCL为高电平,下面主机就可以开始读取操作了if (I2C_R_SDA() == 1) {//如果读取到最高位为1result |= (0x80 >> i);//此时获取到的最高位置1}I2C_W_SCL(0);//读取结束}return result;
}
(7)发送应答
//发送应答
void I2C_sendack(uint8_t ackbit) {I2C_W_SDA(ackbit);I2C_W_SCL(1);I2C_W_SCL(0);}
(8)接收应答
//接收应答
uint8_t I2C_reciveack() {uint8_t ackbit;I2C_W_SDA(1);//主机释放SDA,这时候从机就会拿到SDA放入数据I2C_W_SCL(1);//SCL为高电平,下面主机就可以开始读取操作了ackbit = I2C_R_SDA(); //读取到了0就表示接受到了应答OKI2C_W_SCL(0);//读取结束return ackbit;
}
2.MPU6050.c文件
MPU6050_reg.h
这里还有一个MPU6050_reg.h的文件,这个文件是用来定义好MPU6050芯片相关寄存器地址的头文件。如下所示:
#ifndef __MPU6050_REG_h
#define __MPU6050_REG_h//这里存放的是MPU6050的寄存器地址
#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_REG_h
(1)指定地址写
这里我们就对应着上面这个时序来去编程。
//指定地址写
void MPU6050_writereg(uint8_t regaddress, uint8_t data) {I2C_Start();I2C_sendbyte(MPU6050_ADDRESS); I2C_reciveack(); //返回值这边就不做处理了,这里就直接去进行数据的收发I2C_sendbyte(regaddress);I2C_reciveack();I2C_sendbyte(data);I2C_reciveack();I2C_Stop();
}
(2)指定地址读
//指定地址读
uint8_t MPU6050_readreg(uint8_t regaddress) {uint8_t result;//当前指针指向这个地址I2C_Start();I2C_sendbyte(MPU6050_ADDRESS); I2C_reciveack();I2C_sendbyte(regaddress);I2C_reciveack();//转入读的时序I2C_Start();I2C_sendbyte(MPU6050_ADDRESS | 0x01); //MPU6050_ADDRESS默认是写,这里要或上0x01表示读取操作I2C_reciveack();result = I2C_recivebyte(); //获取到接收的数据I2C_sendack(1); //读取完成之后要给从机回复,如果回复0那么就表示从机继续发送数据,如果回复1就表示读取结束,主机拿回SDA控制权I2C_Stop();return result;
}
(3)获取转换后的数据
这里是通过传入数据的地址来去存储数据,这种操作学过C语音的都很常见的了。
//获取MPU6050转换出加速度和角速度的值
void MPU6050_getdata(int16_t* accX, int16_t* accY, int16_t* accZ,int16_t* gyroX, int16_t* gyroY, int16_t* gyroZ)
{uint16_t dataH, dataL;//加速度值数据dataH = MPU6050_readreg(MPU6050_ACCEL_XOUT_H);dataL = MPU6050_readreg(MPU6050_ACCEL_XOUT_L);*accX = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_ACCEL_YOUT_H);dataL = MPU6050_readreg(MPU6050_ACCEL_YOUT_L);*accY = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_ACCEL_ZOUT_H);dataL = MPU6050_readreg(MPU6050_ACCEL_ZOUT_L);*accZ = (dataH << 8) | dataL;//陀螺仪数据dataH = MPU6050_readreg(MPU6050_GYRO_XOUT_H);dataL = MPU6050_readreg(MPU6050_GYRO_XOUT_L);*gyroX = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_GYRO_YOUT_H);dataL = MPU6050_readreg(MPU6050_GYRO_YOUT_L);*gyroY = (dataH << 8) | dataL;dataH = MPU6050_readreg(MPU6050_GYRO_ZOUT_H);dataL = MPU6050_readreg(MPU6050_GYRO_ZOUT_L);*gyroZ = (dataH << 8) | dataL;
}
(4)初始化
//初始化
void MPU6050_init() {I2C_init();MPU6050_writereg(MPU6050_PWR_MGMT_1, 0x01); //电源管理器1MPU6050_writereg(MPU6050_PWR_MGMT_2, 0x00); //电源管理器2MPU6050_writereg(MPU6050_SMPLRT_DIV, 0x09); //分频器MPU6050_writereg(MPU6050_CONFIG, 0x06); //配置寄存器MPU6050_writereg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器MPU6050_writereg(MPU6050_ACCEL_CONFIG, 0x18);//加速度计配置寄存器
}//获取当前的ID号,也就是MPU6050的I2C地址
uint8_t MPU6050_getID() {return MPU6050_readreg(MPU6050_WHO_AM_I);
}
项目实践
上面讲解了相关模块的功能,那么这里我们就可以去写一个主函数来去进行对MPU6050读取操作了。
接线图:
main.c文件:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"
uint8_t ID;
int16_t aX, aY, aZ, gX, gY, gZ;
int main(void)
{ OLED_Init();MPU6050_init();OLED_ShowString(1, 1, "ID:");ID = MPU6050_getID();OLED_ShowHexNum(1, 4, ID, 2);while (1) {MPU6050_getdata(&aX, &aY, &aZ, &gX, &gY, &gZ);OLED_ShowSignedNum(2, 1, aX, 5);OLED_ShowSignedNum(3, 1, aY, 5);OLED_ShowSignedNum(4, 1, aZ, 5);OLED_ShowSignedNum(2, 8, gX, 5);OLED_ShowSignedNum(3, 8, gY, 5);OLED_ShowSignedNum(4, 8, gZ, 5);}
}
实验效果:
以上就是本期的全部内容了,我们下次见!
今日壁纸: