基于51单片机的电子秤称重计数系统
0 演示效果

演示视频:
1 设计题目
电子秤称重计数系统。
2 设计要求
- 了解扩展版、HX711模块的功能;
- 安装电子秤传感器及支架后,用HX711模块获取测量得到的重力大小,量程为0g~1000g;
- 按下“去皮”键,将数值清零,以当前重力为基准,显示重量数值;
- OLED液晶显示物品重量和设置信息。OLED液晶第1行显示物品重量,四舍五入到小数点后2位;第2行显示设定的重量,四舍五入到小数点后1位,当称重超过设定重量后,累积数量加1(建议16个按键的功能分别为:0~9、设置、左移、右移、上移/去皮、下移、确认);第3行显示自上电以后的累计数量;
- 下载程序第一次上电,设定值有默认值,后续如果修改设定值,设定值存入AT24C02,掉电不丢失;
- 制定通讯协议,可以通过发送指令,在PC机通过串口助手获取当前的累计数量;
- 制定通讯协议,可以发送指令,通过上位机设定重量阈值。
3 设计背景与设计目的
在农业、工业生产的过程中,我们通常需要对农产品、零件等物品进行分等级或者说质量检测。以果农为例,从果园中收获回来的水果有大有小,质量等级不一致,果农常需要对这些产品进行称重,并计算产品中合格品的数量。
本设计的目的是为了解决此类问题,例如单个水果质量大于100g符合质量合格要求,通过设计系统将阈值重量输入,只需挨个将水果放上称重台即可完成对合格水果的计数工作。同时我们定义了接口,每当出现一次合格水果后,能发出特定信号,以控制其他装置,例如分拣装置等。这样就能完成流水线式的自动化质量检测,极大提升了生产的效率,节约了时间和人力成本。
4 设计原理
该系统主要测量原理是基于电阻式应变片实现的。传感器由横梁(金属)和四个应变片组成。安装示意图如下图4-1所示。

利用四个应变片组成全桥电路,如图4-2所示。这样的电路结构具有消除平台、改善线性、温度补偿以及增大灵敏度的优点。

此时,电桥输出$U_o=U\frac{\Delta R}{R}$.
输出电信号经过滤波电路、运放电路、A/D转换电路后可量化为数字量。本模块采用24位精度的AD转换芯片HX-711,具有两路模拟通道输入,内部集成了128倍增益可编程放大器,是一款理想精度、低成本采样前端模块。该传感器的量程可以达到10kg,分辨率可达0.5g,满足本设计题目的要求。
5 所用仪器与目的
- 标准砝码一组。用于传感器标定。
- 万用表。用于电路故障测试。
- 电烙铁。用于焊接。
- PC机。用于编程、调试、画图。
6 总体设计方案
6.1 硬件框架设计
根据设计题目要求,总体硬件方案设计如下图6-1所示。

该硬件方案图是以做出实物为目标设计的,故部分硬件的驱动电路直接纳入相应的模块中。考虑到团队成员间程序联调方便,采用大家都学过的基于STC89C52的51mini板,晶振频率为固定11.5092MHz,基本能满足电子秤系统的实时性要求与算力要求。
系统的信号来源于电阻式应变片,使用四个应变片(2个受拉、2个受压),贴在梁式称重传感器上,所用的信号转换电路为HX-711模块,经过传感器的输出电路、放大电路滤波电路、AD转换电路后转换为数字电压量。传感器组成框图如下图6-2所示。

MCU通过模拟IIC的方式从HX-711模块读取电压数据,后续还进行如多次采样与滑动窗口滤波进行数据处理,处理后的数据需要利用标准砝码对该传感器进行数据标定,从而完成测量任务。
人机交互部分由$4\times 4$的薄膜矩阵键盘与0.96寸的OLED构成,矩阵键盘通过一组IO进行中断扫描,实现按键检测。MCU通过IIC对OLED进行读写操作,完成数据的显示。不同按键分别可实现设置、确认、变数、去皮的操作。测量模式下第一行物品重量有与第三行累计重量刷新,设置模式下第二行数据可改,第三行数据不刷新。
EEPROM部分主要负责完成相关数据的断电存储,通过IIC与MCU互相通信。每次开机后,系统会从EEPROM中取出上次断电前设定值。
PC上位机主要通过串口的方式与单片机进行通讯,MCU出来的串口通讯时TTL电平,而我们的PC机并不能识别TTL电平,使用51mini板载CH340芯片模拟COM口,完成PC与单片机的通讯。另外,在设计中还考虑使用LORA无线通信模块完成MCU与上位机的交互,不同的LORA模块与其主机用串口通信。
6.2 软件框架设计
系统软件的主体框架如图6-3所示。

