首先看看题目要求:
1.方案论证
(1)压力传感器的论证与选择
方案一:采用惠更斯电桥,当电阻应变片承受载荷产生变形时,其阻值将发生变化。从而使电桥失去平衡,产生相应的差动信号,但其线性度不理想。
方案二:采用全桥电路,此电路对压力的灵敏度比惠更斯电桥增加一倍以上,测量的精度将更加准确,线性度更好。还具有机械滞后小、横向效应小以及体积小的特点。
通过上述两种方案比较,全桥差动压力传感器符合设计要求,称重范围为0~5Kg,满足题目要求的5g~500g的测量范围。
(2) A/D 转换模块的选取
方案一:采用分立元件构成的仪用放大电路对电阻应变式传感器输出的模拟信号进行放大,然后外接高精度的A/D模块将模拟信号转换成数字信号送给单片机进行处理。
方案二:采用集成芯片HX711,其内部PGA和24位A/D,放大倍数可以在128和64两档中选择,24位的精度足够准确的分辨出题设中1g的精度变化要求。通过比较,方案二的电路具有简单且高效的特点,节省了很多电路的调试时间而且降低了实物制作的面积,所以,本设计采用方案二。
(3)单片机模块的论证与选取
方案一:采用最常见的51单片机作为主控芯片,其主频范围在11~32MHz范围内可选择,同时具有32个I/O口,完全满足I/O口的使用,价格合适,便于设计整体电路。
方案二:采用STM32F407,主频最高能达到160Mhz,具有较多的I/O口,能够进行更多的控制。
通过比较,方案二的功能更强。但考虑到价格等情况,本设计最终选取方案一。
2.理论方法与计算
(1) 满量程输出电压=激励电压*灵敏度 1.0mv/v 例如: 供电电压是 5v 乘以灵敏度 1.0mv/v=满量程 5mv。 相当于有 5Kg 重力产生时候产生 5mV 的电压。
(2)概述: 711 模块 A 通道带有 128 倍信号增益, 可以将 5mV 的电压放大 128 倍, 然后采样输 出 24bit AD 转换的值, 单片机通过指定时序将 24bit 数据读出。 详细讲解程序计算原理:
步骤 1: 如何计算传感器供电电压 HX711 可以在产生 VAVDD 和 AGND 电压, 即 711 模块上的 E+ 和 E- 电压。 该电压通过 VAVDD=VBG(R1 +R2 ) /R2 计算。 VBG 为模块儿基准电压 1 . 25v R1 = 20K,R2 = 8.2K 因此得出 VAVDD = 4. 3V (为了降低功耗, 该电压只在采样时刻才有输出, 因此用万用表读取的值可能低于 4.3v, 因 为万用表测量的是有效值。)
步骤 2: 如何将 AD 值反向转换为重力值。 假设重力为 A Kg, (x<5Kg) , 测量出来的 AD 值为 y 传感器输出, 发送给 AD 模块儿的电压为 A Kg * 4. 3mV / 5Kg = 0. 86A mV 经过 128 倍增益后为 128 * 0. 86A = 1 10. 08AmV
转换为 24bit 数字信号为 110.08A mV * 224 / 4. 3V = 429496. 7296A 所以 y = 429496. 7296A 因此得出 A = y / 429496. 7296 所以得出程序中计算公式 Weight_Shiwu = (unsigned long)((float)Weight_Shiwu/429.5); 特别注意: 因为不同的传感器斜率特性曲线不是完全一样, 因此, 每一个传感器需要矫正这里的 429.5 这个除数, 才能达到精度很高。 修改以下部分代码用于校准(该段程序在 main.c 最上面) #define GapValue 430 当发现测试出来的重量偏大时, 增加该数值。 如果测试出来的重量偏小时, 减小改数值。 该值可以为小数, 例如 429.5
3.电路与程序设计
(1)系统组成:
(2)原理框图与各部分电路图:
12864液晶显示电路
12864LCD内部构成及功能:它是一种图形点阵液晶显示器,它主要由行驱动器/列驱动器128×64全点阵液晶显示器组成。可完成图形显示,也可以显示8×4个(16×16点阵)汉字或者显示16×4个(8×16点阵)ASCII码。低电压低功耗是其显著特点。
按键电路设计
键盘输入是人机交互界面中重要的组成部分,它是系统接受用户指令的直接途径。键盘是由若干个按键开关组成,键的多少根据单片机应用系统的用途而定。键盘由许多键组成,每一个键相当于一个机械开关触点,当键按下时,触点闭合,当键松开时,触点断开。单片机接收到按键的触点信号后作相应的功能处理。因此,相对于单片机系统来说键盘接口信号是输入信号。
HX711电路设计
HX711是一款专为高精度称重传感器而设计的24位A/D转换器芯片。与同类型其它芯片相比,该芯片集成了包括稳压电源、片内时钟振荡器等其它同类型芯片所需要的外围电路,具有集成度高、响应速度快、抗干扰性强等优点。降低了电子秤的整机成本,提高了整机的性能和可靠性。该芯片与后端MCU芯片的接口和编程非常简单,所有控制信号由管脚驱动,无需对芯片内部的寄存器编程。输入选择开关可任意选取通道A或通道B,与其内部的低噪声可编程放大器相连。通道A的可编程增益为128或64,对应的满额度差分输入信号幅值分别为±20mV或±40mV。通道B则为固定的32增益,用于系统参数检测。芯片内提供的稳压电源可以直接向外部传感器和芯片内的A/D转换器提供电源,系统板上无需另外的模拟电源。芯片内的时钟振荡器不需要任何外接器件。上电自动复位功能简化了开机的初始化过程。
报警电路设计
报警指示原理分析:报警指示电路是由PNP三极管8050驱动蜂鸣器实现的,单片机I0口控制三极管的基极,当单片机的I0口输出为低电平时,三极管导通,蜂鸣器的正极与电源接通,蜂鸣器通电发出报警声,当单片机I0口输出高电平时,三极管截止,蜂鸣器停止报警。
- 系统软件与流程图
系统软件:使用keil4和AD软件
在系统通电后,主程序首先完成系统初始化,其中包括系统变量定义和给系统变量赋初值等,然后调用A/D采集函数,将A/D采集模块输出的24位二进制串行数据转化为十进制,接着进行调零和定标,最后分离出四位十进制数据的千位、百位、十位和个位,调用液晶显示函数,将对应的数值送到对应的液晶上进行显示。
流程图:
4.测试方案与测试结果
测试方案:
(1)精度测试:用2个100g、1个20g1个5g的砝码分别组合来测试本次设计的电子秤的精度。根据显示出来的示重和砝码重量的结果判断是否有偏差,再根据偏差的多少来确定本作品的精度。
(2)计价测试:分别放入不同重量的物体和输入不同的单价,观测是否能准确的得出总价。
(3)去皮测试:先放入一个重物作为“皮”,再按下“去皮按键”,然后将重物置于电子秤上,观察是否能够准确的测出后加重物的重量。
测试结果:
(1)当侧重物体小于50g时,称重误差小于0.5g。
(2)当侧重物体在50g及以上时,称重误差小于1g。
5.设计报告结构及规性
设计报告:秤是一种在实际工作和生活中经常用到的测量器具。随着计量技术和电子技术的发展,传统纯机械结构的杆秤、台秤、磅秤等称量装置逐步被淘汰,电子称量装置电子秤、电子天平等以其准确、快速、方便、显示直观等诸多优点而受到人们的青睐。
和传统秤相比较,电子秤利用新型传感器、高精度AD转换器件、单片机设计实现,具有精度高、功能强等特点。本课题设计的电子秤具有基本称重、键盘输入、计算价格、显示、超重报警功能。该电子秤的测量范围为0-10Kg,测量精度达到5g,有高精度,低成本,易携带的特点。电子秤采用液晶显示汉字和测量记过,比传统秤具有更高的准确性和直观性。另外,该电子秤电路简单,使用寿命长,应用范围广,可以应用于商场、超市、家庭等场所,成为人们日常生活中不可少的必需品。
经过整体测试,设计完成的简易电子秤完全符合设计要求。
5.附加代码
#include <reg52.h> #include <intrins.h> #include <string.h> #define uchar unsigned char #define uint unsigned int bit bdata flag_key; #include "main.h" #include "LCD12864.h" #include "HX711.h" #include "keyboard.h" #include "eeprom52.h" //#include "wannianli.h" //#include "yyxp.h" unsigned long HX711_Buffer = 0; unsigned long Weight_Maopi = 0; unsigned long Weight_Maopi_0 = 0; int qupi=0; long Weight_Shiwu = 0; //键盘处理变量 unsigned char keycode; unsigned char DotPos; //小数点标志及位置 uint GapValue,GapValue1; unsigned long idata price; //单价,长整型值,单位为分 unsigned long idata danjia[8]={11,22,33,44,100,200,300,400}; unsigned char count_danjia; unsigned long idata money,total_money; //总价,长整型值,单位为分 //定义标识 volatile bit FlagTest = 0; //定时测试标志,每0.5秒置位,测完清0 volatile bit FlagKeyPress = 0; //有键按下标志,处理完毕清0 //校准参数 //因为不同的传感器特性曲线不是很一致,因此,每一个传感器需要矫正这里这个参数才能使测量值很更准确。 //当发现测试出来的重量偏大时,增加该数值。 //如果测试出来的重量偏小时,减小改数值。 //该值可以为小数 //#define GapValue 349 sbit LED=P3^6; sbit Buzzer = P2^0; volatile bit ClearWeighFlag = 0; //传感器调零标志位,清除0漂 /******************把数据保存到单片机内部eeprom中******************/ void write_eeprom() { SectorErase(0x2000); GapValue1=GapValue&0x00ff; byte_write(0x2000, GapValue1); GapValue1=(GapValue&0xff00)>>8; byte_write(0x2001, GapValue1); byte_write(0x2060, a_a); } /******************把数据从单片机内部eeprom中读出来*****************/ void read_eeprom() { GapValue = byte_read(0x2001); GapValue = (GapValue<<8)|byte_read(0x2000); a_a = byte_read(0x2060); } /**************开机自检eeprom初始化*****************/ void init_eeprom() { read_eeprom(); //先读 if(a_a != 1) //新的单片机初始单片机内问eeprom { GapValue = 3500; a_a = 1; write_eeprom(); //保存数据 } } //显示单价,单位为元,四位整数,两位小数 void Display_Price() { LCD12864_write_com(0x88); LCD12864_write_word("单价: "); LCD12864_write_data(price/100 + 0x30); LCD12864_write_data(price%100/10 + 0x30); LCD12864_write_data('.'); LCD12864_write_data(price%10 + 0x30); LCD12864_write_word("元"); } //显示重量,单位kg,两位整数,三位小数 void Display_Weight() { LCD12864_write_com(0x90); LCD12864_write_word("重量: "); LCD12864_write_data(' '); LCD12864_write_data(Weight_Shiwu/1000 + 0x30); LCD12864_write_data('.'); LCD12864_write_data(Weight_Shiwu%1000/100 + 0x30); LCD12864_write_data(Weight_Shiwu%100/10 + 0x30); LCD12864_write_data(Weight_Shiwu%10 + 0x30); LCD12864_write_word("Kg"); } //显示总价,单位为元,四位整数,两位小数 void Display_Money() { // unsigned int i,j; LCD12864_write_com(0x98); //指针设置 LCD12864_write_word("总价: "); LCD12864_write_com(0x9f); //指针设置 LCD12864_write_word("元"); if (money>=1000) { LCD12864_write_com(0x9c); LCD12864_write_data(' '); LCD12864_write_data(money/1000 + 0x30); LCD12864_write_data(money%1000/100 + 0x30); LCD12864_write_data(money%100/10 + 0x30); LCD12864_write_data('.'); LCD12864_write_data(money%10 + 0x30); } else if (money>=100) { LCD12864_write_com(0x9c); LCD12864_write_data(' '); LCD12864_write_data(0x20); LCD12864_write_data(money%1000/100 + 0x30); LCD12864_write_data(money%100/10 + 0x30); LCD12864_write_data('.'); LCD12864_write_data(money%10 + 0x30); } else if(money>=10) { LCD12864_write_com(0x9c); LCD12864_write_data(' '); LCD12864_write_data(0x20); LCD12864_write_data(0x20); LCD12864_write_data(money%100/10 + 0x30); LCD12864_write_data('.'); LCD12864_write_data(money%10+ 0x30); } else { LCD12864_write_com(0x9c); LCD12864_write_data(' '); LCD12864_write_data(0x20); LCD12864_write_data(0x20); LCD12864_write_data(0 + 0x30); LCD12864_write_data('.'); LCD12864_write_data(money%10 + 0x30); } } //数据初始化 void Data_Init() { price = 0; DotPos = 0; } //定时器0初始化 void Timer0_Init() { ET0 = 1; //允许定时器0中断 TMOD = 1; //定时器工作方式选择 TL0 = 0xb0; TH0 = 0x3c; //定时器赋予初值 TR0 = 1; //启动定时器 } //定时器0中断 void Timer0_ISR (void) interrupt 1 using 0 { uchar Counter; TL0 = 0xb0; TH0 = 0x3c; //定时器赋予初值 //每0.5秒钟刷新重量 Counter ++; if (Counter >= 10) { FlagTest = 1; Counter = 0; } } //按键响应程序,参数是键值 //返回键值: // 7 8 9 10(清0) // 4 5 6 11(删除) // 1 2 3 12(未定义) // 14(未定义) 0 15(.) 13(确定价格) void KeyPress(uchar keycode) { switch (keycode) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: //目前在设置整数位,要注意price是整型,存储单位为分 if (DotPos == 0) { //最多只能设置到千位 if (price<100) { price=price*10+keycode*10; } }//目前在设置小数位 else if (DotPos==1) //小数点后第一位 { price=price+keycode; DotPos=2; } Display_Price(); break; case 10: //清零键 // speak(41); if(qupi==0) qupi=Weight_Shiwu; else qupi=0; Display_Price(); // FlagSetPrice = 0; DotPos = 0; break; case 11: //删除键,按一次删除最右一个数字 price=0; DotPos=0; Display_Price(); break; case 12: //加 if(GapValue<10000) GapValue++; // Get_Weight(); break; case 13: //减 if(GapValue>1) GapValue--; // Get_Weight(); break; case 14: count_danjia++; if(count_danjia>7) count_danjia=0; price = danjia[count_danjia]; Display_Price(); break; case 15: //小数点按下 DotPos = 1; //小数点后第一位 break; } } //**************************************************** //主函数 //**************************************************** void main() { // yyxp_rest=1; // yyxp_data=1; init_eeprom(); //开始初始化保存的数据 Init_LCD12864(); //初始化LCD1602 EA = 0; Data_Init(); Timer0_Init(); //初中始化完成,开断 EA = 1; // Ds1302Init(); // Get_Maopi(); LCD12864_write_com(0x80); //指针设置 LCD12864_write_word("欢迎使用电子称 "); // LCD12864_write_com(0x90); //指针设置 LCD12864_write_word(" 初始化中 "); LCD12864_write_com(0x88); //指针设置 LCD12864_write_word(" 请稍候。。。"); Delay_ms(2000); Get_Maopi(); // Get_Maopi(); //称毛皮重量 // Weight_Shiwu = 9000; while(1) { //每0.5秒称重一次 if (FlagTest==1/*&&keynum==0*/) { Get_Weight(); } keycode = Getkeyboard(); //有效键值0-15 if (keycode<16) { KeyPress(keycode); Buzzer=0; Delay_ms(100); Buzzer=1; while(keycode<16) { if(keycode==12||keycode==13) { Buzzer=0; Delay_ms(10); Buzzer=1; KeyPress(keycode); Get_Weight(); flag_key=1; } keycode = Getkeyboard(); } write_eeprom(); //保存数据 } } } //**************************************************** //称重 //**************************************************** void Get_Weight() { Weight_Shiwu = HX711_Read(); Weight_Shiwu = Weight_Shiwu - Weight_Maopi; //获取净重 if((int)((float)Weight_Shiwu*10/GapValue)<qupi) qupi=0; Weight_Shiwu = (int)((float)Weight_Shiwu*10/GapValue)-qupi; //计算实物的实际重量 // Weight_Shiwu = (unsigned int)((float)Weight_Shiwu)-qupi; if(Weight_Shiwu > 10000||Weight_Shiwu <0) //超重报警 { Buzzer = !Buzzer; LED=!LED; // if(yyxp_busy==1) // speak(40); LCD12864_write_com(0x94); LCD12864_write_word(" -.---"); } else { if(Weight_Shiwu<10000) LED=0; else if(Weight_Shiwu>10000) LED=1; Buzzer = 1; // if(Weight_Shiwu>10) { Display_Weight(); money = Weight_Shiwu*price/1000; //money单位为分 //显示总金额 Display_Money(); Display_Price(); } } } //**************************************************** //获取毛皮重量 //**************************************************** void Get_Maopi() { unsigned char clear; mm: Weight_Maopi_0 = HX711_Read(); for(clear=0;clear<10;clear++) { // Buzzer=1; LED=1; Delay_ms(100); LED=0; Delay_ms(100); } Weight_Maopi = HX711_Read(); if(Weight_Maopi/GapValue!=Weight_Maopi_0/GapValue) goto mm; Buzzer=0; Delay_ms(500); Buzzer=1; } //**************************************************** //MS延时函数(12M晶振下测试) //**************************************************** void Delay_ms(unsigned int n) { unsigned int i,j; for(i=0;i<n;i++) for(j=0;j<123;j++); } HX711模块的封装代码: #include "HX711.h" //**************************************************** //延时函数 //**************************************************** void Delay__hx711_us(void) { _nop_(); _nop_(); } //**************************************************** //读取HX711 //**************************************************** unsigned long HX711_Read(void) //增益128 { unsigned long count; unsigned char i; HX711_DOUT=1; Delay__hx711_us(); HX711_SCK=0; count=0; while(HX711_DOUT); for(i=0;i<24;i++) { HX711_SCK=1; count=count<<1; HX711_SCK=0; if(HX711_DOUT) count++; } HX711_SCK=1; count=count^0x800000;//第25个脉冲下降沿来时,转换数据 Delay__hx711_us(); HX711_SCK=0; return(count); }