蜂鸣数字计算器

  • 格式:doc
  • 大小:174.50 KB
  • 文档页数:15

一、课程设计内容及要求1、蜂鸣计算器设计利用普中科技HC6800-ES V2.0单片机开发板设计一款带蜂鸣器的电子计算器。

主要使用开发板上的数码管、LED点阵、矩阵键盘、蜂鸣器等模块功能。

开发板配有光盘,里面有各个模块的使用说明和程序范例可供参考。

2、设计要求1)上电开机或者复位键按下之后,数码管自动显示个人学号的后8位。

2)定义矩阵键盘的S1-S9代表数字按键1-9,键盘S10代表数字按键0,键盘S11-S16分别代表按键+、-、×、÷、=、C,其中C按键为计算器清零按键,按下C键计算器开始新的计算。

3)计算器计算过程中,用LED点阵显示+、-、×、÷运算符号,用8位数码管显示键入的数字和运算的结果。

键入时依次显示并自动移位,例如18+9=27,先键入1,显示1,再键入8,1自动左移一位显示18,键入+,LED点阵显示+,键入9,数码管之前显示的18消失并重新显示为9,键入=,数码管显示运算结果27。

4)为这16个矩阵键盘的按键分配不同的蜂鸣器频率,使得按下不同的按键蜂鸣器响声不同,便于识别按键。

5)按键应具有一定的消除抖动功能。

6)所有单片机程序代码都用C语言编写,并烧写到单片机中上电自动运行。

二、设计思想蜂鸣器计算器设计包括矩阵键盘模块、数码管及矩阵LED动态显示模块、输入逻辑计算显示模块和蜂鸣器响应模块这四大模块。

具体实施方法如下:1、矩阵键盘模块,四乘四的矩阵键盘,设计按键扫描函数,在定时中断中调用,调用定时器中断,中断间隔时间设置为1ms,选择P2IO口,P2.0到P2.3做输出KeyOut,P2.4做P2.7做输入KeyIn,每次在按键扫描中断中,每次让矩阵按键的一个 KeyOut 输出低电平,其它三个输出高电平,判断当前所有 KeyIn 的状态,然后再让下一个 KeyOut 输出低电平,其它三个输出高电平,再次判断所有 KeyIn,通过四次扫描,确定按下的键,再通过快速的中断不停的循环进行判断,就可以最终确定哪个按键按下了。

同时在按键模块,还需要消抖使按键稳定。

通常我们采用延迟再次检测的方式消抖,但是太过消耗单片机的CUP。

所以为了避免通过延时消抖占用单片机执行时间,将其转化成了一种按键状态判定而非按键过程判定,只对当前按键的连续 16ms 的 4 次状态进行判断,全为0就断定对于的键按下了,全为1就断定为没有按下。

2、数码管及矩阵LED动态显示模块,通过每毫秒进行动态扫描进行显示,由于数码管和矩阵LED动态显示模块都需要通过P1.3和P1.4进行选择来控制,所以在写刷新数据和符号的时候需要先在函数开始调好对应的控制位。

而所需要的显示的数字和符号保存在数组中,以便需要的时候直接调用。

3、输入逻辑计算显示模块,在编写程序的时候,对于这个简易计算器,以正整数的连续加减乘除为基本编程目的。

在第一次输入为0-9这十个按键的时候,此时设置了标记符号flag=0,直接保存到数据保存数组add[flag]中,若继续输入数字就把add[flag]乘以10再加上新输入的数字;若扫描到加减乘除这四个符号的时候,先把标记符号flag置为1,再把算数符号保存到符号标记sign中,把需要LED矩阵显示的全局符号变量tx设置为对应的数字,继续扫描到数字的时候就会把数据保存到add[1](flag=1)中;如果接下来输入为等于号,就判断sign数值然后对add[0]和add[1]做对应的计算;如果输入还是计算符号,就先计算把计算结果保存到add[0]中,再清零add[1],再继续扫描。

4、蜂鸣器响应模块,由于开发板上面的蜂鸣器是无源蜂鸣器,所以需要通过输出pwm脉冲来驱动蜂鸣器,同时由于脉冲频率不同对于蜂鸣器发声也不一样,所以通过一个需要传入频率参数的函数。

通过该频率参数,来设置中断定时器T1,控制蜂鸣器驱动端口输出高低电平,然后在按键处理函数处判断对应需要的频率送给蜂鸣器响应函数。

