设计一个基于ADuC848的简易计算器,基本功能如下,可以自行添加另外功能。
基本功能:
1)利用小键盘作为数据和命令的输入装置
2)利用LED或LCD作为过程和结果的输出装置
3)可实现基本的数据运算,包括:加、减、乘、除、开方等
4)未工作时显示北京时间,可调整
5)当数据超出范围,可声音提示或报警
6)可通过按键控制,随机模拟滚动显示乘法口诀表
模块分析:
需要用到键盘模块、液晶模块(采用LCD1602)、DS1307模块等
问题设想:
在编写代码之前,我们不妨考虑下要处理哪些问题或者现象,这样便能在第一次编写时优化代码,节省时间。
类型一:闭锁问题
1)由于最开始是显示北京时间,那么我们肯定需要一个按键实现运算模式的切换。那么当我们按下这个按键之后,输入数但还没有结束运算过程,再次按下该按键时,应该会怎样(无效?重新运算?)。如果是无效,那么就涉及到闭锁,在按下按键之前没有按下实现等于功能的按键,就操作无效,没有反应或者报错。
2)在成功切换到运算模式后,能直接先按下加减乘除的按键吗?显然,这是不符合数学运算逻辑的,所以也需要涉及到闭锁。处理方式可以是必须输入至少一位数后才允许按下运算符号的按键有效!!
3)同样,按下运算符号的按键后立马按下等于的按键也是不合理的,处理方式相同,必须先输入第二个数(至少一位)。
4)有时候,按键处理的代码并不是很完善,当我们按下按键后,可能会发生在液晶屏幕上光标连跳两位的现象。当然我们可以通过按键处理的优化解决这个问题,这里我们也可以采用闭锁的方法(具体在之后的代码会详细体现)。
类型二:运算函数功能问题(由于简易计算器的功能还是比较多,不少于16个按键,处理还是一键一用,所以就暂时省略了小数点的功能~~)
这个类型主要是针对除法和开方!在C语言中,比如6/4=1,6%4=2,而没有专门的函数实现6除于4等于1.5;另外,开方函数得到是一个单精度浮点型数据,所以要单独处理!!
代码分析:
1.运算部分
首先可以建一个C文件用于写数学处理,加减乘除等。
#include<yunsuan.h>
#include<math.h> //记得要包含math.h库文件
//加法
uint add(uint a,uint b)
{
uint temp;
temp=a+b;
return temp;
}
//减法
uint sub(uint a,uint b)
{
uint temp;
temp=a-b;
return temp;
}
//乘法
uint cheng(uint a,uint b)
{
uint temp;
temp=a*b;
return temp;
}
//除法
uint chu(uint a,uint b)
{
uint temp;
temp=a/b;
return temp;
}
/*
//开方
uint kaifang(uint a)
{
uint temp;
temp=sqrt(a);
return temp;
} */
//运算
int calculate(uint a,uint b,uchar c)
{
//加法
if(c==1)
{
return add(a,b);
}
//减法
if(c==2)
{
return sub(a,b);
}
//乘法
if(c==3)
{
return cheng(a,b);
}
//除法
if(c==4)
{
return chu(a,b);
}
/* //开方
if(c==5)
{
return kaifang(a);
} */
}
2.液晶模块
这块代码和智能电压监视仪的完全一样,就不弄上来了。
3.键盘模块
#include<lcd1602.h> #include<jianpan.h> #include<DS1307.h> #include<main.h> #include<yunsuan.h> #include<math.h> idata uchar id=1; //跳出显示时间界面的标志量,id=0,跳出;否则,继续显示。 idata uchar kcount=0; //按键次数量,亦即光标显示位置的标志量 idata uchar kcount1; //当与kcount相等时,说明没按下光标移动键 bit tflag=0; //改动时间标志,tflag=1时能改动时间,tflag=0时锁存时间,禁止改动。 uchar count=0; //table数组标号,0对应数字0······ uint pos=0; bit pos_lock; //锁闭确定键,防止按一次跳多位;为0开锁,为1闭锁。 bit yunsuan_lock; //符号锁,输入一个运算符号,就会锁定,即一个算式内不能再输入运算符号。为0开锁,为1闭锁。 bit startcal_lock; //开启运算锁,当输入数字时闭锁;为0开锁,为1闭锁。 bit gundong_lock=0; //滚动锁,为0开锁,为1闭锁。 uchar code table[]={0,1,2,3,4,5,6,7,8,9}; uchar code table1[]={"we are"}; uchar value1[4]; uchar value2[4]; uchar value_count=0; uint cal1,cal2; //计算对象 uint result;//结果 uchar len1=0;//第一位数的数值长度 uchar len2=0;//第二位数的数组长度 //uchar len3=0;//结果的数值长度 uchar cal_flag;//运算标志号,加法对应1,减法对应2,乘法对应3,除法对应4,开方对应5 void delay() //延时函数 { uchar i; for(i = 0;i < 100;i++); } void longdelay() { uint i; for(i = 0;i < 65000;i++); } void delay_beep(unsigned int z) { unsigned int x,y; for(x=z;x>0;x--) { for(y=100;y>0;y--); } } //报警 void alarm() { uint i; uchar code error[]={"Error!Try again!"}; lcd_wc(0x01); //清屏 lcd_wc(0x80); for(i=0;i<16;i++) { lcd_wd(error[i]); } for(i=0;i<3;i++) { beep=0;delay_beep(10); beep=1;delay_beep(10); } } //开方显示 void sqrt_display() { uchar Fir_bit,Sec_bit,Thi_bit,Fou_bit; float d; uint d1; if(len1==1||len1==2) { //数只有一位或两位,开方后结果是1位 d=sqrt(cal1)*1000; d1=d; Fir_bit=d1/1000; Sec_bit=d1/100%10; Thi_bit=d1/10%10; Fou_bit=d1%10; lcd_wc(0x80+pos); lcd_wd(0x30+Fir_bit); lcd_wc(0x80+pos+1); lcd_wd('.'); lcd_wc(0x80+pos+2); lcd_wd(0x30+Sec_bit); lcd_wc(0x80+pos+3); lcd_wd(0x30+Thi_bit); lcd_wc(0x80+pos+4); lcd_wd(0x30+Fou_bit); } if(len1==3||len1==4) { //数有三位或四位,结果两位 d=sqrt(cal1)*100; d1=d; Fir_bit=d1/1000; Sec_bit=d1/100%10; Thi_bit=d1/10%10; Fou_bit=d1%10; lcd_wc(0x80+pos); lcd_wd(0x30+Fir_bit); lcd_wc(0x80+pos+1); lcd_wd(0x30+Sec_bit); lcd_wc(0x80+pos+2); lcd_wd('.'); lcd_wc(0x80+pos+3); lcd_wd(0x30+Thi_bit); lcd_wc(0x80+pos+4); lcd_wd(0x30+Fou_bit); } if(len1==5||len1==6) { //数有五位或六位,结果三位 d=sqrt(cal1)*10; d1=d; Fir_bit=d1/1000; Sec_bit=d1/100%10; Thi_bit=d1/10%10; Fou_bit=d1%10; lcd_wc(0x80+pos); lcd_wd(0x30+Fir_bit); lcd_wc(0x80+pos+1); lcd_wd(0x30+Sec_bit); lcd_wc(0x80+pos+2); lcd_wd(0x30+Thi_bit); lcd_wc(0x80+pos+3); lcd_wd('.'); lcd_wc(0x80+pos+4); lcd_wd(0x30+Fou_bit); } } //结果显示 void result_display() { uchar Fir_bit,Sec_bit,Thi_bit; /* if(calculate(cal1,cal2,cal_flag)<-65536||calculate(cal1,cal2,cal_flag)>65536) { alarm(); }*/ if(calculate(cal1,cal2,cal_flag)>999) { //超出范围,报警 alarm(); } else { if(calculate(cal1,cal2,cal_flag)<0) { Fir_bit= (calculate(cal2,cal1,cal_flag))/100; Sec_bit= (calculate(cal2,cal1,cal_flag))/10%10; Thi_bit= (calculate(cal2,cal1,cal_flag))%10; lcd_wc(0x80+pos); lcd_wd('-'); pos++; } else { Fir_bit= (calculate(cal1,cal2,cal_flag))/100; Sec_bit= (calculate(cal1,cal2,cal_flag))/10%10; Thi_bit= (calculate(cal1,cal2,cal_flag))%10; } if(Fir_bit!=0) { lcd_wc(0x80+pos); lcd_wd(0x30+Fir_bit); lcd_wc(0x80+pos+1); lcd_wd(0x30+Sec_bit); lcd_wc(0x80+pos+2); lcd_wd(0x30+Thi_bit); } if(Fir_bit==0&&Sec_bit!=0) { lcd_wc(0x80+pos); lcd_wd(0x30+Sec_bit); lcd_wc(0x80+pos+1); lcd_wd(0x30+Thi_bit); } if(Fir_bit==0&&Sec_bit==0) { lcd_wc(0x80+pos); lcd_wd(0x30+Thi_bit); } } } //清空存储数组 void clear() { uchar i; for(i=0;i<len1;i++) { value1[i]=0; } for(i=0;i<len2;i++) { value2[i]=0; } } //获得第一个数 uint getcal1(uint len) { uint temp; uchar temp1,temp2,temp3,temp4,temp5; if(len==1) { temp=value1[0]; } if(len==2) { temp1=value1[0]; temp2=value1[1]; temp=temp1*10+temp2; } if(len==3) { temp1=value1[0]; temp2=value1[1]; temp3=value1[2]; temp=temp1*100+temp2*10+temp3; } if(len==4) { temp1=value1[0]; temp2=value1[1]; temp3=value1[2]; temp4=value1[3]; temp=temp1*1000+temp2*100+temp3*10+temp4; } if(len==5) { temp1=value1[0]; temp2=value1[1]; temp3=value1[2]; temp4=value1[3]; temp5=value1[4]; temp=temp1*10000+temp2*1000+temp3*100+temp4*10+temp5; } return temp; } //获得第二个数 uint getcal2(uint len) { uint temp; uchar temp1,temp2; if(len==1) { temp=value2[0]; } if(len==2) { temp1=value2[0]; temp2=value2[1]; temp=temp1*10+temp2; } return temp; } //光标移动 void move() { if(tflag) { //调节时间时光标移动情况 //超过范围则光标复位 if(kcount>16) { kcount=1; } //遇到非数字符号调到下一位有效位 if(kcount==3||kcount==6||kcount==11||kcount==14) { kcount++; } lcd_wc(0x0f);//光标显示并闪烁 //光标闪烁位置 if(kcount>8) { lcd_wc(0xC0+kcount-9); //当kcount大于8时,光标从第二行开始闪烁 } else { lcd_wc(0x80+kcount-1); //当kcount小于8时,光标从第一行开始闪烁 } //当不再按键时,光标停留在原位上 kcount1=kcount; delay(); while(kcount==kcount1) { Keyscan(); } } tflag=0; } //滚动显示 void gundong() //存在问题,还没有完善!!!!!!! { uchar cheng1[]={"1*1=1"}; uchar cheng2[]={"1*2=2"}; uchar cheng3[]={"1*3=3"}; uchar cheng4[]={"1*4=4"}; uint i=0; lcd_wc(0x80); for(i=0;i<5;i++) { lcd_wd(cheng1[i]); } longdelay(); lcd_wc(0xc0); for(i=0;i<5;i++) { lcd_wd(cheng2[i]); } longdelay(); lcd_wc(0x80); for(i=0;i<5;i++) { lcd_wd(cheng3[i]); } longdelay(); lcd_wc(0xc0); for(i=0;i<5;i++) { lcd_wd(cheng4[i]); } } //lcd屏幕显示 void lcd_display(uchar key) { switch(key) { case 1: //开启运算 if(startcal_lock==0) { count=0; gundong_lock=1; lcd_wc(0x01);//清屏 lcd_wc(0x0f);//光标闪烁 lcd_wc(0x80); lcd_wd(0x30+table[count]); lcd_wc(0x80); } break; case 2: //加值 count++; if(count>9) { count=0; } lcd_wc(0x80+pos); lcd_wd(0x30+table[count]); lcd_wc(0x80+pos); break; case 3: //减值 if(count==0) { count+=10; } count--; lcd_wc(0x80+pos); lcd_wd(0x30+table[count]); lcd_wc(0x80+pos); break; case 4: //确认值 if(pos_lock==0) { pos++; lcd_wc(0x80+pos); if(yunsuan_lock==0) { value1[value_count]=table[count]; //储存第一个数位数值 len1++; } if(yunsuan_lock==1) { value2[value_count]=table[count]; //储存第二个数位数值 len2++; } value_count++; } pos_lock=1; count=0;//count清零,下一位从0开始显示 break; case 5: //加法 if(yunsuan_lock==0&&pos!=0) { lcd_wc(0x80+pos); lcd_wd('+'); cal_flag=1; //加法标号为1 yunsuan_lock=1; //不能再输入其他运算符号 cal1=getcal1(len1);//获得第一位数 value_count=0; //存储数组重新存储 if(pos_lock==0) { pos++; lcd_wc(0x80+pos); } pos_lock=1; } break; case 6: //减法 if(yunsuan_lock==0&&pos!=0) { lcd_wc(0x80+pos); lcd_wd('-'); cal_flag=2; //减法标号为2 yunsuan_lock=1; //不能再输入其他运算符号 cal1=getcal1(len1);//获得第一位数 value_count=0; //存储数组重新存储 if(pos_lock==0) { pos++; lcd_wc(0x80+pos); } pos_lock=1; } break; case 7: //乘法 if(yunsuan_lock==0&&pos!=0) { lcd_wc(0x80+pos); lcd_wd('*'); cal_flag=3; //乘法标号为3 yunsuan_lock=1; //不能再输入其他运算符号 cal1=getcal1(len1);//获得第一位数 value_count=0; //存储数组重新存储 if(pos_lock==0) { pos++; lcd_wc(0x80+pos); } pos_lock=1; } break; case 8: //除法 if(yunsuan_lock==0&&pos!=0) { lcd_wc(0x80+pos); lcd_wd('/'); cal_flag=4; //除法标号为4 yunsuan_lock=1; //不能再输入其他运算符号 cal1=getcal1(len1);//获得第一位数 value_count=0; //存储数组重新存储 if(pos_lock==0) { pos++; lcd_wc(0x80+pos); } pos_lock=1; } break; case 9: //开方 if(yunsuan_lock==0&&pos!=0) { lcd_wc(0x80+pos); lcd_wd('$'); cal_flag=5; //除法标号为5 yunsuan_lock=1; //不能再输入其他运算符号 cal1=getcal1(len1);//获得第一位数 value_count=0; //存储数组重新存储 if(pos_lock==0) { pos++; lcd_wc(0x80+pos); } pos_lock=1; } break; case 10: //等于 if(len2!=0||cal_flag==5) { lcd_wc(0x80+pos); lcd_wd('='); yunsuan_lock=1; //不能再输入其他运算符号 cal2=getcal2(len2); //显示结果 pos++; if(cal_flag==5) { sqrt_display(); } // lcd_wc(0x80+pos); // lcd_wd(0x30+calculate(cal1,cal2,cal_flag)); else { result_display(); } if(pos_lock==0) { pos++; lcd_wc(0x80+pos); } count=0; pos=0;//下一次光标复位 clear();//清空存储两个数数值的数组 len1=0; len2=0; value_count=0; startcal_lock=0;//可以再次开启运算 yunsuan_lock=0;//下次可以重新输入运算符 pos_lock=1; lcd_wc(0x0c);//关光标 } break; case 12: //时间显示 Check_Busy(); lcd_wc(lcd_clear); lcd_wc(0x0c); id=1; tflag=1; display(); break; } } //键盘扫描 void Keyscan() { unsigned char temp; unsigned int j=0; P1&=0xf0; //P1口低四位作为数字输入时应往相应引脚写0 P2&=0xf0; //置列扫描信号为0 temp=P1&0x0f; if(temp!=0x0f) //P1口低四位不全为1,则有键被按下 { delay(); //延时消抖 if(temp==(P1&0x0f)) //消抖后再判断键值 { P2|=0x07; //P2.3为0 switch(P1&0x0f) { case 0x0e://开启运算 id=0;//跳出时间显示 lcd_display(1); break; case 0x0d: //加法 pos_lock=0; lcd_display(5); break; case 0x0b: //开方 pos_lock=0; lcd_display(9); break; case 0x07://光标闪烁 kcount++; move(); break; case 0x0f:break; default:break; } P2&=0xf0; P2|=0x0b; //P2.2为0 switch(P1&0x0f) { case 0x0e: //加值 pos_lock=0; startcal_lock=1; lcd_display(2); break; case 0x0d://减法 pos_lock=0; lcd_display(6); break; case 0x0b: //等于 pos_lock=0; lcd_display(10); break; case 0x07: //加光标所在位置的数值 addValue(kcount); break; case 0x0f:break; default:break; } P2&=0xf0; P2|=0x0d; //P2.1为0 switch(P1&0x0f) { case 0x0e: //减值 pos_lock=0; startcal_lock=1; lcd_display(3); break; case 0x0d: //乘法 pos_lock=0; lcd_display(7); break; case 0x0b: //滚动显示乘法口诀表 id=0; lcd_wc(0x01); lcd_wc(0x07); lcd_wc(0x1c); while(!gundong_lock) { gundong(); Keyscan(); } lcd_wc(0x0c); break; case 0x07: //减光标所在位置的数值 subValue(kcount); break; case 0x0f:break; default:break; } P2&=0xf0; P2|=0x0e; //P2.0为0 switch(P1&0x0f) { case 0x0e: //确认值 lcd_display(4); break; case 0x0d: //除法 pos_lock=0; lcd_display(8); break; case 0x0b: //时间显示 lcd_display(12); break; case 0x07: //确认调整时间完毕 id=1; makeSure(); break; case 0x0f:break; default:break; } } } } /* uchar getk() { return kcount; } */ //设置按键次数量,主要负责清零重置 void setk(uchar count) { kcount=count; } //获得跳出显示时间界面的标志量 uchar getid() { return id; } //获得运算符号 uchar getcal_flag() { return cal_flag; }
运算模式的过程是:通过按加减键得到0~9的任一位,然后再按确定键确定输入。得到第一个数和第二个数的方法也很简单,就是建两个数组,分别储存每一位,然后再组合起来;或者建一个数组,在得到第一个数后清空,再放第二位数的各位。此外,我还编写了获得结果的函数,主要是控制结果的输出精度。
4.主函数模块
同样和上篇博客的一模一样,故也不放上来了。
这样,就能实现一个简易计算器的功能。
相关推荐
《ADUC848——全面解析与应用指南》 在微控制器的世界中,ADuC848是一款功能强大的集成模拟和数字处理能力的单片机,由 Analog Devices 公司制造。这款微控制器以其高精度的模拟性能和丰富的外设接口在各种嵌入式...
标题 "ADUC848_EEPROM" 暗示了我们正在探讨一款基于ADUC848微控制器的EEPROM应用。ADUC848是一款集成了模拟和数字功能的微控制器,由Analog Devices(亚德诺半导体)制造。在嵌入式系统设计中,EEPROM(电可擦除可...
ADUC848是一款微控制器,由Analog Devices(ADI)公司生产,它集成了模拟和数字电路,特别适用于需要高精度数据采集系统的应用。在"ADUC848_AD采集"项目中,我们主要关注的是如何利用ADUC848进行高效的模拟信号到...
标题中的“ADUC848_DS1302”很可能是指一个基于ADUC848微控制器的项目,其中整合了DS1302时钟芯片。ADUC848是一款由 Analog Devices(亚德诺半导体)制造的8位微控制单元,具有高性能模拟功能,适用于需要混合信号...
【ADUC848 波形发生器】是一款基于微控制器ADUC848的电子设备,用于生成各种模拟和数字信号,适用于测试、测量、教育以及科研等用途。ADUC848是一款集成了高性能模拟电路和数字处理能力的微控制器,使其成为构建复杂...
标题中的"ADUC848_ADUC848AD控制程序.zip"暗示了这是一个与ADUC848或ADUC848AD相关的控制程序,它被封装在一个ZIP压缩文件中。ADUC848是Analog Devices公司推出的一款微控制器,集成了模拟和数字功能,常用于数据...
标题中的“ADUC848_DAC”指的是一款基于ADUC848微控制器的数字模拟转换器(DAC)设计。ADUC848是Analog Devices公司生产的一款集成了微处理器和模拟电路的混合信号微控制器,常用于需要高精度模拟输出的应用。 **...
标题中的"ADUC848_IIC"暗示我们主要探讨的是ADI公司生产的微控制器ADUC848在IIC(I²C)通信协议下的应用。IIC,全称为Inter-Integrated Circuit,是由飞利浦(现为NXP半导体)开发的一种多主机、多设备的串行通信...
标题中的“ADUC848_ADUC848AD控制程序_源码.zip”表明这是一个与ADUC848微控制器相关的控制程序源代码。ADUC848是一款由Analog Devices公司生产的混合信号微控制器,它集成了数字处理单元(CPU)和模拟功能,适用于...
标题中的"PWM.rar_aduc848"表明这是一个与PWM(脉宽调制)技术相关的项目,使用了ADUC848微控制器,并且包含了在Keil uVision4集成开发环境中编写的源代码。这个项目的目标是实现一首乐曲——《绿袖子》的自动播放...
### ADuC845 ADuC847 ADuC848: 详析与核心特性 #### 微型转换器(MicroConverter®)多通道24-/16-位ADCs简介 ADuC845、ADuC847及ADuC848是Analog Devices公司推出的一系列高性能微型转换器(MicroConverter®),...
ADuC848是一款集成了微控制器(MCU)和模拟功能的混合信号处理器,特别适用于需要高精度测量和控制的应用。本篇文章将围绕"ADuC848 RTC开发参考程序"这一主题,深入探讨相关知识点。 首先,ADuC848是ADI公司生产的...
### ADuC848 资料概览与关键技术知识点 #### 一、产品概述 ADuC848 是 Analog Devices 公司推出的一款高度集成的微控制器单元(MCU),该系列包括 ADuC845、ADuC847 和 ADuC848 三个型号。这些器件结合了高性能的 ...
本文介绍了一种基于ADuC845微控制器的二极管测试仪的设计,该测试仪能够自动显示二极管的极性以及正向电压降,并测试二极管的伏安特性。同时,测试仪具备串行通信功能,便于与计算机通讯并保存不同类型的参数。 ...
### ADuC848BCP8-3:MicroConverter®多通道24-/16-位ADCs与嵌入式62kB Flash及单周期MCU #### 产品概述 ADuC848BCP8-3是一款由Analog Devices生产的高性能微控制器,集成了高分辨率Σ-Δ模数转换器(ADCs)与...