
久违地来一期硬件模块好像也没很久。这不是我估摸着又到了做毕设的季节了而这个MPU6050又是毕设常驻嘉宾用来检测XYZ的加速度和角速度的一共六轴甚至还可以采集温度。碰巧我又还没玩过所以今天来写一期速通一下。不敢写《这可能是全网最详细的关于MPU6050的文章》因为它有个功能我都不敢碰太麻烦了所以退而求其次写《五分钟无伤速通MPU6050》我把它五十页的英文手册看完才发现这玩意是产品规格书我们直接买现成模块的基本用不上然后才找到寄存器手册浪费了我好多时间。下面我就简单概括一下产品规格书里有用的信息。MPU6050和MPU6000共用同一个手册所以理论上用起来是一样的不过我们这里以6050为准6000只是顺带提一下。MPU6000可以使用SPI和I2CMPU6050只能使用I2C所以MPU6000的速率可以很高按这样推MPU6000的性能应该也会更高。不过我们I2C也够用就是了。供电2.375V~3.46V不过我们不用管因为使用现成的模块。有1024Byte的FIFO所以采集到的数据可以先帮我们存起来。I2C支持400KHz的速率SPI支持1MHz如果只读传感器的寄存器和中断寄存器的话可以干到20MHz不过SPI只有MPU6000才能用而且需要先用I2C配置一下寄存器才能改用SPI。I2C的地址为b110100x其中这个x是可以自己设置的给高电平就是1给低电平就是0一般我们接地那么I2C的七位地址就是b110 1000也就是0x68。如果是SPI的话那就是发送寄存器地址的时候在最高位标上读写位1读0写。然后产品规格指南这个手册就没用了。有些内容我们可以在看寄存器的时候一起说了。寄存器蛮多的从0x0D到0x75一百个左右。但是其实我们用到的不多。MPU6050有个功能那就是它能另外单拉一条I2C的总线然后把别的设备挂载到这条单拉的I2C上我们通过读取MPU6050的寄存器来获取到别的设备的数据。买的模块上有两个引脚XDA和XCL就是另外单拉的I2C总线。而这个功能我这边是用不上而且我觉得你们做毕业设计的大概率也用不上所以就不细说这部分内容了。这部分内容又占去了很多寄存器所以实际上我们用到的寄存器不多。它寄存器地址和功能不是在一块的就是说同一部分功能的寄存器可能地址相隔了不少所以接下来我不按照寄存器地址的顺序去讲解了。下面的代码我以ESP-IDF为例用ESP32的小伙伴可以直接抄用其他芯片的小伙伴也可以借鉴参考或者直接跳过反正只是调用的API不一样其他内容都是通的。最后我会把涉及到的手册以及我的工程文件放到网盘同名公众号私信回复“模块”然后找一找MPU6050文件夹就可以了。// 110 1000 #define MPU6050_ADD 0x68 #define MPU6050_SCL 10 #define MPU6050_SDA 11 i2c_master_bus_handle_t i2c_bus_handle NULL; i2c_master_dev_handle_t mpu6050_handle NULL; // 初始化配置 void Z_MPU6050_Init(void){ i2c_master_bus_config_t i2c_bus_initer { .clk_source I2C_CLK_SRC_DEFAULT, .i2c_port -1, .scl_io_num MPU6050_SCL, .sda_io_num MPU6050_SDA, .glitch_ignore_cnt 7, .flags.enable_internal_pullup true, }; ESP_ERROR_CHECK(i2c_new_master_bus(i2c_bus_initer, i2c_bus_handle)); i2c_device_config_t i2c_device_initer { .dev_addr_length I2C_ADDR_BIT_LEN_7, .device_address MPU6050_ADD, .scl_speed_hz 100000, }; ESP_ERROR_CHECK(i2c_master_bus_add_device(i2c_bus_handle, i2c_device_initer, mpu6050_handle)); }首先我们就是读取一下ID确保我们I2C初始化正不正确以及MPU6050是否正常连接上了。寄存器是0x75。Bit6~bit1正常应该是110 100bit7和bit0空着默认是0所以我们读出来应该是b0110 1000也就是0x68跟七位从机地址是一样的。// 读取ID,正常是0x68 uint8_t Z_MPU6050_ReadID(void){ uint8_t temp[2] {0}; temp[0] 0x75; ESP_ERROR_CHECK(i2c_master_transmit_receive(mpu6050_handle, temp[0], 1, temp[1], 1, 100)); printf(get ID is %#X\r\n, temp[1]); return temp[1]; }可以在初始化之后读取一下ID看看是否正常。下一步我们需要通过寄存器0x6B去唤醒芯片。因为上电之后MPU6050默认进入睡眠模式。我们需要给bit6置0同理如果我们需要降低功耗且暂时用不上MPU6050也可以给bit6置1。Bit7置1后就相当于软件复位复位后它会自动置回0但是复位后它又会默认进入睡眠模式所以如果需要复位记得等它复位后再给bit6置0。Bit5置1进入循环模式这个需要搭配另一个寄存器使用另一个寄存器可以设定MPU6050以固定频率去获取数据获取完就睡到点了就起来获取数据获取完接着睡以此达到降低功耗的作用这就是循环模式循环模式的前提是bit7不能为1。Bit3置1即禁用温度传感器如果我们只需要获取6轴数据那么我们置1禁用也可以。Bit2~bit0三位选择时钟源。一般就给0用默认的内部8MHz。所以不出意外的话我们第一步就是给0x6B写入0x00来解除睡眠模式。在睡眠模式里也可以读取ID就是了。所以一开始我在这里栽跟头了因为我一般是按照地址顺序去看寄存器的配了半天发现好像没用但是ID能读出来我还以为是我代码哪里有问题查了半天才发现要先解除睡眠。下个寄存器看0x6C如果要进入循环模式也就是低功耗那我们就需要配置这个寄存器以及0x6B的bit5。Bit7~bit6选择频率频率越低约节能看具体需求吧。其余bit是分别设置我们需要采样的数据置1为待机也就是不采样。我们通过名字就可以知道每个bit对应的数据是哪个XYZ就是XYZ三轴末尾是A的表示accelerometer加速度末尾是g的表示gyroscope角速度。0x0D~0x10是讲自检功能的相当麻烦而且没啥必要主要还是相当麻烦所以就跳过不讲了。0x19用来配置采样率。采样率等于陀螺仪输出速率除上这个寄存器的值加1。而陀螺仪的输出速率又取决于另一个寄存器0x1A的配置。Bit5~bit3用来设置SYNC的也就是通过外部的时钟信号来单独控制数据采样大概率用不上不细说直接给0即可。我们要设置的是bit2~bit0这个来设置DLPF低通滤波这个会影响陀螺仪输出速率。通过配置这个寄存器和上一个来调整采样率。Bit2~bit0为0或者为77是保留位没用的时候陀螺仪输出速率为8kHz其他为1KHz。我会直接拉满给6你输出速率可以慢一点但是精度高就行了。具体看你们的项目需求吧。// 设置低通滤波参数 void Z_MPU6050_SetLDPF(void){ uint8_t temp[2] {0}; temp[0] 0x1A; temp[1] 0x06; ESP_ERROR_CHECK(i2c_master_transmit(mpu6050_handle, temp, 2, 100)); } // 设置采样率 void Z_MPU6050_SetSampleRate(uint16_t sample_rate){ uint8_t temp[2] {0}; temp[0] 0x19; temp[1] 1000 / sample_rate - 1; ESP_ERROR_CHECK(i2c_master_transmit(mpu6050_handle, temp, 2, 100)); }0x1B寄存器的bit7~bit5来设置自检我们不用。用bit4和bit3来配置角速度输出的范围。最高是±2000°/s范围越大精度越低我折中选择±500主要是我就是测试一下对量程和精度都没要求你们按照自己的需求配置。0x1C也是一样的扣掉自检部分bit4和bit3来配置加速度的范围。这俩我们在封装的时候可以放一起。// 设置满量程范围 void Z_MPU6050_SetFSR(void){ uint8_t temp[2] {0}; temp[0] 0x1B; temp[1] 0x08; // 设置为500°/s ESP_ERROR_CHECK(i2c_master_transmit(mpu6050_handle, temp, 2, 100)); temp[0] 0x1C; temp[1] 0x08; // 设置为±4g ESP_ERROR_CHECK(i2c_master_transmit(mpu6050_handle, temp, 2, 100)); }0x1F设置一个无符号8位的数据这是运动检测阈值配合中断使用。当加速度的数值超过这个阈值了就触发中断。不用中断的可以跳过那我可就跳过了。0x23用来选择开启FIFO你可以理解为选择一些数据每次采集完就自动搬到一个缓冲区里我们读取数据就可以从缓冲区里读取。根据名字我们就可以看得出来上表里每个bit代表什么。Bit7是温度数据bit6是X轴角速度bit5是Y轴角速度bit4是Z轴角速度bit3是XYZ三轴的加速度。Bit2~bit0是从机数据也就是如果你要单拉一条I2C总线并挂载了从机就可以把从从机读取到的数据也放进MPU6050的FIFO缓冲区里。当我们从缓冲区里读取数据的时候顺序就是bit7~bit0的顺序。比如说我设置的是0x78那么采集的顺序就是XG、YG、ZG、XA、YA、ZA。如果我设置的是0x50那么顺序就是XG、ZG。记得每次读取FIFO的时候都得要读完整的一帧每个数据两个字节如果是设置0x78那么每次就要读12个字节否则会导致你数据错乱这个应该不难理解吧。从寄存器0x24~0x360x49~0x60都是设置以MPU6050为主机单拉I2C总线的我用不上而且很麻烦所以我就直接跳过了。0x37来设置中断相关的配置。Bit7为0的时候MPU6050的中断信号为高电平有效为1就是低电平有效。Bit6为0的时候MPU6050的中断信号为推挽模式为1就是开漏模式。Bit5为0的时候中断信号为50us的脉冲为1则是MPU6050的中断引脚保持高电平直到中断消除。Bit4为0的时候中断只能通过读取一个中断状态寄存器来消除为1则是任何读取操作都会消除。剩下位是关于SYNC和MPU6050主机的就不说了说了也听不懂因为我们跳过了这两部分。0x3A就是中断状态寄存器也就是上一个寄存器的bit4提到的。每个bit代表一种中断原因因它而中断的置1在我们读取过后就会自动置0。Bit6是运动检测中断bit5是FIFO缓冲区溢出中断bit4是跟MPU6050主机中断相关的我们不管bit0是数据准备就绪时的中断。与中断相关的寄存器还有0x38每位的含义和上一个一样不过这边是哪一位置1则表示开启哪种中断。0x69寄存器bit5和bit4用来设置加速度传感器上电延迟时间基础是4msbit5和bit4组成的数每多1则多加1ms我也不懂有啥用我没去配置。配置相关的就说这么多接下来就是我们最关心的数据读取了。从0x3B开始连续六个字节是XYZ三轴的加速度数据。每个数据都有两个字节组成高位在前低位在后组成的是有符号16位数据也就说这是分正负的其实这一点在我们设置范围的时候就知道了。比如说我设置的加速度范围是±4g那么我采集数据就是下面这样。// 读取寄存器获取XYZ加速度数据 void Z_MPU6050_ReadAccelXYZ(double* X, double* Y, double* Z){ uint8_t temp[7] {0}; temp[0] 0x3B; ESP_ERROR_CHECK(i2c_master_transmit_receive(mpu6050_handle, temp[0], 1, temp[1], 6, 100)); *X ((double)((int16_t)(temp[1] 8) | temp[2])) / 8192.0; *Y ((double)((int16_t)(temp[3] 8) | temp[4])) / 8192.0; *Z ((double)((int16_t)(temp[5] 8) | temp[6])) / 8192.0; printf(X is %fg, Y is %fg, Z is %fg\r\n, *X, *Y, *Z); }一共16位数据共同瓜分8g正4g负4g一共8g16位数据最多可以表示65536个数据分给每g就是65536/8 8192所以我把拼接来的数据再除8192之后单位就是g了。其他范围也是一样的计算方式或者我们直接看下表。平放的时候X轴和Y轴都是在0g附近而Z轴因为自身存在的重力加速度所以在1g附近这样可以稍微自测一下是否正常读取数据。0x43~0x48也是类似获取的是XYZ三轴的角速度。也是根据自己设置的范围去除上一个数。// 读取寄存器获取XYZ角速度数据 void Z_MPU6050_ReadGyroXYZ(double* X, double* Y, double* Z){ uint8_t temp[7] {0}; temp[0] 0x43; ESP_ERROR_CHECK(i2c_master_transmit_receive(mpu6050_handle, temp[0], 1, temp[1], 6, 100)); *X ((double)((int16_t)(temp[1] 8) | temp[2])) / 65.5; *Y ((double)((int16_t)(temp[3] 8) | temp[4])) / 65.5; *Z ((double)((int16_t)(temp[5] 8) | temp[6])) / 65.5; printf(X is %f°, Y is %f°, Z is %f°\r\n, *X, *Y, *Z); }0x41和0x42读取的是温度数据。公式是将这个寄存器里16位有符号数据除340然后再加36.53得到的单位是摄氏度。// 读取寄存器获取温度数据 void Z_MPU6050_ReadTemperature(double* temperature){ uint8_t temp[3] {0}; temp[0] 0x41; ESP_ERROR_CHECK(i2c_master_transmit_receive(mpu6050_handle, temp[0], 1, temp[1], 2, 100)); *temperature ((double)((int16_t)(temp[1] 8) | temp[2])) / 340.0 36.53; printf(temperature is %f℃\r\n, *temperature); }接下来刨去MPU6050主机模式下读取从机的数据的寄存器之外剩下的就是FIFO缓冲区寄存器了。在读取之前我们可以先获取FIFO缓冲区内数据的数量。读取0x72~0x73是无符号16位数据。// 获取FIFO缓冲区数据数量 最多为1024 uint16_t Z_MPU6050_ReadFIFONum(void){ uint8_t temp[3] {0}; temp[0] 0x72; ESP_ERROR_CHECK(i2c_master_transmit_receive(mpu6050_handle, temp[0], 1, temp[1], 2, 100)); uint16_t count ((uint16_t)temp[1] 8) | temp[2]; printf(FIFO buffer num is %d\r\n, count); return count; }读取FIFO的数据很简单就是读取0x74。我们要注意的就是每次读取数据要一次性读取一帧完整的数据另外就是解析的时候要注意FIFO内数据类型的顺序了。如果我们读取数据的时候发现刚刚读还好好的但是突然就不行了那我们可以使用下面的寄存器。信号通道复位bit2置1就会对角速度传感器的信号通道复位bit1是加速度bit0是温度。其实基本用不到但还是提一嘴。MPU6050就将这么多了漏掉了SYNC和MPU6050主机模式但是这都不是重点正常使用的话是用不上这些的不讲的原因是我也不会而且也懒得去研究。