电工学习网

 找回密码
 立即注册

单片机最小系统电路解析

2015-1-12 10:34| 编辑:电工学习网| 查看: 32345| 评论: 2


5.4 按键、数码管简单加法运算
这一小节内容只有一个程序,使用我们的矩阵按键实现计算器中简单的整数加法运算,大家可以先把程序复制到Keil中编译下载到板子上试试效果。这是我们第一次做一个算的上的综合性程序,实现了按键和数码管以及C语言灵活运用的一个例程。作为初学者针对这种程序的学习方式是,先从头到尾读一到三遍,边读边理解,然后边抄边理解,彻底理解透彻后,自己尝试独立写出来。完全采用记忆模式来学习这种例程,一两个例程你感觉不到什么提高,当这种例程背过上百八十个的时候,厚积薄发的感觉就会体现出来了。同时,在抄读的过程中注意学习我们程序的编程规范,尽量规整一些。
 
#include 
 
sbit KEY_IN_1  = P2^4;  //矩阵按键的扫描输入引脚1
sbit KEY_IN_2  = P2^5;  //矩阵按键的扫描输入引脚2
sbit KEY_IN_3  = P2^6;  //矩阵按键的扫描输入引脚3
sbit KEY_IN_4  = P2^7;  //矩阵按键的扫描输入引脚4
sbit KEY_OUT_1 = P2^3;  //矩阵按键的扫描输出引脚1
sbit KEY_OUT_2 = P2^2;  //矩阵按键的扫描输出引脚2
sbit KEY_OUT_3 = P2^1;  //矩阵按键的扫描输出引脚3
sbit KEY_OUT_4 = P2^0;  //矩阵按键的扫描输出引脚4
 
sbit  ADDR0 = P1^0;
sbit  ADDR1 = P1^1;
sbit  ADDR2 = P1^2;
sbit  ADDR3 = P1^3;
sbit  ENLED = P1^4;
 
unsigned char code LedChar[] = {
    0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
    0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8e
};   //数码管真值表
const unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到PC标准键盘键码的映射表
    { '1',  '2',  '3', 0x26 }, //数字键1、数字键2、数字键3、向上键
    { '4',  '5',  '6', 0x25 }, //数字键4、数字键5、数字键6、向左键
    { '7',  '8',  '9', 0x28 }, //数字键7、数字键8、数字键9、向下键
    { '0', 0x1B, 0x0D, 0x27 }  //数字键0ESC键、  回车键、 向右键
};
unsigned char KeySta[4][4] = {  //全部矩阵按键的当前状态
    {1, 1, 1, 1},
    {1, 1, 1, 1},
    {1, 1, 1, 1},
    {1, 1, 1, 1}
};           //由于数组不能定义成bit型,这里定义成unsigned char
unsigned char LedBuf[6] = {    //数码管动态扫描显示缓冲区
    0xC0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
 
void DisplayNum(unsigned long num);
void KeyAction(unsigned char keycode);
 
void main(void)
{
    unsigned char i, j;
    unsigned char backup[4][4] = {  //按键值备份,保存前一次的值
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1}
    };
    
    //选择数码管进行显示
    P0 = 0xFF;
    ADDR3 = 1;
    ENLED = 0;
    
    //配置T0工作在模式1,定时1ms
    TMOD = 0x01;
    TH0 = 0xFC;
    TL0 = 0x67;
    TR0 = 1;
    ET0 = 1;
    EA = 1;
 
    while(1)
    {
        //检索按键状态的变化
        for (i=0; i<4; i++)
        {
            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];
                }
            }
        }
    }
}
 
void KeyAction(unsigned char keycode)
{
    static unsigned long result = 0;  //用于保存运算结果
    static unsigned long addend = 0;  //用于保存输入的加数
    
    if ((keycode>='0') && (keycode<='9'))  //输入0-9的数字
    {
        addend = (addend*10) + (keycode-'0'); //原数据扩大10倍,由新输入的数字填充其个位
        DisplayNum(addend);    //运算结果显示到数码管
    }
    else if (keycode == 0x26)  //向上键用作加号,执行加法或连加运算
    {
        result += addend;      //进行加法运算
        addend = 0;
        DisplayNum(result);    //运算结果显示到数码管
    }
    else if (keycode == 0x0D)  //回车键,执行加法运算(实际效果与加号并无区别)
    {
        result += addend;      //进行加法运算
        addend = 0;
        DisplayNum(result);    //运算结果显示到数码管
    }
    else if (keycode == 0x1B)  //Esc键,清零结果
    {
        addend = 0;
        result = 0;
        DisplayNum(addend);    //清零后的加数显示到数码管
    }
}
 
void DisplayNum(unsigned long num)
{
    signed char i;
    unsigned char buf[6];
    
    for (i=0; i<6; i++)   //把长整型数转换为6位十进制的数组
    {
        buf[i] = num % 10;
        num /= 10;
    }
    for (i=5; i>=1; i--)  //从最高位起,遇到0即转换为空格,遇到非0即退出
    {
        if (buf[i] == 0)
        {
            LedBuf[i] = 0xFF;
        }
        else
        {
            break;
        }
    }
    for ( ; i>=0; i--)    //剩余低位都如实转换为数字
    {
        LedBuf[i] = LedChar[buf[i]];
    }
}
 
void InterruptTimer0() interrupt 1
{
    unsigned char i;
    static unsigned char ledcnt = 0;  //数码管扫描计数器
    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}
    };
    
    TH0 = 0xFC;  //溢出后进入中断重新赋值
    TL0 = 0x67;
 
    //将一行的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,即16ms(4*4ms)内都只检测到按下状态时,可认为按键已按下
            KeySta[keyout][i] = 0;
        }
        else if ((keybuf[keyout][i] & 0x0F) == 0x0F)
        {   //连续4次扫描值为1,即16ms(4*4ms)内都只检测到弹起状态时,可认为按键已弹起
            KeySta[keyout][i] = 1;
        }
    }
    
    //执行下一次的扫描输出
    keyout++;
    keyout &= 0x03;
    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;
    }
    
    //执行数码管动态扫描显示
    P0 = 0xFF;
    switch (ledcnt)
    {
        case 0: ADDR0=0; ADDR1=0; ADDR2=0; break;
        case 1: ADDR0=1; ADDR1=0; ADDR2=0; break;
        case 2: ADDR0=0; ADDR1=1; ADDR2=0; break;
        case 3: ADDR0=1; ADDR1=1; ADDR2=0; break;
        case 4: ADDR0=0; ADDR1=0; ADDR2=1; break;
        case 5: ADDR0=1; ADDR1=0; ADDR2=1; break;
        default: break;
    }
    P0 = LedBuf[ledcnt];
    ledcnt++;
    if (ledcnt >= 6)
    {
        ledcnt = 0;
    }
1234567

看过《单片机最小系统电路解析》的人还看了以下文章:

发表评论

最新评论

引用 游客 2016-3-6 18:57
图二应该是27M无源晶振
引用 游客 2015-9-3 08:06
后面六页看不见

查看全部评论(2)

  • 实时时钟芯片DS1302
  • 8255的控制字
  • 单片机点亮led灯程序详解
  • 单片机引脚功能定义
  • 单片机数码管显示原理
  • RS485通信和Modbus协议

电工学习网 ( )

GMT+8, 2023-4-26 05:21

Powered by © 2011-2022 www.shop-samurai.com 版权所有 免责声明 不良信息举报

技术驱动未来! 电工学习网—专业电工基础知识电工技术学习网站。

栏目导航: 工控家园 | 三菱plc | 西门子plc | 欧姆龙plc | plc视频教程

返回顶部