STM32外接HX711称重传感器完整教程:从硬件接线到串口输出

发布时间:2026/9/12 21:57:23
STM32外接HX711称重传感器完整教程:从硬件接线到串口输出 简介面向STM32嵌入式开发者的压力传感器工程资源基于HX711高精度24位ADC芯片实现称重数据采集涵盖电阻应变式传感器的信号调理、差分放大、AD转换及串口输出等完整链路适合学习仪表放大器与嵌入式数据采集的开发者参考。压缩包含211个文件其中大量h头文件、c源文件、o目标文件以及hex、axf等编译产物齐全并附带uvprojx等Keil工程配置整体大小约7.53MB目录结构完整可直接导入工程二次开发。已有606人下载学习。工程设计上省去LCD显示改用USART1配合printf将称重数据发送至串口调试助手波特率115200便于在电脑端实时观察与调试代码中内置称重前清零逻辑确保加载前质量为零同时提供A/B双传感器接线方案并保留完整初始化与主循环流程适合课程设计、电子竞赛或小型称重模块的快速验证。1. 为什么称重要外挂 HX711而不是直接用 STM32 片内 ADC同样是测压力传感器的毫伏级差分电压STM32F103 的 12 位 ADC 在 3.3V 参考下一个 LSB 约 0.8mV而 HX711 内部 PGA 先把信号放大 128 倍再做 24 位 Sigma-Delta 转换实际可分辨电压远低于 0.1mV。直接把称重桥接到 PA1 上空载和 5g 砝码可能只差两三个 LSB噪声一抖就看不出来换上 HX711 后同样负载能拉开几十上百个计数。这就是电子秤模块普遍外挂 HX711 而不是用片内 ADC 的原因。这套工程是“STM32 标准外设库 双压力传感器 HX711 USART1 串口输出”的可编译源码适合基于 STM32 的毕业设计、智能台灯重力感应或把电子秤改成串口数据源。2. 硬件连接PC0/PC1 引脚映射和双压力传感器桥路并联2.1 供电与引脚映射为什么用 3.3V 而不是 5VSTM32F103 的 PC0/PC1 不是 5V 容忍引脚所以这份工程把 HX711 的 VCC 接到 3.3VSCK 和 DT 直接由 PC0/PC1 驱动两边电平一致省掉电平转换。HX711 的 AVDD 由模块内部 LDO 产生常见红色模块会从 VCC 得到约 2.8V 的模拟电源AVDD 的稳定性直接决定称重值会不会漂。上电顺序我习惯先给 STM32 供电再给传感器桥路供电避免热插拔时毛刺灌进 PC1 的输入保护二极管。GPIO 初始化顺序也很关键先打开 GPIOC 时钟再把 PC0 配成推挽输出、PC1 配成上拉输入GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOC, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; GPIO_Init(GPIOC, GPIO_InitStructure);逻辑说明PC0 作为 SCK 输出必须用推挽模式速度最好设到 50MHz否则边沿太慢会让 HX711 采样到不稳定的电平PC1 作为 DOUT 输入上拉输入是为了防止初始化瞬间读到随机电平误触发读时序。参数说明如果换成 HAL 库这段对应HAL_GPIO_Init的Alternate或GPIO_MODE_OUTPUT_PP但 HX711 不需要复用功能普通 GPIO 即可。双传感器接法按原工程的手册整理如下表最容易接反的是 A/A- 两根差分信号信号STM32/HX711 端A传感器B传感器说明VCC3.3V--HX711 数字电源GNDGND--数字地DTPC1--数据输出低电平表示就绪SCKPC0--时钟输入EAVDD端白单传感器接电源黑激励正E-AGND端黑白单传感器接地激励负AA 通道-红差分信号正A-A- 通道红-差分信号负这块接线来自原工程手册。双传感器时 E 并接 A 的白和 B 的黑E- 并接 A 的黑和 B 的白A 接 B 红A- 接 A 红。别把它理解成两个独立的全桥并联这里实际是把两只传感器部分桥臂交叉组合让两路受力信号在 A/A- 之间叠加而不是抵消。单传感器实验时不用的那根线要悬空不能随便接地。2.2 上电前用万用表验证桥路是否接对在不通电的情况下量 E 与 E- 之间两只 350Ω 传感器按上述接法并联后理论值约 175Ω量 A 与 A- 之间约 350Ω。如果量到接近 0Ω说明红线被接到了同一侧必须按桥臂定义重新分线。原始工程里强调“加载前清零前后两次采样值相同”本质就是要求桥路在零负载时差分输出尽量接近 0硬件如果偏到几百微伏软件清零虽然能用但温度一变就漂。项目里传感器线较长时SCK 和 DT 要与电源线分开走最好中间夹一根地线。HX711 的 SCK 对毛刺敏感多收到一个脉冲会让后续 24 位数据整体错位现象就是串口输出的称重值间歇性跳到满量程再跳回。28AWG 以上的杜邦线做短距离实验问题不大超过 30cm 建议改成双绞线或屏蔽线。3. GPIO 模拟 HX711 读时序24 位数据采样与清零实现3.1 读时序拆解和 25 个 SCK 脉冲的增益含义HX711 不用 I2C 也不用 SPI读数据全靠 PC0 的 SCK 负脉冲和 PC1 的 DOUT 电平。数据就绪时 DOUT 从高变低主机收到这个下降沿后连续给出 24 个 SCK 脉冲每个脉冲高电平期间采样 DOUT先读到的是最高位。第 25 个脉冲负责把状态机复位并设置增益为 128。如果给 26 个脉冲就切到增益 3227 个脉冲切到增益 64。SCK 脉冲数通道与增益适用场景25A 通道增益 128称重传感器标准接法26B 通道增益 32某些差分输入模块27A 通道增益 64传感器输出较大时原工程硬件接的是 A/A- 通道所以读函数固定打 25 个脉冲。下面是可编译的简化版本#define HX711_SCK_PORT GPIOC #define HX711_SCK_PIN GPIO_Pin_0 #define HX711_DT_PORT GPIOC #define HX711_DT_PIN GPIO_Pin_1 int32_t HX711_Read(void) { int32_t val 0; uint8_t i; // 产生一个下降沿让 DOUT 进入可移位状态 GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN); GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); // 等待 DOUT 变低表示内部转换完成 while (GPIO_ReadInputDataBit(HX711_DT_PORT, HX711_DT_PIN) SET) ; for (i 0; i 24; i) { GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN); val 1; if (GPIO_ReadInputDataBit(HX711_DT_PORT, HX711_DT_PIN) SET) val | 1; GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); } // 第 25 个脉冲保持增益 128同时让 DOUT 恢复高电平 GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN); GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); // 24 位补码扩成 32 位有符号数 if (val 0x800000) val | 0xFF000000; return val; }逻辑说明循环内先拉高 SCK读一位再拉低val 1让先读到的位最终落在 bit23。第 25 个脉冲不能省省了之后模块不会开始下一次转换下一次读函数会在等待 DOUT 变低时卡死。0x800000是 24 位补码的符号位不处理的话负数会变成接近 1677 万的大正数重量显示会明显异常。参数说明这个函数没有加超时保护如果传感器没接或接触不良DOUT 一直为高程序会死等。实验时建议在while循环里加一个计数上限比如 100000 次后返回上一次读到的值至少让串口还能继续吐数据。HX711 用两根 GPIO 就能读数不占定时器通道这比用 STM32 片内 ADC 还要开 DMA 的方式简单不少。3.2 清零为什么必须“前后两次采样值相同”原工程说明里有一句“称重前首先清 0即 HX711 的前后两次采样值相同保证加载前质量为 0”。这个要求实现起来不复杂int32_t tare_value 0; void HX711_Tare(void) { int32_t first HX711_Read(); int32_t second HX711_Read(); // 两次读数一致说明桥路稳定直接作为零位 if (first second) tare_value first; else tare_value (first second) / 2; } int32_t HX711_GetWeight(void) { return HX711_Read() - tare_value; }逻辑说明HX711 每次转换结果都带有量化噪声如果连续两次采样完全一致说明传感器桥路在这个时刻的输出非常稳定把它存为零位才有意义。两次不一致时取平均只是兜底不代表硬件已经稳定。工程里应该先调用两次读函数再判断不能在第一次读数之前清零。很多基于 STM32 的称重项目都栽在这一步刚上电时 HX711 还没完成第一次转换直接读容易卡死或者读到一个跳变的初始值后面所有重量都带上这个偏差。参数说明对于 10SPS 的模块两次采样间隔约 100ms如果系统里还有 LCD 或别的外设占时间要保证调用 Tare 前至少给了 200ms 延时让传感器从激励建立到输出稳定。做智能台灯坐姿检测时也是一样上电时如果台灯在充电充电器带来的共模噪声会让零位偏大所以实际产品里零位标定要在空秤状态下持续采样 1 秒再写入 Flash。3.3 原始码转重量标定系数怎么取读回来的val只是 ADC 原始码和克数之间的关系要靠标定系数连接。常见做法是先清零再放标准砝码得到“计数差 / 克数”的斜率float scale 0.0f; // 单位码/克 float weight (float)(HX711_GetWeight()) / scale;逻辑说明这里scale的单位是码每克用 HX711_GetWeight() 返回减去零位后的计数差再除以砝码克数得到斜率。参数说明放砝码前一定要先执行 HX711_Tare()否则 scale 会把零位偏差也纳入标定导致小重量区间的线性变差。例如空载零位是 823400放 1000g 后读 843400那么 scale 20000 / 1000 20.0 码/克。注意这个 scale 只在同一电源电压和同一温度下有效AVDD 变了后需要重新标定。我一般把 scale 和 tare_value 都定义成全局变量方便后续用串口指令在线修改而不是每改一次参数就重新编译下载。4. USART1 串口输出替代 LCD 显示printf 重定向和称重排错4.1 为什么放弃 LCD改走 USART1原工程里本来有 lcd.c但 LCD 要占掉一整套 GPIO并且刷新过程会拖慢称重循环。改成 USART1 后只需要 PA9 一根 TX 线就能把数据送到电脑。串口调试助手最大的优势是能连续记录数据再用 Excel 打开分析漂移和重复性这是 LCD 做不到的。工程里直接使用 printf 输出重量标准外设库下需要重写 fputcint fputc(int ch, FILE *f) { USART_SendData(USART1, (uint8_t)ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET) ; return ch; }逻辑说明USART_FLAG_TXE表示数据寄存器空寄存器空时才能写入下一个字节这个等待必须放在每发送一个字符之后否则高波特率下会丢字符。参数说明如果等待TC标志也能工作但TC要等移位寄存器完全送出速度会慢很多。原工程手册明确要求串口调试助手波特率改为 115200如果还是默认 9600电脑端会出现整行乱码或数据错位。然后是 USART1 初始化代码只发不收void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitStructure.USART_BaudRate 115200; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode USART_Mode_Tx; USART_Init(USART1, USART_InitStructure); USART_Cmd(USART1, ENABLE); }逻辑说明这个函数只初始化 USART1 的 TX 引脚PA9 配成复用推挽打开 USART1 时钟配置 115200-8-N-1只启用发送模式。参数说明如果后面想通过串口指令触发清零就把USART_Mode改成USART_Mode_Rx | USART_Mode_Tx并打开USART_IT_RXNE。Keil 工程里还要勾选 MicroLIB或者处理半主机模式否则 printf 会在启动后进 HardFault。4.2 主循环里的清零策略前面 3.2 的 Tare 只是一次性清零实践里不要只在代码开头调用一次。台秤场景下用户可能随时把重物放上再取下零位会变化。常见的做法是接受串口单字符指令t触发清零while (1) { if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) ! RESET) { if (USART_ReceiveData(USART1) t) { HX711_Tare(); printf(zero ok\r\n); } } printf(weight: %.1f g\r\n, weight); delay_ms(200); }逻辑说明主循环检查 RXNE 标志有数据就读出来和字符t比较清零后再打一条确认。因为没有开中断USART_ReceiveData本身会清掉 RXNE 标志。参数说明weight用全局变量保存由HX711_GetWeight()除以 scale 得到如果把读重量放在 printf 参数里执行要注意调用顺序避免先打印后读数造成一帧滞后。判断时如果秤台上已经放了东西清零会把货物重量当零位所以产品上要加“稳定判断”或物理按键不让误触。4.3 串口读数为 0、跳变、满量程的排查顺序下表是我在调试多个 HX711 工程时积累的经验核心思路是先区分是硬件问题还是时序问题现象可能原因检查点一直输出 0读函数卡死或 SCK 没翻转用示波器看 PC0 是否有脉冲DOUT 是否被拉低数值随机跳几十万SCK 收到毛刺数据错位检查传感器线是否太长SCK 是否与电源线平行固定输出 0x7FFFFF 或 0x800000传感器 A/A- 接反或信号未接入万用表量 A/A- 之间电压零负载应接近 0V空载稳定放重物后漂移AVDD 供电不足或滤波电容老化在 HX711 模块的 AVDD 与 AGND 间并联 100uF 电解电容最容易被忽略的是接线表里红线的定义因为不同厂家的称重传感器颜色标准并不完全一致。如果 A/A- 量到的是反相电压把交叉的两根红线互换后再测试即可。5. HX711 采样率、滑动滤波和标定斜率的工程调优5.1 10SPS 与 80SPS 如何选择HX711 模块通常有两种输出速率10SPS 和 80SPS。用逻辑分析仪量 DOUT 两次下降沿之间的时间就能区分10SPS 约 100ms80SPS 约 12.5ms。称重场景下我优先选 10SPS因为 80SPS 模式数据噪声更大有效位数会掉 2 到 3 位最终显示值的抖动反而更明显。只有在皮带秤、检重秤这类需要快速响应的场合才把模块切到 80SPS再配合硬件低通滤波。对基于 STM32 的智能台灯这类应用10SPS 已经足够人坐下去的动作是几百毫秒级的变化读一个稳定值比读十个噪声值更有意义。5.2 用滑动平均代替一次性读值HX711 的原始码会有 ±2 到 ±5 个码的低频波动直接换算成重量后显示末位来回跳。常见做法是取最近 8 次重量做环形队列平均#define FILTER_LEN 8 int32_t filter_buf[FILTER_LEN]; uint8_t filter_idx 0; int32_t HX711_GetStableWeight(void) { int32_t sum 0; uint8_t i; filter_buf[filter_idx] HX711_GetWeight(); filter_idx (filter_idx 1) % FILTER_LEN; for (i 0; i FILTER_LEN; i) sum filter_buf[i]; return sum / FILTER_LEN; }逻辑说明这里把新读数写入环形队列再叠加所有缓存求平均% FILTER_LEN让索引自动回绕避免数组越界。每次调用只多一次 HX711 读操作不会影响主循环时序。参数说明窗口取 8是因为 10SPS 下 8 个点正好覆盖 0.8 秒能滤掉桌面的低频晃动又不会让放砝码的阶跃响应迟滞超过 1 秒。窗口取 16 以上时读数会非常平滑但上下料瞬间重量恢复稳定的时延也会拉长到 1.6 秒。如果想兼顾平滑和实时性可以做一阶互补滤波new old * 0.9 current * 0.1缺点是要手动调系数。5.3 标定时不要用整数除法有效位数才是真实指标标定系数 scale 如果用整数类型保存1 克对应只有百级计数整数除法和浮点除法之间可能差出 0.2 到 0.5 克。正确做法是float scale (float)(HX711_GetWeight()) / 1000.0f; // 放 1000g 砝码时 float weight_g (float)(HX711_GetWeight()) / scale;逻辑说明Tare 之后放 1000g 砝码此时HX711_GetWeight()是相对零位的计数差除以 1000.0f 得到每克对应的码数后续所有重量都用这个浮点系数换算。参数说明串口打印时保留一位小数就够打印太多位只会暴露传感器噪声。关于 HX711 有效位数不要只看 24 位这个宣传值实际有效位数受 PGA 增益、AVDD 噪声和传感器桥路线性度影响。用串口连续记录 300 个空载采样值计算标准差再除以标定系数就能得到这台设备实际可分辨的克数这个数字比模块手册上的位数更有参考价值。本文还有配套的精品资源点击获取