抢答器及计算器的讲解

单片机内容回顾


矩阵键盘

>原理图

抢答器及计算器的讲解_第1张图片

>原理

行列扫描:
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 按照程序编写的先后次序,对各个外设,进行巡回检查与处理。这就是查询式工作方式,貌似公平,实际效率却不高。如果有了中断系统,整个计算机系统,就具有了应付突发事件的处理能力。这就是中断式工作方式。

>中断的概念

抢答器及计算器的讲解_第2张图片
抢答器及计算器的讲解_第3张图片

>外部中断基本流程

抢答器及计算器的讲解_第4张图片
这里写图片描述
这里写图片描述

(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

>原理图及功能指令

抢答器及计算器的讲解_第5张图片

清屏指令
这里写图片描述
<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)。
抢答器及计算器的讲解_第6张图片

>基本代码

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();  //核心处理函数
    }       
}

你可能感兴趣的:(51单片机)