STM32 学习 —— 个人学习笔记10-1(I2C 通信协议及 MPU6050 简介 & 软件 I2C 读写 MPU6050)

声明

文中内容为观看 BiliBili 视频【STM32入门教程-2023版 细致讲解 中文字幕】后学习并扩展总结。

本文章为个人学习使用,版面观感若有不适请谅解,文中知识仅代表个人观点,若出现错误,欢迎各位批评指正。

一、I2C 通信协议

1.1 简介

I2C(Inter IC Bus,集成电路间总线) 是由荷兰 Philips 公司(现 NXP 半导体公司)于 20 世纪 80 年代初开发的通用串行数据总线,旨在解决集成电路间短距离、低速率数据传输问题,简化板内器件连接,降低系统成本与功耗,目前已纳入国际标准(IEC 62386),广泛应用于嵌入式系统、消费电子等领域。其核心特性由下表所示:

核心特性 具体说明
通信线组成 仅2根双向线:SCL(串行时钟线,主设备控制,同步通信节奏)、SDA(串行数据线,传输数据与控制信号)
通信时序 同步通信(遵循SCL时钟信号),半双工传输(同一时刻仅单向通信,支持读写切换)
数据保障机制 带数据应答(ACK/NACK):每传1字节,接收方在SCL第9时钟周期反馈,确保传输可靠
设备挂载能力 支持多设备挂载,可实现一主多从(单主设备控时序)、多主多从(总线仲裁防冲突)拓扑

SCL 的时钟频率 决定 I2C 通信速率 ,常见标准模式(100kbps)、快速模式(400kbps)等,总线空闲时,SCL 与 SDA 均由上拉电阻维持高电平。半双工设计虽限制了通信效率,但简化了控制逻辑,降低了硬件设计难度。

数据应答机制 是通信可靠性的关键:接收方成功接收数据则反馈低电平 ACK,主设备继续传输;接收异常则反馈高电平 NACK,主设备停止或重发,有效避免误码、丢包。

多设备挂载 特性大幅简化了系统布线,从设备通过唯一 7 位 / 10 位地址被主设备选中,未选中设备处于空闲状态;多主多从结构中,总线仲裁机制可解决设备竞争总线的冲突问题。

此外,I2C 还具备时钟拉伸(从设备调节时钟周期)、地址广播(同步控制所有从设备)等辅助特性,共同奠定其灵活性高、兼容性好的优势,适配各类短距离低速率嵌入式通信场景。

1.2 硬件电路
  • 总线拓扑结构
       I2C 总线采用多设备共享的双线串行通信架构,所有挂载于总线上的设备(包括主控 CPU 与各被控 IC)均将其串行时钟引脚(SCL)与串行数据引脚(SDA)分别并联至同一物理线路,形成共享的时钟总线与数据总线。这种拓扑结构允许多个设备在同一组物理线路上进行半双工通信,通过设备地址区分通信对象,实现系统级的器件互联与数据交互。
  • 电气接口与输出模式
      为支持多设备共享总线并避免信号冲突,所有 I2C 设备的 SCL 与 SDA 引脚必须配置为开漏输出(open-drain output)模式 。开漏输出的核心特性在于其输出级仅能通过下拉 MOS 管将总线拉至低电平 ,而无法主动输出高电平 ,这一设计是实现总线 "线与" 逻辑与多主设备仲裁的基础。
      从器件内部电路可见,SCL 与 SDA 通道均由反相驱动的下拉 NMOS 管构成输出级:当输出使能信号(如 SCLKN1_OUT、DATAN1_OUT)为高电平时,NMOS 管导通,将对应总线拉至地(低电平);当使能信号为低电平时,NMOS 管截止,总线电平由外部上拉电阻决定,从而实现高阻态与高电平的切换。同时,各引脚内置输入缓冲器(SCLK IN、DATA IN),用于实时采样总线电平,保障设备对总线状态的可靠感知。
  • 上拉电阻配置与电气特性
      为确保总线在空闲状态下维持稳定的高电平,并为信号上升沿提供驱动能力,SCL 与 SDA 总线需分别外接上拉电阻至系统正电源( V D D V_{DD} VDD)。工程实践中,上拉电阻阻值通常选取 4.7 kΩ 左右,该取值需在信号完整性与功耗之间取得平衡:
      (1)阻值过小会导致总线下拉时的灌电流增大,增加器件功耗与热应力;
      (2)阻值过大则会延长信号上升沿时间,限制总线最高通信速率(通常 I²C 标准模式速率为 100 kbps,快速模式为 400 kbps)。
      上拉电阻与总线电容共同构成 RC 充放电回路,决定了信号的上升沿斜率与噪声裕量,是保障 I2C 通信可靠性的关键硬件设计环节。
  • 工作原理与通信机制
      在通信过程中,主控设备(如 CPU)通过控制 SCL 与 SDA 引脚的开漏输出,交替 拉低 / 释放总线,生成符合 I2C 协议的起始条件、地址帧、数据帧与停止条件。由于开漏输出的 "线与" 特性 ,多个设备可同时拉低总线,从而实现冲突检测与多主设备仲裁;当总线被释放时,上拉电阻将其电平拉回 V D D V_{DD} VDD,为下一次通信做好准备。
      这种硬件架构与电气设计共同构成了 I2C 总线的核心基础,使其成为嵌入式系统中广泛应用的低速、短距离串行通信标准。