三、程序流程图1、整体流程图2、算数逻辑流程四、详细程序1、总程序Calculator.c#include <reg52.h>#include <stdlib.h>#include <sys.h>#include <fun.h>void main(){Init();ShowNumber(135043); //显示学号while (1){KeyDriver(); //调用按键驱动函数}}/* T0中断服务函数,用于数码管显示扫描与按键扫描*/ void InterruptTimer0() interrupt 1{TH0 = 0xFC; //重新加载初值TL0 = 0x67;LedScan(); //调用数码管显示扫描函数KeyScan(); //调用按键扫描函数ShowPhoto(tx); //调用点阵显示函数}/* T1中断服务函数,用于蜂鸣器*/void InterruptTimer1() interrupt 3{TH1 = T1RH; //重新加载重载值TL1 = T1RL;BUZZ = ~BUZZ; //反转蜂鸣器控制电平}2、宏定义头文件sys.c#ifndef __sys_h__#define __sys_h__#include "sys.h"#define uint unsigned int#define uchar unsigned charsbit ADDR0 = P1^0;sbit ADDR1 = P1^1;sbit ADDR2 = P1^2;sbit ADDR3 = P1^3;sbit ENLED = P1^4;sbit KEY_IN_1 = P2^4;sbit KEY_IN_2 = P2^5;sbit KEY_IN_3 = P2^6;sbit KEY_IN_4 = P2^7;sbit KEY_OUT_1 = P2^3;sbit KEY_OUT_2 = P2^2;sbit KEY_OUT_3 = P2^1;sbit KEY_OUT_4 = P2^0;sbit BUZZ = P1^6; //蜂鸣器控制引脚unsigned char T1RH = 0; //T0重载值的高字节unsigned char T1RL = 0; //T0重载值的低字节unsigned char tx = 0; //矩阵led显示unsigned char code LedChar[] = { //数码管显示字符转换表0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E};unsigned char LedBuff[6] = { //数码管显示缓冲区0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表{ 0x31, 0x32, 0x33, 0x26 }, //数字键1、数字键2、数字键3、加键{ 0x34, 0x35, 0x36, 0x25 }, //数字键4、数字键5、数字键6、乘键{ 0x37, 0x38, 0x39, 0x28 }, //数字键7、数字键8、数字键9、减键{ 0x30, 0x1B, 0x0D, 0x27 } //数字键0、ESC键、等号键、除键};unsigned char KeySta[4][4] = { //全部矩阵按键的当前状态{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};unsigned char code image[6][8] = { //符号的字模表{0xFF,0xBD,0x5A,0xFF,0xFF,0xBD,0xDB,0xE7},//笑脸{0xE7,0xE7,0xE7,0x00,0x00,0xE7,0xE7,0xE7},//加号{0xFF,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF}, //减号{0x3C,0x18,0x81,0xC3,0xC3,0x81,0x18,0x3C}, //乘号{0xE7,0xE7,0xFF,0x00,0x00,0xFF,0xE7,0xE7}, //除号{0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF} //等号};void Init();void ShowNumber(unsigned long num);void KeyAction(unsigned char keycode);void KeyDriver();void KeyScan();void LedScan();void OpenBuzz(unsigned int frequ);void StopBuzz();void Delay();void Buzz(unsigned int frequ);void ShowPhoto(unsigned int a);#endif3、函数头文件fun.c#ifndef __fun_h__#define __fun_h__#include "fun.h"void Init(){EA = 1; //使能总中断TMOD = 0x11; //设置T0为模式1TH0 = 0xFC; //为T0赋初值0xFC67,定时1msTL0 = 0x67;ET0 = 1; //使能T0中断TR0 = 1; //启动T0}/* 将一个无符号长整型的数字显示到数码管上,num-待显示数字*/ void ShowNumber(unsigned long num){signed char i;unsigned char buf[6];for (i=0; i<6; i++) //把长整型数转换为6位十进制的数组{buf[i] = num % 10;num = num / 10;}for (i=5; i>=1; i--) //从最高位起,遇到0转换为空格,遇到非0则退出循环{if (buf[i] == 0)LedBuff[i] = 0xFF;elsebreak;}for ( ; i>=0; i--) //剩余低位都如实转换为数码管显示字符{LedBuff[i] = LedChar[buf[i]];}}/* 按键动作函数,根据键码执行相应的操作,keycode-按键键码*/void KeyAction(unsigned char keycode){static unsigned long result = 0; //用于保存运算结果static unsigned long add[2] = 0; //用于保存输入的数字static unsigned int sign[2] = 0; //用作统计前一次和当前运算符号,0~3依次代表加减乘除static unsigned int flag = 0; //用于统计次数switch(keycode){ //不同按键蜂鸣器发出不同频率声音case 0x30 : Buzz(1000);break;case 0x31 : Buzz(1500);break;case 0x32 : Buzz(2000);break;case 0x33 : Buzz(2500);break;case 0x34 : Buzz(3000);break;case 0x35 : Buzz(3500);break;case 0x36 : Buzz(4000);break;case 0x37 : Buzz(4500);break;case 0x38 : Buzz(5000);break;case 0x39 : Buzz(5500);break;case 0x26 : Buzz(6000);break;case 0x28 : Buzz(6500);break;case 0x25 : Buzz(7500);break;case 0x27 : Buzz(8000);break;case 0x0D : Buzz(8500);break;case 0x1B : Buzz(9000);break;default : break;}if ((keycode>=0x30) && (keycode<=0x39)) //输入0-9的数字{ if(flag == 0){add[0] = (add[0]*10)+(keycode-0x30); //整体十进制左移,新数字进入个位ShowNumber(add[0]); //运算结果显示到数码管}else{add[1] = (add[1]*10)+(keycode-0x30); //整体十进制左移,新数字进入个位ShowNumber(add[1]); //运算结果显示到数码管}}else if (keycode == 0x26) //向上键用作加号,执行加法或连加运算{sign[0] = sign[1]; //保存前一次的运算符号sign[1] = 0; //保存当前运算符号flag = 1;tx = 1;switch(sign[0]){ //用于连加case 0:add[0]= add[0]+add[1];break;case 1:add[0]= add[0]-add[1];break;case 2:add[0]= add[0]*add[1];break;case 3:add[0]= add[0]/add[1];break;default : break;}ShowNumber(add[0]); //运算结果显示到数码管add[1]=0; //清零add[1];}else if (keycode == 0x28) //向下键用作减号,执行减法{ sign[0] = sign[1]; //保存前一次的运算符号sign[1] = 1; //保存当前运算符号flag = 1;tx = 2;switch(sign[0]){case 0:add[0]= add[0]+add[1];break;case 1:add[0]= add[0]-add[1];break;case 2:add[0]= add[0]*add[1];break;case 3:add[0]= add[0]/add[1];break;default : break;}ShowNumber(add[0]); //运算结果显示到数码管add[1]=0; //清零add[1];}else if (keycode == 0x25) //向下键用作乘号,执行乘法{ sign[0] = sign[1]; //保存前一次的运算符号sign[1] = 2; //保存当前运算符号flag = 1;tx = 3;switch(sign[0]){case 0:add[0]= add[0]+add[1];break;case 1:add[0]= add[0]-add[1];break;case 2:add[0]= add[0]*add[1];break;case 3:add[0]= add[0]/add[1];break;default : break;}ShowNumber(add[0]); //运算结果显示到数码管add[1]=0; //清零add[1];}else if (keycode == 0x27) //向下键用作除号,执行除法{ sign[0] = sign[1]; //保存前一次的运算符号sign[1] = 3; //保存当前运算符号flag = 1;tx = 4 ;switch(sign[0]){case 0:add[0]= add[0]+add[1];break;case 1:add[0]= add[0]-add[1];break;case 2:add[0]= add[0]*add[1];break;case 3:add[0]= add[0]/add[1];break;default : break;}ShowNumber(add[0]); //运算结果显示到数码管add[1]=0; //清零add[1];}else if (keycode == 0x0D) //等号键,执行运算{switch(sign[1]){case 0:{ result = add[0]+add[1]; //进行加法运算ShowNumber(result); //运算结果显示到数码管break;}case 1:{ result = add[0]-add[1]; //进行减法运算ShowNumber(result); //运算结果显示到数码管break;}case 2:{ result = add[0]*add[1]; //进行乘法运算ShowNumber(result); //运算结果显示到数码管break;}case 3:{ result = add[0]/add[1]; //进行除法运算ShowNumber(result); //运算结果显示到数码管break;}default : break;}tx = 5 ;flag = 0;add[0] = 0;add[1] = 0;sign[0]=0;sign[1]=0;}else if (keycode == 0x1B) //Esc键,清零结果{ add[0] = 0;add[1] = 0;sign[0]=0;sign[1]=0;flag = 0;result = 0;tx = 0 ;ShowNumber(result); //清零后的加数显示到数码管}}/* 按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用*/ void KeyDriver(){unsigned char i, j;static unsigned char backup[4][4] = { //按键值备份,保存前一次的值{1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1} };for (i=0; i<4; i++) //循环检测4*4的矩阵按键{for (j=0; j<4; j++){if (backup[i][j] != KeySta[i][j]) //检测按键动作{if (backup[i][j] != 0) //按键按下时执行动作{KeyAction(KeyCodeMap[i][j]); //调用按键动作函数}backup[i][j] = KeySta[i][j]; //刷新前一次的备份值}}}}/* 按键扫描函数,需在定时中断中调用,调用间隔1ms */void KeyScan(){unsigned char i;static unsigned char keyout = 0; //矩阵按键扫描输出索引static unsigned char keybuf[4][4] = { //矩阵按键扫描缓冲区{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},{0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF} };//将一行的4个按键值移入缓冲区keybuf[keyout][0] = (keybuf[keyout][0] << 1) | KEY_IN_1;keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN_2;keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN_3;keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN_4;//消抖后更新按键状态for (i=0; i<4; i++) //每行4个按键,所以循环4次{if ((keybuf[keyout][i] & 0x0F) == 0x00){ //连续4次扫描值为0,即4*4ms内都是按下状态时,可认为按键已稳定的按下KeySta[keyout][i] = 0;}else if ((keybuf[keyout][i] & 0x0F) == 0x0F){ //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起KeySta[keyout][i] = 1;}}//执行下一次的扫描输出keyout++; //输出索引递增keyout = keyout & 0x03; //索引值加到4即归零switch (keyout) //根据索引,释放当前输出引脚,拉低下次的输出引脚{case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0; break;case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;case 3: KEY_OUT_3 = 1; KEY_OUT_4 = 0; break;default: break;}}/* 数码管动态扫描刷新函数,需在定时中断中调用*/void LedScan(){static unsigned char i = 0; //动态扫描的索引ENLED = 0; //选择数码管进行显示ADDR3 = 1;P0 = 0xFF; //显示消隐switch (i){case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;case 5: ADDR2=1; ADDR1=0; ADDR0=1; i=0; P0=LedBuff[5]; break;default: break;}}/* 蜂鸣器启动函数,frequ-工作频率*/void OpenBuzz(unsigned int frequ){unsigned int reload; //计算所需的定时器重载值reload = 65536 - (11059200/12)/(frequ*2); //由给定频率计算定时器重载值T1RH = (unsigned char)(reload >> 8); //16位重载值分解为高低两个字节T1RL = (unsigned char)reload;TH1 = 0xFF; //设定一个接近溢出的初值,以使定时器马上投入工作TL1 = 0xFE;ET1 = 1; //使能T0中断TR1 = 1; //启动T0}/* 蜂鸣器停止函数*/void StopBuzz(){ET1 = 0; //禁用T0中断TR1 = 0; //停止T0}/*延时函数*/void Delay(){unsigned int i=0;for(i=10000;i>0;i--){;}}/*蜂鸣器*/void Buzz(unsigned int frequ){OpenBuzz(frequ);Delay();StopBuzz();}/*LED矩阵显示运算符号*/void ShowPhoto(unsigned int a){static unsigned char i = 0;ENLED = 0; //使能U4,选择LED点阵ADDR3 = 0;P0 = 0xFF; //显示消隐switch (i){case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=image[a][0]; break;case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=image[a][1]; break;case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=image[a][2]; break;case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=image[a][3]; break;case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=image[a][4]; break;case 5: ADDR2=1; ADDR1=0; ADDR0=1; i++; P0=image[a][5]; break;case 6: ADDR2=1; ADDR1=1; ADDR0=0; i++; P0=image[a][6]; break;case 7: ADDR2=1; ADDR1=1; ADDR0=1; i=0; P0=image[a][7]; break;default: break;}}#endif五、实践收获和体会在这一次的电子设计的过程中,通过我们两个人的合作完成了这次的蜂鸣计算器的设计,通过对每个模块的例程的理解,最后把各个模块组合在一起。