尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32+HX711拉力传感器称重方案:从原理到代码全解析

STM32+HX711拉力传感器称重方案:从原理到代码全解析 做嵌入式这几年称重、测力、扭矩这类需求我接过不少——电子秤、拉力测试机、按钮手感压力检测、料仓称重甚至宠物自动喂食器里都藏着一颗测力芯片。ST的MCU配上HX711这个方案几乎是我碰到这类需求时最先想到的组合。这篇文章就从一个最简单的项目说起STM32读一个HX711再接一只拉力传感器5分钟把拉力数据采回来代码完整给到直接抄作业。别被“拉力传感器”这个名字唬住它本质上就是一颗特殊的电阻应变片桥路受力后输出毫伏级的差分电压而HX711就是把这路微弱信号放大、AD转换、再通过两根线送给单片机的桥梁。整套系统的难点不在代码而在接线和标定这两点我会在文章里掰开揉碎讲清楚。适合谁看如果你正在做电子秤、拉力测试、压力监测、张力控制这类项目或者刚接触HX711想快速跑通一个demo这篇文章都能帮你省下不少查手册、试错的时间。我尽量少讲空理论多给能直接用、能复现的干货。1. 项目概述与方案选型思路开始动手前先想清楚一个问题为什么市面上的电子秤、拉力计方案几乎都是“传感器HX711单片机”而不是“传感器仪表放大器高精度ADC”1.1 这套组合能做什么适合谁STM32HX711拉力传感器核心能力就一句话把力变成数字。你可以用它做电子秤的称重数据采集做拉力试验机的实时张力反馈做工业设备上的过载保护甚至做机器人关节的力感应。只要是直流性质的力、静态或低速变化的力这套方案基本都能覆盖。HX711本身是一颗24位ADC芯片内置了低噪声可编程增益放大器PGA专门为桥式传感器应变片、称重传感器、拉力传感器设计。它最省心的地方在于输出接口只有两根线SCK和DOUT和单片机之间走的是类似SPI的时序协议软件上几行代码就能读完一个数据。我接过不少类似项目选型时对比过几种方案一是“仪表放大器独立ADC”比如AD620ADS1232性能确实好但外围电路多、调零和增益设置麻烦成本也高二是“直接用STM32自带的ADC”省了外围但分辨率只有12位而且参考电压纹波稍大一点测力精度就很难看。HX711这种专用芯片正好卡在中间外围电路极简、分辨率足够、价格便宜哪怕只做原型验证也很合适。1.2 为什么选STM32HX711而不是其他组合如果只是做简单电子秤51单片机甚至Arduino都能带HX711这点我承认。但我个人习惯用STM32的原因有三个第一STM32的GPIO翻转速度快时序控制更稳。HX711虽然协议简单但SCK和DOUT的时序要求纳秒级别的稳定用51这类低主频芯片也能跑但用STM32时延时的精度和一致性更好把控尤其在以后要加滤波、加显示、加通信功能时主频余量很重要。第二资源丰富方便扩展。很多测力项目不可能只读一个传感器后面往往还要接OLED屏、WiFi模块、RS485、CAN总线等。STM32的串口、I2C、SPI、DMA都是现成的代码生态也成熟网上参考例程多踩坑成本低。第三工程移植方便。标准库、HAL库都有对应的例程同一套代码从F103换到F407、G4等系列改动量很小。如果你以前用标准库后面想切HAL库HX711的驱动部分其实只需要改GPIO初始化的几行。当然如果项目极其简单比如只需要一个电子秤显示重量用一个几毛钱的STC单片机也完全没问题。方案选型没有绝对优劣看你的扩展需求和团队技术栈。1.3 从电子秤到工业测力的应用场景这套组合能做的东西比很多人想象中多。我大体整理过几类常见场景电子秤/厨房秤单点标定就能达到不错的精度量程覆盖几十克到几十千克。拉力试验机配合电机拖动实时采集拉力值判断材料断裂点采样率虽然不如专用采集卡高但做教学演示和小型设备完全够用。张力控制比如线缆绕线机、纺织机的张力调节拉力传感器作为反馈元件。料仓/液位监测通过称重传感器测重量间接算液位或料位比超声波方案更稳定不受粉尘影响。机器人关节力反馈小型机械臂末端装拉力或压力传感器做碰撞检测和力矩控制HX711作为低成本传感方案。说这些是为了让你心里有个底这个demo不是玩具它背后的架构和原理是可以直接往工业项目上迁移的。你只要把当中有共性的部分——传感器类型、精度需求、接口协议、滤波策略——搞清楚换个传感器量程、换颗主控芯片就又是一套新产品。2. 硬件连接与物料清单硬件连接是很多人翻车的地方。HX711和传感器的接线其实不难难在不知道原理、凭颜色瞎接结果读出来的数值要么是满量程乱跳要么完全没反应。2.1 接线前先搞懂拉力传感器的信号特性拉力传感器内部的敏感元件是一个或多个电阻应变片组成的惠斯通电桥。电桥的四个桥臂上应变片在受力时电阻发生变化导致电桥输出一个与受力成正比的差分电压。但注意这个电压通常是毫伏级别的比如2mV/V的含义是当激励电压为5V、满量程受力时输出差分电压只有10mV。这么微弱的信号直接用单片机ADC去采基本等于白搭所以中间必须有高增益、低噪声的放大器。HX711内置的PGA可以在128倍增益下工作把这10mV级别信号放大到1V多24位ADC才能采出有意义的数值。选拉力传感器时要注意几个参数量程传感器能承受的最大力。建议实际使用量程保持在传感器量程的50%-80%不要长期工作在满量程附近否则线性度和寿命都会恶化。激励电压常见有5V、10V。HX711模块一般输出5V激励电压所以选传感器时要确保它的推荐激励电压范围包含5V。输出灵敏度常见2mV/V或1mV/V灵敏度低的传感器在超量程时可能输出很高但微弱信号的分辨率会掉。接线颜色国内常见的拉力传感器线色是红色接E、黑色接E-、白色接A-、绿色接A。但不同厂家可能有差异最靠谱的方法是看传感器自带的说明书或者用万用表测桥路电阻判断。2.2 HX711模块引脚与工作时序基础市面上的HX711模块引脚大同小异一般有VCC、GND、SCK、DOUT四个信号引脚有些还引出E、E-、A、A-这类接传感器的接线端子。HX711上电后DOUT输出高电平表示传感器数据还在转换中。当一次转换完成DOUT会输出低电平这时候单片机可以通过SCK引脚来移出数据在SCK上产生24个高脉冲每个脉冲的下降沿读取DOUT电平组合成一个24位有符号数紧接着第25个脉冲用来控制接下来的增益和通道选择。如果第25个脉冲存在表示下一个采集周期走的是通道A、增益128如果给两个脉冲表示通道B、增益32如果再给一个回到通道A、增益64。这个时序是HX711的核心后面讲代码时我会再详细展开。这里你只需要记住一件事第25个脉冲不是数据位而是“设置键”它决定下一轮读数的放大倍数和通道。所以代码里读完24位之后一定要记得补一个SCK脉冲。2.3 接线图和电平匹配注意事项整个系统的接线非常简洁拉力传感器E接红线E-接黑线A接绿线A-接白线。如果接反了A和A-读出来的数据会变成负值正常现象把线换回来就行。HX711模块VCC接STM32的5V引脚GND接GNDSCK接一个普通GPIODOUT接另一个普通GPIO。STM32如果板子上没有5VHX711模块可以接3.3V只是传感器的激励电压低了满量程输出差分信号也会按比例降低分辨率会略受影响但仍然能工作。这里有个特别容易踩的坑HX711模块的工作电压范围虽然是2.6V到5.5V但它的逻辑电平是按供电电压来的。如果模块用5V供电DOUT输出高电平就是5V而STM32的GPIO不一定耐受5V——F103系列的大部分引脚可以直接兼容5V输入但有些新系列不行。稳妥的做法是要么模块和STM32都用3.3V供电要么在DOUT到STM32之间串一个1kΩ电阻或者用分压电阻把电平降到3.3V。我的建议是开发阶段直接用3.3V供电跑通逻辑做产品时再根据实际供电环境评估是否需要电平转换。道理很简单开发阶段少一个变量就少一个问题。3. 核心原理与软件设计HX711的软件驱动是整个项目的灵魂。你不需要完全搞懂内部的Σ-Δ ADC原理但一定要明白数据是怎么读出来的、为什么会有噪声、滤波怎么加才合理。3.1 HX711是怎么把微弱信号变成数字量的HX711内部集成了一个24位Σ-Δ ADC。Σ-Δ ADC和普通的逐次逼近型ADC不太一样它通过过采样和噪声整形把一个分辨率要求很高的转换用很低的硬件成本实现。简单理解它是用“时间换精度”——以很高的采样速率对输入信号反复比较再经过数字滤波器输出一个高分辨率的稳定值。HX711的可编程增益放大器提供三档增益通道A支持增益64和128通道B固定增益32。对于大多数拉力传感器建议用通道A和增益128因为传感器输出信号太微弱需要高增益才能把信号幅度拉到ADC的合理量程范围内。有人可能会问增益128会不会把噪声也放大了确实是所以HX711模组在设计时已经考虑到了噪声抑制问题内部有低噪声放大器。真正影响噪声的往往是外部供电和走线这在后面排查章节会详细说。3.2 数据读取时序SCK和DOUT的那点事HX711的数据读取时序用文字描述是这样的等待DOUT从高电平变成低电平表示转换完成。DOUT拉低后单片机在SCK上输出高脉冲。在SCK高电平期间单片机读取DOUT的电平。DOUT电平如果为高则当前位为1否则为0。依次读完24个位组合成一个有符号整数。再输出1个额外脉冲设置下一次转换的通道和增益。这个时序本质上就是一个同步串行读取过程。我见过有人把它类比成“SCK是时钟DOUT是数据线每个时钟边沿吐一个bit”。这个类比没问题实际操作时唯一要注意的是时序图上SCK高电平必须保持足够长的时间不能太短。看HX711手册SCK高电平和低电平的脉宽都要求不小于100ns在STM32的主频下只要不是用那种编译器优化都搞不定的极简延时基本都满足。我在编写代码时习惯用标准库GPIO来做时序控制因为GPIO的翻转时间非常可控而且代码可读性好。有些STM32HAL的库会拖慢GPIO翻转速度但HX711对这种纳秒级时序要求其实很宽容所以HAL库也能用。3.3 滤波与抗干扰数值乱跳怎么办24位ADC听起来很美好但实际在读取时最后几个bit通常会跳动。这很正常因为传感器的模拟信号、电源纹波、PCB走线干扰都会叠加到转换结果上。HX711在10Hz输出速率下噪声会更低而80Hz或1000Hz速率下噪声会明显增大。如果读数跳动影响使用我常用的处理方法是移动平均滤波。简单的做法是连续读5到10次去掉最大值和最小值再做算术平均。这样可以在保留动态响应的前提下把随机跳变压下去。有人会为了追求高精度把滤波窗口开得很大比如采样50次取平均但那样重量响应的“手感”会变肉实时性差。实际项目中我一般采样次数控制在8到16之间标定时用大窗口测量时用小窗口。4. 完整代码实现与移植下面直接进入到最接地气的部分完整代码。以下代码基于STM32标准库编写使用F103系列如果你用HAL库改动量不大主要就是GPIO初始化和延时函数要换一下。4.1 工程结构与GPIO初始化本例中HX711的SCK接PB0DOUT接PB1。如果你用其他引脚只需要修改宏定义。#include stm32f10x.h #include stdio.h #define HX711_SCK_PORT GPIOB #define HX711_SCK_PIN GPIO_Pin_0 #define HX711_DOUT_PORT GPIOB #define HX711_DOUT_PIN GPIO_Pin_1 #define HX711_SCK_HIGH() GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN) #define HX711_SCK_LOW() GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN) #define HX711_DOUT_READ() GPIO_ReadInputDataBit(HX711_DOUT_PORT, HX711_DOUT_PIN)GPIO初始化部分注意SCK必须是推挽输出DOUT必须是上拉输入。很多新手把DOUT配成浮空输入也能读到数据但上拉能让逻辑电平更稳定减少误判。void HX711_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin HX711_SCK_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(HX711_SCK_PORT, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin HX711_DOUT_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(HX711_DOUT_PORT, GPIO_InitStructure); HX711_SCK_LOW(); }4.2 HX711读取函数这是最核心的一段代码。它严格按照3.2节描述的时序读取数据。long HX711_Read(void) { unsigned char i; long value 0; while (HX711_DOUT_READ()); // 等待DOUT拉低表示数据就绪 delay_us(5); for (i 0; i 24; i) { HX711_SCK_HIGH(); delay_us(1); value value 1; if (HX711_DOUT_READ()) { value; } HX711_SCK_LOW(); delay_us(1); } // 第25个脉冲选择通道A、增益128 HX711_SCK_HIGH(); delay_us(1); HX711_SCK_LOW(); delay_us(1); return value; }读取出来的value是一个24位有符号数。由于C语言中long类型至少是32位当我们把第24位的数据移位到最高位时负数在补码表示下自然就是负数。比如传感器受压力时某些接法下读出来可能就是负数这没关系后面标定环节会统一处理。要注意的一点是代码开头的while (HX711_DOUT_READ());是一个死等循环。如果HX711没有正常工作比如没接传感器或接线错误DOUT可能一直为高程序就会卡在这里。在产品级代码里这里最好加一个超时机制防止整个系统卡死。示例代码如下long HX711_Read(void) { unsigned char i; long value 0; unsigned int timeout 0; while (HX711_DOUT_READ()) { if (timeout 1000000) { return 0; // 返回0并退出避免死循环 } } // ... 后续读取过程不变 }4.3 标定与重量计算读取到原始数据后还不能直接显示重量。因为每个传感器、每个HX711模块、每次接线的零点偏移和增益都不同必须做标定。标定的原理很简单先记下空载时的读数作为零点再放上已知重量的标准物读出变化量计算出“每单位重量对应的码值变化量”也就是标定系数。下面是完整的标定代码#define CAL_WEIGHT 1000.0f // 标定砝码重量单位g long zero_offset 0; float scale_coeff 1.0f; void HX711_Calibrate_Zero(void) { long sum 0; int i; for (i 0; i 20; i) { sum HX711_Read(); delay_ms(10); } zero_offset sum / 20; } void HX711_Calibrate_Scale(void) { long sum 0; int i; long cal_value; // 标定时在传感器上放置已知重量的砝码 for (i 0; i 20; i) { sum HX711_Read(); delay_ms(10); } cal_value sum / 20; // 计算系数(读数差) 对应多少重量 scale_coeff (float)(cal_value - zero_offset) / CAL_WEIGHT; }标定完成后测量时只需要读取当前码值减去零点再除以标定系数就得到当前的真实重量float HX711_GetWeight(void) { long sum 0; int i; long raw; float weight; for (i 0; i 8; i) { sum HX711_Read(); delay_ms(5); } raw sum / 8; weight (float)(raw - zero_offset) / scale_coeff; if (weight 0) { weight 0; } return weight; }这里省去了一个优化点实际项目中我会把零点偏移和标定系数保存在STM32内部Flash里这样设备掉电再上电时不需要重新标定。但如果只是做demo每次上电重新标定也够用。4.4 主循环与串口输出我们把上面几块整合到主函数里加上串口输出方便直接在串口助手上观察数据。int main(void) { float weight; delay_init(); USART1_Init(115200); HX711_GPIO_Init(); printf(HX711 Demo Start...\r\n); // 上电预热等待传感器稳定 delay_ms(500); // 空载标定 HX711_Calibrate_Zero(); printf(Zero Offset: %ld\r\n, zero_offset); // 放入标准砝码后按一下复位键或按键触发标定 printf(Place Calibration Weight, then press reset...\r\n); delay_ms(3000); HX711_Calibrate_Scale(); printf(Scale Coeff: %f\r\n, scale_coeff); while (1) { weight HX711_GetWeight(); printf(Weight: %.1f g\r\n, weight); delay_ms(200); } }这段代码用了一个比较笨的标定触发方式先等3秒这3秒内你赶紧把砝码放到传感器上然后调用标定函数。实际项目中可以用按键触发标定或者通过串口命令触发代码逻辑完全一样。如果你手上的传感器量程比较大比如是500kg的拉力传感器显示单位用g会很不直观可以在最后输出重量时除以1000显示为kg。另外如果测力单位是牛顿标定砝码就换成标准力值来标定本质上也是同一条公式。5. 常见问题与排查技巧实录这个章节是我最想写的。这些坑我在项目里几乎都踩过每次排查都要翻手册、查时序、量电平耽误不少时间。整理出来你照着检查能少走很多弯路。5.1 读数恒为0或满量程的排查链路先给出一个排查顺序按这个顺序检查基本能覆盖90%的问题第一检查DOUT是否一直为高。如果是说明HX711没有进入数据就绪状态。先量模块的VCC和GND电压确认供电正常再量E和E-之间的电压正常应该在3.3V到5V之间。如果E和E-没有电压说明传感器没接好或模块坏了。第二检查STM32和HX711是否共地。有些模块自己带了一个稳压芯片单独给它供电时不和STM32共地就会导致信号电平识别异常。解决办法是GND连在一起。第三确认GPIO初始化的SCK是推挽输出DOUT是输入模式。DOUT如果被误配成复用功能或者没有使能GPIO时钟程序就可能卡死或读到全0。第四检查接线颜色。我曾经遇到过一批传感器厂家把红绿线定义反了导致怎么读都是满量程。用万用表测E和A之间电阻再对比传感器说明书里的桥路电阻能快速确认线序。常见问题速查表如下现象可能原因解决措施程序卡死在等待DOUT传感器没接/供电异常/模块损坏检查供电与接线换模块验证读数恒为0传感器A/A-接反或激励电压缺失交换A/A-检查E/E-电压读数恒为满量程跳动传感器线序错误/输入信号悬空对照说明书核对线序确认桥路电阻读数漂移严重供电纹波大/未共地/长线干扰检查地线加滤波电容缩短信号线5.2 数值漂移和温漂问题HX711配传感器上电前几分钟数值会缓慢漂移这是很正常的现象。应变片传感器在通电后自身会因为电流热效应发生微小的温度变化导致电阻值变化表现就是零点漂移。我的做法是正式测量前让系统预热30秒到1分钟在预热后再执行零点标定。如果设备长期运行可以每隔几个小时自动重新做一次空载标定校正零点漂移。温度变化对传感器的影响也是不可忽视的。同样一块传感器夏天和冬天的零点可能差不少满量程输出也会有细微偏差。如果产品使用环境温差大建议在程序里做温度补偿但那是另外一个话题了。对于大多数室内应用标定后保持环境温度稳定精度完全够用。另外拉力传感器通常只允许承受垂直方向的拉力安装时如果引入了侧向力或者传感器底部固定不牢会导致读数明显波动和非线性。这个不是软件能解决的安装结构一定要稳。5.3 采样速率选择与动态测量细节HX711默认模块上的RATE引脚会设置输出速率常见有两种10Hz和80Hz。有些模块支持通过焊盘切换有些固定一种。10Hz模式噪声最小适合静态称重80Hz模式响应更快适合动态张力测量但噪声会稍大。如果你要做动态测量比如拉力试验机建议把采样速率调到80Hz甚至更高的档位。但要注意HX711在80Hz时每个数据转换时间约12.5ms如果你的主循环里还要做显示、通信、存储等操作实际采样间隔可能不稳定导致曲线看起来有毛刺。解决办法是用定时器中断去读HX711把采集频率稳定下来主循环只处理数据。我做过一个拉力测试项目主循环里读HX711每次都卡在等待DOUT上导致整体响应速度不稳定。后来改成定时器里调用HX711_Read()把数据放到一个全局变量中主循环只做显示和判断整个系统的采样间隔就稳定了。还有一个细节在读取HX711数据时SCK脉冲期间如果被中断打断可能会导致第25个脉冲错位进而影响下一次转换通道和增益设置。所以如果系统里开了很多中断最好在读取HX711的关键时序段关闭中断或者用临界区保护一下。在实际项目中我一般用一条简单的__disable_irq()和__enable_irq()包住读取过程防止时序被破坏。最后再分享一个小技巧如果你发现读取出的数值在零点附近总是微幅抖动不用急着加复杂滤波。先检查传感器是否锁紧、桌面是否晃动很多“软件问题”其实都是结构问题。软件滤波是最后一道防线不能替代良好的机械安装和稳定的供电环境。这套STM32HX711方案我前前后后在四五个项目里用过。从最早跑通的电子秤demo到后来改造成车间用的拉力检测仪底层驱动代码几乎没有大改过变的只是标定流程、滤波策略和数据展示方式。这大概也是它值得写一篇文章的原因——基础架构足够稳扩展起来又很顺手。
返回列表