1.3 I2C 时序基本单元

I2C 总线通信的完整时序逻辑由一系列基础操作单元构成,其核心基本时序单元主要包含六类:起始条件、终止条件、字节发送、字节接收、发送应答位及接收应答位。上述基本单元按规范组合,即可完成 I2C 总线上可靠的数据交互与设备间的同步通信。

  • (1)起始条件
      SCL 高电平期间,SDA 从高电平 切换到低电平
  • (2)终止条件
      SCL 高电平期间,SDA 从低电平 切换到高电平
  • (3)字节发送
      SCL 低电平期间,主机 将数据位依次放到 SDA 线上(高位先行),然后释放 SCL,从机将在 SCL 高电平期间读取数据位,所以 SCL 高电平期间 SDA 不允许有数据变化,依次循环上述过程 8 次,即可发送一个字节。
  • (4)字节接收
      SCL 低电平期间,从机 将数据位依次放到 SDA 线上(高位先行),然后释放 SCL,主机将在 SCL 高电平期间读取数据位,所以 SCL 高电平期间 SDA 不允许有数据变化,依次循环上述过程 8 次,即可接收一个字节(主机在接收之前,需要释放 SDA)。
  • (5)发送应答位
      主机在接收完一个字节之后,在下一个时钟发送一位数据,数据 0 表示应答,数据 1 表示非应答。
  • (6)接收应答位
      主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放 SDA)。
1.4 I2C 时序样例
  • 指定地址写
      对于指定设备(Slave Address),在指定地址(Reg Address)下,写入指定数据(Data)。
  • 当前地址读
      对于指定设备(Slave Address),在当前地址指针指示的地址下,读取从机数据(Data)。
  • 指定地址读
      对于指定设备(Slave Address),在指定地址(Reg Address)下,读取从机数据(Data)。

二、姿态传感器

2.1 MPU6050 简介

MPU6050 是由 TDK InvenSense 公司研发的一款集成式六轴微机电系统(MEMS)运动跟踪传感器 ,属于惯性测量单元(IMU)的典型代表,其核心功能是同步采集芯片自身 X、Y、Z 三个正交轴的线加速度与角速度参数 ,并通过内置数据处理单元实现多源传感数据的融合,进而解算出目标物体的姿态角 (俯仰角、横滚角),为姿态检测与运动控制提供精准的传感支撑,广泛应用于各类需要实时感知自身姿态的智能设备与控制系统中,如平衡车、小型飞行器、机器人及可穿戴设备等。

