单片机数字钟有必要用定时器中断来扫描键盘吗
时间: 2023-06-15 11:06:07 浏览: 52
使用定时器中断来扫描键盘可以提高数字钟的实时性和稳定性。如果不使用定时器中断,需要在主循环中不断地轮询键盘,会占用CPU的大量时间,影响其他任务的执行。而使用定时器中断可以让CPU在执行其他任务的同时,每隔一段时间自动扫描键盘,提高了系统的实时性和稳定性。此外,使用定时器中断还可以避免因为按键响应不及时而导致的误操作。
相关问题
51单片机定时器T0产生时钟,定时器T1中断扫描键盘编程
以下是基于51单片机的定时器T0产生时钟、定时器T1中断扫描键盘的简单程序:
```
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
uchar code keymap[4][4]={//键盘按键映射表
{'1','2','3','+'},
{'4','5','6','-'},
{'7','8','9','*'},
{'C','0','=','/'}
};
uchar code keyplace[4]={0x7f,0xbf,0xdf,0xef};//键盘行扫描码
bit row_flag=0;//行扫描标志位
uchar row,column;//当前行、列
uchar key;//当前按键
uchar timer_count=0;//定时器计数
void T0_init(){//定时器T0初始化
TMOD|=0x01;//工作在模式1(16位自动重装载)
TH0=0xfc;//定时器初值,1ms
TL0=0x66;
ET0=1;//使能T0中断
TR0=1;//启动T0
}
void T1_init(){//定时器T1初始化
TMOD|=0x10;//工作在模式2(8位自动重装载)
TH1=0xfd;//定时器初值,5ms
TL1=0xfd;
ET1=1;//使能T1中断
TR1=1;//启动T1
}
void key_scan(){//键盘扫描函数
uchar i,j;
for(i=0;i<4;i++){//按行扫描
P0=keyplace[i];//将当前行扫描码输出到P0口
if(P0!=keyplace[i]){//如果有列有按键按下
for(j=0;j<4;j++){//按列扫描
if(P0!=keyplace[i]){//如果当前列有按键按下
while(P0!=keyplace[i]);//等待按键松开
key=keymap[i][j];//记录当前按键
return;
}
P0=P0<<1;//切换到下一列
}
}
}
}
void main(){
T0_init();//初始化定时器T0
T1_init();//初始化定时器T1
EA=1;//开启总中断
while(1){
if(row_flag){//如果需要扫描键盘
key_scan();//扫描键盘
row_flag=0;//清除扫描标志位
}
}
}
void T0_isr() interrupt 1{//定时器T0中断服务函数
TH0=0xfc;
TL0=0x66;//重新加载计时初值
timer_count++;//计数器加1
if(timer_count>=20){//1ms*20=20ms
timer_count=0;//计数器清零
row_flag=1;//设置扫描标志位
}
}
void T1_isr() interrupt 3{//定时器T1中断服务函数
TH1=0xfd;
TL1=0xfd;//重新加载计时初值
column++;//列计数器加1
if(column>=4){//扫描完4列
column=0;//列计数器清零
row++;//行计数器加1
if(row>=4){//扫描完4行
row=0;//行计数器清零
}
}
P0=keyplace[row];//输出当前行扫描码
P2=0x0f;//设置P2口低4位为1,用于读取列扫描码
if((P2&0x0f)!=0x0f){//如果有列有按键按下
key_scan();//扫描键盘
}
}
```
该程序中,定时器T0产生1ms的时钟信号,计数器每计满20次(20ms)就设置扫描键盘的标志位,定时器T1中断每5ms扫描键盘的一行和一列,如果有按键按下就记录当前按键。需要注意的是,程序中使用了定时器T0、T1中断服务函数,需要在程序开头使用`#include<reg51.h>`来包含51单片机的头文件。
程序实现定时器中断方式扫描键盘的实例代码
下面是一个使用定时器中断方式扫描键盘的 C 语言示例代码:
```
#include <reg51.h> // 引入 8051 单片机的头文件
#define KEY_PORT P1 // 定义键盘端口为 P1
#define TIMER_VALUE 65536 - 50000 // 定义定时器初值
unsigned char scan_key(); // 声明扫描键盘的函数
void timer0() interrupt 1 // 定义定时器中断服务函数
{
TH0 = TIMER_VALUE / 256; // 设置定时器初值
TL0 = TIMER_VALUE % 256;
scan_key(); // 每次中断扫描一次键盘
}
void main()
{
TMOD = 0x01; // 设置定时器 0 为模式 1
TH0 = TIMER_VALUE / 256; // 初始化定时器初值
TL0 = TIMER_VALUE % 256;
IE = 0x82; // 打开定时器 0 中断和总中断
TR0 = 1; // 启动定时器 0
while(1); // 无限循环
}
unsigned char scan_key() // 扫描键盘的函数
{
unsigned char key = 0xFF; // 定义键值为 0xFF(表示无键按下)
unsigned char i, j; // 定义计数器变量
for (i = 0; i < 4; i++) // 遍历行
{
KEY_PORT = ~(1 << i); // 先将当前行的输出置为低电平
for (j = 0; j < 3; j++) // 遍历列
{
if (!(KEY_PORT & (1 << (j + 4)))) // 判断当前列是否被按下
{
switch (i * 3 + j) // 根据行列对应的键值进行转换
{
case 0: key = '1'; break;
case 1: key = '2'; break;
case 2: key = '3'; break;
case 3: key = '4'; break;
case 4: key = '5'; break;
case 5: key = '6'; break;
case 6: key = '7'; break;
case 7: key = '8'; break;
case 8: key = '9'; break;
case 9: key = '*'; break;
case 10: key = '0'; break;
case 11: key = '#'; break;
}
while (!(KEY_PORT & (1 << (j + 4)))); // 等待键释放
}
}
}
return key; // 返回键值
}
```
以上示例代码使用定时器 0 中断每隔一段时间(本例中为 50 毫秒)扫描一次键盘,如果有键按下,则将其对应的键值返回。在主函数中,我们只需要启动定时器、打开中断即可,无需不断轮询扫描键盘,从而实现了高效率的键盘输入。