前言:基于CW32L012的多功能电子秤系统是集称重,计价,标定,去皮,计件等功能于一体的系统,实现了事实上的电子秤全功能。
一、实物展示:
二、HX711模块介绍:
本称重系统使用HX711芯片,HX711是海芯专利的24位高精度AD芯片,专为电子秤设计。它集成稳压、时钟等外围电路,集成度高、速度快、抗干扰强,能降成本、提稳定性。芯片对接单片机简单,靠引脚控制无需寄存器配置;支持两路输入,A通道增益可调,适配称重信号,B通道固定增益用于系统检测。自带稳压电源与时钟电路,还具备上电自复位,外围极简,使用便捷。下附实物图片及电路图。
模块特性:
1.两路可选择差分输入。
2.片内低噪声可编程放大器,可选增益为 32,64 和 128 • 片内稳压电路可3.直接向外部传感器和芯片内 A/D 转换器提供电源。
4.片内时钟振荡器无需任何外接器件,必要时 也可使用外接晶振或时钟上电自动复位电路。
5.简单的数字控制和串口通讯:所有控制由管 脚输入,芯片内寄存器无需编程。
6.可选择 10Hz 或 80Hz 的输出数据速率。
7.同步抑制 50Hz 和 60Hz 的电源干扰。
8.耗电量(含稳压电源电路): 典型工作电流:< 1.6mA, 断电电流:< 1µA。
9.工作电压范围:2.6 ~ 5.5V;工作温度范围:-40 ~ +85℃。
10.16 管脚的 SOP-16 封装。
该模块实现称重的逻辑:
1.压力转电信号称重传感器受压力形变,输出微弱差分电压信号。2.信号放大信号送入HX711,经内部可编程放大器放大。3.AD转换24位模数转换器将模拟电压转为数字量。4.数据传输HX711通过IO引脚把数字值发给主控MCU。5.软件换算单片机读取数值,做去皮、校准、公式运算,最终算出重量并显示。
三、软件讲解:
上面讲解为称重系统核心部分。其他部分将在下面keil工程中具体呈现,其中不乏含有物理键盘,串口通讯,TFT彩屏以及main文件的核心部分。
HX711.c和h
#include "HX711.h"#include "delay.h"#include "global.h"#include "cw32l012_gpio.h"#include "cw32l012_sysctrl.h"#include "stdlib.h"u32 HX711_Buffer;u32 Weight_Maopi;s32 Weight_Shiwu;u8 Flag_Error = 0;u32 Maopi_flag;/* HX711 专用延时,不依赖 delay_us,纯循环消耗时间 */static void hx711_delay(void){volatile unsigned int i;for (i = 0; i < 20; i++) __nop();}void Init_HX711pin(void){GPIO_InitTypeDef GPIO_InitStructure = {0};__SYSCTRL_GPIOB_CLK_ENABLE();/* PB8 — SCK 推挽输出 */GPIO_InitStructure.IT = GPIO_IT_NONE;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Pins = GPIO_PIN_8;GPIO_Init(CW_GPIOB, &GPIO_InitStructure);/* PB9 — DOUT 上拉输入 */GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;GPIO_InitStructure.Pins = GPIO_PIN_9;GPIO_Init(CW_GPIOB, &GPIO_InitStructure);HX711_SCK(0);}u32 HX711_Read(void){unsigned long count = 0;unsigned char i;unsigned int timeout = 0;HX711_SCK(0);hx711_delay();/* 等待 DOUT 变低,加超时保护 */while (HX711_DOUT){timeout++;hx711_delay();if (timeout > 50000){Flag_Error = 1;return Weight_Maopi;}}Flag_Error = 0;for (i = 0; i < 24; i++){HX711_SCK(1);hx711_delay();count = count << 1;if (HX711_DOUT) count++;HX711_SCK(0);hx711_delay();}/* 第25个脉冲 */HX711_SCK(1);count = count ^ 0x800000;hx711_delay();HX711_SCK(0);hx711_delay();return count;}void Get_Maopi(void){u32 val = HX711_Read();if (Flag_Error == 0)Weight_Maopi = val;}u32 tempweight;void Get_Weight(void){static s32 last_weight = 0;unsigned int i;long max = 0;s32 new_weight;/* 取2次平均,*/for (i = 0; i < 2; i++){HX711_Buffer = HX711_Read();max += HX711_Buffer;}max /= 2;HX711_Buffer = (u32)max;if (HX711_Buffer > Weight_Maopi){new_weight = (s32)(HX711_Buffer - Weight_Maopi);new_weight = (s32)((float)new_weight / scalar);if (new_weight < 0) new_weight = 0;}else{new_weight = 0;}/* 死区滤波:变化小于 2g 不更新,消除抖动 */if (new_weight == 0 || abs(new_weight - last_weight) >= 2){Weight_Shiwu = new_weight;last_weight = new_weight;}}#ifndef __HX711_H#define __HX711_H#include "cw32l012.h"#include "cw32l012_gpio.h"#include "Lcd_Driver.h"/* SCK — PB8,DOUT — PB9 */#define HX711_SCK(x) GPIO_WritePin(CW_GPIOB, GPIO_PIN_8, (x) ? GPIO_Pin_SET : GPIO_Pin_RESET)#define HX711_DOUT GPIO_ReadPin(CW_GPIOB, GPIO_PIN_9)extern void Init_HX711pin(void);extern u32 HX711_Read(void);extern void Get_Maopi(void);extern void Get_Weight(void);extern u32 HX711_Buffer;extern u32 Weight_Maopi;extern s32 Weight_Shiwu;extern u8 Flag_Error;extern u32 Maopi_flag;#endif
usart2.c和h
#include "usart2.h"#include <string.h>#include "cw32l012.h"#include "cw32l012_gpio.h"#include "cw32l012_uart.h"#include "cw32l012_sysctrl.h"#include "global.h"#include "delay.h"#include "Lcd_Driver.h"extern float Single_Weight;extern int goods_count;extern unsigned int Weight_Maopi;extern unsigned int Maopi_flag;extern int Weight_Shiwu;extern int total;extern int price;extern int ax;extern float ex;extern short int Temp_num;extern char txt1[];extern void Key_Character(uint16_t uKey);extern void Gui_DrawFont_GBK16(u16 x, u16 y, u16 fc, u16 bc, char *str);extern u32 HX711_Buffer;extern float scalar;extern unsigned int SaveFlag;/* 标志位,中断里只设标志,主循环里执行实际操作 */unsigned char tare_flag = 0;unsigned char calib_flag = 0;/* ========== 业务函数 ========== */void On_App_Calibrate(void){calib_flag = 1; /* 只设标志,主循环里处理 */}void On_App_NumInput(uint8_t num){char temp_txt[10];if (price < 1000)price = price * 10 + num;sprintf(temp_txt, "%d ", price);Gui_DrawFont_GBK16(60, 47, YELLOW, BLACK, temp_txt);}void On_App_SetCount(void){if (Weight_Shiwu > 1){Single_Weight = (float)Weight_Shiwu;Gui_DrawFont_GBK16(10, 110, RED, BLACK, "SAMPLE OK! ");}else{Gui_DrawFont_GBK16(10, 110, RED, BLACK, "NO GOODS! ");}}void On_App_SetConfirm(void){total = (u32)(price * Weight_Shiwu);sprintf(txt1, "%d ", total);Gui_DrawFont_GBK16(60, 69, YELLOW, BLACK, txt1);}void On_App_SetTare(void){tare_flag = 1; /* 只设标志,主循环里处理 */}void On_App_SetClear(void){Weight_Maopi = Maopi_flag;total = 0; price = 0; Weight_Shiwu = 0;goods_count = 0; Single_Weight = 0.0f;memset(txt1, 0, 50);Gui_DrawFont_GBK16(60, 69, YELLOW, BLACK, " ");Gui_DrawFont_GBK16(60, 47, YELLOW, BLACK, " ");Gui_DrawFont_GBK16(10, 110, BLACK, BLACK, " ");}void On_App_SetPoint(void) {}void On_App_SetAdd(void) {}void On_App_SetPay(void) {}/* ========== 协议解析 ========== */static uint8_t Calculate_FCS(uint8_t *pkt, uint8_t len){uint8_t calculatedXOR = pkt[0];int i;for (i = 1; i < len; i++)calculatedXOR ^= pkt[i];return calculatedXOR;}void Protocol_ParseCommand(uint8_t *rx_buf, uint8_t rx_len){uint8_t cmd, code, length;if (rx_buf[0] != 0x55 || rx_buf[rx_len - 1] != 0x56) return;cmd = rx_buf[1];code = rx_buf[2];length = rx_buf[3];if (rx_len != (length + 6)) return;if (Calculate_FCS(&rx_buf[1], 3 + length) != rx_buf[4 + length]) return;if (cmd == 0x03){switch (code){case 0x01: On_App_SetTare(); break;case 0x02: On_App_SetClear(); break;case 0x03: On_App_SetCount(); break;case 0x04: On_App_SetConfirm(); break;case 0x05: On_App_NumInput(0); break;case 0x06: On_App_NumInput(1); break;case 0x07: On_App_NumInput(2); break;case 0x08: On_App_NumInput(3); break;case 0x09: On_App_NumInput(4); break;case 0x0A: On_App_NumInput(5); break;case 0x0B: On_App_NumInput(6); break;case 0x0C: On_App_NumInput(7); break;case 0x0D: On_App_NumInput(8); break;case 0x0E: On_App_NumInput(9); break;case 0x0F: On_App_Calibrate(); break;case 0x10: On_App_SetAdd(); break;case 0x11: On_App_SetPay(); break;default: break;}}}/* ========== 串口2中断 ========== */#define RX_BUF_SIZE 32uint8_t g_rx_buffer[RX_BUF_SIZE];uint8_t g_rx_index = 0;void UART2_IRQHandler(void){static uint8_t rx_state = 0;static uint8_t data_len = 0;uint8_t rx_byte;if (UART_GetITStatus(CW_UART2, UART_IT_RC) != RESET){UART_ClearITPendingBit(CW_UART2, UART_IT_RC);rx_byte = UART_ReceiveData(CW_UART2);switch (rx_state){case 0:if (rx_byte == 0x55){g_rx_index = 0;g_rx_buffer[g_rx_index++] = rx_byte;rx_state = 1;}break;case 1:g_rx_buffer[g_rx_index++] = rx_byte;rx_state = 2;break;case 2:g_rx_buffer[g_rx_index++] = rx_byte;rx_state = 3;break;case 3:g_rx_buffer[g_rx_index++] = rx_byte;data_len = rx_byte;if (data_len < (RX_BUF_SIZE - 6))rx_state = (data_len == 0) ? 5 : 4;elserx_state = 0;break;case 4:g_rx_buffer[g_rx_index++] = rx_byte;data_len--;if (data_len == 0) rx_state = 5;break;case 5:g_rx_buffer[g_rx_index++] = rx_byte;rx_state = 6;break;case 6:g_rx_buffer[g_rx_index++] = rx_byte;if (rx_byte == 0x56)Protocol_ParseCommand(g_rx_buffer, g_rx_index);rx_state = 0;break;default:rx_state = 0;break;}if (g_rx_index >= RX_BUF_SIZE){rx_state = 0;g_rx_index = 0;}}}/* ========== 串口2初始化 ========== */void Usart2_Init(u32 USART_BAUDRATE){GPIO_InitTypeDef GPIO_InitStructure = {0};UART_InitTypeDef UART_InitStruct = {0};__SYSCTRL_GPIOA_CLK_ENABLE();__SYSCTRL_GPIOC_CLK_ENABLE();__SYSCTRL_UART2_CLK_ENABLE();PA02_AFx_UART2TXD();PA03_AFx_UART2RXD();GPIO_InitStructure.Pins = GPIO_PIN_2;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_Init(CW_GPIOA, &GPIO_InitStructure);GPIO_InitStructure.Pins = GPIO_PIN_3;GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;GPIO_Init(CW_GPIOA, &GPIO_InitStructure);UART_InitStruct.UART_BaudRate = USART_BAUDRATE;UART_InitStruct.UART_Source = UART_Source_PCLK;UART_InitStruct.UART_UclkFreq = 96000000;UART_InitStruct.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;UART_Init(CW_UART2, &UART_InitStruct);UART_ITConfig(CW_UART2, UART_IT_RC, ENABLE);UART_ClearITPendingBit(CW_UART2, UART_IT_RC);NVIC_SetPriority(UART2_IRQn, 1);NVIC_EnableIRQ(UART2_IRQn);}/* ========== 发送函数 ========== */void Usart_SendByte(UART_TypeDef *pUARTx, uint8_t ch){UART_SendData(pUARTx, ch);while (UART_GetFlagStatus(pUARTx, UART_FLAG_TXE) == RESET);}void Usart_SendString(UART_TypeDef *pUARTx, char *str){unsigned int k = 0;do{Usart_SendByte(pUARTx, *(str + k));k++;} while (*(str + k) != '');while (UART_GetFlagStatus(pUARTx, UART_FLAG_TXE) == RESET);}void Usart_SendHalfWord(UART_TypeDef *pUARTx, uint16_t ch){Usart_SendByte(pUARTx, (ch & 0xFF00) >> 8);Usart_SendByte(pUARTx, ch & 0x00FF);}#ifndef __USART2_H#define __USART2_H#include "cw32l012.h"#ifndef u32#define u32 unsigned int#endif#ifndef u16#define u16 unsigned short#endif#ifndef uchar#define uchar unsigned char#endif/* 标志位,供 main.c 主循环使用 */extern unsigned char tare_flag;extern unsigned char calib_flag;/* 业务函数声明 */void On_App_Calibrate(void);void On_App_NumInput(uint8_t num);void On_App_SetCount(void);void On_App_SetConfirm(void);void On_App_SetTare(void);void On_App_SetClear(void);void On_App_SetPoint(void);void On_App_SetAdd(void);void On_App_SetPay(void);void Protocol_ParseCommand(uint8_t *rx_buf, uint8_t rx_len);/* 串口底层函数声明 */void Usart2_Init(u32 USART_BAUDRATE);void Usart_SendByte(UART_TypeDef *pUARTx, uint8_t ch);void Usart_SendString(UART_TypeDef *pUARTx, char *str);void Usart_SendHalfWord(UART_TypeDef *pUARTx, uint16_t ch);#endif
四、总结与建议:
整个电子秤系统涉及多个模块包括HX711称重模块,TFT彩屏,物理键盘,蓝牙模块,CW32L012主控板。
在进行称重时应先去皮再放重物,如不准则进行标定,标定后重新放重物进行称重。
进行称重时,重物应尽量放在电子秤的中心,如不放中心可能会存在微小误差。
按键要做消抖处理,否则可能会不准确。
称重要数据滤波,合理设置数值范围与存储位数,避免跳数、溢出。
串口要设置正确的波特率否则会造成通信不成功。
扫码加入QQ群3群| 610403240
256