MPU6050 的核心优势在于将 3 轴加速度计(Accelerometer)3 轴陀螺仪传感器(Gyroscope) 集成于同一硅基芯片之上,共享公共 MEMS 衬底,有效消除了板级层面的轴间对准误差,降低了传感器漂移与稳定时间效应,同时通过紧凑的4×4×0.9mm QFN 封装,兼顾了小型化、低功耗与低成本需求,适配空间受限、功耗敏感的嵌入式应用场景,其工作电压范围为2.375V 至 3.46V,睡眠模式电流低至5μA,具备优良的工程实用性。

3 轴加速度计(Accelerometer)是 MPU6050 的核心传感单元之一,基于电容式 MEMS 结构设计,其内部采用梳齿状可动质量块与固定电极构成差分电容对,当芯片沿某一轴向产生线加速度时,惯性作用会使可动质量块发生微小位移,导致差分电容容值产生反向变化,该变化经片内电荷放大器、滤波器及16位模数转换器(ADC)调理后,输出数字化的加速度数据。该加速度计支持 ±2g、±4g、±8g、±16g 四种可编程满量程范围,在 ±2g 量程下灵敏度可达 16384 LSB/g,分辨率约为 0.061 mg/LSB,可精准测量芯片在三个正交轴上的线加速度,静止状态下可感知重力加速度在各轴的投影,为姿态角解算提供静态基准。

3 轴陀螺仪传感器(Gyroscope) 是 MPU6050 的另一核心传感单元,同样基于 MEMS 工艺,利用科里奥利效应实现角速度的间接测量,其内部采用音叉式振动环结构,通过静电驱动使质量块在特定谐振频率下持续振动,当芯片绕任一轴发生旋转时,振动质量块会受到与旋转角速度成正比的科里奥利力,引发垂直于振动方向的二次振动,该振动通过差分电容结构检测并经片内电路处理后,输出数字化的角速度数据。该陀螺仪支持 ±250°/s、±500°/s、±1000°/s、±2000°/s 四种可编程满量程范围,可精准追踪快速与慢速旋转运动,在 ±250°/s 量程下灵敏度可达 131 LSB/(°/s),其输出不受重力影响,具备高带宽与低延迟的动态响应特性,为姿态角解算提供动态旋转信息。

MPU6050 内置数字运动处理器(DMP) ,可离线运行复杂的六轴数据融合算法,将加速度计与陀螺仪的原始数据进行融合处理,有效弥补单一传感器的固有缺陷------加速度计长期稳定性优良但动态响应较差,陀螺仪动态响应灵敏但存在零点漂移,通过互补滤波、卡尔曼滤波等融合算法,可输出高精度的姿态角数据,无需外部微控制器承担大量数据处理任务,显著降低了系统集成复杂度与计算开销。此外,该传感器通过 I2C 总线实现数据传输与配置,支持最高 400kHz 的通信速率,可通过辅助 I2C 总线连接外部磁力计等传感器,扩展为九轴运动跟踪系统,进一步提升姿态检测的完整性与精度,可解算完整的三维欧拉角(俯仰角、横滚角、偏航角)。

在实际应用场景中,MPU6050 凭借其高集成度、高性价比与可靠的性能,已广泛应用于多个领域:在机器人与平衡车领域,用于实时检测设备姿态,为闭环控制提供反馈,保障设备的平衡与稳定运行;在小型飞行器(如无人机)领域,用于采集飞行姿态数据,辅助实现姿态控制与飞行稳定;在可穿戴设备与康复监测领域,用于捕捉人体运动姿态,为运动分析与康复评估提供数据支撑;在工业领域,可用于设备振动监测与姿态校准;同时也广泛应用于航空电子教育、运动感测游戏、电子稳像等场景,成为低功耗、低成本姿态检测场景中的首选传感器之一。需要说明的是,MPU6050 目前已处于停产(EOL)状态,其推荐替代型号为 ICM-42670-P,但由于其成熟的技术、低廉的成本与广泛的应用生态,仍在各类科研、教学及民用项目中被广泛采用。

2.2 MPU6050 参数

