抢答器及计算器的讲解
单片机内容回顾
矩阵键盘
>原理图
>原理
行列扫描:
1.高四位全部输出低电平,低四位输出高电平。当接收到的数据,低四位不全为高电平时,说明有按键按下,然后通过接收的数据值,判断是哪一列有按键按下。
2.高四位输出高电平,低四位输出低电平,然后根据接收到的高四位的值判断是哪那一行有按键按下。
>实现代码
/*******************************************************************************
* 函 数 名 : KeyDown
* 函数功能 : 检测有按键按下并读取键值
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;break;
case(0Xb0): KeyValue=KeyValue+4;break;
case(0Xd0): KeyValue=KeyValue+8;break;
case(0Xe0): KeyValue=KeyValue+12;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);
a++;
}
}
}
}
外部中断
>中断的作用
如果没有中断系统,就只能由 CPU 按照程序编写的先后次序,对各个外设,进行巡回检查与处理。这就是查询式工作方式,貌似公平,实际效率却不高。如果有了中断系统,整个计算机系统,就具有了应付突发事件的处理能力。这就是中断式工作方式。
>中断的概念
>外部中断基本流程
(P3.2)可由IT0(TCON.0)选择其为低电平有效还是下降沿有效。当CPU检测到P3.2引脚上出现有效的中断信号时,中断标志IE0(TCON.1)置1,向CPU申请中断。
>实现代码
void Int0Init()
{
//设置INT0
IT0=1;//跳变沿出发方式(下降沿)
EX0=1;//打开INT0的中断允许。
EA=1;//打开总中断
}
void Int0() interrupt 0 //外部中断0的中断函数
{
//中断发生时进行的操作
}
LCD1602
>原理图及功能指令
清屏指令
<1> 清除液晶显示器,即将 DDRAM 的内容全部填入”空白”的 ASCII码 20H;
<2> 光标归位,即将光标撤回液晶显示屏的左上方;
进入模式设置指令
I/D 0=写入新数据后光标左移 1=写入新数据后光标右移
S 0=写入新数据后显示屏不移动 1=写入新数据后显示屏整体右移 1 个字符
显示开关控制指令
D 0=显示功能关 1=显示功能开
C 0=无光标 1=有光标
B 0=光标闪烁 1=光标不闪烁
功能设定指令
DL 0=数据总线为4 位 1=数据总线为 8 位
N 0=显示1 行 1=显示 2 行
F 0=5×7点阵/每字符 1=5×10 点阵/每字符
DDRAM地址
写入显示地址时要求最高位 D7恒定为高电平 1 所以实际写入的数据应该是 01000000B(40H)
+10000000B(80H)=11000000B(C0H)。
>基本代码
void wrc(u8 command) //LCD1602写入指令
{
delay(1000);
rs=0;
rw=0;
e=0;
P0=command;
e=1;
delay(10);
e=0;
}
void wrd(u8 dat) //LCD1602写入数据
{
delay(1000);
rs=1;
rw=0;
e=0;
P0=dat;
e=1;
delay(10);
e=0;
}
void lcdinitial()
{
delay(1000);
wrc(0x01); //清屏
wrc(0x06); //写入数据后光标右移,显示屏不移动
wrc(0x0c); //显示开,无光标,不闪烁
wrc(0x38); //八线数据总位,显示两行,5*7显示
}
抢答器代码
分析
利用外部中断实现每一次的开局
用矩阵键盘获取抢答者输入的信息
静态数码管输出抢答序号
#include "reg52.h"
typedef unsigned int u16;
typedef unsigned char u8;
#define GPIO_KEY P1 //获取矩阵键盘按键值
int KeyValue,flag; //flag为标志变量
u8 code smgduan[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//共阴数码管表
void delay(u16 i)
{
while(i--);
}
void Int0Init() //外部中断初始化
{
//设置INT0
IT0=1;//跳变沿出发方式(下降沿)
EX0=1;//打开INT0的中断允许。
EA=1;//打开总中断
}
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;flag=0;break;
case(0Xb0): KeyValue=KeyValue+4;flag=0;break;
case(0Xd0): KeyValue=KeyValue+8;flag=0;break;
case(0Xe0): KeyValue=KeyValue+12;flag=0;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);
a++;
}
}
}
}
void keypros()
{
while(flag)
{
KeyDown();
if(!flag){
P0=~smgduan[KeyValue];
}
}
}
void main()
{
Int0Init();
flag=0;
while(1)
{
if(flag)
{
keypros();
}
}
}
void Int0() interrupt 0 //外部中断0的中断函数
{
P0=0xff;
flag=1;
}
计算器代码
运行图片
键盘按键对应设计
1 2 3 +
4 5 6 –
7 8 9 *
C 0 = /
LCD显示:
第一行为算式 3*6
第二行为结果 =18
运算范围:
仅支持两个正整数的运算,结果可为负数,范围可达十位数
流程
不断扫描矩阵键盘,设立标志变量识别按键按下
对按下的按键分类:数字,运算符,清除,等号
将运算结果显示在LCD1602上
#include "reg52.h"
typedef unsigned int u16; //0-65535
typedef unsigned char u8; //0-255
typedef unsigned long u32; //0- 2^32-1
sbit rs=P2^6; //lcd1602特殊位定义
sbit rw=P2^5;
sbit e=P2^7;
#define GPIO_KEY P1 //获取矩阵键盘按键值
int KeyValue,flag,t; //flag为标志变量
u32 a,b,num=0; //储存运算过程变量
long answer;
void delay(u16 i)
{
while(i--);
}
void KeyDown(void)
{
char a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下
{
delay(1000);//延时10ms进行消抖
if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
{
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue;flag=0;break;
case(0Xb0): KeyValue=KeyValue+4;flag=0;break;
case(0Xd0): KeyValue=KeyValue+8;flag=0;break;
case(0Xe0): KeyValue=KeyValue+12;flag=0;break;
}
while((a<50)&&(GPIO_KEY!=0xf0)) //检测按键松手检测
{
delay(1000);
a++;
}
}
}
}
void wrc(u8 command) //LCD1602写入指令
{
delay(1000);
rs=0;
rw=0;
e=0;
P0=command;
e=1;
delay(10);
e=0;
}
void wrd(u8 dat) //LCD1602写入数据
{
delay(1000);
rs=1;
rw=0;
e=0;
P0=dat;
e=1;
delay(10);
e=0;
}
void display(long i)
{
int j=0;
u8 a[10]; //储存从结果中分离出的数字
wrc(0x80+0x40); //光标移至第二行
wrd('=');
if(i<0)
{
wrd('-');
i=-i;
}
while(i)
{
a[j]=i%10; //将结果从最低位分裂出来
i=i/10;
j++;
}
for(j=j-1;j>=0;j--) //从最高位开始显示
{
wrd(a[j]+'0');
}
}
void lcdinitial()
{
delay(1000);
wrc(0x01); //清屏
wrc(0x06); //写入数据后光标右移,显示屏不移动
wrc(0x0c); //显示开,无光标,不闪烁
wrc(0x38); //八线数据总位,显示两行,5*7显示
}
void calcupros(){
while(1)
{
KeyDown();
if(!flag)
{
switch(KeyValue)
{
case(0):num=num*10+1;wrd('1');break; //数字显示
case(1):num=num*10+2;wrd('2');break;
case(2):num=num*10+3;wrd('3');break;
case(4):num=num*10+4;wrd('4');break;
case(5):num=num*10+5;wrd('5');break;
case(6):num=num*10+6;wrd('6');break;
case(8):num=num*10+7;wrd('7');break;
case(9):num=num*10+8;wrd('8');break;
case(10):num=num*10+9;wrd('9');break;
case(13):num=num*10+0;wrd('0');break;
case(3):a=num;num=0;wrd('+');t=1;break; //为运算符添加标志变量
case(7):a=num;num=0;wrd('-');t=2;break;
case(11):a=num;num=0;wrd('*');t=3;break;
case(15):a=num;num=0;wrd('/');t=4;break;
case(14):b=num;num=0;
switch(t)
{
case(1):answer=a+b;display(answer);break; //对不同的运算符计算
case(2):answer=a-b;display(answer);break;
case(3):answer=a*b;display(answer);break;
case(4):answer=a/b;display(answer);break;
}
break;
case(12):
lcdinitial();
wrc(0x00+0x80);
a=0;b=0;num=0;answer=0;
}
flag=1;
}
}
}
void main()
{
flag=1;
lcdinitial(); //LCD初始化
wrc(0x00+0x80);
while(1)
{
calcupros(); //核心处理函数
}
}
上一篇: 20190403华为笔试题
下一篇: Quartz定时器使用示例