C51 5.中断系统
前言
补上定时器篇的中断系统。
C51内部的中断结构较为复杂,看得比较晕的话结合上篇定时器内的简易中断图对比来看,他们的大体结构是对应的。
祝愉快
1.中断
如果没有中断,我们的很多操作就只能在main里面完成。
想想看如果我们按下按键后,因为Key函数内因为按键消抖需求写下的Delay函数会带来什么样的后果。
Delay把整个main停下,那么如果我们这时候还要运行一个数码管每过一秒加1的函数,这个数码管函数就会被按键消抖的Delay给停下。
所以我们需要把各个函数给区分开来,放到不同的执行周期里面。(代码会展示)
2.中断结构
此为C51内部中断结构
中断源
我们从左往右看,左边为输入中断系统的中断源。
每一个中断源对应一个中断号。
IE
- 中断的开关
- EA为公共中断开关
- ET为定时器/计数器中断开关
- ES为UART中断开关
- EX为外部中断开关
XICON
xicon中有中断允许和中断优先级的额外内容。
因为C51相较简单的中断系统额外增加了多个中断源和中断优先级,所以需要额外的寄存器来配置增加的功能。
IP
为了达到4优先级,它使用了三个寄存器来保存配置信息。
我们不看优先级选择寄存器的标识哪个中断源的标识符,(PX3H为INT3的一个标识符,PX3为宁外一个,这里我们把它看成PH和P)
由PH和P组成的二位序列:
- 0 0 最低优先级
- 0 1 较低优先级
- 1 0 较高优先级
- 1 1 最高优先级
这样就做到了4优先级,如果想设计成8优先级那就用3位二进制序列就好了,当然这要分配更多的寄存器资源,以及我们一般也不需要这么细化的优先级秩序。
基本规则
3.C代码
在函数的末尾加上interrupt 中断号,一个中断函数就设置好了。
//定时器中断函数模板
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
{
T0Count=0;
}
}
定时器扫描按键和数码管
- 我们需要对key函数和Nixie函数进行改造。
- 改造的基本原理是
- 定时器扫描按一个周期取一个时间点调用函数
- 那么Key函数里面的消抖Delay就不需要了,因为我们是离散的采触发点,没准就跳过了抖动的时间端
- 其实就算取到了抖动的时间点也没有问题,抖动为1那就是判断按下了,也符合我们的要求,抖动为0那取下一个点就好了
- 改造后的Loop函数记得要符合循环的要求
void Timer0_Routine() interrupt 1
{
static unsigned int T0Count1,T0Count2,T0Count3;
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count1++;
if(T0Count1>=20)
{
T0Count1=0;
Key_Loop(); //20ms调用一次按键驱动函数
}
T0Count2++;
if(T0Count2>=2)
{
T0Count2=0;
Nixie_Loop();//2ms调用一次数码管驱动函数
}
T0Count3++;
if(T0Count3>=10)
{
T0Count3=0;
Sec_Loop(); //10ms调用一次数秒表驱动函数
}
}
Key_Loop函数
#include <REGX52.H>
#include "Delay.h"
unsigned char Key_KeyNumber;
/**
* @brief 获取按键键码
* @param 无
* @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
*/
unsigned char Key(void)
{
unsigned char Temp=0;
Temp=Key_KeyNumber; //Key_KeyNumber的数值快速变化,为了保存稳定,先存在中间变量里,传也是传中间变量
Key_KeyNumber=0; //注意标志位要写清零,这端代码的清零写在这里,为了保障Temp能接收到数值
return Temp;
}
/**
* @brief 获取当前按键的状态,无消抖及松手检测
* @param 无
* @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
*/
unsigned char Key_GetState()
{
unsigned char KeyNumber=0;
if(P3_1==0){KeyNumber=1;}
if(P3_0==0){KeyNumber=2;}
if(P3_2==0){KeyNumber=3;}
if(P3_3==0){KeyNumber=4;}
return KeyNumber;
}
/**
* @brief 按键驱动函数,在中断中调用
* @param 无
* @retval 无
*/
void Key_Loop(void)
{
static unsigned char NowState,LastState;
LastState=NowState; //按键状态更新
NowState=Key_GetState(); //获取当前按键状态
//如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
if(LastState==1 && NowState==0) //我们调换LastState和NowState的判定数值,可以达到按下就响应
{
Key_KeyNumber=1;
}
if(LastState==2 && NowState==0)
{
Key_KeyNumber=2;
}
if(LastState==3 && NowState==0)
{
Key_KeyNumber=3;
}
if(LastState==4 && NowState==0)
{
Key_KeyNumber=4;
}
}
总结
中断的资源和单片机的型号是有关的,它到底有几个中断源,几个优先级,几个寄存器,具体查看它的手册,本篇只是带着看一下C51的中断资源。