简介
2024-6-18-更新 我们要实现PID算法,需要反馈值。那么,这个值,我们该怎么获取呢? 这里我拿平衡小车举个例子。我们的平衡小车;既然想要做到平衡且不倒的话,就需要把它的三轴角速度和三轴加速度的值放到PID算法中,然后得出值再运用到PWM,最后通过PWM来控制电机调动。(MPU6050可以胜任)
所以我们的思路如下:
- I2C来获取MPU6050的六轴数据Data
- 把获取的Data 传输到 PID算法中获取反馈值 Tes
- 再把Tes用与PWM中
- PWM调控电机模块(我称为Motor)
那么事不宜迟,开始吧!!!
MPU6050模块
何为MPU6050?
1.MPU6050是一个6轴姿态传感器(3轴加速度计和3轴陀螺仪传感器),可以测量芯片自身X、Y、Z轴的加速度、角度参数(角速度),通过数据融合,可以得到姿态角(由上图所示)。并且我们可以看到横向的是x轴,而纵向的为y轴,垂直与芯片的为z轴,这里我们可以类比中学时期的立体几何坐标图
2. 以立创给的图为例,飞机机身对应三个轴的夹角,机头下倾或者上仰,这个轴的夹角叫俯仰(pitch)。飞机机身左翻滚或者右翻滚,这个轴的夹角叫做滚转(roll),飞机机身向左转向或者向右转向,这个轴的夹角叫做偏航(raw)。(2024-6-18 记得插入解释视频)
续—-欧拉角是为表示姿态的一种参数。为了保持飞机的姿态平稳(无人机也是)我们必须得到一个精确且问题的角;(此处—-江协科技有提过)一种传感器不能获得精确且稳定的欧拉角,要获得精确稳定的欧拉角,需要多个传感器进行数据融合。常见的数据融合算法有互补滤波、卡尔曼滤波等。
提示: 加速度只有静态稳定性,不具有动态稳定新性;而陀螺仪传感器只有动态稳定性,而不具有静态稳定性。 具体请看:”https://blog.csdn.net/qq_44852376/article/details/130470815
此CSDN博客的基本介绍,我就不在此处一一介绍了
MPU6050模块的基本参数
1. 16位ADC采集传感器的模拟信号,量化范围:-32768~32767。
2. 加速度计满量程范围选择:±2、±4、±8、±16(g) g表示重力加速度1g=9.8m/s²
3. 陀螺仪满量程选择:±250、±500、±1000、±2000(°/sec) 每秒钟旋转了多少度(如果测量的物体运动非常剧烈,可以把满量程选择大一些,如果运动比较平缓,可以选择更小的量程,这样分辨率会更大。)
4. 可配置的数字低通滤波器(在这个芯片可以配置寄存器来选择对输出的数据进行低通滤波)
5. 可配置的时钟源和可配置的采样分频(为AD转换和芯片内部其他电路提供时钟,控制分频系数,可以控制AD转化的快慢。)
6. I2C从机地址:当AD0=0时,地址为1101000,当AD0=1时,地址为1101001,AD0是板子引出来的引脚,可以调节I2C从机地址的最低位。(16位表示时,有两种方式,①是把1101000转成16进制0x68,但是因为还有一位读写位,一般使用(0x68<<1)|读写位。读1写0。②把0x68左移一位后的数据当作从机地址,也就是0xD0,再或上读写位。写就发送0xD0,读就发送0xD1。 这里读写位很重要。后面写代码具体用到
MPU6050读写
2024-6-19 更新
软件读写MPU6050
Tips: 这里我就不写头文件了,头文件同样写在HardWare中
1. MyI2C.c\HardWare
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
| #include "stm32f10x.h" #include "Delay.h"
void MyI2C_W_SCL(uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_10, (BitAction)BitValue); Delay_us(10); }
void MyI2C_W_SDA(uint8_t BitValue) { GPIO_WriteBit(GPIOB,GPIO_Pin_11, (BitAction)BitValue); Delay_us(10); }
uint8_t MyI2C_R_SDA(void) { uint8_t BitValue; BitValue = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11); Delay_us(10); return BitValue; }
void MyI2C_Init(void) { 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(void) { MyI2C_W_SDA(1); MyI2C_W_SCL(1); MyI2C_W_SDA(0); MyI2C_W_SCL(0);
}
void MyI2C_Stop(void) { MyI2C_W_SDA(0); MyI2C_W_SCL(1); MyI2C_W_SDA(1); }
void MyI2C_SendByte(uint8_t Byte) { uint8_t i; for (i = 0; i < 8; i++) { MyI2C_W_SDA(Byte & (0x80 >> i)); MyI2C_W_SCL(1); MyI2C_W_SCL(0); }
}
uint8_t MyI2C_ReceiveByte(void) { uint8_t i,Byte = 0x00; 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(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(void) { uint8_t AckBit; MyI2C_W_SDA(1); MyI2C_W_SCL(1); AckBit = MyI2C_R_SDA(); MyI2C_W_SCL(0); return AckBit; }
|
2.MPU6050_Reg.h\Hardware
作用:把寄存器地址写在同一个头文件中,方便调用并且增强代码可读性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #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
|
3. MPU6050.c\Hardware
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| #include "stm32f10x.h" #include "MyI2C.h"
#define MPU6050_ADDRESS 0xD0
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveACK(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveACK(); MyI2C_SendByte(Data); MyI2C_ReceiveACK(); MyI2C_Stop(); }
uint8_t MPU6050_ReadReg(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(void) { MyI2C_Init(); MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); MPU6050_WriteReg(MPU6050_CONFIG, 0x06); MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); }
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;
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; }
|
硬件与MPU6050进行通讯
Tips: STM32自带有2个I2C硬件资源,分别是I2C1,I2C2,并且它们的外设时钟挂在在APB1时钟总线上;在这里,那些软件模拟的I2C的应答呀,SCL,SDA写读都被标准库封装成函数,我们直接调用即可。无需再写一个底层I2C。
MPU6050\Hardware
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
| #include "stm32f10x.h" #include "MPU6050_Reg.h"
#define MPU6050_ADDRESS 0xD0
void MPU6050_WaitEven(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) { uint32_t Time = 10000; while(I2C_CheckEvent(I2Cx,I2C_EVENT) != SUCCESS) { Time--; if (Time == 0) { return; } } }
void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { I2C_GenerateSTART(I2C2,ENABLE); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); I2C_SendData(I2C2,RegAddress); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING); I2C_SendData(I2C2,Data); MPU6050_WaitEven(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_WaitEven(I2C2,I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); I2C_SendData(I2C2,RegAddress); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); I2C_GenerateSTART(I2C2,ENABLE); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_MODE_SELECT);
I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); I2C_AcknowledgeConfig(I2C2,DISABLE); I2C_GenerateSTOP(I2C2,ENABLE); MPU6050_WaitEven(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED); Data = I2C_ReceiveData(I2C2); I2C_AcknowledgeConfig(I2C2,ENABLE); return Data; }
uint8_t MPU6050_GetID(void) { return MPU6050_ReadReg(MPU6050_WHO_AM_I); }
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_Pin = GPIO_Pin_10 | GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); I2C_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 50000; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C2,&I2C_InitStructure); I2C_Cmd(I2C2,ENABLE); MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); MPU6050_WriteReg(MPU6050_CONFIG, 0x06); MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); }
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;
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; }
|
读取MPU6050实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include "stm32f10x.h" #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,9,GX,5); OLED_ShowSignedNum(3,9,GY,5); OLED_ShowSignedNum(4,9,GZ,5); } }
|
Tips: 这里的AX,AY,AZ分别代表着x轴上的加速度,y轴上的加速度,z轴上的加速度。而GX,GY,GZ代表的x轴的角速度,y轴的角速度,z轴的角速度。