设计要求
- 同时为16支参赛队提供抢答功能,抢答成功后应能通过数码管显示出参赛队号数,同时点亮发光二极管示意抢答成功。
- 加入独立开关,可启动10秒倒计时功能,通过数码管显示出倒计时时间(倒计时状态下抢答功能不起作用,反之亦然)。
电路原理图
硬件原理
时钟信号(晶振)
单片机晶振部位电路,详情请参考《51单片机入门——单片机最小系统》,在此项目中我们选择 11.0592 MHz的晶振。
矩阵按键与独立按键
在该项目中矩阵按键用于选手的抢答器,独立按键用于主持人复位重置抢答。
代码解析
矩阵按键部分代码: keyboard.c
#include "KEYBOARD.H"
uchar keySta[4][4] = {
{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];
}
}
}
}
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}
};
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 ++)
{
if ((keybuf[keyout][i] & 0x0f) == 0x00)
{
keySta[keyout][i] = 0;
}
else if ((keybuf[keyout][i] & 0x0f) == 0x0f)
{
keySta[keyout][i] = 1;
}
}
keyout ++;
keyout = keyout & 0x03;
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<reg52.h>
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 <reg52.h>
#include "KEYBOARD.H"
uchar T0RH = 0;
uchar T0RL = 0;
bit a = 0;
bit countdownRunning = 1;
uchar integerPart = 10;
void ConFigTimer0(uchar ms);
void KeyControl();
void CountdownDisplay();
void main()
{
P0 = ~ledChar[0];
ConFigTimer0(2);
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;
}
}
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;
TMOD |= 0x01;
TH0 = T0RH;
TL0 = T0RL;
ET0 = 1;
TR0 = 1;
}
void InterruptTimer0() interrupt 1
{
uint t;
TH0 = T0RH;
TL0 = T0RL;
KeyScan();
t ++;
if (t > 500)
{
CountdownCount();
t = 0;
}
}
关于这个程序有1点值得提一下:定时器配置函数ConFigTimer0(uchar ms),虽然这样在程序里通过计算得出初值(重载值)增加了些许代码,但它换来的是便利性和编程效率,因为只要你完成这个函数,之后所有需要用定时器定时 x 毫秒的场合,你都可以直接把函数拿过去,用所需要的毫秒数作为实参调用它即可,不需要在用计算器埋头算一通了,是不是很值呢。
|