主程序开始,进行一系列的初始化,然后进入死循环,一直反复获取测量数据、数据滤波、计数判断、OLED屏幕刷新。与此同时,每5ms进一次中断扫描矩阵键盘按键。串口部分中断将在收到上位机发送的信号时,进入中断完成通讯协议逻辑的实现。框图细节在后续各成员方案中有进一步介绍。
6.3 系统性能指标
7 个人部分设计方案
7.1 概述
本人负责系统人机交互部分设计与制作、调试。
主要内容包括:OLED屏幕的显示接口、矩阵键盘与OLED的协同交互、与其他模块的联调、在整体程序中的性能优化。
7.2 功能介绍
人机交互部分主要由$4\times 4$的薄膜矩阵键盘与0.96寸的OLED屏幕构成,通过一组IO口与MCU的P0连接(键盘),通过P1.0~P1.1分别作为SCL和SDA用于IIC协议通讯(OLED)。矩阵键盘通过行列循环扫描的方式进行检测。为了不占用CPU资源,使用每5ms一次定时器中断的方式实现。
OLED方面,第一行显示物品重量,保留小数点后一位;第二行显示设定重量,保留小数点后两位;第三行显示累计数量,最大可计数999个。显示屏示意图如下图6-4所示。

系统启动后,初始化过程中会先读出EEPROM中存储的设定重量值,并显示于第二行,随后进入测量模式。测量模式下,OLED会实时刷新第一行和第三行的数据,当物品重量大于设定重量时,累计数量+1.此处会通过软件算法对相邻两次测量进行判断,防止一次测量中累计数量多次累加。按下“A”可实现去皮功能。
此时按下“*”键后,系统进入设定模式。设定模式下,第一行仍然会实时刷新测得重量,第二行会对可设定位进行反白显示,其效果如下所示。此时按下不同的数字键,反白位就会变成相应数字。按下“C”和“D”键可实现反白位的左移和右移,可循环移动。按下“#”键完成设定,重新进入测量模式,去皮键再次生效。
7.3 软件方案
软件(人机交互部分)代码逻辑如下图所示。

软件框图只选择了重要步骤进行绘制,不完全代表整体程序内容。
7.4软件方案说明
-
流程解释
进入主程序后,配置TIM0为定时5ms中断一次,通过IIC的方式向OLED的各个寄存器写入相应的值,已完成OLED的初始化,此时采用默认初始化方式。随后初始化OLED的背景色与显示色,并将OLED上固定不会变的字符写进OLED。这样做的原因是提高屏幕刷新率,因为以STC89C52RC的晶振频率与处理性能,利用负刷新的方式刷新率会极低,具体表现为逐行清除再写入,肉眼可见的慢。而实际需要实时刷新的部分只有9个数字位,即$16\times 8\times 9$个像素,而整个屏幕刷新需要写$128\times 64$个像素,采用这样的刷新方式速率提高了
$$ \Delta = \frac{128\times 64-16 \times 8 \times 9}{128\times 64}\times 100\% = 83.6 \% $$的速率。需要指出的是,这里物品重量和累计数量开机初始化为0,而设定重量需要先从EEPROM事先存放weight_set变量的寄存器读出u16位数的值并赋给weight_set变量,再进行初始化显示。以上,所有初始化完成。
预先定义一个bit类型的setting_mode变量,为0时表示测量模式,为1时表示设定模式。
进入主循环后,先判断模式。若为测量模式,查询“ * ”、“A”按键标志位,若“ * ”按下,则setting_mode=1,下次循环进入设定模式;若“A”按下,则实现去皮功能,去皮的具体原理将会在后文进行阐述;若这两个键均未按下,则进入下一显示环节。若为设定模式,则此时去皮键失效,数字键、确认键与移位键有效。通过三种类型键的组合可以完成对weight_set变量值的更改并返回测量模式。在此过程中若查询到“#”键标志位为按下状态,则将setting_mode值更改回0,并将此时weight_set的值存入EEPROM指定区域,方便下次开机读出,而后进入下一显示环节。(按键更改设定重量值得具体原理会在后文提出)
显示环节会首先确定目前是测量模式还是设定模式,因为刷新的数据与现实的方式会有所不同。此时若为测量模式,则刷新第三行与第一行数据;若为设定模式,则刷新第一行与第二行数据。其中第二行的数据显示需要对游标位(即可更改位)进行反白显示,从而实现用户与机器的交互方便性。反白的原理会在后文中具体阐述。进行设定模式后停止累计数量的计算与显示,这是符合日常使用习惯的。
以上为主程序中人机交互部分的程序,对按键的扫描消抖和标志位处理会在定时器中断中实现。将按键操作放在中断中能使系统具有良好的反馈,特别是在较大型、较复杂的系统中,若采取在主循环中扫描的方式会有很大概率产生按键未检测到的情况,这对人机交互来说非常不利,而采取每5ms一次中断的方式,可以保证人类在一次按键动作中用时大约20ms的时间内有效对按键进行检测,并用标志位表示,节省主程序的空间,这样的方式可以保证每次按键都能被检测到。
中断程序触发后,会对每个按键进行扫描并更新其状态key_sta,具体扫描原理在后文中会进行详细阐述。此时,可以直接判断按键状态并转换标志位吗?显然,此时可能会因按键抖动产生误判,故需“消抖”。但常规的延时消抖在此处并不适用,其理由有两点:
-
延时消抖会占用CPU资源,毫无意义;
-
中断不宜停留时间过长。
因此,此处采用了特殊的消抖方式,其原理大意为:
1. 第一个5ms进入中断,此时假设“A”的key_sta = 1,即按下,那么进入第1阶段,judge_sta[A] + 1;
2. 第二个5ms进入中断,此时“A”的key_sta若仍为1,则表明它还是按下的,并已经持续10ms,进入第2阶段,judge_sta[A] + 1;若key_sta = 0,则表明上一次为误判,将judge_sta置0方便下一次判断;
- 第三个5ms进入中断同理,当进入到第四次中断,若key_sta仍为1,表明按键已经按下20ms,足以确定这是一次有效的按键动作,此时终于可以将“A”的标志位置1了,千万不能忘记把judge_sta返回为初始阶段0,方便下一次检测。此原理也就是中断流程图的内容了。
至此,人机交互主体程序的部分已经介绍完毕。
-
-
各部分原理设计解释
-
矩阵键盘
$4\times 4$矩阵键盘的原理图如下。


