1. 项目概述当小熊派遇上MPU6050与OLED最近在捣鼓小熊派Hi3861开发板想用它来做个姿态传感器的小玩意儿。核心想法很简单用I2C总线挂载一个MPU6050六轴传感器实时读取它的加速度和角速度数据然后通过另一个I2C接口把这些数据动态地显示在一块小小的OLED屏幕上。听起来像是嵌入式开发的“Hello World”级项目对吧但真上手了才发现从驱动适配、数据滤波到屏幕刷新每一步都有不少门道。这个项目非常适合刚接触Hi3861或者想巩固I2C、传感器驱动、实时数据显示的开发者。你不需要复杂的电路一块Hi3861核心板、一个MPU6050模块、一块0.96寸的OLED通常是SSD1306驱动再加上几根杜邦线就能搭建起来。最终的效果是OLED屏幕上会清晰地滚动显示三轴加速度Accel X/Y/Z和三轴角速度Gyro X/Y/Z的数值让你直观地“看见”开发板的每一个微小移动和转动。我之所以选择Hi3861一方面是看中了它作为鸿蒙生态轻量级开发板的潜力另一方面它的性能对于处理MPU6050的数据流和刷新OLED是绰绰有余的。整个过程中你会涉及到HarmonyOS LiteOS-M内核的任务管理、I2C底层通信协议的实现、传感器数据的解析与处理以及OLED的图形库应用算是一个小而全的练手项目。2. 硬件选型与电路连接解析2.1 核心硬件清单与选型理由要完成这个项目你需要准备以下几样核心硬件。别小看选型合适的硬件能让开发过程顺畅不少。小熊派BearPi-HM Nano开发板Hi3861这是我们的主控。Hi3861是一颗基于ARM Cortex-M4的Wi-Fi SoC运行HarmonyOS LiteOS-M内核。我选择它而不是更常见的STM32主要是想深入了解一下鸿蒙的轻量级设备开发环境。它的GPIO、I2C等外设资源对于本项目来说完全够用官方提供的HDF驱动框架也简化了外设驱动开发。MPU6050模块这是项目的“感觉器官”。MPU6050集成了三轴MEMS陀螺仪和三轴MEMS加速度计通过一个I2C接口与主控通信。市面上模块很多建议选择带电平转换通常到3.3V和内置滤波电容的版本这样直接连接Hi3861的3.3V GPIO会更稳定。特别注意有些模块的AD0引脚默认接地地址0x68有些则悬空地址0x69购买时最好确认一下或者选择引脚可调的模块。0.96寸OLED显示屏SSD1306驱动这是项目的“眼睛”。我推荐使用四针的I2C接口版本VCC, GND, SCL, SDA比SPI接口版本接线更简单节省GPIO。分辨率通常是128x64足够显示多行数据。选择时注意工作电压是否为3.3V兼容。杜邦线若干用于连接。建议使用母对母的杜邦线连接模块和开发板上的排针最方便。注意在焊接或连接任何线路之前务必断开所有电源。确认MPU6050和OLED模块的VCC引脚电压范围是否支持3.3V。大多数模块标称支持3.3V-5V但为求稳妥最好用万用表量一下模块上LDO低压差线性稳压器的型号确认其输入电压范围。2.2 I2C总线连接方案与原理图Hi3861开发板通常引出了多个I2C接口。我们需要使用两个独立的I2C通道分别连接MPU6050和OLED。这是因为虽然它们协议相同但设备地址不同理论上可以挂载在同一条总线上。但实践中为了调试方便和避免潜在的地址冲突或驱动加载顺序问题我强烈建议使用两个独立的I2C接口。以小熊派BearPi-HM Nano为例其扩展接口上有明确的I2C0和I2C1定义。我的连接方案如下MPU6050 连接至 I2C0:VCC - 开发板上的3.3V引脚GND - 开发板上的GND引脚SCL - GPIO13 (对应I2C0_SCL)SDA - GPIO14 (对应I2C0_SDA)如果模块AD0引脚可调通常接地或接3.3V以设定地址OLED (SSD1306) 连接至 I2C1:VCC - 开发板上的3.3V引脚注意不要和MPU6050共用一根电源线最好从开发板不同引脚引出避免大电流波动干扰GND - 开发板上的GND引脚SCL - GPIO2 (对应I2C1_SCL)SDA - GPIO3 (对应I2C1_SDA)为什么这么连接首先查看开发板原理图确认这些GPIO的复用功能确实是I2C。其次将两个设备分开在软件层面可以初始化两个独立的I2C控制器代码结构更清晰。当OLED刷新率较高时也不会影响MPU6050的数据读取时序。最后在调试阶段如果某一个设备不工作你可以单独测试其对应的I2C总线排查问题更快捷。实操心得连接时务必先断电。连接好后可以用手轻轻拉扯每根杜邦线确保接触牢固。虚接是嵌入式调试中最头疼的问题之一往往导致时好时坏的现象。有条件的话可以用示波器或逻辑分析仪抓一下I2C总线的波形这是最权威的调试手段。3. 软件开发环境搭建与工程创建3.1 HarmonyOS开发环境配置详解Hi3861开发需要HarmonyOS的专用开发环境。目前主要推荐使用Windows下的DevEco Device Tool基于VSCode插件或Linux下的DockerHPM环境。我以Windows下的DevEco Device Tool为例因为它对新手更友好图形化界面能完成大部分配置。安装基础软件确保你的Windows系统已安装Python 3.7-3.9版本、Node.js和VSCode。然后从华为开发者联盟官网下载并安装DevEco Device Tool最新版。安装过程会提示你安装一系列依赖包括编译工具链gn, ninja, hc-gen等按照指引完成即可。导入SDK与工程安装完成后打开VSCode你会看到DevEco插件图标。点击“导入工程”选择HarmonyOS的SDK路径通常是一个包含build.py,applications,device等目录的大文件夹。然后我们需要基于一个现有样例来创建我们的项目。最方便的是在applications/sample/wifi-iot/app目录下复制一个I2C的示例文件夹例如i2c_example将其重命名为mpu6050_oled_demo作为我们自己的工程目录。工程配置修改进入我们新建的mpu6050_oled_demo目录修改关键的配置文件BUILD.gn。这个文件告诉编译系统如何构建你的应用。你需要确保它正确引用了I2C驱动、传感器驱动框架如果使用以及OLED显示相关的源文件。一个典型的BUILD.gn内容骨架如下static_library(mpu6050_oled_demo) { sources [ mpu6050_drv.c, // MPU6050驱动文件 ssd1306_oled.c, // OLED驱动文件 main_task.c, // 主任务文件 ] include_dirs [ //utils/native/lite/include, //device/hisilicon/hispark_pegasus/sdk_liteos/include, //foundation/communication/dsoftbus/sdk/bus_center/service_registry/include, //路径/到/其他头文件, ] deps [ //drivers/adapter/khdf/liteos/model/sensor/driver/hdf_sensor:libsensor, //drivers/adapter/khdf/liteos/model/misc/driver/hdf_misc:libmisc, ] }这里的关键是sources里要包含你将要编写的所有.c文件include_dirs要包含所有必要的头文件路径deps要声明依赖的组件比如传感器驱动框架。3.2 工程目录结构与代码管理清晰的目录结构能让项目维护起来事半功倍。在我的mpu6050_oled_demo文件夹内我是这样组织的mpu6050_oled_demo/ ├── BUILD.gn # 编译脚本 ├── mpu6050_drv.h/c # MPU6050驱动头文件和源文件 ├── ssd1306_oled.h/c # OLED驱动头文件和源文件 ├── main_task.h/c # 应用主任务包含业务逻辑 ├── i2c_hal.h/c # I2C硬件抽象层可选用于封装Hi3861的I2C操作 └── README.md # 项目说明main_task.c是程序的入口它负责创建任务、初始化硬件、调度业务逻辑。mpu6050_drv.c和ssd1306_oled.c是设备驱动层它们封装了与具体芯片通信的底层细节向上提供简洁的API如MPU6050_ReadData()OLED_ShowString()。i2c_hal.c是一个可选的中间层目的是将Hi3861特定的I2C操作如调用I2cWriteI2cRead函数封装成通用的I2C_WriteByteI2C_ReadByte函数这样你的设备驱动代码就与具体平台解耦了未来移植到其他MCU会方便很多。注意事项HarmonyOS的编译系统对文件路径和依赖非常敏感。如果你在头文件中引用了非标准库路径一定要在BUILD.gn的include_dirs中显式声明否则编译时会报“找不到头文件”的错误。另外每次添加新的.c文件别忘了在BUILD.gn的sources列表里加上它。4. MPU6050驱动开发与数据解析4.1 I2C通信基础与Hi3861 HDF驱动调用MPU6050和OLED都通过I2C通信。在HarmonyOS下我们通常通过HDFHardware Driver Foundation框架来操作I2C。HDF提供了标准化的设备驱动模型我们需要在配置文件中声明对I2C控制器的使用然后在代码中通过设备号来打开和操作它。首先需要在vendor/hisilicon/hispark_pegasus/config.json或类似路径的板级配置文件中确保I2C0和I2C1的控制器的配置是使能的。然后在我们的应用代码中操作步骤如下获取I2C控制器句柄使用DevHandle I2cOpen(int id)函数传入控制器编号如0代表I2C0来打开设备获取一个操作句柄。这个句柄相当于一个“遥控器”后续所有针对这个I2C总线的操作都要用它。配置I2C传输速率通过I2cSetBaudrate函数设置通信频率MPU6050和SSD1306通常支持标准模式100kHz和快速模式400kHz。为了数据读取的实时性我建议设置为400kHz。进行读写操作核心是I2cTransfer函数。它需要一个I2cMsg结构体数组每个结构体描述一次起始条件、从设备地址含读写位、数据缓冲区指针和长度。这里有个关键点MPU6050的读写通常需要两个I2cMsg。第一个消息用于写入要访问的寄存器地址第二个消息才是读取或写入该寄存器的数据。这被称为“复合传输”需要在I2cMsg中设置I2C_FLAG_READ或I2C_FLAG_WRITE标志并且I2cTransfer函数会自动在两次消息之间发送一个重复起始条件Repeated Start而不是停止条件。关闭句柄操作完成后使用I2cClose关闭句柄释放资源。我将这些操作封装在了i2c_hal.c中提供了诸如I2C_WriteReg(I2C_ID, DevAddr, RegAddr, Value)和I2C_ReadReg(I2C_ID, DevAddr, RegAddr, pValue)这样的函数这样在MPU6050驱动里调用起来就非常直观了。4.2 MPU6050寄存器配置与数据读取MPU6050上电后处于睡眠模式需要初始化才能正常工作。驱动开发主要就是通过I2C读写其内部寄存器。唤醒与时钟源设置向PWR_MGMT_1寄存器地址0x6B写入0x00选择内部8MHz振荡器作为时钟源并唤醒设备。配置量程与采样率GYRO_CONFIG0x1B配置陀螺仪量程。我选择±500°/s对应写入值0x08。量程越小灵敏度越高但容易溢出。ACCEL_CONFIG0x1C配置加速度计量程。我选择±4g对应写入值0x08。SMPLRT_DIV0x19配置采样率分频器。MPU6050的陀螺仪和加速度计采样频率由内部时钟通常1kHz和这个分频器决定。计算公式采样频率 1kHz / (1 SMPLRT_DIV)。我设置SMPLRT_DIV为9得到100Hz的采样率对于OLED显示来说足够流畅且不占用过多CPU。读取传感器数据加速度和陀螺仪数据分别存储在6个连续的寄存器中ACCEL_XOUT_H, ACCEL_XOUT_L ... GYRO_ZOUT_H, GYRO_ZOUT_L。我们可以使用I2cTransfer进行一次复合传输先写入首个寄存器地址0x3B然后连续读取14个字节6个加速度 6个陀螺仪 2个温度。读取到的原始数据是16位有符号整数补码形式。原始数据转换根据之前设置的量程将原始数据转换为物理值。加速度加速度(g) 原始值 / 灵敏度。对于±4g量程灵敏度为8192 LSB/g。角速度角速度(°/s) 原始值 / 灵敏度。对于±500°/s量程灵敏度为65.5 LSB/(°/s)。在我的mpu6050_drv.c中初始化函数MPU6050_Init()完成了上述配置步骤。而数据读取函数MPU6050_ReadData(float* accel, float* gyro)则封装了读取和转换的全过程最终输出三个浮点数数组分别代表三轴加速度单位g和三轴角速度单位°/s。避坑技巧MPU6050的寄存器写入后有时需要一小段稳定时间。特别是在修改量程或滤波器设置后最好延迟几毫秒再读取数据。另外连续读取时确保I2C读取的字节顺序是正确的高字节在前。读取到的原始值建议先存入int16_t类型变量再进行浮点运算避免数据溢出或精度问题。4.3 传感器数据滤波处理直接从MPU6050读出的数据往往带有高频噪声在OLED上显示时会不停跳动影响观感。因此必须加入滤波算法。对于这个显示项目我们不需要复杂的姿态解算如卡尔曼滤波简单的低通滤波或移动平均滤波就能极大改善显示效果。我采用了一阶互补滤波这是一种在嵌入式系统中非常高效且有效的算法。它的思想是结合加速度计长期稳定但动态响应慢和陀螺仪短期精确但存在漂移的优点。不过对于纯显示数值我们可以对加速度和陀螺仪数据分别进行独立的低通滤波。以加速度X轴为例在每次读取到新数据accel_x_new后进行如下计算#define ALPHA 0.2f // 滤波系数介于0~1之间越小越平滑但滞后越大 float accel_x_filtered ALPHA * accel_x_new (1 - ALPHA) * accel_x_filtered_old;ALPHA是一个经验值我通过实测发现0.2到0.3之间对于100Hz的采样率显示效果比较好。滤波后的数据accel_x_filtered再送去显示跳动就会明显减弱。更进一步的优化你可以实现一个移动平均队列。创建一个固定长度的数组比如10个元素每次新数据放入队尾并剔除队首的数据然后计算队列中所有数据的平均值作为输出。这种方法能更有效地抑制突发性噪声。在main_task.c中我定义了一个滤波函数SensorData_LowPassFilter()在每次读取MPU6050数据后调用它对六个轴的数据分别进行滤波处理然后将滤波后的结果存入全局变量供显示任务使用。5. OLED屏幕驱动与数据显示实现5.1 SSD1306驱动初始化与基础绘图函数SSD1306是一款通过I2C控制的OLED点阵驱动芯片。我们的目标是驱动一块128x64的屏幕。驱动开发的第一步是初始化。初始化序列SSD1306上电后需要发送一系列命令来配置显示参数。这些命令包括关闭显示、设置显示时钟分频和振荡频率、设置多路复用率、设置显示偏移、设置显示起始行、开启电荷泵、设置内存地址模式、设置列地址和页地址范围、开启显示等。这些命令字节通过I2C发送控制字节为0x00表示后续是命令流。我将其封装在OLED_Init()函数中。实现基础绘图函数SSD1306支持页寻址模式将屏幕水平分为8页Page每页8行即128x8像素。我们操作的最小单位是一个字节对应一页中某一列的8个像素点垂直排列。OLED_SetCursor(uint8_t page, uint8_t col)设置当前写入的页和列地址。OLED_WriteData(uint8_t data)向当前地址写入一个字节数据点亮对应的8个像素。OLED_Clear()向整个显存写入0x00清屏。OLED_Refresh()将本地显存缓冲区一个128x8字节的数组的内容通过I2C一次性刷入SSD1306的GRAM。这是一个关键优化避免每次画点都进行I2C通信而是先在内存中修改缓冲区最后统一刷新效率极高。基于这些底层函数我们可以构建高级功能OLED_DrawPoint(uint8_t x, uint8_t y, uint8_t mode)在(x,y)坐标画点或擦除。需要计算该点位于哪个页page y / 8以及在该页字节中的具体位bit y % 8然后通过位操作修改缓冲区对应字节的指定位。OLED_ShowChar(uint8_t x, uint8_t y, char chr, uint8_t size)显示一个字符。需要预置一个字库数组如8x16像素的ASCII码表。函数根据字符编码找到对应的字模数据然后循环调用OLED_DrawPoint或直接操作缓冲区字节来绘制。OLED_ShowString(uint8_t x, uint8_t y, char *str, uint8_t size)循环调用OLED_ShowChar显示字符串。5.2 数据格式化与动态显示界面设计有了显示字符的能力接下来就是设计OLED上的显示界面。我们的目标是清晰、实时地展示六轴数据。数据格式化从MPU6050读取并滤波后得到的是浮点数如加速度1.023g。我们需要将其转换为字符串才能显示。使用标准库的sprintf函数虽然方便但在资源受限的嵌入式系统中可能比较耗时且占用内存。我推荐使用轻量级的实现或者将浮点数先乘以一个系数转换为整数再处理。 例如为了显示两位小数我可以这样做int16_t accel_x_int (int16_t)(accel_x_filtered * 100); // 放大100倍 char str_buf[10]; // 简单整数转字符串函数自己实现或使用itoa my_itoa(accel_x_int, str_buf); // 然后在str_buf中手动插入小数点得到类似“102”的字符串显示为“1.02”界面布局设计在128x64的屏幕上我设计了如下布局第0行Page 0显示标题如“MPU6050 Data”。第2-3行Page 2-3显示“Accel X: [数值] g”。X、Y、Z分三行显示。第5-6行Page 5-6显示“Gyro X: [数值] °/s”。同样分三行。 每个数值预留了足够的字符宽度例如“-3.45”并右对齐这样数据变化时不会左右抖动。动态刷新策略如果每次数据更新都全屏刷新即先清屏再重绘所有文本屏幕会有明显的闪烁。优化的方法是局部刷新。我只更新数值变化的区域。具体实现是在内存中保存上一次显示的字符串本次刷新前将新格式化的字符串与旧字符串逐字符比较只更新那些发生变化的字符所在的位置。对于数字显示通常只有最后几位数字在变这样能极大减少I2C通信量和绘图操作实现平滑更新。在我的main_task.c中我创建了一个专门的显示任务Display_Task()。这个任务以固定的频率比如10Hz被唤醒。它从全局变量中获取最新的滤波后传感器数据格式化然后调用OLED_ShowString等函数结合局部刷新逻辑更新OLED屏幕上的数值。6. 多任务系统整合与性能优化6.1 LiteOS-M任务创建与调度HarmonyOS LiteOS-M是一个实时操作系统内核我们需要创建任务来管理不同的工作。在这个项目中我至少创建了两个任务传感器数据采集任务(SensorRead_Task)这个任务负责以固定的时间间隔例如10ms对应100Hz读取MPU6050数据并进行滤波处理。它需要较高的定时精度以确保数据采样的均匀性。在LiteOS-M中可以使用LOS_TaskDelay函数进行延时但要注意这个延时是基于系统Tick的可能存在微小误差。对于更精确的定时可以考虑使用硬件定时器触发中断在中断服务例程中释放一个信号量任务等待该信号量。数据显示任务(Display_Task)这个任务负责更新OLED屏幕。它的频率可以低于采集任务比如20ms一次50Hz。过高的刷新率对于肉眼观察没有意义反而会增加I2C总线负担和CPU占用。这个任务等待一个信号量或事件标志当传感器任务完成一次数据采集和滤波后就触发这个信号量通知显示任务可以读取最新的数据进行刷新。在main_task.c的入口函数里我进行了如下操作void main_task_entry(void) { // 1. 硬件初始化 I2C_Init(I2C0_ID); // 初始化I2C0 for MPU6050 I2C_Init(I2C1_ID); // 初始化I2C1 for OLED MPU6050_Init(); OLED_Init(); OLED_Clear(); OLED_ShowString(0, 0, MPU6050 Data, 16); // 2. 创建任务 LOS_TaskCreate(g_sensor_task_id, sensor_task_attr, SensorRead_Task, NULL); LOS_TaskCreate(g_display_task_id, display_task_attr, Display_Task, NULL); // 3. 主任务可以休眠或处理其他事件 while (1) { LOS_TaskDelay(1000); // 每1秒执行一些低优先级维护工作如打印调试信息 } }任务属性task_attr中需要定义任务优先级、栈大小等。通常传感器采集任务的优先级应高于显示任务以确保数据采样的实时性。6.2 内存、功耗与实时性优化要点当系统跑起来后还需要关注一些优化点让项目更健壮、更高效。栈空间分配在LiteOS-M中每个任务都有独立的栈。务必在任务属性中分配足够的栈空间。对于传感器任务和显示任务栈大小建议至少2KB。你可以通过系统提供的API如LOS_TaskInfoGet监控任务栈的水位防止栈溢出。共享数据保护传感器任务和显示任务通过全局变量滤波后的数据通信。当多个任务访问共享资源时存在竞争风险。虽然在这个简单项目中因为数据是简单的浮点数组且写入传感器任务和读取显示任务频率固定发生冲突的概率极低但良好的编程习惯是使用互斥锁Mutex或信号量Semaphore进行保护。LiteOS-M提供了LOS_MuxCreate,LOS_MuxLock,LOS_MuxUnlock等函数。功耗考虑Hi3861本身是低功耗芯片但MPU6050和OLED屏幕是耗电大户。如果项目是电池供电可以考虑间歇工作让系统大部分时间休眠定时唤醒采集并显示数据。降低MPU6050采样率在满足要求的前提下降低SMPLRT_DIV。关闭OLEDSSD1306有专门的命令可以关闭显示但芯片仍在工作或者直接切断其电源需硬件支持。I2C总线效率这是性能瓶颈之一。优化措施包括使用DMA进行I2C传输如果Hi3861的I2C控制器支持。对OLED的刷新使用“页编程模式”即一次性发送多字节数据减少起始/停止条件的开销。确保I2C时钟频率设置正确400kHz并检查上拉电阻是否合适通常4.7kΩ-10kΩ波形是否干净。实操心得在调试多任务系统时善用串口打印日志至关重要。我在每个任务的关键节点如开始、结束、错误处添加了条件编译的打印语句。通过日志的时间戳可以清晰看到两个任务的执行时序判断是否存在阻塞或调度问题。另外可以使用LiteOS-M的系统状态查看命令如果Shell支持来监控CPU利用率和任务状态。7. 常见问题排查与调试实录即使按照步骤操作也难免会遇到问题。下面是我在实现过程中遇到的一些典型问题及解决方法希望能帮你快速排雷。7.1 硬件连接与电源问题问题现象OLED或MPU6050完全不工作I2C扫描不到设备地址。排查步骤检查电源用万用表测量模块VCC和GND之间的电压确认是否为稳定的3.3V。有时杜邦线接触不良会导致电压跌落。检查地址使用一个简单的I2C扫描程序很多开发环境提供例程扫描I2C总线上的所有地址。确认MPU60500x68或0x69和SSD1306通常0x3C或0x3D是否出现。如果都没出现可能是I2C总线本身有问题。检查上拉电阻I2C总线需要上拉电阻。开发板内部可能已经集成如果外部模块也有上拉可能导致电阻并联阻值过小拉高能力太强。可以尝试断开模块上的上拉电阻如果有。检查线序再三确认SCL和SDA是否接反。这是最常见的低级错误。问题现象屏幕显示乱码或雪花点。排查步骤检查初始化序列SSD1306的初始化命令顺序非常关键特别是开启电荷泵的命令0x8D, 0x14必须在设置显示ON/OFF之前。对照数据手册逐条检查发送的命令。检查电源稳定性OLED在工作时电流会有波动如果电源纹波过大会导致显示异常。尝试在模块的VCC和GND之间并联一个10uF的电解电容和一个0.1uF的陶瓷电容进行滤波。检查I2C速度过高的I2C时钟频率可能导致通信错误。尝试将频率降低到100kHz试试。7.2 软件驱动与数据问题问题现象能读取到MPU6050的IDWHO_AM_I寄存器值为0x68但读取到的加速度和陀螺仪数据全是0或固定值。排查步骤确认设备已唤醒检查是否已向PWR_MGMT_1寄存器写入0x00。有时芯片会处于睡眠或循环模式。检查量程配置确认GYRO_CONFIG和ACCEL_CONFIG寄存器写入的值是否正确。检查数据读取流程确保使用的是“复合传输”模式来读取连续寄存器。单独读取每个寄存器的高低位很容易出错。用逻辑分析仪抓取I2C波形看地址、寄存器地址、数据是否都正确。检查数据转换确认原始数据从两个8位寄存器合并成16位有符号整数时顺序是否正确高字节在前。确认转换时使用的灵敏度系数与设置的量程匹配。问题现象OLED显示的数据跳动非常剧烈即使静止不动也在大幅变化。排查步骤检查滤波算法确认滤波函数被正确调用滤波系数ALPHA设置是否合理太小会导致响应迟钝太大会滤波不足。可以先将滤波关闭观察原始数据的跳动范围判断噪声水平。检查MPU6050放置确保传感器模块放置平稳没有受到风扇、手部等引起的振动。MPU6050对微振动非常敏感。检查电源噪声为MPU6050模块的电源引脚增加滤波电容如10uF和0.1uF并联可以有效抑制电源引入的噪声。启用MPU6050内置滤波器MPU6050本身有可配置的低通滤波器CONFIG寄存器0x1A。可以尝试设置一个较低的截止频率如5Hz让芯片硬件先滤除一部分高频噪声再交给软件处理。问题现象系统运行一段时间后死机或重启。排查步骤检查栈溢出这是多任务系统最常见的死机原因。增大任务的栈大小或者在任务入口函数中打印栈使用情况。检查内存泄漏确保没有在任务中动态分配内存malloc而未释放。在嵌入式系统中应尽量避免动态内存分配。检查中断冲突如果使用了硬件定时器中断确保中断服务函数执行时间非常短没有调用可能导致阻塞的API。看门狗检查是否开启了硬件看门狗WDT而未及时喂狗。调试是一个反复迭代的过程。我的习惯是模块化调试先确保I2C读写一个已知寄存器如MPU6050的WHO_AM_I能成功再确保OLED能显示静态字符然后让MPU6050输出数据并在串口打印最后才将两者结合在OLED上动态显示。每一步都确认无误能大大缩小问题范围。