转载自互联网!!! 作者:朱珍奇 1、设计目的 单片机以其功能强,体积小,功耗低,易开发等很多优势被广泛应用。但单片机不是万能的,也存在不适合的场合, 1. 利用单片机对数字电子秤的各种功能进行控制。 2. 数字电子秤能够显示商品的名称,价格,总重,总价。 3. 数字电子秤能够自动完成商品的价格计算。 4. 数字电子秤具有储存几种简单商品价格的功能。 5. 数字电子秤具有超重提醒功能,一旦重量超出了自身测量重量的范围,即发出警报。 6. 数字电子秤的测量范围要求达到5KG,测量精度要求达到0.001。
2、设计的主要内容和要求 本文主要完成一个简单实用数字电子秤的硬件电路部分和软件部分的设计。在设计的过程学会使用单片机对数字电子秤的各种功能进行控制。本设计中的数字电子秤要求能够显示商品的名称、价格、总量、总价等;能够自动完成商品的价格计算;能够储存几种简单商品的价格;能够具有超重提醒功能,一旦重量超出了自身重量的测量的范围,发出警报;同时对数字电子秤的测量范围要达到5KG,测量精度要求达到0.001。 3、整体设计方案 3.1设计方案 整个数字电子秤电路由电源电路、单片机主控制电路、LM4229显示电路、蜂鸣器报警电路、4*4键盘电路和压力传感电路(ADC0832采样)6个部分组成。如图3.1所示。 3.2工作原理 打开电源开关,数字电子秤开始工作。接通电源时,数字电子秤进入欢迎界面“欢迎使用电子秤设计······”。此时数字电子秤上MCU开始工作,键盘不断进行扫描,同时 通过ADC0832也不断进行外部称量数据采样,LCD上显示“实用电子秤 名称 单价······”。当载物台上放有物体时,ADC0832立即将数据收集送给单片机处理。此时工作人员只要输入对应商品的代码编号,在240*128的LCD上可以看到相应商品的名称,单价,总重,总价格等信息。在称量的过程中,一旦物体自身的重量超出电子秤的称量范围,蜂鸣器立即会发出“滴滴····”警报声告诉工作人员,所称量物品超重。
图3.1 整体设计方框图
4、硬件电路的设计 数字电子秤采用AT89C51单片机作为微处理器,接口电路由晶振、LM4229显示电路、4*4按键电路、ADC0832电路、报警电路、存储器等组成。控制器系统的硬件电路原理图如图4.1所示。 控制器系统硬件电路的工作过程是:打开电源时,MCU及各个部分电路开始工作,MCU调用内部存储数据对各部分接口电路初始化。200ms后LM4229进入欢迎界面,ADC0832不断对外部数据进行采样交给MCU进行处理,一旦有物品放入载物台,ADC0832立即发送中断请求,并将本次采集数据交给MCU处理,之后LM4229显示相应数据量。在此过程中,键盘也在不断进行扫描,一旦有键按下,单片机也会对其数据进行相应处理,然后将对LM4229进行写操作。 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image004.jpg 图4.1 数字电子秤硬件电路图 5、软件设计 本设计中的程序由主控制程序、LM4229液晶显示驱动程序、ADC0832采样程序和4*4键盘程序组成。
5.1主程序设计 整个设计中采用由下向上的设计思路。主程序中主要完成对LCM4229、ADC0832、及键盘扫描程序的调用。在编写程序的过程中,各变量统一采用全局变量方式命名,同时考虑到电子秤对精度的要求,本设计中的价格及总量相关的变量全部采用浮点数。主程序流程图如图5.1所示。 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image006.jpg
图5.1 主程序流程图file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image008.jpg
5.2 LM4229液晶显示驱动程序 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image010.jpg 图5.2LM4229液晶显示驱动程序流程图 LM4229液晶能够显示比较复杂的汉字和图形,首先必须对其写入控制操作字,包括图形的显示方式,字体的模式。然后写入初始行地址,指针自动左移,直到写完全部数据为止。 write_data(place&0xff); //写入地址高位 write_data(place/256); //写入地址低位 write_com(0x24); //地址设置 write_com(0xb0); //设置数据自动写 write_data(ASC_MSK[(c1-0x20)*16+k]); /*---例如:0的ASCII码为0x30, 在ASC_MSK中的位置为0x10*16 ---16字节字码依次写入LCD---*/ write_com(0xb2); //自动复位 place=place+30; 5.3 ADC0832采样程序 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image012.jpg
图5.3 ADC0832采样程序程序流程图 MCU通过拉低CS、拉高CLK来启动ADC0832进行外部压力传感转换后的电压信号进行采样,每产生8个CLK脉冲,DATA获得一位完整的8bit数据,此时MCU发送中断请求,拉高CS,拉低CLK,并将数据DAT返回。 5.4 4*4键盘程序
file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image013.jpg 图5.4 4*4键盘程序 本设计中采用了4*4矩阵式键盘,单片机定时进行查询。首先单片机发送行扫描代码,然后进行列扫描,当发现某一列出现了低电平时,即返回相应的键盘值。若没有发现则说明当前行没有键按下,行扫描右移一位,继续执行列扫描。 MCU根据相应的键值,即可确定被按下的键。 6、系统仿真 在protues的ISIS 7.4sp2软件环境下画出电路原理图,接下来就是将设计的程序在Keil C51 μVision3开发集成环境上编译成机器语言,进入Protues的ISIS,双击AT89C51,在"ProgramFile中"添加"main.hex"文件到AT89C51中,如图6.1所示。 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image015.jpg 图6.1 数字电子秤仿真设置图 该仿真验证的过程:首先按开始按file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image017.jpg,此时数字电子秤进入欢迎界面。LM4229 上显示"欢迎使用电子秤 设计·····"。如图6.2所示。
图6.2 数字电子秤欢迎界面图
接下来调节压力传感模拟电路电压,将电压设为0.00表示此时载物台上没有物体。此时LM4229切换到称量画面。显示指示“实用电子秤名称······ 单价:0.000元/千克 总重量:0.000千克 总价:00.000元”。如图6.3所示。 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image021.jpg 图6.3 数字电子秤模拟空载图 最后,上调压力传感电压表示已载有商品,同时按下“6”号键,表示选择6号商品“苹果”。此时LM4229上显示“名称:苹果 单价:6.800元/千克 总重量:3.496千克总价:“23.733元”(实际6.8*3.496=23.7758元)。达到基本要求。如图6.4所示。最大称量重量4.980KG,如图6.5所示。 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image023.jpg 图6.4 数字电子秤模拟载物显示仿真图 file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image025.jpg 图6.4数字电子秤模拟最大称量范围仿真图 7、使用说明
在使用仿真的过程中,首先打开仿真电路图,并加载好“HEX”文件到单片机中。后将压力传感电压调制0.00,点击file:///C:/Users/FSY/AppData/Local/Temp/msohtml1/05/clip_image026.jpg按钮,开始仿真。(为了能够看到清晰的演示过程,请将电路的背景设为无。)整个电子秤设置了“0~15”个按键,其中的“0”键表示取消,其他键代表15种可供选择商品价格。(另本设计的仿真文件需要安装protues7.4以上版本才能打开。) 8、设计总结 通过本次电子技能课程数字电子秤的设计,让我更加熟悉了单片机的整个设计流程。在设计过程中,我也意识到好的编程书写习惯对软件设计的重要性。刚刚开始由于自己变量大部分采用的是局部变量,导致程序仿真出错,给自己的修改创造不小的麻烦。经过一天的努力,统一采用全局变量终于实现最终的仿真结果和设计的全部任务和要求。当然本数字电子秤还有很多可以改进的地方,例如可以改进按键功能方便工作人员随时对各数据进行修改等等。由于设计时间有限,只能在以后的学习过程中做更进一步的改进。
程序源
- #include<reg51.h>
- #include<intrins.h>
- #include <absacc.h>
- #include <math.h>
- #include<lm4229.h>
- #define uchar unsigned char
- #define uint unsigned int
- sbit ADCS =P3^5;
- sbit ADDI =P3^7;
- sbit ADDO =P3^7;
- sbit ADCLK =P3^6;
- uint x1,y1,z1=0,w1;
- uchar ad_data,k,n,m,e,num,s; //采样值存储
- uint temp1;
- sbit beep =P3^0;
- char press_data; //标度变换存储单元
- float press;
- unsigned char ad_alarm,temp; //报警值存储单元
- unsigned char abc[5]={48,46,48,48,48};
- unsigned char price_all[6]={48,48,46,48,48,48};
- floatprice_unit[10]={5.5,2.8,3.6,4.5,5.8,6.8,7.9,8.0,9.5,0}; //商品初始单价
- uchar price_danjia[5]={48,46,48,48,48};
- float price;
- uint price_temp1,price_temp2; //商品总价
- uchar Adc0832(unsigned char channel);
- void alarm(void);
- void data_pro(void);
- void delay(uint k);
- void keyscan();
- void disp_init();
- void price_jisuan();
- /************
- 主函数
- ************/
- void main(void)
- {
- delay(500); //系统延时500ms启动
- //ad_data=0; //采样值存储单元初始化为0
- lcd_init(); //显示初始化
- disp_init(); //开始进入欢迎界面
- delay(1000); //延时进入称量画面
- clear_lcd(0,4,40);
- clear_lcd(16,0,80);
- clear_lcd(28,0,40);
- clear_lcd(44,0,80);
- clear_lcd(56,0,40);
- clear_lcd(72,0,80);
- clear_lcd(84,0,40);
- clear_lcd(100,0,80);
- clear_lcd(112,0,40);
- write_lcd(0,8,"实用电子秤");
- while(1)
- {
- ad_data =Adc0832(0); //采样值存储单元初始化为0
- alarm();
- data_pro(); //读取重量
- keyscan(); //查询商品种类
- write_lcd(40,0,"------------------------------");
- write_lcd(56,0,"单 价:");
- write_lcd(56,11,price_danjia);
- write_lcd(56,20,"元/千克");
- write_lcd(72,0,"总重量:");
- write_lcd(72,11,abc);
- write_lcd(72,20,"千克");
- write_lcd(88,0,"总 价:");
- price_jisuan(); //计算出价格
- write_lcd(88,10,price_all);
- write_lcd(88,20,"元");
- write_lcd(112,0,"设计学生: 0612201*16 朱珍奇");
- }
- }
- /************
- 读ADC0832函数
- ************/
- //采集并返回
- uchar Adc0832(unsigned char channel) //AD转换,返回结果
- {
- uchari=0;
- uchar j;
- uintdat=0;
- ucharndat=0;
- if(channel==0)channel=2;
- if(channel==1)channel=3;
- ADDI=1;
- _nop_();
- _nop_();
- ADCS=0;//拉低CS端
- _nop_();
- _nop_();
- ADCLK=1;//拉高CLK端
- _nop_();
- _nop_();
- ADCLK=0;//拉低CLK端,形成下降沿1
- _nop_();
- _nop_();
- ADCLK=1;//拉高CLK端
- ADDI=channel&0x1;
- _nop_();
- _nop_();
- ADCLK=0;//拉低CLK端,形成下降沿2
- _nop_();
- _nop_();
- ADCLK=1;//拉高CLK端
- ADDI=(channel>>1)&0x1;
- _nop_();
- _nop_();
- ADCLK=0;//拉低CLK端,形成下降沿3
- ADDI=1;//控制命令结束
- _nop_();
- _nop_();
- dat=0;
- for(i=0;i<8;i++)
- {
- dat|=ADDO;//收数据
- ADCLK=1;
- _nop_();
- _nop_();
- ADCLK=0;//形成一次时钟脉冲
- _nop_();
- _nop_();
- dat<<=1;
- if(i==7)dat|=ADDO;
- }
- for(i=0;i<8;i++)
- {
- j=0;
- j=j|ADDO;//收数据
- ADCLK=1;
- _nop_();
- _nop_();
- ADCLK=0;//形成一次时钟脉冲
- _nop_();
- _nop_();
- j=j<<7;
- ndat=ndat|j;
- if(i<7)ndat>>=1;
- }
- ADCS=1;//拉搞CS端
- ADCLK=0;//拉低CLK端
- ADDO=1;//拉高数据端,回到初始状态
- dat<<=8;
- dat|=ndat;
- return(dat); //returnad k
- }
- void data_pro(void)
- {
- unsigned int;
- if(0<ad_data<256)
- {
- int vary=ad_data;
- press=(0.019531*vary);
-
- temp1=(int)(press*1000); //放大1000倍,便于后面的计算
- abc[0]=temp1/1000+48; //取压力值百位
- abc[1]=46;
- abc[2]=(temp1%1000)/100+48; //取压力值十位
- abc[3]=((temp1%1000)%100)/10+48; //取压力值个位
- abc[4]=((temp1%1000)%100)%10+48; //取压力值十分位
- }
- }
- /*****************报警子函数*******************/
- void alarm(void)
- {
- if(ad_data>=256)
- beep=0; //则启动报警
-
- else
- beep=1;
- }
- void delay(uint k)
- {
- uint i,j;
- for(i=0;i<k;i++)
- for(j=0;j<100;j++);
- }
- //开机欢迎界面
- void disp_init()
- {
- write_lcd(0,8,"欢迎使用电子秤");
- write_lcd(16,0,"------------------------------");
- write_lcd(28,0,"设计学生:朱珍奇");
- write_lcd(44,0,"------------------------------");
- write_lcd(56,0,"班级学号:0612201*16");
- write_lcd(72,0,"------------------------------");
- write_lcd(84,0,"指导老师:龚伦峰");
- write_lcd(100,0,"------------------------------");
- write_lcd(112,0,"设计日期:2009年6月16日");
- }
- //键盘服务程序
- void keyscan()
- {
- P1=0xfe;
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- delay(5);
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- temp=P1;
- switch(temp)
- {
- case0xee:num=1,price=price_unit[0], write_lcd(24,0,"名 称: 杏 仁");
- break;
- case0xde:num=2,price=price_unit[1],write_lcd(24,0,"名 称: 李 子");
- break;
- case0xbe:num=3,price=price_unit[2],write_lcd(24,0,"名 称: 草 莓");
- break;
- case0x7e:num=4,price=price_unit[3],write_lcd(24,0,"名 称: 葡 萄");
- break;
- }
- while(temp!=0xf0)
- {
- temp=P1;
- temp=temp&0xf0;
- }
- }
- }
- P1=0xfd;
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- delay(5);
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- temp=P1;
- switch(temp)
- {
- case0xed:num=5,price=price_unit[4],write_lcd(24,0,"名 称: 西 瓜");
- break;
- case0xdd:num=6,price=price_unit[5],write_lcd(24,0,"名 称: 苹 果");
- break;
- case0xbd:num=7,price=price_unit[6],write_lcd(24,0,"名 称: 雪 梨");
- break;
- case0x7d:num=8,price=price_unit[7],write_lcd(24,0,"名 称: 核 桃");
- break;
- }
- while(temp!=0xf0)
- {
- temp=P1;
- temp=temp&0xf0;
- }
- }
- }
- P1=0xfb;
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- delay(5);
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- temp=P1;
- switch(temp)
- {
- case0xeb:num=9,price=price_unit[8],write_lcd(24,0,"名 称: 香 蕉");
- break;
- case0xdb:num=10,price=price_unit[9],write_lcd(24,0,"名 称: 商品代码");
- break;
- case0xbb:num=11,price=price_unit[1];
- break;
- case0x7b:num=12,price=price_unit[2];
- break;
- }
- while(temp!=0xf0)
- {
- temp=P1;
- temp=temp&0xf0;
- }
- }
- }
- P1=0xf7;
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- delay(5);
- temp=P1;
- temp=temp&0xf0;
- while(temp!=0xf0)
- {
- temp=P1;
- switch(temp)
- {
- case0xe7:num=13,price=price_unit[3];
- break;
- case0xd7:num=14,price=price_unit[4];
- break;
- case0xb7:num=15,price=price_unit[5];
- break;
- case0x77:num=16,price=price_unit[6];
- break;
- }
- while(temp!=0xf0)
- {
- temp=P1;
- temp=temp&0xf0;
- }
- }
- }
- price_temp1=(int)(price*1000);
- price_danjia[0]=price_temp1/1000+48; //取单价个位
- price_danjia[1]=46;
- price_danjia[2]=(price_temp1%1000)/100+48; //取单价十分位
- price_danjia[3]=((price_temp1%1000)%100)/10+48; //取单价百分位
- price_danjia[4]=((price_temp1%1000)%100)%10+48; //取单价千分位
- }
- void price_jisuan()
- {
- price_temp2=(int)(price*press*1000);
- price_all[0]=price_temp2/10000+48;
- price_all[1]=(price_temp2/1000)%10+48;
- price_all[2]=46;
- price_all[3]=(price_temp2%1000)/100+48;
- price_all[4]=((price_temp2%1000)%100)/10+48;
- price_all[5]=((price_temp2%1000)%100)%10+48;
- }
复制代码
|