简易计算器.docx
《简易计算器.docx》由会员分享,可在线阅读,更多相关《简易计算器.docx(25页珍藏版)》请在冰点文库上搜索。
简易计算器
单片机课程设计
题目:
简易计算器的设计
专业:
06电子信息科学与技术
姓名:
倪耀兴
学号:
060303008
指导老师:
蔡植善
理工学院电信系
设计日期:
2009年5月12日
简易计算器的设计
1.引言------------------------------------------------2
2.设计任务和要求-------------------------------------2
2.1设计要求-------------------------------------------0
2.2设计方案的确定-------------------------------------2
2.3主要元器件介绍--------------------------------------------------------3
3.简易计算器的硬件设计--------------------------------4
3.1键盘电路的设计----------------------------------4
3.2显示电路的设计---------------------------------4
4.简易计算器的软件设计-------------------------------6
4.1简易计算器的软件规则--------------------------------6
4.2中断查键的按键程序设计-------------------------------7
4.3显示模块程序的设计----------------------------7
4.4主程序的设计---------------------------------------7
5.调试及性能分析--------------------------------------7
5.1硬件调试--------------------------------------------7
5.2软件调试--------------------------------------------7
5.3相关可靠性软件设计----------------------------------7
5.4完整的源程序---------------------------------------------------------8-
6.关键程序的流程图-------------------------------------17
7.设计心得------------------------------------------19
8.参考文献-----------------------------------------19
1.引言
单片机实用接口技术是电子信息工程专业本科生的必修课程。
在完成理论学习和必要的实验后,本科学生掌握了单片机的基本原理和各种基本功能的应用,但对单片机的硬件实际应用设计和单片机完整的用户程序设计还不清楚,实际动手能力不够,因此对该课程进行一次课程设计是有必要的。
单片机实用接口技术既要让学生巩固课本学到的理论,还要让学生学习单片机硬件电路设计和用户程序设计,同时学习查阅资料、参考资料的方法。
单片机实用接口技术主要是通过学生独立设计方案并自己动手用计算机电路设计软件,编写和调试用户程序,来加深对单片机的认识,充分发挥学生的个体创新能力。
2.设计任务和要求
2.1设计要求
功能要求:
①可以对1000以内的正负数进行加减乘除的运算,有效位数取6位。
②用1602LCD作显示器。
③键盘为4×5,包括0~9十个数字键以及+、-、×、÷、enter、clear、负号、光标左移、光标右移和小数点等20个键。
键盘扫描可选用非编码按键,也可用元件CH451(编码按键)。
2.2设计方案的确定
在日常生活中,绝大部分的计算器都是由按键控制模块、LCD液晶显示模块两个模块组成。
按照2.1的设计要求,本课题需要使用LCD1602液晶显示和CH451键盘扫描4*5键盘.4*5键盘定义十个数字键,十个功能键,使用串行静态显示显示运算结果。
主程序进行初始化,采用中断扫描查键,每次按键后调用显示子程序。
设计总框图如下:
2.3主要元器件介绍
1、LCD液晶显示模块:
现在的字符型液晶模块已经是单片机应用设计中最常用的信息显示器件了。
专门用于显示字母、数字、符号等点阵式LCD。
1602型LCD显示模块具有体积小,功耗低,显示内容丰富等特点。
1602型LCD可以显示2行16个字符,有8位数据总线D0~D7和RS,R/W,EN三个控制端口,采用AT89C51单片机控制。
2、按键控制模块:
CH451是一个的整合了数码管显示驱动和键盘扫描控制及μP监控的多功能外围芯片。
CH451内置RC振荡电路,可以动态驱动8位数码管或者64位LED,具有BCD译码、闪烁、移位等功能;同时还可以进行64键的键盘扫描;CH451通过可以级联的串行接口与单片机等交换数据;并且提供上电复位和看门狗等监控功能。
另外,其内置64键键盘控制器,键盘中断,低电平有效输出。
还提供按键释放标志位,可供查询按键按下与释放。
上图是在未启用键盘扫描功能时时序
下图为单片机从CH451获得
按键代码的过程是:
①输出一位数据,即向DIN输出读取按键代码命令的最低位数据B0,并向DCLK输出低电平脉
冲;
②以同样的方式,输出读取按键代码命令的位数据B1~B11;
③向LOAD输出低电平脉冲,其中包括一个上升沿使CH451加载串行数据,CH451分析出是读取按键代码命令,立即在DOUT输出按键代码的最高位数据K6;
④读取一位数据,即从DOUT输入按键代码的最高位数据K6,并向DCLK输出低电平脉冲;
⑤以同样的方式,输入按键代码的位数据K5~K0。
3.简易计算器的硬件设计
简易计算器主要包括:
键盘电路,显示电路。
3.1键盘电路的设计
键盘可分为两类:
编码键盘和非编码键盘。
编码键盘是较多按键(20个以上)和专用驱动芯片的组合,当按下某个按键时,它能够处理按键抖动、连击等问题,直接输出按键的编码,无需系统软件干预。
通用计算机使用的标准键盘就是编码键盘。
在单片机设计中,使用CH451芯片可以组成编码键盘,同时还可以兼顾LCD的显示驱动,其相关的接口电路和接口软件均可在芯片资料中得到。
当系统功能比较复杂,按键数量很多时,采用编码键盘可以简化软件设计。
本课题需要的是20个按键,故选择用编码键盘,分别由SEG0-7、DIG0-7端口控制,低电平有效。
在键盘扫描期间,DIG7~DIG0引脚用于列
扫描输出,SEG7~SEG0引脚都带有内部下拉电阻,用于行扫描输入;当启用键盘扫描功能后,DOUT引脚的功能由串行接口的数据输出变为键盘中断以及数据输出。
CH451定期在显示驱动扫描过程中插入键盘扫描,并实行两次扫描,只有当两次键盘扫描的结果相同时,按键才会被确认有效。
3.2显示电路的设计
根据任务的要求,采用LCD数码管进行显示是一种经济实用的方法,1602型LCD的显示由单片机的P0口驱动,由于是P0口驱动,需加一个排阻增大其驱动电流。
P1口控制LCD的数据/命令、读/写和使能信号。
最终电路如下图所示:
设计的PCB图:
4.简易计算器的软件设计
4.1简易计算器的软件规则
简易计算器的程序主要包括以下功能模块:
1、中断查键模块,分为读键程序、判键程序段、运算操作子程序等部分;
2、基于LCD的静态显示模块;
3、主模块,为系统的初始化。
4.2中断查键的按键程序设计
进入外部中断0的中断程序后,首先重新付初值,然后调用读键程序,如果有键按下,则判断按键是否与上次按键相同,如果相同则判断按键相应位是否为1,如果不为1,说明这不是持续按键导致的按键相应,并且进行相应的程序。
如果不是则退出中断程序。
数字键按下则将相应的数字送入缓存区,功能键按下则执行相应的程序。
首先对数字键的程序段进行相应的设计,如果运算键(+、-,×,÷)响应标志不为1,则将输入的数字送入第一个操作数缓存区,并且清空所有的响应位。
否则送入第二个操作数缓存区。
其次对功能键的程序段进行相应的设计。
如果运算键(+、-,×,÷)第一次被按下,则置相应的标志位为1,并且将运算键响应标志位置1,清空第二个操作数的缓存区,为输入操作数做准备,如果是第二次按下则先调用运算操作子程序,执行上次按下的运算键的运算,置相应的标志位为1,并且将运算键响应标志位置1,清空第二个操作数的缓存区,为输入操作数做准备。
如果是’=’键按下,则调用运算操作子程序。
从上面的设计中我们可以看到,本程序段需要八个输入缓存区,其中四个为第一个操作数BCD码缓存区,另外四个为第二个操作数BCD码缓存区,需要五个标志位,一个为运算键响应标志位,另外四个为运算符号标志位,分别为加法响应标志位、减法响应标志位、乘法响应标志位和除法响应标志位。
并且调用了一个运算操作子程序,对此我们进行如下的子程序设计。
首先将第二个操作数的BCD码进行转换,得到相应的二进制数,并且放入相应的缓存区,判断结果响应位是否为1,如果不为1,则将第一个操作数的BCD码进行转换,得到相应的二进制数,并且放入相应的缓存区,按照运算符号响应标志位执行相应的程序,并将运算结果放入相应的缓存区,并且将结果响应标志位置1,将其它响应标志位置0;如果结果响应位为1,则将上次的运算结果存入第一个操作数缓存区,再执行相应的操作,最后将得到的运算结果放入相应的缓存区,并且将其它响应标志位置0。
运算操作子程序需要十个缓存区,两个为第一个操作数的二进制数,两个为第二个操作数的二进制数,两个为结果的二进制数,四个为结果的BCD码缓存区,一个标志位,为结果响应标志位。
如果要支持负运算,只要在定义一个标志位:
负数标志位,并将运算操作子程序作一定的修改,首先考虑到负数是运算减法时产生的(键盘上没有负数输入键),所以运算减法时,如果负数标志为0时,当第一个操作数比第二个操作数小时,则将两个操作数互换相减,并置负数标志位为1;如果为1,则将两个操作数相加,并置负数标志位为1。
运算加法时,如果负数标志位为1,则先比较两个操作数大小,如果第一个操作数大,则将第一个操作数减去第二个操作数,并置负数标志位为1,否则将第二个操作数减去第一个操作数,并将负数标志位置0。
乘法与除法运算是不需要考虑负数的影响的,故不需要更改。
4.3显示模块程序的设计
由于使用的是静态显示,故先要对SCON进行相关设置,让串口工作在方式0下,设置1602为8位的数据接口,两行显示,5、10点阵字符,显示打开,光标开并不闪烁。
4.4主程序的设计
主程序主要是用来进行初始化的,调用自检程序,清空各个标志位,清空缓存区,给定外部中断0工作方式。
在等待外部中断0中断时主程序在相关程序段内循环,这样可以降低功耗。
5.调试及性能分析
5.1硬件调试
在本次实验中首次采用单面板,但本实验要求有CH451D的贴片,所以在画PCB是要特别注意,而且在做板的过程中需要注意的细节很多,由于之前有做过相应的实验,所以难度降低了很多。
硬件调试时首先要检查5V电源是否有短路,再插上电源检查各个芯片是否会发热。
然后要检查晶振是否会正常起振,看AT89S51的18脚是否有约12MHZ的频率,看30是否有1/6的晶振频率;再检查CH451的的使能端是否正常工作,,最后看LCD是否有显示。
当检查没问题了就可以进行软件调试了。
5.2软件调试
因为CH451和LCD没有直接的联系,所以他们可以独立调试。
首先,可察看LCD是否可以显示字符、字符串。
然后在看是否可以显示自行定义的内容和控制显示的位置。
然后,调试CH451扫描的按键,看按下按键时是否会进入中断,各个键子的值是否对应正确,扫描按键的调试。
主要利用那个扫描,设一个中断函数,每次有键按下,单片机的外部中断被激活,然后进入中断,在中断函数里读一组数据,然后再LCD里输出,编写就是要编读数据那部分就行,编写加载数据那部分,按照时序图来编啊。
再则,把前面两个程序合在一起,直接下载到芯片上来调试看按下按键时LCD是否会显示该按键的值。
最后,把加减乘除运算子程序和一些功能按键的子程序加入到主程序中,进行调试,看按键的操作和功能是否已经实现计算器要求的功能。
5.3相关可靠性软件设计
1、为防止程序死循环,可以在软件中加入看门狗技术
2、由于CH451带有防抖措施,所以本身可以防止键盘抖动造成按键错误。
5.4完整的源程序
//按键对应功能//
/*12345
67890
+-*/=
.clear左移右移负号*/
#include
#include
#defineucharunsignedchar
#defineuintlongint
//---------定义引脚--------------------
bitclr=0;
bitok=0;
bitxiaoshu=0;
bitjiego=0;
sbitdout=P3^2;
sbitload=P2^2;
sbitdin=P2^1;
sbitdclk=P2^0;
sbitLCD1602_RS=P1^0;
sbitLCD1602_RW=P1^1;
sbitLCD1602_E=P1^2;
//---------定义变量--------------------
ucharch451_key=0xff;
ucharyun_sign;
ucharxiabiao=0;
uchartab[32];
floatopr_1=0,opr_temp=0,end=0;
ucharf_flag1=0,f_flag2=0,p_flag=0,f_wei=0;
//---------声明函数--------------------
voidch451_init(void);//CH451初始化
voidch451_write(uintcommand);//写命令或数据到ch451
ucharch451_read(void);//读按键值
voiddelay(uintk);//延时程序
ucharget_char(void);//按键对应字符
voidLCD_init(void);//LCD初始化;
voidLCD_inter_command(unsignedcharcommand);//LCD写入控制字
voidLCD_inter_dat(unsignedchardat);//LCD写入要显示的数据
voidlcdbusy();//LCD查忙时
voiddisplay(void);//显示
voidspec(void);//按键特殊功能
voidget_end(void);//结果
voidf_flag();//判断负号
//--------主函数----------------------
voidmain()
{
LCD_init();
ch451_init();
EA=1;
LCD_inter_command(0x01);
while
(1)
{
if(ok){
display();
ok=0;clr=1;
}
}
}
voidch451_init(void)
{
EX0=1;
din=0;
din=1;
ch451_write(0x403);
ch451_write(0x580);
ch451_write(0x800|1);
ch451_write(0x900|1);
ch451_write(0xa00|1);
}
voidch451_write(uintcommand)
{uchari;
EX0=0;
load=0;
for(i=0;i<12;i++)
{
din=command&1;
dclk=0;
command>>=1;
dclk=1;
}
load=1;
EX0=1;
}
ucharch451_read(void)
{ucharkey=0x07;
uchari;
EX0=0;
load=0;
for(i=0;i<4;i++)
{
din=key&1;
dclk=0;
key>>=1;
dclk=1;
}
load=1;
key=0;
for(i=0;i<7;i++)
{
key<<=1;
key|=dout;
dclk=0;
dclk=1;
}
EX0=1;
returnkey;
}
voidEX0_ISR(void)interrupt0
{
uchartemp;
ch451_key=ch451_read();
spec();
if(clr){LCD_inter_command(0x01);clr=0;}
temp=get_char();
if(temp){tab[xiabiao++]=temp;LCD_inter_dat(temp);}
if(ok)get_end();
}
ucharget_char(void)
{
uchardis=0;
switch(ch451_key)
{
case0x40:
dis='1';break;
case0x41:
dis='2';break;
case0x42:
dis='3';break;
case0x43:
dis='4';break;
case0x44:
dis='5';break;
case0x48:
dis='6';break;
case0x49:
dis='7';break;
case0x4A:
dis='8';break;
case0x4B:
dis='9';break;
case0x4C:
dis='0';break;
case0x50:
dis='+';f_wei=1;break;
case0x51:
dis='-';f_wei=1;break;
case0x52:
dis='x';f_wei=1;break;
case0x53:
dis=0xfd;f_wei=1;break;
case0x54:
dis='=';
ok=1;LCD_inter_command(0xc0);
break;
case0x58:
dis='.';break;
case0x59:
LCD_inter_command(0x01);
xiabiao=0;f_flag1=0;f_flag2=0;
p_flag=0;f_wei=0;
break;
case0x5C:
{dis=0xb0;p_flag++;f_flag();}break;
default:
break;
}
returndis;
}
voidspec(void)
{
switch(ch451_key)
{
case0x5A:
LCD_inter_command(0x10);{if(xiabiao>0)xiabiao-=1;}break;
case0x5B:
LCD_inter_command(0x14);{xiabiao+=1;}break;
default:
break;
}
}
voiddelay(unsignedintk)
{
while(k--);
}
voidLCD_inter_command(unsignedcharcommand)
{
delay(5000);
LCD1602_RS=0;
LCD1602_RW=0;
LCD1602_E=1;
P0=command;
LCD1602_E=0;
lcdbusy();
}
voidLCD_init(void)
{delay(5000);
LCD_inter_command(0x01);
delay(5000);
LCD_inter_command(0x3C);
delay(5000);
LCD_inter_command(0x0E);
delay(5000);
}
voidLCD_inter_dat(unsignedchardat)
{
delay(5000);
LCD1602_RS=1;
LCD1602_RW=0;
LCD1602_E=1;
P0=dat;
LCD1602_E=0;
lcdbusy();
}
voidlcdbusy()
{
P0=0xFF;
LCD1602_RS=0;
LCD1602_RW=1;
LCD1602_E=1;
while((P0&0x80)==1);
}
voiddisplay(void)
{
longinttemp=end;
uintxiao_temp;
uintxx;
xx=fabs(end);
xiao_temp=fabs(end)*1000-xx*1000;
if(xx>999999)LCD_inter_dat((xx/1000000)%10+'0');
if(xx>99999)LCD_inter_dat((xx/100000)%10+'0');
if(xx>9999)LCD_inter_dat((xx/10000)%10+'0');
if(xx>999)LCD_inter_dat((xx/1000)%10+'0');
if(xx>99)LCD_inter_dat((xx/100)%10+'0');
if(xx>9)LCD_inter_dat((xx/10)%10+'0');
LCD_inter_dat(xx%10+'0');
if(xiao_temp!
=0)
{
LCD_inter_dat('.');
LCD_inter_dat((xiao_temp/100)%10+'0');
LCD_inter_dat((xiao_temp/10)%10+'0');
LCD_inter_dat(xiao_temp%10+'0');
}
}
voidget_end(void)
{ucharf_flag=0;
floatxiaoshu=1,b=0;
ucharxiao_flag=0;
uchari=0;
while(i<=xiabiao)
{
while(tab[i]<=0x39&&tab[i]>=0x30)
{
opr_1*=10;
opr_1+=tab[i++]-0x30;
}
switch(tab[i])
{
case'.':
xiao_flag=1;break;
case'+':
xiaoshu=1;yun_sign='+';if(opr_temp==0){end=opr_temp=opr_1;}else{opr_temp+=opr_1;}opr_