设计要求
同时为16支参赛队提供抢答功能,抢答成功后应能通过数码管显示出参赛队号数,同时点亮发光二极管示意抢答成功。
加入独立开关,可启动10秒倒计时功能,通过数码管显示出倒计时时间(倒计时状态下抢答功能不起作用,反之亦然)。
电路原理图
硬件原理
时钟信号(晶振)
单片机晶振部位电路,详情请参考《51单片机入门——单片机最小系统》,在此项目中我们选择 11.0592 MHz的晶振。
矩阵按键与独立按键
在该项目中矩阵按键用于选手的抢答器,独立按键用于主持人复位重置抢答。
代码解析
矩阵按键部分代码:
keyboard.c
#include "KEYBOARD.H"
uchar keySta[4][4] = {//矩阵按键的当前状态 1为高电平 ,0为低电平
{1 , 1 , 1 , 1} , {1 , 1 , 1 , 1} , {1 , 1 , 1 , 1} , {1 , 1 , 1 , 1}
};
uchar ledChar[16] = { //共阳极数码管显示字符转换表
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
/* 按键驱动函数,检测按键动作,调度相应动作函数*/
void KeyDriver()
{
uchar i , j;
static 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 ++)
{
for (j = 0 ; j < 4 ; j ++)
{
if (keySta[i][j] != backup[i][j]) //检测按键动作
{
if (backup[i][j] != 0) //按键按下时执行动作
{
P0 = ~ledChar[i*4+j];
LED1 = 1;
}
backup[i][j] = keySta[i][j]; //备份按键状态
}
}
}
}
/* 按键扫描函数 , 在定时中断中调用,推荐1ms*/
void KeyScan()
{
uchar i;
static uchar keyout = 0; // 矩阵按键扫描输出索引
static uchar 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_IN1;
keybuf[keyout][1] = (keybuf[keyout][1] << 1) | KEY_IN2;
keybuf[keyout][2] = (keybuf[keyout][2] << 1) | KEY_IN3;
keybuf[keyout][3] = (keybuf[keyout][3] << 1) | KEY_IN4;
// 消抖后更新按键状态
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_OUT4 = 1; KEY_OUT1 = 0; break;
case 1: KEY_OUT1 = 1; KEY_OUT2 = 0; break;
case 2: KEY_OUT2 = 1; KEY_OUT3 = 0; break;
case 3: KEY_OUT3 = 1; KEY_OUT4 = 0; break;
default: break;
}
}
keyborad.h
#ifndef _KEY_BOARD_H_
#define _KEY_BOARD_H_
#include typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned long ulong; sbit KEY_OUT1 = P2^7; sbit KEY_OUT2 = P2^6; sbit KEY_OUT3 = P2^5; sbit KEY_OUT4 = P2^4; sbit KEY_IN1 = P2^3; sbit KEY_IN2 = P2^2; sbit KEY_IN3 = P2^1; sbit KEY_IN4 = P2^0; sbit KEY1 = P3^0; sbit LED1 = P3^1; extern uchar ledChar[16]; void KeyDriver(); void KeyScan(); #endif 主函数代码: #include #include "KEYBOARD.H" uchar T0RH = 0; // T0 重载值的高字节 uchar T0RL = 0; // T0 重载值的低字节 bit a = 0; // 独立按键索引位 bit countdownRunning = 1; // 倒计时运行标志 uchar integerPart = 10; //计数 void ConFigTimer0(uchar ms); void KeyControl(); void CountdownDisplay(); void main() { P0 = ~ledChar[0]; // 初始化数码管 ConFigTimer0(2); // 定时2ms EA = 1; // 开启总中断 while(1) { if ((LED1 != 1) && (countdownRunning == 0)) KeyDriver(); KeyControl(); CountdownDisplay(); } } /* 倒计时复位函数 */ void CountdownReset() { countdownRunning = 1; //重启 integerPart = 10;//初始计数值 LED1 = 0; } /* 倒计数函数 */ void CountdownCount() { if (countdownRunning) { if (integerPart != 0) integerPart --; else countdownRunning = 0; } } /* 倒计时显示函数 */ void CountdownDisplay() { if (countdownRunning == 1) { LED1 = 0; P0 = ~ledChar[integerPart]; } } /* 复位函数 */ void KeyControl() { if (KEY1 == 0 && a == 0) { a = 1 ; } if (KEY1 == 1 && a == 1) { CountdownReset(); a = 0; } } /* 配置并启动T0 ,11.0592MHz */ void ConFigTimer0(uchar ms) { ulong tmp ; //临时变量 tmp = 11059200 / 12; //定时器计数频率 tmp = (tmp * ms) / 1000; //计算所需的计数值 tmp = 65536 - tmp; //计算定时器重载值 tmp += 2; //补偿中断响应延时造成的误差 T0RH = (uchar)(tmp >> 8); //定时器重载值拆分为高低字节 T0RL = (uchar)tmp; TMOD &= 0xF0; //清零 T0 的控制位 TMOD |= 0x01; //配置 T0 为模式 1 TH0 = T0RH; //加载 T0 重载值 TL0 = T0RL; ET0 = 1; //使能 T0 中断 TR0 = 1; //启动 T0 } /* T0 中断服务函数,用于按键状态的扫描并消抖,倒计时的时间计算 */ void InterruptTimer0() interrupt 1 { uint t; TH0 = T0RH; TL0 = T0RL; KeyScan(); t ++; if (t > 500) { CountdownCount(); //调用倒计数函数 t = 0; } } 关于这个程序有1点值得提一下:定时器配置函数ConFigTimer0(uchar ms),虽然这样在程序里通过计算得出初值(重载值)增加了些许代码,但它换来的是便利性和编程效率,因为只要你完成这个函数,之后所有需要用定时器定时 x 毫秒的场合,你都可以直接把函数拿过去,用所需要的毫秒数作为实参调用它即可,不需要在用计算器埋头算一通了,是不是很值呢。
上一篇:51单片机入门——(新)简易数字时钟
下一篇:51单片机入门——LCD1602
推荐阅读最新更新时间:2024-11-19 11:18