IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 51单片机入门——16路抢答器 -> 正文阅读

[嵌入式]51单片机入门——16路抢答器

设计要求

  1. 同时为16支参赛队提供抢答功能,抢答成功后应能通过数码管显示出参赛队号数,同时点亮发光二极管示意抢答成功。
  2. 加入独立开关,可启动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<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; // 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 毫秒的场合,你都可以直接把函数拿过去,用所需要的毫秒数作为实参调用它即可,不需要在用计算器埋头算一通了,是不是很值呢。

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-14 10:06:25  更:2022-05-14 10:07:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 1:01:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码