MPU6050 配备 16 位模数转换器(ADC)用于采集传感器模拟信号,量化范围 为 -32768~32767,可实现高精度信号转换;加速度计支持 ±2g、±4g、±8g、±16g 四种满量程可配置选择,陀螺仪支持 ±250°/sec、±500°/sec、±1000°/sec、±2000°/sec 四种满量程可配置选择,适配不同精度与动态范围的应用需求;此外,该传感器集成可配置数字低通滤波器可配置时钟源可配置采样分频模块 ,可根据实际应用场景灵活调整信号滤波效果、时钟基准与采样频率;其 I2C 从机地址可通过 AD0 引脚配置,当 AD0=0 时地址为 1101000,当 AD0=1 时地址为 1101001,便于多传感器级联使用。

2.2 MPU6050 硬件电路

本电路采用 5V 输入电源,经 3.3V 低压差线性稳压器(LDO)稳压后为 MPU6050 及周边电路供电,输入侧配置 4.7μF 滤波电容,输出侧并联 10μF 与 0.1μF 电容以抑制电源纹波,并通过 1kΩ 限流电阻驱动 LED 实现电源状态指示。

MPU6050 的 VDD、VLOGIC 引脚直接接入 3.3V 电源,GND 引脚接地,REGOUT 与 CPOUT 引脚分别通过 0.1μF、2200pF 电容接地以完成内部稳压与电荷泵滤波。核心通信接口方面,SDA/SCL 引脚经 4.7kΩ 上拉电阻至 3.3V 后引出,构成 I2C 主通信总线;XDA/XCL 引脚作为辅助 I2C 接口同步引出,支持外设扩展。AD0 引脚通过 4.7kΩ 下拉电阻置 0,固定 I2C 从机地址最低位;INT 引脚经滤波后引出,用于向主控设备输出中断信号。电路通过去耦电容(0.1μF)与上拉电阻的配置,保障了电源稳定性与通信可靠性,可满足姿态检测等场景的高精度传感需求。

引脚 功能
VCC、GND 电源
SCL、SDA I2C 通信引脚
XCL、XDA 主机 I2C 通信引脚
AD0 从机地址最低位
INT 中断信号输出

三、软件 I2C 读写 MPU6050

3.1 软件 I2C 读写 MPU6050的实现
  • 首先,按下图接线方式,搭建面包板电路连接 OLED 显示屏,并将 MPU6050 的 SCL 和 SDA 分别与 PA4 和 PA5 连接,然后将 DAP-Link / ST-Link 连接到 STM32 最小系统板上,为使 OLED 显示屏的 VCC 和 GND 正确连接正负极,请先连接对应正负极跳线(或直接使用 GPIO 口进行供电)。

  • 直接复制先前演示的已有文件目录,重命名并双击后缀名为 .uvprojx 的文件打开工程文件,并对 main.c 进行修改,工程中所使用的全部头文件其详细内容已放于文末。

    #include "stm32f10x.h" // Device header
    #include "Soft_I2C.h"
    #include "OLED.h"
    #include "MPU6050.h"

    int16_t AX, AY, AZ, GX, GY, GZ;

    int main(void)
    {
    OLED_Init();
    MPU_6050_Init();

    复制代码
      // 点名, 判断设备是否存在(有应答显示 000, 无应答显示 001)

    // Soft_I2C_Init();
    //
    // Soft_I2C_Start();
    // Soft_I2C_SendByte(0xD0); // 0xD0: 1101 000 0, 0xD2: 1101 001 0
    // uint8_t Ack = Soft_I2C_ReceiveAck();
    // Soft_I2C_Stop();
    //
    // OLED_ShowNum(1, 1, Ack, 3);

    复制代码
      // MPU6050 读写操作

    // MPU_6050_Init();
    // // 读取芯片的 ID 号, 要读的地址查看手册:WHO_AM_I 0x75
    // uint8_t ID = MPU6050_ReadReg(0x75);
    // OLED_ShowHexNum(1, 1, ID, 2);
    //
    // // 写寄存器之前, 解除睡眠模式: 0x6B
    // MPU6050_WriteReg(0x6B, 0x00);
    // // 采样率分频寄存器: 0x19
    // MPU6050_WriteReg(0x19, 0x66);
    //
    // // 对写入分频寄存器的内容进行读操作, 验证是否写入成功
    // uint8_t Demo = MPU6050_ReadReg(0x19);
    // OLED_ShowHexNum(2, 1, Demo, 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);
      }

    }

  • MPU6050 的 AD0 引脚为从机地址配置引脚,当该引脚接高电平时,设备从机地址由默认的 0xD0 切换为 0xD2:对 0xD2 地址寻址时,设备正常应答(显示 000);若仍对原地址 0xD0 寻址,设备无应答(显示 001)。

  • 程序调用MPU6050_GetData()函数读取六轴数据(三轴加速度 AX/AY/AZ、三轴角速度 GX/GY/GZ),并通过OLED_ShowSignedNum()函数将加速度数据依次显示在 OLED 屏第 2-4 行第 1 列位置,角速度数据显示在第 2-4 行第 8 列位置,所有数值均以 5 位有符号数格式呈现。
      当设备保持静止且 Z 轴垂直向上时,加速度计 Z 轴(AZ)输出值约为 32768(MPU6050 加速度计满量程 ±16g 时,数值范围为 - 32768~32768),可通过公式验证重力加速度:已知换算关系:2065/32768 = 0.059296,且 0.0630188 = x/16(x 为实际重力加速度,单位 g),解得 x =0.0630188 × 16 = 1.0083008 ≈ 1,即该数值对应标准 1g 重力加速度,验证了加速度计 Z 轴输出与实际重力的匹配性。

