最新消息:

51单片机 – 秒表程序Coding练习

51单片机 244浏览 0评论

本程序是矩阵按键扫描,数码管动态扫描,定时器中断的综合练习,其中秒表显示计数函数用了2种写法,是2种不同的思路。

代码已经在KST-51 v1.3.2开发板验证通过。

#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^1;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
sbit keyIn1 = P2^4;
sbit keyIn2 = P2^5;
sbit keyIn3 = P2^6;
sbit keyIn4 = P2^7;
sbit keyOut1 = P2^3;
sbit keyOut2 = P2^2;
sbit keyOut3 = P2^1;
sbit keyOut4 = P2^0;

//数码管显示字符转换表
unsigned char code ledChar[16] = {
    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 T0RH = 0;                 //Timer0重载值的高字节(高8位)
unsigned char T0RL = 0;                 //Timer0重载值的低字节(低8位)
bit stopWatchRunning = 0;               //秒表运行标志位
bit stopWatchRefresh = 1;               //秒表刷新标志位
unsigned int integerPart = 0;           //秒表的整数部分
unsigned char decimalPart = 0;          //表秒的小数部分

void ConfigTimer0(unsigned int ms);
void StopWatchDisplay();
void KeyDriver();

void main()
{
	EA = 1;                             //使能总中断
	ENLED = 0;                          //选择数码管进行显示
	ADDR3 = 1;
    ConfigTimer0(2);                    //配置Timer0,定时2ms

    while (1)
    {
        if (stopWatchRefresh)           //秒表数刷新
        {
            stopWatchRefresh = 0;
            StopWatchDisplay();         //显示当前的秒表数值
        }
        KeyDriver();                    //按键驱动函数
    }

}
//配置并启动Timer0,ms为Timer0的定时时间,单位毫秒
void ConfigTimer0(unsigned int ms)
{
    unsigned long reload;               //临时变量

    reload = 11059200 / 12;             //定时器的计数频率
    reload = (reload * ms) / 1000;      //计算所需的计数值,也就是溢出值
    reload = 65536 - reload + 18;       //计算定时器的重载值,也就是初值,并且补偿中断响应误差
    T0RH = (unsigned char)(reload >> 8);//定时器重载值拆分为高低字节
    T0RL = (unsigned char)reload;
    TMOD &= 0xF0;                       //清零Timer0的控制位
    TMOD |= 0x01;                       //配置Timer0为模式1
    TH0 = T0RH;                         //加载Timer0的重载值
    TL0 = T0RL;
    ET0 = 1;                            //使能Timer0中断
    TR0 = 1;                            //启动Timer0
}
/*
//秒表计数显示函数
void StopWatchDisplay()
{
    signed char i;
    unsigned char buff[4];

    //将小数部分分离,并给数码管缓冲区赋值
    ledBuff[0] = ledChar[decimalPart % 10];
    ledBuff[1] = ledChar[decimalPart / 10 % 10];
    //将整数部分分离
    buff[0] = integerPart % 10;
    buff[1] = integerPart / 10 % 10;
    buff[2] = integerPart / 100 % 10;
    buff[3] = integerPart / 1000 % 10;
    //整数部分高位为零不显示
    for (i = 3; i >= 1; i--)
    {
        if (buff[i] == 0)
        {
            ledBuff[i + 2] = 0xFF;
        }
        else
        {
            break;
        }
    }
    for ( ; i >= 0; i--)
    {
        ledBuff[i + 2] = ledChar[buff[i]];
    }
    //点亮小数点
    ledBuff[2] &= 0x7F;
}
*/
//秒表计数显示函数
void StopWatchDisplay()
{
    signed char i;
    static unsigned char buff[6];
    static unsigned int temp;

    temp = integerPart;                 //将整数部分赋给静态变量temp
    buff[0] = decimalPart % 10;         //分离小数部分,并赋值给数值缓冲区
    buff[1] = decimalPart / 10 % 10;

    for (i = 2; i <= 5; i++)            //循环分离整数部分,并赋值给数值缓冲区
    {
        buff[i] = temp % 10;
        temp /= 10;
    }

    for (i = 5; i >= 3; i--)            //数码管的高位遇到0不显示(小数部分和整数部分的最低位是必须显示的)
    {
        if (buff[i] == 0)
        {
            ledBuff[i] = 0xFF;
        }
        else{
            break;
        }
    }
    for ( ; i >= 0; i--)
    {
        ledBuff[i] = ledChar[buff[i]];
    }
    ledBuff[2] &= 0x7F;                 //点亮数码管小数点
}

//按键动作函数
void KeyAction(unsigned char keyCode)
{
    if (keyCode == 0x0D)                            //按下回车键
    {
        if (stopWatchRunning)                       //如果秒表是运行状态
        {
            stopWatchRunning = 0;                   //让秒表停止状态
        }
        else if (stopWatchRunning == 0)             //如果秒表是停止状态
        {
            stopWatchRunning = 1;                   //让秒表运行状态
        }
    }
    else if (keyCode == 0x1B)                       //按下ESC键
    {
        stopWatchRunning = 0;                       //秒表停止
        integerPart = 0;                            //秒表整数部分清零
        decimalPart = 0;                            //秒表小数部分清零
        stopWatchRefresh = 1;                       //秒表刷新
    }
}
//按键驱动函数
void KeyDriver()
{
    unsigned char i, j;
    static unsigned char keyBackup[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 (keySta[i][j] != keyBackup[i][j])    //检测当前按键值和备份值不一致,说明按键有动作了
            {
                if (keySta[i][j] == 0)              //如果当前按键是按下的状态
                {
                    KeyAction(keyCodeMap[i][j]);    //调用动作函数
                }
                keyBackup[i][j] = keySta[i][j];     //备份一次当前的按键值
            }
        }
    }
}
//数码管扫描函数
void LedScan()
{
    static unsigned char i = 0;

    P0 = 0xFF;                  //关闭所有段选位,消隐
    P1 = (P1 & 0xF8) | i;       //位选索引赋值到P1口的低3位
    P0 = ledBuff[i];            //缓冲区索引位置的数据送到P0口,
    if (i < 5)                  //索引递增循环,遍历整个缓冲区
    {
        i++;
    }
    else{
        i = 0;
    }
}
//按键扫描函数
void KeyScan()
{   
    unsigned char i;
    static unsigned keyout = 0;
    //按键扫描缓冲区
    static unsigned char keyBuff[4][4] = {                      
        {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
    };
    //按键值一位一位的移入缓冲区
    keyBuff[keyout][0] = (keyBuff[keyout][0] << 1) | keyIn1;
    keyBuff[keyout][1] = (keyBuff[keyout][1] << 1) | keyIn2;
    keyBuff[keyout][2] = (keyBuff[keyout][2] << 1) | keyIn3;
    keyBuff[keyout][3] = (keyBuff[keyout][3] << 1) | keyIn4;
    //每行4个按键,所以循环4次
    for (i = 0; i < 4; i++)
    {
        if ((keyBuff[keyout][i] & 0x0F) == 0x00)        //连续4次扫描值为0,即2ms * 4 * 4 = 32ms内都是按下状态
        {
            keySta[keyout][i] = 0;
        }
        else if ((keyBuff[keyout][i] & 0x0F) == 0x0F)   //连续4次扫描值为1,即2ms * 4 * 4 = 32ms内都是按下状态
        {
            keySta[keyout][i] = 1;
        }
    }

    keyout++;               //输出索引递增
    keyout &= 0x03;         //输出索引加到4即归零

    switch (keyout)         //根据索引,释放当前的输出引脚,拉低下次的输出引脚
    {
    case 0: keyOut4 = 1; keyOut1 = 0; break;
    case 1: keyOut1 = 1; keyOut2 = 0; break;
    case 2: keyOut2 = 1; keyOut3 = 0; break;
    case 3: keyOut3 = 1; keyOut4 = 0; break;
    default: break;
    }
}
//秒表计数函数,每10ms调用一次秒表计数累加
void StopWatchCount()
{
    if (stopWatchRunning)               //秒表处于运行状态
    {
        decimalPart++;                  //小数部分+1
        if (decimalPart >= 100)         //小数部分加到100时进位到整数部分
        {
            decimalPart = 0;            //小数部分清零
            integerPart++;              //整数部分+1
            if (integerPart >= 10000)   //整数部分加到10000时归零
            {
                integerPart = 0;
            }
        }
        stopWatchRefresh = 1;           //秒表刷新
    }
}
void InterruptTimer0() interrupt 1
{
    static unsigned int tmr10ms = 0;
    TH0 = T0RH;                         //重新加载重载值
    TL0 = T0RL;

    LedScan();                          //数码管刷新
    KeyScan();                          //矩阵按键刷新
    //定时10ms进行一次秒表计数
    tmr10ms++;
    if (tmr10ms >= 5)
    {
        tmr10ms = 0;
        StopWatchCount();
    }
}

您必须 登录 才能发表评论!