
简介面向嵌入式开发与STM32入门学习者一份基于STM32F103的IIC读取MMA7455三轴倾角传感器例程包演示了从总线通信到角度转换的完整流程。内容涵盖GPIO模拟IIC时序、HAL/LL库配置、寄存器读写、测量模式与分辨率设置以及滑动平均滤波和错误处理思路适合需要快速上手传感器通信或在项目中集成倾角检测功能的中级开发者。压缩包内含261个文件整体大小6.72MB除C源文件、H头文件外还有工程编译生成的o、axf、lst等中间文件以及PDF、doc、htm说明文档便于对照源码理解工程结构。已有522人浏览学习。代码按模块划分清晰主要提供IIC初始化、数据收发、MMA7455寄存器操作和数据解析等关键函数并附有可直接导入的工程配置。通过学习这份例程可以掌握STM32软件模拟IIC的时序控制、三轴加速度数据转倾角的计算方法以及实际调试中常见的滤波与补偿技巧为后续自研传感器驱动提供可复用的参考模板。1. 为什么选MMA7455从一次实际选型说起前阵子帮朋友做一个小型倾角监测装置要求不高能测出设备朝哪个方向歪、歪了多少度串口能读到角度就行。翻了一下手头模块发现有一颗很老的MMA7455L飞思卡尔现在归恩智浦早年推的三轴加速度计。说实话现在做倾角测量大家第一反应都是MPU6050甚至直接上高精度的专用倾角传感器但MMA7455在这类场景里反而有一个很明显的优势调起来快驱动逻辑极其简单而且它对处理器的占用很低。MMA7455是数字输出的三轴加速度计2.2V~3.6V供电内置了I2C和SPI两种接口量程可以切±2g、±4g、±8g。跟MPU6050这类带陀螺仪、带FIFO、带DMP的传感器不一样它没有那么多需要配置的中间环节上电之后设一下量程、切到测量模式就能连续读数据。对于只关心物体相对于重力方向偏了多少的倾角应用来说MPU6050的那些DMP姿态解算功能反而用不上纯属徒增配置复杂度。还要提一点MMA7455的功耗很低测量模式下的工作电流大概是400μA左右待机模式更低。做电池供电的便携倾角标签、宠物项圈姿态检测这类项目这个功耗水平有它的价值。所以别看这颗芯片老在特定场景下仍然是合理选择尤其适合学生做课程设计、上班族快速验证原型。本文就把STM32驱动MMA7455的完整思路整理出来从寄存器配置、I2C读取、角度解算到标定和滤波一条线走完。2. 接线、地址和初始化先把传感器叫醒2.1 硬件连接很简单但也有几个细节MMA7455通常有LGA-14封装很多模块已经帮你引出了引脚。用I2C方式接STM32时只需要四根线MMA7455引脚STM32引脚说明VCC3.3V千万不要接5V芯片手册耐压上限3.6VGNDGND共地SCLPB6I2C1_SCL需要上拉电阻模块自带就不管SDAPB7I2C1_SDA需要上拉电阻模块自带就不管如果拿的是纯芯片自己画板子SCL和SDA必须各接一个4.7kΩ上拉电阻到3.3V这是I2C总线的基本要求。没有上拉电阻总线拉不高电平通信必失败而且失败得很奇怪——有时候能读到有时候读不到完全看引脚电容心情。另一个容易忽略的是SA0引脚。这个引脚决定I2C从机地址SA0接高电平7位地址是0x1DSA0接低电平7位地址是0x1C。换算成STM32标准库或HAL库里常用的8位读写地址0x1D左移一位就是写地址0x3A读地址0x3B。很多模块直接把SA0拉高所以代码里默认用0x1D通常没问题但如果你发现怎么都扫描不到设备第一件事就是检查SA0接的什么电平。2.2 MCTL寄存器决定量程和模式MMA7455的核心控制寄存器是MCTL地址0x16。它里面有几位直接决定芯片怎么工作bit6、bit5GLVL1、GLVL0量程选择00是±2g01是±4g10是±8gbit3、bit2MOD1、MOD0模式选择00是待机01是测量10是电平检测11是脉冲检测bit1STON自检使能平时置0bit0SPIENSPI接口使能用I2C就置0这里有个新手容易懵的点到底选哪个量程。我的建议是倾角测量直接用±2g。因为你要测的是重力加速度在三个轴上的分量日常倾角场景下重力分量范围在-1g到1g之间用±2g档位时灵敏度最高分辨率最好。只有设备会剧烈运动、加速度超过2g的场景才需要切到±4g或±8g比如做手持设备跌落检测。初始化代码很简单I2C底层初始化之后往MCTL写0x05。0x05的二进制是0000 0101表示2g量程、测量模式、自检关闭、I2C模式。如果要用4g量程就写0x248g量程写0x44。写成寄存器操作函数大概是这样的void MMA7455_WriteReg(uint8_t reg, uint8_t value) { I2C_Start(); I2C_SendByte(0x3A); // MMA7455写地址 I2C_WaitAck(); I2C_SendByte(reg); I2C_WaitAck(); I2C_SendByte(value); I2C_WaitAck(); I2C_Stop(); } void MMA7455_Init(void) { I2C_StartInit(); // 初始化STM32 I2C引脚和时钟 MMA7455_WriteReg(0x16, 0x05); // MCTL: 2g量程, 测量模式 }2.3 上电后先确认传感器在线不要急着读数先做一次设备在位检查。I2C读任意寄存器如果能正常读到并且数据不是全0xFF或全0x00基本说明通信链路通了。我习惯先读STATUS寄存器0x09检查bit2的DOVR位数据溢出标志和bit3的DRDY位数据就绪标志。DRDY置1说明芯片已经完成一次采样可以在这个标志位之后去读数据能避免读到新旧数据混在一起的情况。如果你的硬件调试条件有限也可以用逻辑分析仪抓I2C波形。正常的读时序应该是起始位S、设备写地址0x3A、寄存器地址0x09、重复起始位Sr、设备读地址0x3B、然后读到1字节数据、主控发NACK、停止位P。看到这个节奏基本就没问题。3. 读取加速度:寄存器组合与符号扩展3.1 8位模式还是10位模式MMA7455支持8位和10位两种输出精度。8位数据在XOUT8、YOUT8、ZOUT8三个寄存器里地址分别是0x06、0x07、0x08。读回来直接当成int8_t用简单粗暴。但8位模式下2g量程对应的灵敏度只有8 LSB/g重力的1g只能体现为8个计数角度稍微变一点读数就跳一格精度不太够看。10位模式的数据分布在XOUTL、XOUTH、YOUTL、YOUTH、ZOUTL、ZOUTH六个寄存器里地址从0x00连续到0x05。低字节XOUTL存的是10位数据的低8位高字节XOUTH只用了低两位存的是10位数据的高两位。这个模式下2g量程灵敏度是64 LSB/g1g对应64个计数比8位模式好很多。做倾角测量强烈建议用10位模式。3.2 组数据时最关键的符号扩展从寄存器里把高低字节拼起来看似简单其实有个大坑10位数据是补码表示的最高位是符号位。如果不做符号扩展读到负角度数据会出现莫名其妙的数值跳变。正确的组合方式是先把高字节和低字节拼成16位数据然后左移6位再算术右移6位把低12位截掉的同时保留符号。用C语言写就是这样void MMA7455_ReadAcc10(int16_t *ax, int16_t *ay, int16_t *az) { uint8_t xl MMA7455_ReadReg(0x00); uint8_t xh MMA7455_ReadReg(0x01); uint8_t yl MMA7455_ReadReg(0x02); uint8_t yh MMA7455_ReadReg(0x03); uint8_t zl MMA7455_ReadReg(0x04); uint8_t zh MMA7455_ReadReg(0x05); *ax (int16_t)(((uint16_t)xh 8) | xl); *ay (int16_t)(((uint16_t)yh 8) | yl); *az (int16_t)(((uint16_t)zh 8) | zl); *ax (*ax 6) 6; *ay (*ay 6) 6; *az (*az 6) 6; }这里解释一下运算逻辑组合后的值如果10位最高位是1说明是负数。左移6位把高符号位推到16位整型的最高位再算术右移6位让低位的无效数据被丢弃同时更高位用符号位填充。这样就能正确得到带符号的加速度原始值。3.3 硬件I2C还是软件模拟I2CSTM32F1系列的老用户应该都听说过硬件I2C的传言——容易卡死、要处理总线错误标志、时序配置不对就没响应。我在F103上实际用过大多数情况下硬件I2C是能用的前提是把错误中断和超时处理写好。但如果你要快速出原型或者不想为这些细节折腾直接软件模拟I2C反而更省心因为时序完全由你控制出问题容易排查。软件模拟I2C的核心是GPIO翻转和延时。SCL高电平期间SDA不能变化起始条件、停止条件、ACK检测这些时序都要卡准。网上这类代码很多找个稳定的改改就能用。我个人建议是做产品用硬件I2C加完整错误处理做毕设、做DIY、快速验证用软件I2C时间成本差别很大。还有一个容易被忽略的问题MMA7455的I2C最大速率是400kHz。STM32硬件I2C如果不小心配成了400kHz以上会不稳定。如果用软件模拟记得把延时调到位别用无条件延时循环最好用定时器或SysTick计时。4. 倾角解算atan2是比asin更好的选择4.1 从加速度到角度公式详解MMA7455测的是加速度不是直接输出角度。要得到倾角核心原理是静止状态下传感器只受到重力作用重力在X、Y、Z三轴上的投影分量跟传感器的姿态一一对应。如果你把传感器水平放平Z轴正对地面那么Z轴读数大约为1g即64个计数X轴和Y轴读数接近0。如果把设备绕Y轴翻转30度X轴就会有一部分重力分量Z轴的重力分量会变小。计算角度最常见的是用atan2函数#include math.h float roll atan2f(acc_y, acc_z) * 57.2958f; // 绕X轴侧倾角 float pitch atan2f(acc_x, acc_z) * 57.2958f; // 绕Y轴俯仰角这里的acc_x、acc_y、acc_z要换成以g为单位的浮点数也就是原始值除以64.0f。57.2958是180/π的近似值用来把弧度转成角度。为什么用atan2而不是asin很多新手看到倾角 重力分量的反正弦会自然想到asin(acc_x / 1g)。但asin在接近±90度时斜率趋于无穷大响应非常敏感而且asin本身的值域限制在-90度到90度无法区分一些姿态。atan2正好避开了这两个问题它在四个象限都能正确输出角度滚动覆盖-180度到180度而且算法内部处理的比值关系天然抑制了部分灵敏度变化问题。4.2 原始数据必须加滤波MMA7455的输出噪声并不小尤其在没有做好电源去耦的模块上原始数据跳变能达到好几十个码值。倾角计算对加速度值的跳变非常敏感因为角度和加速度是反正切关系在角度接近0度时微小扰动就会引起读数明显抖动。实测下来不加滤波时角度数据能跳±1.5度加滤波后能稳定在±0.3度以内。我用得比较顺手的是轻量级一阶低通滤波#define ALPHA 0.2f float filter_value(float raw, float *filtered) { *filtered ALPHA * raw (1.0f - ALPHA) * (*filtered); return *filtered; }调用时每个轴维护一个滤波变量每次采样后更新一次。ALPHA值越小滤波越平滑但延迟越大ALPHA值越大响应越快但噪声越多。倾角测量这种缓变场景ALPHA取0.1~0.2比较合适。如果是检测设备拍击、跌落这种瞬态事件就把ALPHA调大甚至直接不滤波。如果觉得一阶低通还不够顺可以再加一个滑动平均窗口取最近10次采样的平均值。不过滑动平均会引入明显相位延迟实时性要求高的场景慎用。4.3 一组实测数据作为参考我拿一块MMA7455模块平放在桌面上初始化后连续采集100次X轴原始值在-2到2之间波动换算成角度就是0度附近±1度左右。把传感器竖起来让X轴垂直朝下时X轴读数稳定在62~65之间对应1g附近角度读数约90度加了低通滤波后稳定在89.7度到90.2度之间波动。这说明整个链路是通的传感器读值和芯片手册标注一致角度解算逻辑正确。如果你的实测数据和这个差距很大先回头检查量程配置是否正确或者有没有把原始值当成已经换算成克的数去用。5. 标定与踩坑零点偏移、数据跳变、I2C异常5.1 零点偏移和灵敏度标定加速度计即使出厂校准过焊接到板上、经历了过炉回流焊之后零点也会偏移。最典型的现象是平放时X轴读数不是0而是某个固定值比如7。如果不做零点校准角度输出就带一个固定的偏差。最实用的标定办法是三步第一步把设备平放静止采集100次以上X、Y轴原始值求平均得到x_offset和y_offset。Z轴理论上平放时约1g不用做零点偏移。第二步把设备绕Y轴翻转让X轴分别朝上和朝下各采100次平均得到x_pos和x_neg。增益校正系数 (x_pos - x_neg) / 2。对于2g量程10位模式这个值理论上应该是128附近1g对应64正负1g相差128。如果偏了说明实际灵敏度有偏差用这个系数替换公式里的64.0f。第三步Y轴同理。这个做法虽然比不上工业级的六面标定但对付绝大多数倾角测量场景已经够了。我实际标定过一块焊接在底板上的MMA7455标定前水平放置时roll角读出3.8度标定后误差小于0.2度。5.2 常见问题排查清单整个项目做完把遇到过的奇怪问题整理一下给后面做的人排雷第一I2C扫描不到设备。先查SA0电平、再查上拉电阻、再查供电。曾经有一块模块万用表量VCC有3.3V但SDA和SCL没有上拉结果就是一直通信失败补上4.7k上拉立刻就好了。第二MCTL写进去了但数据全是0x00。这种情况通常是芯片根本没进入测量模式或者芯片还在待机状态。写完MCTL之后加一个10ms延时再读数据别上电就立刻读。第三数据偶发跳变到超大值。多半是I2C时序没处理好读取过程中SCL毛刺导致数据错位。软件模拟I2C的话把GPIO翻转之间的延时加大一点硬件I2C的话检查时钟极性配置。第四用8位读数时角度分辨率不够。这是芯片自身的限制不是代码问题。要么切10位模式要么换一颗量程更小的传感器不要在8位模式下硬扛。5.3 这整套驱动还能扩展到什么场景倾角测量只是MMA7455的用途之一。芯片还支持电平检测模式MCTL切到10二进制10再配置CTL1和CTL2两个寄存器的阈值可以在加速度超过某个值时让INT引脚输出中断。做振动报警、冲击记录、跌落检测都行。它也有自检功能MCTL的bit1置1后芯片内部会施加一个偏置电压模拟一个加速度值利用这个功能可以做上电自检。如果项目里只有I2C总线不够用MMA7455还支持SPI把MCTL的bit0 SPIEN置1就能切换SPI通信。SPI读数据的速度比I2C快很多在需要高采样率的场景下值得一试。我个人的使用体验是MMA7455虽然从参数上看不如新一代传感器亮眼但它接口简单、文档齐全、例程多对做底层驱动学习和快速原型验证来说仍然是一块好料子。把I2C通信、寄存器配置、补码处理、角度解算这一条链路跑通你对数字传感器的理解会上一个台阶后面再写MPU6050、ICM20602这类更复杂的传感器思路都是通用的。本文还有配套的精品资源点击获取