四、演示代码关联的头文件与源文件说明

  • OLED 相关头文件请从 STM32 学习 ------ 个人学习笔记4(OLED 显示屏及调试工具) 文末查看,此处不重复展示。

  • Delay 相关头文件请从 STM32 学习 ------ 个人学习笔记3-1(GPIO 输出) 文末查看,此处不重复展示。

  • Soft_I2C.c

    #include "stm32f10x.h" // Device header
    #include "Delay.h"

    void Soft_I2C_W_SCL(uint8_t BitValue){
    GPIO_WriteBit(GPIOA, GPIO_Pin_4, (BitAction)BitValue);
    Delay_us(10);
    }

    void Soft_I2C_W_SDA(uint8_t BitValue){
    GPIO_WriteBit(GPIOA, GPIO_Pin_5, (BitAction)BitValue);
    Delay_us(10);
    }

    uint8_t Soft_I2C_R_SDA(void){
    uint8_t BitValue;
    BitValue = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5);
    Delay_us(10);
    return BitValue;
    }

    void Soft_I2C_Init(void){

    复制代码
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      
      GPIO_SetBits(GPIOA, GPIO_Pin_4 | GPIO_Pin_5);

    }

    void Soft_I2C_Start(void){
    Soft_I2C_W_SDA(1);
    Soft_I2C_W_SCL(1);
    Soft_I2C_W_SDA(0);
    Soft_I2C_W_SCL(0);
    }

    void Soft_I2C_Stop(void){
    Soft_I2C_W_SDA(0);
    Soft_I2C_W_SCL(1);
    Soft_I2C_W_SDA(1);
    }

    void Soft_I2C_SendByte(uint8_t Byte){
    uint8_t i;
    for (i=0; i < 8; i++){
    Soft_I2C_W_SDA(Byte & (0x80 >> i));
    Soft_I2C_W_SCL(1);
    Soft_I2C_W_SCL(0);
    }
    }

    uint8_t Soft_I2C_ReceiveByte(void){
    uint8_t i, Byte = 0x00;
    Soft_I2C_W_SDA(1);
    for (i = 0; i < 8; i++){
    Soft_I2C_W_SCL(1);
    if (Soft_I2C_R_SDA() == 1){Byte |= (0x80 >> i);}
    Soft_I2C_W_SCL(0);
    }
    return Byte;
    }

    void Soft_I2C_SendAck(uint8_t AckBit){
    Soft_I2C_W_SDA(AckBit);
    Soft_I2C_W_SCL(1);
    Soft_I2C_W_SCL(0);
    }

    uint8_t Soft_I2C_ReceiveAck(void){
    uint8_t AckBit;
    Soft_I2C_W_SDA(1);
    Soft_I2C_W_SCL(1);
    AckBit = Soft_I2C_R_SDA();
    Soft_I2C_W_SCL(0);

    复制代码
      return AckBit;

    }

  • Soft_I2C.h

    #ifndef __SOFT_I2C_H
    #define __SOFT_I2C_H

    void Soft_I2C_Init(void);
    void Soft_I2C_Start(void);
    void Soft_I2C_Stop(void);
    void Soft_I2C_SendByte(uint8_t Byte);
    uint8_t Soft_I2C_ReceiveByte(void);
    void Soft_I2C_SendAck(uint8_t AckBit);
    uint8_t Soft_I2C_ReceiveAck(void);

    #endif

  • MPU6050.c

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

    #define MPU6050_ADDRESS 0xD0

    void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data){
    Soft_I2C_Start();
    Soft_I2C_SendByte(MPU6050_ADDRESS);
    Soft_I2C_ReceiveAck(); // 通过这个应答位可以判断从机是否收到数据,此处不做演示
    Soft_I2C_SendByte(RegAddress);
    Soft_I2C_ReceiveAck();
    Soft_I2C_SendByte(Data);
    Soft_I2C_ReceiveAck();
    Soft_I2C_Stop();
    }

    uint8_t MPU6050_ReadReg(uint8_t RegAddress){
    uint8_t Data;

    复制代码
      Soft_I2C_Start();
      Soft_I2C_SendByte(MPU6050_ADDRESS);
      Soft_I2C_ReceiveAck();
      Soft_I2C_SendByte(RegAddress);
      Soft_I2C_ReceiveAck();
      
      Soft_I2C_Start();
      Soft_I2C_SendByte(MPU6050_ADDRESS | 0x01);
      Soft_I2C_ReceiveAck();
      Data = Soft_I2C_ReceiveByte();
      Soft_I2C_SendAck(1);    
      Soft_I2C_Stop();
      
      return Data;

    }

    void MPU_6050_Init(void){
    Soft_I2C_Init();

    复制代码
      // 配置电源管理寄存器1: 解除睡眠并选择陀螺仪时钟
      MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01);
      
      // 配置电源管理寄存器2: 6 个轴均不待机
      MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00);
      
      // 配置采样率分频寄存器: 采样分频为 10
      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.h

    #ifndef __MPU6050_H
    #define __MPU6050_H

    void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
    void MPU_6050_Init(void);
    uint8_t MPU6050_ReadReg(uint8_t RegAddress);
    void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ,
    int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);

    #endif

  • MPU6050_Reg.h

    #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


