单片机的定时器和中断可以说是贯穿了各个模块的始终,这一个概念可以说是最最最重要的了,每一个模块的调用很多都需要在中断里面,包括后面的I2C,Uart等等
如有错误,欢迎大佬指出
原理图
使用:独立键盘
独立键盘模块是很简单的一部分,只需要了解按下开关之后,串口会被拉低到0即可判断。
简单的一个独立键盘代码,供参考
这里面还有一个可以学习的地方就是防止抖动
当人不去触碰按键的时候,按键会有可能因为各种原因产生抖动,从到使得串口读取到的电压会在0/1之间来回跳动,这也就使得程序没有使用的意义,这时候就需要防抖动来达到稳定电压的作用,下面代码中的防抖动的代码是一个比较常用的代码。
/*功能:按下开发板左下角S2按键数码管值+1,最大到9,按下S3按下,值减一,最小减到0*/
#include
#include
#define uint unsigned int
#define uchar unsigned char
sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
//共阴数码管段选表0-9
uchar code tabel[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//毫秒级延时函数定义
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
void main()//main函数自身会循环
{
WE = 1;//打开位选锁存器
P0 = 0XFE; //1111 1110
WE = 0;//锁存位选数据
while(1)
{
if(key_s2 == 0)//判断S2是否被按下
{
delay(20);//按键消抖
if(key_s2 == 0)
{
if(num != 9)//如果值不等于9则+1,功能把值限定为小于9
num++;
while(!key_s2);//松手检测
}
}
if(key_s3 == 0)//判断S3是否被按下
{
delay(20);//按键消抖
if(key_s3 == 0)
{
if(num > 0) //如果大于0则执行减一
num--;
while(!key_s3);//松手检测
}
}
//松手之后刷新显示
DU = 1;//打开段选锁存器
P0 = tabel[num];//
DU = 0;//锁存段选数据
}
}
使用:矩阵键盘
和数码管一样,矩阵键盘如果采用独立键盘的方式,那么需要太多太多的串口,这会导致浪费和成本上升,所以就采用了上面接线的方法来实现8个串口控制16个按键的方法
(在单片机的一个公众号上面看到,5个串口可以控制25个按键…)
对于8个串口,我们只需要先进行列扫描,即先输出00001111,再输出11110000,然后再使用一个 | 运算符,就可以判断出按键的具体位置在哪里了。
模块代码
这里采用的是状态机法来扫描矩阵键盘
/*pbdata.h*/
#ifndef __PBDATA_H__
#define __PBDATA_H__
#define uchar unsigned char
#define uint unsigned int
#include
#include "KeyBoard.h"
/* 矩阵键盘扫描 */
//矩阵键盘的数据口
sbit S1 = P3^0; //四个独立键盘模块
sbit S2 = P3^1;
sbit S3 = P3^2;
sbit S4 = P3^3;
#endif
/*KeyBoard.h*/
#ifndef __KeyBoard_H__
#define __KeyBoard_H__
#define key P3 //矩阵键盘的数据口
#define no_key 0xff //无按键按下
#define key_state0 0 //状态0,此时无按键按下
#define key_state1 1 //状态1,此时处于确定按键是否按下
#define key_state2 2 //状态2,此时判断按键是否释放
uchar Keyscan();
#endif
#include "pbdata.h"
/*矩阵键盘扫描,返回值按键扫描得到的值可以更改*/
/*在定时中断中调用此函数*/
//通过返回值来确定是哪一个按键
uchar Keyscan()
{
uchar key_state; //状态指示
uchar key_value; //键值返回
uchar key_temp;
uchar key1,key2;
key=0xf0;
key1=key;
key1=key&0xf0; //确定哪一行的按键按下
key=0x0f;
key2=key;
key2=key&0x0f; //确定哪一列的按键按下
key_temp=key1|key2; //确定按键位置
switch(key_state) //检测当前状态
{
case key_state0: //之前无按键被按下
if(key_temp!=no_key) //说明有按键按下或者抖动
{
key_state=key_state1; //转换为状态1,然后去判断是否真的按下
}
break;
case key_state1: //状态1,说明之前已经有按键按下或者抖动
if(key_temp==no_key) //全为高电平,说明是抖动
{
key_state=key_state0; //返回到状态1,
}
else //确实有按键被按下
{
switch(key_temp) //当确定按键按下后,列举所有的按键情况
{
case 0xee: key_value=0;break;
case 0xde: key_value=1;break;
case 0xbe: key_value=2;break;
case 0x7e: key_value=3;break;
case 0xed: key_value=4;break;
case 0xdd: key_value=5;break;
case 0xbd: key_value=6;break;
case 0x7d: key_value=7;break;
case 0xeb: key_value=8;break;
case 0xdb: key_value=9;break;
case 0xbb: key_value=10;break;
case 0x7b: key_value=11;break;
case 0xe7: key_value=12;break;
case 0xd7: key_value=13;break;
case 0xb7: key_value=14;break;
case 0x77: key_value=15;break;
}
key_state=key_state2; //跳到状态2,进而判断是否被释放
}
break;
case key_state2: //状态2,判断是否被释放
if(key_temp==no_key) //释放,转回到状态0
{
key_state=key_state0;
}
break;
}
return key_value;
}
首先需要明确什么是中断
而51子系列是由以下几种中断的
每个中断又有不同的优先级,也就是当两个中断同时响应的时候,这个优先级会决定你先去执行哪一个中断
使用中断有三个步骤
#include
#include
#define uchar unsigned char
#define uint unsigned int
sbit WE = P2^7;
sbit DU = P2^6;
sbit key_s2 = P3^0;
sbit flag = P3^3;
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
//外部中断1初始化
void int1Init()
{
EA = 1; //开总中断
EX1 = 1;//开外部中断1
IT1 = 1;//外部中断1下降沿触发
}
void main()//main函数自身会循环
{
int1Init();//外部中断1初始化
while(1)
{
if(key_s2 == 0)//判断S2是否被按下
{
delay(20);//按键消抖
if(key_s2 == 0)
{
flag = 1;
delay(5);
flag = 0;//产生下降沿
while(!key_s2);//松手检测
}
}
}
}
//外部中断1中断服务程序
void int1() interrupt 2
{
P1 = ~P1;
}
使用方式:
首先需要先了解TCON和TMOD两个控制器,TCON前面已经写了
注:定时器最高定时时间为65535*1.085us
而如何才能达到实现计自己的那么多时间了
按照以下代码
/* 配置并启动T0,ms-T0 定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 18; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0 的控制位
TMOD |= 0x01; //配置T0 为模式1 16位计数器
TH0 = T0RH; //加载T0 重载值
TL0 = T0RL;
ET0 = 1; //打开定时器0中断
TR0 = 1; //打开定时器0
}
下面是一个简单编程实例,通过中断来实现显示数字,通过按键来加一或者减一
#include
#include
#define uint unsigned int
#define uchar unsigned char
sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
sbit key_s2 = P3^0;//独立按键S2
sbit key_s3 = P3^1;//独立按键S3
uchar num;//数码管显示的值
uchar mSec, Sec;//毫秒和秒储存变量
//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};
/*====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================*/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x--)
for(y = 114; y > 0 ; y--);
}
/*====================================
函数 :display(uchar i)
参数 :i 显示数值,取值范围0-255
返回值 :无
描述 :三位共阴数码管动态显示
====================================*/
void display(uchar i)
{
static uchar wei;
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = SMGwei[wei];
WE = 0;//锁存位选数据
switch(wei)
{
case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;
case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;
}
wei++;
if(wei == 3)
wei = 0;
}
/* 配置并启动T0,ms-T0 定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 18; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零T0 的控制位
TMOD |= 0x01; //配置T0 为模式1 16位计数器
TH0 = T0RH; //加载T0 重载值
TL0 = T0RL;
ET0 = 1; //打开定时器0中断
TR0 = 1; //打开定时器0
}
void main()//main函数自身会循环
{
ConfigTimer0(5);//定时器0初始化
while(1)
{
if(key_s2 == 0)//判断S2是否被按下
{
delay(20);//按键消抖
if(key_s2 == 0)
{
if(num != 120)
num++;
while(!key_s2);//松手检测
}
}
if(key_s3 == 0)//判断S3是否被按下
{
delay(20);//按键消抖
if(key_s3 == 0)
{
if(num > 0)
num--;
while(!key_s3);//松手检测
}
}
}
}
//定时器0中断函数
void timer0() interrupt 1
{
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
display(num); //数码管显示函数
}