经本人测试将实体键盘按键关系绘制如图7-3。
扫描原理为:
先将8个IO口全部置1.column 1输出0,检测Row1,Row2,Row3,Row4的电平。若Row1为0,则“D”按下;若Row2为0,则“#”按下$\dots$;同理,column中仅让column2输出0,分别检测Row1,Row2,Row3,Row4的$\dots$;以此完成column3, column4的输出检测。通过两个4次循环的嵌套,以实现对每个键状态的遍历。
-
按键更改设定重量值原理
定义变量Set_No表示设定重点时光标处于的位数,Set_No = 1,2,3,4时分别对应百、十、个、小数位。当数字键按下时,先分离原数字的百、十、个、小数位,再将对应位改为按下键的数值,再通过
$$ Weight\_Set = 百位\times1000+十位\times 100+个位\times 10+小数位\times 1 $$来计算得到新值。
-
系统的数字表示方法
因为C语言没有适用的小数变量,同时为了节约空间、方便显示,故对于物品重量Weight_Measured和设定重量Weight_Set用unsigned int即16位数表示。这是因为量程最大处显示999.9g,这里用9999的整形表示,在显示时拆分每一位分别显示在OLED的固定位置。同理累计数量Accumulated_Counter用u8表示即可。(u16范围:0 ~ 66535;u8范围:0 ~ 255)
-
反白显示原理
涉及到OLED显示原理,这里只需要用取模软件取数字0 ~ 9的阳码即可,加入到字库中并在设定模式下反白显示Set_No的数字即可。此处我为了方便,定义了一个Showchar_Invert()函数,避免了字库索引的复杂。
-
8 调试过程与解决的问题
-
问题一:内存不足问题
在我们小组成员各自模块调试过程中,进展都很顺利,但在联调过程中发现了代码量较大导致编译器不通过的情况,显示为“OVERFLOW”字样。
众所周知,STC89C52单片机的RAM为512Bytes,ROM为8KBytes。故我们的Code已经超过了8000的数量,程序需要精简。通过微机原理课程学习过的知识与资料的查阅,我主要从以下几个方面对代码量进行了精简,使人机交互部分的代码量从“data = 97.0, code = 6000”降低到了“data = 52.0, code = 4000”。
-
精简类型变量。
原程序中许多二值变量我都用的unsigned char来表示的,后来改为了bit型变量,节约了一部分RAM。
-
字库的精简。
原采用的器件商提供的字库文件,对于本项目大量符号用不到,故自己重新建立了字库与索引方式,节省了大量的ROM空间。这一步使程序体积大幅减小,但仍有优化空间,可采用更小号的字或使用英文代替汉字;
-
自己写库函数。
原来OLED、IIC均使用了网络上提供的现有库函数,发现有两个痛点:
-
许多函数用不到、可删除;
-
部分函数的定义比较占用内存,有精简的空间。当然为了代码量小不惜牺牲部分通用性;
改进后OLED、KEY、IIC全部消除,编译 0 error, 0 warning通过。
-
-
结构体和数组的更改。
经测试发现,结构体和数组对内存空间的占用非常大,原来所有按键信息均定义在一个结构体里的,管理非常方便,但由于本项目特殊性,将按键标志位数组key_flag[i]改为用一个u16 key_flag表示,每一位表示一个按键的标志位状态,为此还添加了一些位操作函数。同理对多个变量进行了类似处理。但如此做损失了系统代码的可修改、可移植性。
-
-
问题二:精度问题
系统搭建完成后,对传感器进行标定,同时还加入了滑窗滤波,但仍然发现精度只能达到0.2g,具体表现为数据会在标准重量的上下0.1g范围产生波动。小数点后两位同样,波动发生得更厉害,解决办法为换用精度更高的传感器或采用更优秀的滤波算法。不过滤波后的数据如果是基于波动较大的数据,其有效性仍有待考究。
相应的在人机交互部分我也将显示位数改为了小数点后一位。
-
问题三:按键消抖问题
原采用了循环扫描延时消抖的方法,在放入整体程序联调后发现按键的漏检会较多,故更改为中断多次判断消抖。