广东科学技术职业学院广州学院 实训报告
一、 设计任务与要求
设计16键盘简易计算器,实现以下功能。 1、4*4键盘输入,用按键输入数和运算符号。 1 2 3 + 4 5 6 - 7 8 9 * C 0 = ∕ 2、数码管显示运算过程和结果(十进制数),负号用一个LED灯显示。 3、具有清零和复位功能。 4、具有连续运算功能。 5、具有简易报错和提示功能。
二、 总体方案与说明 利用PXA270的16个键盘,通过中断输入。16个按键分为10个数字键(0-9), +、-、*、/、=、清零等功能。整个计算器实现在结果为-9999到9999范围内的加 减乘除运算。负数的时候通过LED1亮表示,加减乘除被按下后,对应得LED 灯会亮,在输入数字后自动灭掉提示输入运算符号的LED灯。按下C键具有 清零的效果(清除数码管显示,和相关变量初始化,以及LED灯初始化等) 。“=”的功能是:若没有输入数,则还是显示0;若输入了一个数(正数或 负数)则直接显示这个数(负数显示的时候,要LED1亮);具有连续运算的 功能(即具有记忆最近输入的运算符和操作数的功能,如:刚输入了1+5=, 显示6之后,再按下“=”,则执行6+5=的操作,以此类推)。当操作数大于 -9999到9999的范围后,显示Eoor和亮所有LED灯报错,当按下清零键后 ,清除报错。同样,在运算中,若被除数为0,则报错。 声明:本程序不具备多级运算,如:1+15-45=这种一次运算超过一个运算 符的运算。若按下了多次运算符,则取最后按下的为有效。 由于本人使用的硬件是4个直入键盘和12个矩阵键盘,所以程序分开处理。 三、 电路原理图 数码管:
LED灯: 直入键盘: 矩阵键盘:
四、 软件主要模块流程图 紧接上面的加减乘除、等于、清零键的流程图: 五、 问题分析与解决方案 问题1:如何做到连续运算功能? 答:首先,你要知道你在数学中是如何运算的。如:我要做(25+3-4)*2/3的运算,那么我的计算步骤为:25+3=28,28-4=24,24*2=48,48/3=16。其次,由数学运算推导出一个规律。(以刚才的举例为例)从刚才的运算可以看出,我们在做完一次运算后,其结果都作为了下一次运算的一个成员。那么我们不妨把这个结果放到一个变量如:firstnum中,同时我们第一个运算数25也可以放到变量firstnum中。然后,我们在把要运算的数,即在运算符号后面的数存放到另一个变量secondnum中。这时,我们就可以把运算表达式写为:firstnum = fisrtnum + secondnum;(在程序中,结果都是在=号左边的。)之后的几个表达式分别为:firstnum = firstnum – secondnum; firstnum = firstnum * secondnum; firstnum = firstnum / (secondnum*1.0);(在程序中除号运算的结果为整数,要做到真正的除法运算,就要运算数中至少一个为小数即可。) 最后,从这几个表达式我们可以看出其基本规律是:firstnum = firstnum 运算符 secondnum; 这样只要我们把第一个输入的数和第二个输入的数分开来存,就可以做到连续运算的效果。 问题2:如何正确的把一个表达式中的第一个运算数保存到firstnum中,第二个运算数保存到secondnum中? 答:根据问题1中的运算表达式,我们可以看出,在没有到运算符之前的是firstnum,在运算符后面的是secondnum。这样我们就可以根据有没有输入运算符来把输入的数存放到相应的变量中。当然,由于你的firstnum是可以作为下一个运算的一个运算数的,所以这里要把firstnum定义为全局变量。 问题3:如何实现大于10而小于9999的数显示? 答:由于我的硬件为4个数码管,所以这里仅以我的硬件为例解释。在我们的实验箱上面,都有两组两个的静态数码管,它们的控制端从左到右为:LED_CS2,LED_CS3。一个“CS”就控制两个数码管。从这里我们就可以知都CS2是显示千位和百位的,CS3是显示十位和个位的。由于旧版的PXA270实验箱的硬件时把要控制小数点端口用来控制数码管的开与关,所以整个程序不考虑小数,只考虑整数的运算。 回到正题,在这里我们用一个数码管显示一个数字。如:我们要显示14这个数,那么我们就要把这个两位数分开来分别送到CS3的两个数码管显示。如何分开这两个数?在程序中,我们可以通过这两个表达式获取这个数(用num表示)的十位(用swei表示)和各位(用gwei表示):swei = num/10%10; gwei = num%10;在这里有必要解释一下num/10和num%10的意思。num/10是表示num除以10,但这不是数学上的除法运算,而是把num除以10之后,取其能够整除的部分,即可以理解为整数部分。而num%10刚好和num/10相反,它是把num除以10后,不能整除的部分,即余数部分取出来的意思。这样,我们取到十位和个位后,就可以把这两个数分别送到数码管CS3中显示就可以了。同样的道理,要显示百位(用bwei表示)和千位(用qwei表示),可以通过这两个表达式获得:bwei = num /100%10; qwei = num /1000%10;之后再把千位和百位送到数码管CS2中显示就可以了。 问题4:如何做到输入几位数(在-9999到9999之间)的效果? 答:要做到输入几位数的效果,我们不妨把整个效果想一下。在硬件上,我们是一个按键一个按键的按的,也就是说我们如果要输入123的话,就要分别按下1键、2键、3键。而每按下一个键,我们的程序就会记录刚按下的这个键。再考虑一下在硬件上按下123的效果:首先,我们按下1的时候,数码管就显示1;再按下2的时候,数码管要显示12,才能达到我们要的结果;再次按下3的时候,数码管就要显示123。那么从这里我们可以找出一个数学规律,那就是:1=1;12=1*10+2;123=12*10+3;这个规律就是第二个按下的键要显示的结果就是第一个按下的数字乘以10,再加上第二个按下的数字;而第三个按下的键的结果就是第二个按下的结果乘以10,再加上第三个按下的数字。用程序的表达式可以写成:firstnum = firstnum*10 +justnum;(justnum为最近按下的键)这个表达式就解决了硬件上实现输入多位数的问题。如果你不相信这个表达式可以实现的话,那我们再来验证一下,比如说你要输入5,那么这个表达式就应该为firstnum = 0*10 +5;最后结果为5(这里要说明一点,firstnum的初始值为0)。又比如说,你要输入的数为5678:那么每输入一个数字的对应表达式分别为:firstnum = 0*10 + 5;即firstnum此时为5;输入6的时候,firstnum = 5*10 +6;此时firstnum = 56;输入7,firstnum = 56*10 +7;此时firstnum = 567;最后按下8的时候,firstnum = 567*10 + 8;最后firstnum = 5678;这就说明我们这个表达式是正确的。 问题5:为何实现了操作数为多位数运算,却不能做到连续运算? 答:在解释这个问题前,先说明一点:由于我们的按键都是中断按键,所以每按一下按键就会重新进入一次键盘处理函数。在进行运算的时候,我们把第一个操作数和第二个操作数都定义为了全局变量。当我们进行完第一次运算,由于没有清零,所以再按下运算符继续运算的时候,你会发现数码管是接着第一次运算的结果为上一次的输入乘以10再加上你刚按下键显示出来的。比如说:你进行完的第一次运算为56+64=120;然后你再按下+1的时候,数码管却显示了641。这是为什么??如何解决这个问题?出现这个问题的原因是:在进行完第一次运算后,firstnum的值变了,而secondnum中的值还是64,而且由于你判断把第二个操作数存放到secondnum中的条件是有没有输入运算符。这就导致你再输入一次运算符的时候,它还是满足条件,而直接进入到表达式:secondnum = secondnum*10 + justnum;其中的secondnum还保存到上一次输入的值64,现在再按下一个数字1,程序重新进入到这个函数就执行了secondnum = 64*10 + 1;的表达式,这就是问题出现的原因。那如何解决这个问题?在我看来,因为你运算完一次运算后,都会有一次=号被按下的机会。我们可以给等号定义一个标志位,这样就可以通过在输入第二个操作数之前判断这个等号是否被按下,按下就把secondnum清零,同时把等号标志位也清掉。这样就可以避免上述情况的发生。 问题6:为何在硬件上调试的时候,会经常出现卡死的情况(就是数码管显示一个东西不变,按任何键都没反应)? 答:开始我的程序出现这种情况的时候,检测程序好几遍都没发现程序上有啥错,也不关硬件的事。后来才知道原来是在boot.s里面把MDMRS配置成0x2800,MDMRS配置成这个的时候,会经常产生冲突,只要把其配置成0x320032就OK了。如: 原来是: ;MDMRS ldr r1, =0x2800 str r1,[r11] mov pc,r14 更改后: ;MDMRS ldr r1, =0x320032 str r1, [r11] mov pc,r14 问题7:为何下载镜像文件到实验箱的时候老是提示失败? 答:出现这类情况的原因有多种,这里只介绍软件设置错误的解决方法。软件上到底是哪设置的问题会导致这种情况呢?这个地方的设置很容易被人忽视。下面以截图说明: 步骤1: 步骤2: 步骤3: 步骤4: 步骤5: 如果以上的设置都没错,但下载还是总是失败的话,那就要看你的bootloader配置了。
六、 实验体会 心得体会:在刚接触到这个项目的时候,大概想了一下挺简单的。但是在实践写程序的时候总是会出现各种各样的原因,现实往往和自己想的不一样。就像我自己按照自己的思路写好的程序,在上硬件调试的时候会发现有很多情况自己都没有考虑到。通过这个项目,使得自己对PXA270的这一部分硬件了解了很多,在写程序方面也有点点的长进。在试验中遇到的比较常见的情况都写出来了,在这里就不罗嗦了。 - /******************************************************
- *
- *项目名称:简易计算器
- *
- *函 数:cal.c
- *
- *作 用:按键处理,主函数
- *
- *有待改进:本程序还可以进一步的完善,其一就有如按下1+2,
- * 再按下+号的时候,就直接计算出结果并显示。
- * 希望有兴趣的朋友能够加以改进。
- *
- *制 作 人:沐雨青林
- *
- *******************************************************/
- #include "led_8.h"
- #define uchar unsigned char
- #define usint unsigned short int
- #define uint unsigned int
- extern void Led8_disp(int num);
- extern const uchar key_source_code[];
- extern void init_key(void);
- #define key_add '+'
- #define key_de '-'
- #define key_mul '*'
- #define key_mol '/'
- #define key_canal 'C'
- #define key_means '='
- const uchar key_code[] ={ 1, 2, 3, key_add, // +
- 4, 5, 6, key_de, // -
- 7, 8, 9, key_mul, // *
- key_canal, 0x00, key_means, key_mol};// /
- int firstnum=0;//存放第1个数
- int secondnum=0;//存放第2个数
- uchar operation='\0';//运算符号
- uchar mean_sign=0;//等号标志位 1为开,0位关
- uint optimes=0;//运算符号计数位
- //以运算符+、-、*、/为标志,当这些标志出现后,则输入的数字存放到
- //secondnum中
- //变量初始化
- void init_clear(void)
- {
- firstnum = 0;
- secondnum = 0;
- operation = '\0';
- optimes = 0;
- LED_CS4 = 0xff;
- Led8_disp(0);
- return;
- }
- //计算多位数,num为新输入的一位数
- //算好的多位数存放在snum,snum为原来存放有数的变量
- int duoweishu(int snum, int num)
- {
- snum = num + snum*10;
- return snum;
- }
- //直入式键盘处理
- void op_disdir(usint key_d)
- {
- uint i;
- for(i=0; i<4; i++)
- {
- if(key_d == key_source_code[i])
- {
- if(i == 3)// ‘+’ 按键处理
- {
- LED_CS4 = 0x7f;
- optimes++;
- operation = key_code[i];//operation 为‘+’
- }
- else
- {
- //在没有按下运算符的时候,把按键对应的键值存到firstnum
- if(operation == '\0')
- {
- firstnum = duoweishu(firstnum,key_code[i]);
- Led8_disp(firstnum);
- }
- else//按下运算符的时候,把按键对应的键值存到secondnum
- {
- if(mean_sign == 1)//等号标志位
- {
- secondnum = 0;
- mean_sign = 0;//清等号标志位
- }
-
- if(operation == '-' && optimes == 1)//负数输入
- {
- firstnum = duoweishu(firstnum,-key_code[i]);
- Led8_disp(firstnum);
- }
- else
- {
- secondnum = duoweishu(secondnum, key_code[i]);
- Led8_disp(secondnum);
- }
- }
- }
- }
- }
- }
- //矩阵键盘处理
- void op_dismatrix(usint key_d)
- {
- uint i;
- for(i=4; i<16; i++)
- {
- if(key_d == key_source_code[i])
- {
- if(i == 7) // ‘-’ 键处理
- {
- LED_CS4 = 0xbf;
- if(operation == '\0' && firstnum == 0)
- optimes = 1;
- else
- optimes += 2;
-
- operation = key_code[i];
- }
- else if(i == 11) // ‘*’ 键处理
- {
- LED_CS4 = 0xdf;
- optimes++;
- operation = key_code[i];
- }
- else if(i == 12) // ‘C’ 键处理
- {
- init_clear();//变量初始化
- break;
- }
- else if(i == 14) // ‘=’ 键处理
- {
- mean_sign=1;//等号标志位
- if(operation == '+')
- firstnum = firstnum + secondnum;
- else if(operation == '-')
- firstnum = firstnum - secondnum;
- else if(operation == '*')
- firstnum = firstnum * secondnum;
- else if(operation == '/')
- {
- if(secondnum == 0)//被除数为0,结果为无穷大
- firstnum = 88888;
- else
- firstnum = firstnum / secondnum*1.0;
- }
-
- //secondnum = 0;
- Led8_disp( firstnum);//显示和
- }
- else if(i == 15)// ‘/‘ 键处理
- {
- LED_CS4 = 0xef;
- optimes++;
- operation = key_code[i];
- }
- else // 数字键输入
- {
- //在没有按下运算符的时候,把按键对应的键值存到firstnum
- if(operation == '\0')
- {
- firstnum = duoweishu(firstnum,key_code[i]);
- Led8_disp(firstnum);
- }
- else//按下运算符的时候,把按键对应的键值存到secondnum
- {
- if(mean_sign == 1)//等号标志位
- {
- secondnum = 0;
- mean_sign = 0;//清等号标志位
- }
-
- if(operation == '-' && optimes == 1)//负数输入
- {
- firstnum = duoweishu(firstnum,-key_code[i]);
- Led8_disp(firstnum);
- }
- else
- {
- secondnum = duoweishu(secondnum, key_code[i]);
- Led8_disp(secondnum);
- }
- }
- }
- }
- }
- }
- void Main(void)
- {
- LED_CS4 = 0xff;
- //Led8_disp(0);
- init_clear();//变量初始化
- init_key();
- while (1)
- {
- }
- }
复制代码- /******************************************************
- *
- *项目名称:简易计算器
- *
- *函 数:key.c
- *
- *作 用:按键GPIO定义及其相关定义,按键中断函数,按键
- * 初始化函数
- *
- *有待改进:本程序还可以进一步的完善,其一就有如按下1+2,
- * 再按下+号的时候,就直接计算出结果并显示。
- * 希望有兴趣的朋友能够加以改进。
- *
- *制 作 人:沐雨青林
- *
- *******************************************************/
- #define uchar unsigned char
- #define usint unsigned short int
- #define uint unsigned int
- #define KPC (*((volatile uint *)(0x41500000)))
- #define KPDK (*((volatile uint *)(0x41500008)))
- #define KPREC (*((volatile uint *)(0x41500010)))
- #define KPMK (*((volatile uint *)(0x41500018)))
- #define KPAS (*((volatile uint *)(0x41500020)))
- #define KPKDI (*((volatile uint *)(0x41500048)))
- #define GPSR2 (*((volatile uint *)(0x40E00020)))
- #define GPCR2 (*((volatile uint *)(0x40E0002C)))
- #define GPDR2 (*((volatile uint *)(0x40E00014)))
- #define GPSR3 (*((volatile uint *)(0x40E00118)))
- #define GPCR3 (*((volatile uint *)(0x40E00124)))
- #define GPDR3 (*((volatile uint *)(0x40E0010C)))
- #define GAFR3_L (*((volatile uint *)(0x40E0006C)))
- #define GAFR2_U (*((volatile uint *)(0x40E00068)))
- #define ICMR (*((volatile uint *)(0x40D00004)))
- #define ICLR (*((volatile uint *)(0x40D00008)))
- #define CKEN (*((volatile uint *)(0x41300004)))
- extern void op_disdir(usint key_d);
- extern void op_dismatrix(usint key_d);
- const uchar key_source_code[] ={ 0x40, 0x02, 0x04, 0x20,
- 0x00, 0x01, 0x02, 0x05,
- 0x10, 0x11, 0x12, 0x15,
- 0x20, 0x21, 0x22, 0x25 };
- void Delay(uint x)
- {
- uint i, j, k;
- for (i =0; i <=x; i++)
- for (j = 0; j <0xff; j++)
- for (k = 0; k <0xff; k++);
- }
- #define key_down 1
- #define key_up 0
- volatile uchar key_f;
- void init_key(void)
- {
- CKEN |= (1 << 19);
-
- //----gpio-----
- //dirkey
- //94-->KP_DKIN1(01)
- GAFR2_U |= (1<<28);
- GAFR2_U &=~(1<<29);
- //95-->KP_DKIN2(01)
- GAFR2_U |= (1<<30);
- GAFR2_U &=~(1ul<<31);
- //98-->KP_DKIN5(01)
- GAFR3_L |= (1<<4);
- GAFR3_L &= ~(1<<5);
- //99-->KP_DKIN6(01)
- GAFR3_L |= (1<<6);
- GAFR3_L &= ~(1<<7);
-
- //matrixkey
- //in
- //100
- GAFR3_L |= (1<<8);
- GAFR3_L &= ~(1<<9);
- GPDR3 &= ~(1<<4);
- //101
- GAFR3_L |= (1<<10);
- GAFR3_L &= ~(1<<11);
- GPDR3 &= ~(1<<5);
- //102
- GAFR3_L |= (1<<12);
- GAFR3_L &= ~(1<<13);
- GPDR3 &= ~(1<<6);
- //out
- //108-->KP_MKOUT5(10)
- GAFR3_L &= ~(1<<24);
- GAFR3_L |= (1<<25);
- GPDR3 |= (1<<12);
- //107-->KP_MKOUT4(10)
- GAFR3_L &= ~(1<<22);
- GAFR3_L |= (1<<23);
- GPDR3 |= (1<<11);
- //106-->KP_MKOUT3(10)
- GAFR3_L &= ~(1<<20);
- GAFR3_L |= (1<<21);
- GPDR3 |= (1<<10);
- //105-->KP_MKOUT2(10)
- GAFR3_L &= ~(1<<18);
- GAFR3_L |= (1<<19);
- GPDR3 |= (1<<9);
- //104-->KP_MKOUT1(10)
- GAFR3_L &= ~(1<<16);
- GAFR3_L |= (1<<17);
- GPDR3 |= (1<<8);
- //103-->KP_MKOUT0(10)
- GAFR3_L &= ~(1<<14);
- GAFR3_L |= (1<<15);
- GPDR3 |= (1<<7);
-
- //--------
- KPC = 0x3fbff3c2;
- KPC |= 0x01 ;
- KPC |= (1<<11);
- KPKDI = 0x6464;
-
- //interrupt
- ICMR |= (1<<4);
- ICLR &= ~(1<<4);
-
- //key_up
- key_f = key_up;
- }
- //key_interrupt
- void key_del()
- {
-
- ICMR &= ~(1<<4);
- key_f = !key_f;
- if(key_f == key_down)
- {
- usint key_d;
- uint kpc_tmp;
- kpc_tmp = KPC;
- if(kpc_tmp & (1<<5))
- {
- key_d = KPDK & 0xff;
-
- op_disdir(key_d);//直入式键盘处理
- //LED8_CS2 = LED_VALUE(10 , 10);
- }
- if(kpc_tmp & (1<<22))
- {
- key_d = KPAS & 0xff;
- op_dismatrix(key_d);//矩阵键盘处理
- //LED8_CS2 = LED_VALUE(10 , 10);
-
- }
- }
- KPC = KPC;
- ICMR |= (1<<4);
- }
- void key_dismatrix()
- {
- usint key_d;
- key_d = KPAS & 0xff;
-
- // LED8_CS3 = LED_VALUE((key_d & 0xf0) >> 4 ,key_d & 0xf);
- }
- void key_disdir()
- {
- usint key_d;
- while(key_d = KPDK & 0xff, key_d == 0x00);
- // LED8_CS3 = LED_VALUE((key_d & 0xf0) >> 4 ,key_d & 0xf);
- }
复制代码
所有资料下载地址:
http://www.51hei.com/bbs/dpj-4740-1.html
|