文中部分知识参考:B 站 ------ 江协科技;百度百科

相关推荐
小陈phd2 小时前
多模态大模型学习笔记(二十二)——大模型微调全解:从全量调参到LoRA的参数高效训练实战
笔记·学习
Engineer邓祥浩2 小时前
JVM学习笔记(3) 第二部分 自动内存管理 第2章 Java内存区域与内存溢出异常
jvm·笔记·学习
inputA2 小时前
C语言可变参数(va_list、va_start、va_end、va_arg)
c语言·笔记
chinalihuanyu2 小时前
Linux-应用编程学习笔记(十二、GPIO控制)
笔记·学习
nap-joker2 小时前
【表格+影像】两全其美:多模态对比学习结合表格和成像数据
学习·对比学习·表格+影像·集成梯度可解释性
星雨流星天的笔记本2 小时前
3.含量子点的三口瓶怎么洗
学习
claider2 小时前
Vim User Manual 阅读笔记 usr_25.txt Editing formatted text 编辑有格式的文本
linux·笔记·vim
LCG元3 小时前
STM32实战:基于LVGL的嵌入式GUI界面开发(智能手表UI)
stm32·智能手表
承渊政道3 小时前
ToClaw是什么?一句话:装在云端的OpenClaw
windows·科技·学习·其他·macos·claw