蓝桥杯----数码管、按键、定时器与中断 - 详解
(十)、数码管
1、原理
如图 三十七,首先我们需要选中8个数码管中哪个数码管显示,该操作称为位选,选择com1-com8中某个进行显示。
在选中某一个数码管后,再选择a、b、c、d、e、f、g、dp(小数点)小LED灯进行点亮(段选),这里的小LED灯都是共阳接法。也就是给低电平亮,所以要让数码管熄灭,就是给全部小LED高电平,拼接成二进制数据1111 1111,十六进制就是0xFF(依次为dp、g、f、e…)(高位先行)。显示数据0(图 三十八),就是a、b、c、d、e、f亮,g、dp位熄灭,拼接成 二进制数据就是1100 0000(0xc0)。同理可以推出1、2、3、4、5、6、7、8、9、A、B(b)、C、d、E、F…
在显示前我们需要消影,就是上一次数据的残影。就是输入0xFF数据到数码管段选寄存器中P00-P07,打开段选锁存器即可。
图 三十七 数码管位选原理图
图 三十八 数码管显示数字0
2、代码分析(图 三十九)
首先我们需要定义段选的数组,存储0-9数据、全灭、-,首先进行一次段选消影,也就是传入数据给P0 = 0xff(全灭),打开段选寄存器Y7C,1110 0000就是0xE0。
开始位选,选中第一个数码管(最低位)对应二进制数据就是0000 0001,那么选中第二个就是wei == 1时,0x01<<1按位左移一位变成0000 0010就是0x02…依次类推。打开位选寄存器Y6C (1100 0000)(0xc0),关闭位选寄存器。
开始段选,选择数组中某一位进行显示,P0 = duanselect[duan];显示0就是形参duan == 0,显示1就是形参duan == 1…显示全灭就是形参duan == 10。
判断小数点位是否使能,使能则P0 = P0 & 0x7f,让最高位变成0,其余位置保持不变,再打开段选寄存器,关闭段选寄存器即可。
图 三十九 数码管代码展示
3、主函数扫描代码分析
首先定义一个Seg_Scan变量,在定时器中自加,达到八时自动清零即if(++Seg_Scan == 8) Seg_Scan = 0,再定义一个Seg_Buf[8]数组,分别对应数码管1-8显示内容。
现在我们直接看代码,在中断中操作Seg_Scan自加,判断Seg_Buf[Seg_Scan] > 20,显示带小数点数据就是Seg_Buf[Seg_Scan](原本显示数据) + ‘,’ (加一个,的ASCII值44,使能小数点位),如果需要修改Seg_Buf数组中的值就可以定义一个Seg_Proc扫描函数。
图 四十 数码管扫描代码
(十一)、矩阵按键、独立按键(原理图图 四十一)(对比图图 四十三)
1、原理
当跳线帽连接在12口时,此时就是独立按键,只有S4-S7生效,如何检测哪一个按键按下呢?按下时通过P30~P33来检测低电平(0)。
矩阵按键是通过一列一列检测,即P44 = 0、P42 = 1、P35 = 1、P34 = 0,此时只有第一列生效,当S4按下时P33 = 0,S5按下时P32 = 0。同理第二列生效时即只有P42 = 0,其余为1,通过检测P30~P33状态是否为0来判断是否按下,注意P34引脚可用于频率检测,我们一般不操作P34,操作P34会引起电平变化,导致测量频率的误差。当串口(定时器二)作用时要停止定时器二来确保按键检测的准确性。
(图 四十五)在主程序中运用简单四行代码就可以判断按下、松手、长按了。Key_Val检测键码值、Key_Up检测松手、Key_Down检测按下、Key_Old检测长按,记下这几行代码后,就可以通过判断Key_Up、Key_Down、Key_Old来检测了。
思考:如何检测双按键按下?
同时按下S8、S9的判断在底层中实现 <if((P33 == 0)&&(P32 == 0))temp=89>再判断Key_Val。
图 四十一 按键原理图
2、代码解读—独立按键(图 四十一)、矩阵按键(图 四十三)
图 四十二 代码解读(独立按键)
图 四十三 独立按键与矩阵对比
图 四十四 代码解读(矩阵按键检测)
图 四十五 主程序按键检测
(十二)、定时器配置与中断函数
打开STC-ISP软件,选择定时器计算器,时钟频率选12.000MHZ,选择定时器1/0,我们这选择定时器1,16位自动重载,时钟选12T,定时长度选1ms,生成代码,添加EA = 1打开总中断,ET1 = 1 //打定时器1中断允许位,是定时器0就写,ET0= 1。
书写中断函数void Timer1_Routine() interrupt 3 注意这里名字可以任意,但是后面interrupt +中断号必须写。定时器0中断号为1,定时器二中断号为12,外部串口借用定时器二中断号为4。
图 四十六 定时器配置
图 四十七 定时器1代码展示
图 四十八 中断函数代码
提供参考代码希望对读者有助
1、数码管底层
#include
idata unsigned char duanselect[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf};
//定义数据0-9、全灭、-的段选数据
void Seg_Disp(unsigned char wei,duan,point) //wei位选、duan段选、point(1-小数点使能)
{
unsigned char temp;
//段选消影
P0 = 0xff;
temp = P2 & 0x1f;
temp = temp | 0xe0;
P2 = temp;
temp = P2 & 0x1f;
P2 = temp;
//先位选
P0 = 0x01<
2、按键底层
#include
unsigned char Key_Read(void)
{
unsigned char Key_Num = 0; //一定记得赋初始值0
// AUXR &= ~0x10; //关闭定时器二(有串口时加上)
//扫描第一列
P37 = 0;
P36 = 1;
P35 = 1;
//P34 = 1; //有频率采样时必须注释
if(P33 == 0) Key_Num = 4;
if(P32 == 0) Key_Num = 5;
if(P31 == 0) Key_Num = 6;
if(P30 == 0) Key_Num = 7;
//扫描第二列
P37 = 1;
P36 = 0;
P35 = 1;
//P34 = 1;
if(P33 == 0) Key_Num = 8;
if(P32 == 0) Key_Num = 9;
if(P31 == 0) Key_Num = 10;
if(P30 == 0) Key_Num = 11;
//扫描第三列
P37 = 1;
P36 = 1;
P35 = 0;
//P34 = 1;
if(P33 == 0) Key_Num = 12;
if(P32 == 0) Key_Num = 13;
if(P31 == 0) Key_Num = 14;
if(P30 == 0) Key_Num = 15;
// P37 = 1;
// P36 = 1;
// P35 = 1;
// P34 = 0;
//
// if(P33 == 0) Key_Num = 16;
// if(P32 == 0) Key_Num = 17;
// if(P31 == 0) Key_Num = 18;
// if(P30 == 0) Key_Num = 19;
// AUXR |= 0x10; //打开定时器二(有串口时加上)
P3 = 0xff;
return Key_Num;
}
3、定时器扫描按键主程序
#include
#include "Key.h"
idata unsigned char Key_Slow_Down; //按键延时变量
idata unsigned char Key_Val,Key_Down,Key_Up,Key_Old; //按键参数
//定时器一初始化,自己加上EA = 1;ET1 = 1;
void Timer1_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
EA = 1; //打开总中断
ET1 = 1; //打开定时器一中断允许位
}
void System_Init() //系统初始化
{
unsigned char temp;
//关闭所有LED
P0 = 0xff;
temp = P2 & 0x1f;
temp = temp | 0x80;
P2 = temp;
temp = P2 & 0x1f;
P2 = temp;
//关闭外设
P0 = 0x00;
temp = P2 & 0x1f;
temp = temp | 0xa0;
P2 = temp;
temp = P2 & 0x1f;
P2 = temp;
}
void Key_Proc() //按键处理函数
{
if(Key_Slow_Down <20) return; //按键减速
Key_Slow_Down = 0;
Key_Val = Key_Read(); //读取键码值
Key_Down = Key_Val & (Key_Old ^ Key_Val); //判断按下
Key_Up = ~Key_Val & (Key_Old ^ Key_Val); //判断松开
Key_Old = Key_Val; //判断长按
switch(Key_Down)
{
}
}
void main()
{
System_Init();
Timer1_Init();
while(1)
{
Key_Proc();
}
}
void Timer1_Routine() interrupt 3
{
Key_Slow_Down++;
}
4、定时器扫描数码管显示数据
#include
#include "Seg.h"
idata unsigned char Seg_Slow_Down; //数码管延时变量
idata unsigned char Seg_Buf[8]={0,1,2,3,4,5,6,7}; //数码管显示内容
idata unsigned char Seg_Scan; //数码管扫描
//定时器一初始化,自己加上EA = 1;ET1 = 1;
void Timer1_Init(void) //1毫秒@12.000MHz
{
AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
EA = 1; //打开总中断
ET1 = 1; //打开定时器一中断允许位
}
void Seg_Proc() //数码管处理函数
{
if(Seg_Slow_Down 20) //判断带小数点的数据
Seg_Disp(Seg_Scan,Seg_Buf[Seg_Scan]-',',1);
else
Seg_Disp(Seg_Scan,Seg_Buf[Seg_Scan],0);
}