CW32L012多功能电子秤案例实现分享 前言基于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 — PB8DOUT — 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; #endifusart2.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 32 uint8_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; else rx_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) ! \0); 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主控板。在进行称重时应先去皮再放重物如不准则进行标定标定后重新放重物进行称重。进行称重时重物应尽量放在电子秤的中心如不放中心可能会存在微小误差。按键要做消抖处理否则可能会不准确。称重要数据滤波合理设置数值范围与存储位数避免跳数、溢出。串口要设置正确的波特率否则会造成通信不成功。