STM32F103C6T6驱动TM7711:24位ADC称重传感器调试实战

📅 发布时间:2026/9/1 7:39:52
STM32F103C6T6驱动TM7711:24位ADC称重传感器调试实战
简介面向使用STM32F103C6T6微控制器配合24位模数转换器TM7711进行高精度数据采集的开发者覆盖从底层驱动到应用层的完整工程实现。压缩包内共218个文件以C源代码和H头文件为主体配合编译中间文件、链接脚本与工程配置文件整体大小约66MB目录结构清晰可在STM32CubeIDE中直接导入查阅。该工程重点演示了HAL库环境下ADC外设的初始化、TM7711读取时序、串口数据输出等环节并附带一款串口调试工具方便实时观察转换数值。与常规12位ADC相比24位分辨率能够捕捉更微弱的模拟信号变化适用于称重、应变、温度等传感器数据采集场景示例中从时钟配置、GPIO设定到数据解析的完整流程为工程师提供了可复用的代码框架和排错思路适合作为学习高精度ADC设计及二次开发的参考。目前已有859人学习下载受到嵌入式开发者关注。 刚拿到STM32F103C6T6-TM7711.rar这个工程包时我以为是网上常见的把 HX711 驱动改个引脚名就发出来的换皮代码。真正打开、接线、通电、读到数据之后才发现 TM7711 这颗 24 位高精度 ADC 和 STM32F103C6T6 配合起来水比想象中深不少。这颗芯片在电子秤、拉力传感器、气压传感器这类小信号采集场景里出镜率越来越高但网上资料要么只讲 HX711要么把 TM7711 的时序讲得含糊。这篇文章我就把这次从选型到调试的完整过程写清楚重点是那些数据手册里没写透、却直接影响数据稳定性的细节。1. 为什么是 STM32F103C6T6 搭 TM7711从选型逻辑说起1.1 TM7711 解决了什么痛点做称重或压力采集的工程师基本都绕不开 HX711。HX711 便宜、资料多、用的人多但它的短板也很明显内部 LDO 精度一般噪声水平在要求高的场合不太够看而且只有一个差分输入通道做多路采集就得外扩。TM7711 同样是 24 位 Sigma-Delta ADC但它内置了可编程增益放大器PGA增益档位可调配合桥式传感器时可以把微伏级别的信号直接放大到 ADC 能分辨的范围。更重要的是它的串行通信接口只有两根线DOUT 负责数据输出和状态指示SCLK 负责时钟输入这跟 HX711 的接法非常接近硬件的迁移成本很低。1.2 STM32F103C6T6 的定位与取舍有人会问现在有那么多新款 MCU为什么还要用已经烂大街的 F103C6T6我的回答是这个项目场景根本用不上新东西。STM32F103C6T6 基于 Cortex-M3 内核主频 72MHzFlash 32KBRAM 10KB封装是 LQFP48价格在 F103 系列里属于最便宜的那一档。对 TM7711 这种低速传感器读取场景72MHz 主频绰绰有余32KB Flash 放一个完整的称重固件加上标定算法也够用。选它还有一个现实原因F103 的生态太成熟了标准外设库、HAL 库、各种开源工程随手就能找到出了问题排查起来效率高。新手和老手都能快速上手没必要为了追新而追新。注意F103C6T6 的 Flash 只有 32KB比常见的 C8T664KB少一半。如果你把 HAL 库全套拉进来再跑 FreeRTOS空间会比较吃紧。我自己实际用的时候直接以寄存器或标准外设库方式操作 GPIO把 Flash 占用控制在 20KB 以内留足余量。2. TM7711 的数据手册没明说但你必须知道的通讯细节2.1 引脚定义与信号时序TM7711 的通讯接口是两个引脚VCC 供电DOUT 数据输出SCLK 时钟输入。第一次上电后芯片需要一段稳定时间这段时间 DOUT 保持高电平。当一次转换完成DOUT 会主动拉低告诉主机数据准备好了。MCU 看到 DOUT 变低后开始在 SCLK 上输出脉冲每一个脉冲的下降沿或上升沿取决于你的采样时机读取一位数据一共读 24 位这 24 位就是 ADC 转换结果的二进制补码。这里有一个非常容易踩的坑第 25 个脉冲的作用。当你把 24 位数据读完之后紧接着再发一个 SCLK 脉冲这个脉冲会触发芯片切换下一次转换的通道或者增益设置。也就是说如果你只想读数据不想改增益这个第 25 个脉冲也必须发否则芯片会不知道你什么时候结束DOUT 释放和下一次数据的准备时序就会乱掉。很多从 HX711 移植过来的代码在这里栽过跟头因为 HX711 同样有第 25 个脉冲机制但有些精简版代码直接跳过了这步导致数据偶发错位。2.2 通道和增益的软件联动关系TM7711 内部支持不同的增益配置这些配置由第 25 个脉冲之后芯片内部逻辑决定具体映射关系要看芯片型号后缀。以我手头这颗 TM7711 为例它支持两个差分输入通道通道 A 和通道 B其中通道 A 的增益档位比通道 B 高适合接电桥传感器通道 B 增益低适合接一些输出幅度较大的传感器。在实际编程中通道和增益的选择不是简单配置某个寄存器而是通过你每次读取时发的脉冲个数来暗示芯片。这一点和传统 SPI/I2C 设备完全不同很多人在这一步卡了很久——他们以为像 I2C 一样写一个寄存器地址就行实际上 TM7711 是在每次完整的读取周期里通过最后一个脉冲来设置下一轮转换的状态。理解了这一点你的驱动代码就会写得非常简洁循环里直接做读 24 位 发第 25 个脉冲然后把通道切换的逻辑编成另一个函数和读取过程解耦。3. 硬件连接与电路布局的几个关键细节3.1 最小系统接线STM32F103C6T6 和 TM7711 的连线非常少硬件上真正考验反而不是接线而是供电和地线的处理。我的接法是STM32F103C6T6 的 PA0 接 TM7711 的 DOUTPA1 接 TM7711 的 SCLKTM7711 的 VCC 接 3.3V 电源AVDD 和 DVDD如果芯片分开供电的话都接同一个 3.3V但中间加磁珠或 10Ω 电阻隔离VREF 参考电压引脚如果芯片引出接一个高精度基准源或者直接从 3.3V 经过 RC 滤波后接入传感器差分输出接到 TM7711 的 VINP 和 VINN注意极性不能反否则读出来的值会是负数。一个很多人忽略的点是TM7711 的 DOUT 和 SCLK 在 MCU 端最好配置为推挽输出和浮空输入或上拉输入。DOUT 是芯片推挽输出直接连 STM32 的输入引脚没问题SCLK 是芯片的输入你用 STM32 推挽输出驱动它即可。不要在这两根线上加太大的外部上拉电阻因为 TM7711 内部已经有上拉逻辑外部再并联一个电阻会改变电平阈值和上升时间极端情况下导致时序错误。3.2 模拟电源滤波与地线处理TM7711 是 24 位 ADC对电源纹波和地弹非常敏感。如果你直接用 STM32 开发板上的 3.3V 给 TM7711 供电同时电机、继电器或者数码管也在跑那读出来的数据大概率会在低位跳个不停。我的经验是TM7711 的供电必须单独走一条线从电源入口处分出来经过一个 10μF 钽电容 0.1μF 陶瓷电容并联滤波再进芯片的电源引脚。地线方面传感器、TM7711、STM32 的地不要形成环路尽量用单点接地的方式。还有一点是关于 SCLK 频率的。TM7711 的 SCLK 虽然理论上支持挺高的频率但在实际布线和 MCU GPIO 翻转速度都很快的情况下SCLK 边沿太陡会把噪声耦合到 DOUT 上。我试验过STM32 的 GPIO 翻转速度设置为 2MHz 低速模式就够用读一次数据可能也就几百微秒完全不影响实时性但数据稳定性提升明显。4. 驱动代码的核心逻辑和移植要点4.1 GPIO 模拟时序的底层实现TM7711 的驱动本质上就是 GPIO 模拟时序和软件 I2C 有点像但更简单。以下是我在 STM32F103C6T6 上验证过的核心代码使用标准外设库编写#define TM7711_SCLK_PORT GPIOA #define TM7711_SCLK_PIN GPIO_Pin_1 #define TM7711_DOUT_PORT GPIOA #define TM7711_DOUT_PIN GPIO_Pin_0 // 读取一次转换结果channel_gain 控制下一次转换的通道/增益 // 返回值是 24 位有符号数负数以补码形式出现 int32_t TM7711_Read(uint8_t channel_gain) { int32_t raw 0; uint8_t i; // 等待 DOUT 变低表示数据已就绪 uint32_t timeout 100000; while (GPIO_ReadInputDataBit(TM7711_DOUT_PORT, TM7711_DOUT_PIN) ! Bit_RESET) { if (--timeout 0) { return -1; // 超时芯片可能未上电或损坏 } } // 读取 24 位数据高位在前 for (i 0; i 24; i) { GPIO_SetBits(TM7711_SCLK_PORT, TM7711_SCLK_PIN); // 这里稍微延时确保时钟高电平时间足够 delay_us(1); raw 1; if (GPIO_ReadInputDataBit(TM7711_DOUT_PORT, TM7711_DOUT_PIN) Bit_SET) { raw | 1; } GPIO_ResetBits(TM7711_SCLK_PORT, TM7711_SCLK_PIN); delay_us(1); } // 第 25 个脉冲设置下一次转换的通道/增益 GPIO_SetBits(TM7711_SCLK_PORT, TM7711_SCLK_PIN); delay_us(1); GPIO_ResetBits(TM7711_SCLK_PORT, TM7711_SCLK_PIN); delay_us(1); // 若是负值做符号扩展 if (raw 0x800000) { raw | 0xFF000000; } return raw; }这段代码的精髓不在于长短而在于每个细节都到位超时保护、MSB 先读、符号扩展。超时保护尤其重要因为在系统刚上电或者传感器断线时DOUT 可能长时间不拉低没有超时机制的程序会死等导致整个主循环卡死。4.2 数据滤波与平稳度优化读到的 24 位原始数据只是第一步要变成稳定的称重结果滤波是绕不开的。我在项目里用了两级处理第一级是滑动平均滤波窗口长度取 8 或者 16第二级是一阶低通滤波系数根据需要的响应速度调整。滑动平均能快速把突发噪声压下去低通滤波能抑制持续性的小幅抖动。#define FILTER_WINDOW 16 static int32_t filter_buf[FILTER_WINDOW]; static uint8_t filter_index; // 滑动平均滤波 int32_t TM7711_Filter(int32_t new_value) { int32_t sum 0; uint8_t i; filter_buf[filter_index] new_value; filter_index (filter_index 1) % FILTER_WINDOW; for (i 0; i FILTER_WINDOW; i) { sum filter_buf[i]; } return sum / FILTER_WINDOW; }这里有个容易被忽略的坑如果你的传感器信号是缓慢变化的比如重量慢慢增加滑动平均窗口太大会让响应变慢实时性变差。对电子秤这种静态称重场景窗口 16 没问题但如果是做动态力监测窗口就得缩到 4 或者更小否则峰值会被抹平。根据场景调参数比盲目追求窗口越大越好要靠谱得多。5. 实测中的问题排查与校准经验5.1 零漂和温漂的应对方法上电后第一次读到的数据往往不是零而是几百甚至几千的偏移量这是正常的。处理方法是上电后做一次去皮或者零点校准在传感器空载时读取 N 次数据取平均把这个平均值存为 zero_offset以后每次读取都减去这个值。我习惯在初始化完成后延时 1 秒再校零因为芯片和传感器刚刚上电时有一个预热过程内部基准和传感器应变片都还没进入稳定状态。温漂的问题更隐蔽。传感器和 ADC 的温度特性叠加后零点可能随着环境温度变化慢慢移动。如果你做的是工业级产品建议在固件里增加温度补偿用一颗温度传感器比如 NTC 或数字温度芯片同时采集温度建立零点偏移和温度之间的线性关系然后每读一次数据就做一次补偿。这个功能看起来复杂但其实就是一个一元线性回归查表的事成本几乎为零对精度提升非常明显。5.2 我踩过的三个时序坑第一个坑是 SCLK 高电平时间太短。我之前优化代码时把delay_us(1)去掉改用 GPIO 直接翻转结果数据读出来全是乱的。原因是 SCLK 从低到高的跳变瞬间TM7711 内部逻辑还在更新数据线上的数据如果主机立刻采样 DOUT很可能采到的是上一位的电平。后来我在时钟上升沿之后加了一个__nop()级别的延时问题立刻解决。第二个坑是第 25 个脉冲的通道/增益设置顺序。我一开始把通道切换逻辑写在读取函数外部每次读取后单独再发一个脉冲结果发现数据偶尔跳到另一通道的满量程值。后来才意识到第 25 个脉冲里的通道/增益信息是用来控制下一次转换的也就是说你这个周期读到的数据是上一个周期设置的参数决定的。把代码改成先设置再读取的顺序后逻辑就顺了。第三个坑是最常见的DOUT 一直为高导致超时。排查了半天发现是 TM7711 的供电电压只有 2.8V低于数据手册建议的最低工作电压芯片根本没启动。很多时候问题出在供电而不是代码大家排查时先量电压再查时序方向对了才能省时间。最后说个实用技巧TM7711 的原始数据中最低几位通常是不稳定的噪声位在做高精度称重时可以先把数据右移 3 到 4 位丢掉无效低位再做滤波和标定。这样既能减少噪声干扰又能提高滤波效果的直观表现。我之前做的一个小量程电子秤项目采用这个方案后静置状态下最后一位基本稳定不需要额外的硬件屏蔽措施。本文还有配套的精品资源点击获取