#include<avr/io.h>#include<util/delay.h> //延时函数的头文件#include<avr/interrupt.h> // 中断函数的头文件#define uint unsigned int#define uchar unsigned char#define BIT(k) (1<<k) //利用上ICC软件的优点#define DI PORTC //夜晶的数据输入端#define No_key 255 //没有按键按下的返回值#define key_port PORTD //键盘输入#define key_ddr DDRD#define key_pin PIND //宏定义方便以后程序移植#define lone_key_port PORTB //独立键盘接口用于输入小数点#define lone_key_ddr DDRB#define lone_key_pin (PINB&BIT(0)) //读取独立键盘接口的电平#define rs_0 PORTA&=~BIT(0) //RS复位#define rs_1 PORTA|=BIT(0) //RS置位#define en_0 PORTA&=~BIT(1) //使能端复位#define en_1 PORTA|=BIT(1) //使能端置位const uchar table1[]=" You are welcome!"; // 初始显示字符const uchar table2[]="ERROR!"; //出错提示字符uchar a[7]={0,0,0,0,0,0,0},b[11]={0,0,0,0,0,0,0},a1[5]={0,0,0,0,0},b1[5]={0,0,0,0,0}; //用来存储输入的两个数字,位数不可超过10位uchar aa,bb,cc,dd; //用来记数输入的位数uchar sym; //用来保存符号uchar flag;//起动标志uchar fuhao; //符号标志uchar deflag1 ; //小数点标志uchar deflag2 ; //小数点标志uchar overflag; //数值溢出标志uchar allowflag=1; //符号允许标志,用于禁止连续两个符号的输出uchar negative; //负数标志,当是负数要作相应的处理long temp3,temp4; //计算的数值得数用其中一个变量存储即可,不用再浪费存开辟其他变量float temp1, temp2;const unsigned char key_table[16] ={7, 8, 9, 10,4, 5, 6, 11,1, 2, 3, 12,50,0, 21,13}; // 键盘编码可根据具体情况而设定void port_init(); //端口初始化void device_init(); //夜晶初始化void time_init(); //定时器初始化void wright_(uchar ); //夜晶写指令函数void wright_data(uchar date) ; //夜晶写数据函数void lcd_handle(uchar temp); //夜晶显示处理,该显示什么,不该显示什么uchar keyscan(); //键盘扫描void judgechar(uchar t); //判断字符为符号还是数字void decimal(uchar de) ; //判断是不是小数点,只能出现两个小数点,且不能连续出现void reset() ; //复位函数void calculate(void); //对数值进行计算void result(long value); //显示结果void flow_clew() ; //溢出提示函数int main(){port_init();device_init();time_init(); //初始化while(1){flow_clew() ; //溢出提示函数,一旦扫描到出错就会显示错误}}/***********中断服务程序***********/volatile unsigned int i; //中断服务程序中要使用全局变量,且要加volatile SIGNAL(SIG_OVERFLOW0){TCNT0=205; //重装初值i=keyscan(); //返回键盘扫描值if(i!=No_key) //确认有按键按下{if(i==50){wright_(0x01) ; //清显示_delay_ms(10); //延时等待清屏指令完成wright_(0x80);wright_data('0'); //重新显示0reset(); //初始化flag=1; // 标志起动}if(i<50&&flag) //条件为已经启动而且输入的是有效字符{judgechar(i); //判断是不是操作符,要是则要作相应的处理decimal(i); //判断是不是小数点输入,要是则要作相应的处理lcd_handle(i) ; //对输入的数据进行合理存储并加以显示}if(i==21) //按下了等号键{wright_data('='); //显示等号calculate(); //计算数值result(temp1); //拆分数值后显示结果}}}/*******定时器初始化*********/void time_init(){TCNT0=205; //初值255-205=50 50微秒TIMSK|=(1<<TOIE0);sei(); //开启总中断TCCR0|= (1<<CS01);//8分频8M/8=1M}/****端口初始化****/void port_init(){DI=0xff;DDRA|=BIT(0);DDRA|=BIT(1);//输出状态lone_key_ddr&=~BIT(0);lone_key_port|=BIT(0); //独立键盘接口设置为输入,并打开上拉电阻DDRD=0; //全部设为输入,有个上拉电阻,可以输入高电平DDRB=0xff;}/*******夜晶初始化********/void device_init(){en_0; //使能端拉低wright_(0x38) ; //初始化格式wright_(0x0c) ; //0x0e打开光标0x0c不显示光标0x0e光标不闪,0x0f光标闪wright_(0x01) ; //清显示wright_(0x80);for(i=0;i<17;i++){wright_data(table1[i]);_delay_ms(20);}}/******夜晶写入指令的函数***********/void wright_(uchar ){rs_0; //低电平时写指令DI=;_delay_ms(1);en_1;_delay_ms(1);en_0;}/******夜晶写入数据的函数**********/void wright_data(uchar data){rs_1; //高电平时写数据DI=data;_delay_ms(1);en_1;_delay_ms(1);en_0;}/**********4x4矩阵键盘及一个独立键盘的扫描函数*********/uchar keyscan(void){unsigned char temp,temp1,key,row,column;key_ddr = 0x0f;// 高四位输入列线/低四位输出到行线key_port = 0xf0;// 高四位打开上拉电阻/低四位输出低电平上拉电阻会把电平拉高if(lone_key_pin==0){_delay_ms(5); //延时消抖if(lone_key_pin==0){_delay_ms(90); //等待松手return(31);}}if((key_pin & 0xF0)!= 0xF0) // 作初检查有否键按下,没有,就返回如果列线不全为1,可能有键按下{_delay_ms(5);// 延时去抖动if((key_pin & 0xF0)!= 0xF0) //确认有按键按下{_delay_ms(1000); //延时等待松手for(row=0,key_port=0b11111110;row<4;row++){for(column=0,temp=0b11101111;column<4;column++)// 设置列线初始值1110{if((key_pin & 0xF0)==(temp & 0xF0))// 输入列线,查看这列有否键按下{key=4*row+column;// 键编码=4*行输入值+列扫描值key=key_table[key];// 键盘编码转换键值return (key);}temp<<=1;// 列线左移1位}key_port=((key_port<<1)|0x01);//行线扫描值左移1位,扫描下一行}}}return (No_key);}/****对键盘扫描返回值进行判断是不是为符号*******/void judgechar(uchar t) //用带参数的函数可以减少键盘扫描次数{if(t>9&&t<14&&allowflag) //符号围{fuhao=1; //标志为符号,为后面的程序作决断算完后再将其清零allowflag=0;//禁止下一个符号的输入,只能输入一个符号if(t==10) //输入的是减号{wright_data('/');}if(t==11) //输入的是减号{wright_data('*');}if(t==12) //输入的是减号{wright_data('-');}if(t==13) //输入的是减号{wright_data('+');}sym=t; //把符号的编号保存下来,方便以后调用}}/*****夜晶显示处理,该显示什么,不该显示什么*****/void lcd_handle(uchar temp) //用带参数的函数可以减少键盘扫描次数{if(temp>=0&&temp<10) //以下处理仅对数字有效{if(fuhao) //表示已经写了符号了,提示是输入第二个数了{if(deflag2) //判断为小数部分{if(dd<=3){wright_data('0'+temp); //输入一个数就显示一个数b1[++dd]=temp; //保存小数点后面的数字}elseoverflag=1;}else //整数部分if(bb<=5) //条件为位数还不足六位{wright_data('0'+temp); //输入一个数就显示一个数b[++bb]=temp; //输入的是第二个数保存的是整数部分}elseoverflag=1; //数值溢出}else // 输入的是第一个数{if(deflag1) //判断为小数部分{if(cc<=3){wright_data('0'+temp); //输入一个数就显示一个数a1[++cc]=temp; //保存小数点后面的数字}elseoverflag=1;}else //整数部分if(aa<=5) //条件为位数还不足六位{if(aa==0){wright_(0x80); //从每个位置起写}wright_data('0'+temp); //输入一个数就显示一个数a[++aa]=temp; // 保存的是整数部分}elseoverflag=1; //数值溢出}}}/*********重新初始化的函数*********/void reset() //按下复位键后变量要全部变到初始状态{uchar k;for(k=1;k<7;k++){a[k]=0;b[k]=0;}overflag=0; //溢出标志复位flag=0; //启动标志复位allowflag=1; //符号允许标志置位fuhao=0; //符号已使用标志复位aa=0;bb=0; //元素个数清零sym=0; // 符号的编码置0;}/***********对数值进行计算,a[],b[]存储的是数,sym操作符编号***********/void calculate(void){switch(aa) //根据位数代入对应的公式{case 1: temp1=a[aa];break;case 2: temp1=a[2]+a[1]*10;break;case 3: temp1=a[3]+a[2]*10+a[1]*100;break;case 4: temp1=a[4]+a[3]*10+a[2]*100+a[1]*1000;break;case 5: temp1=a[5]+a[4]*10+a[3]*100+a[2]*1000+a[1]*10000;break;case 6: temp1=a[6]+a[5]*10+a[4]*100+a[3]*1000+a[2]*10000+a[1]*100000;}switch(bb) //根据位数代入对应的公式{case 1: temp2=b[bb];break;case 2: temp2=b[2]+b[1]*10;break;case 3: temp2=b[3]+b[2]*10+b[1]*100;break;case 4: temp2=b[4]+b[3]*10+b[2]*100+b[1]*1000;break;case 5: temp2=b[5]+b[4]*10+b[3]*100+b[2]*1000+b[1]*10000;break;case 6: temp2=b[6]+b[5]*10+b[4]*100+b[3]*1000+b[2]*10000+b[1]*100000;}temp3=a1[1]*1000+a1[2]*100+a1[3]*10+a1[4];temp4=b1[1]*1000+b1[2]*100+b1[3]*10+b1[4]; //小数部分放大10000倍temp1=temp1+temp3/10000.0;temp2=temp2+temp4/10000.0; //整数与小数的结合switch(sym) //判断输入的是哪个操作符并作相应的计算{case 10: temp1=temp1/temp2;break;case 11: temp1=temp1*temp2;break;case 12: temp1=temp1-temp2;break;case 13: temp1=temp1+temp2;break;}if(temp1>999999.9999) //数据溢出要作处理overflag=1;temp1*=10000; //变成整数处理,方便if(temp1<0){temp1=-temp1; //变负为正negative=1; //负数标志}}/*****把结果数值拆分显示*********/void result(long value){uchar we[10];uchar k;uchar j;we[10]=value/1000000000;we[9]=value/100000000%10;we[8]=value/10000000%10;we[7]=value/1000000%10;we[6]=value/100000%10;we[5]=value/10000%10;we[4]=value/1000%10;we[3]=value/100%10;we[2]=value/10%10;we[1]=value%10;if(negative){wright_data('-'); //要是得到的结果是个负数得先输入个负号}for(k=10;k>5;k--) //只能十位以上的数字进行检测,个位的不管是不是零都要显示{if(we[k]!=0){break; //确定第一个非零数字,只显示有效数字}}for(j=k;j>0;j--) //从第一个非零数字开始显示{if(j==4)wright_data('.'); //小数部分前面要加个小数点wright_data('0'+we[j]);_delay_ms(2);}}/**********溢出提示函数*******************/void flow_clew(){uchar num;-if(overflag){wright_(0x80+0x40+5);for(num=0;num<6;num++){wright_data(table2[num]);_delay_ms(2);}}}/***********判断是不是小数点**************/void decimal(uchar de){if(de==31){if(fuhao==0&&deflag1==0) //条件为正在输入的是第一个数且此数之前还没有小数点出现{wright_data('.'); //显示小数点deflag1=1; //不能再有下一个小数点了,置位可以防止有第二个小数点}if(fuhao&&deflag2==0) //条件为正在输入的是第一个数且此数之前还没有小数点出现{wright_data('.'); //显示小数点deflag2=1; //不能再有下一个小数点了,置位可以防止有第二个小数点}}}- . - 总结资料-。