目录
在蓝桥杯单片机比赛中,LED灯、数码管、按键这三个模块的重要性我就不明说了,总而言之,这三个模块都成该比赛的家常便饭了。下面就让小编带大家看看这三个模块吧!
一、LED模块
国信长天单片机一共有8个LED灯,是一组连接着一块74HC573芯片的共阳LED灯。(原理图如下)
这也就是说,每个LED灯是低电平有效(也就是0有效),因此,我们向我们需要点亮的LED灯引脚输入一个低电平就可点亮。又因为74HC573芯片的输出是等于输入的,因此我们只需要将P0的所有引脚全部至1即可关闭所有的LED灯;
二、数码管模块
在讲述国信长天单片机8个数码管之前,我们先来看看八段数码管的构造:
八段数码管有两种不同的形式:一种是八个发光二极管的阳极都连在一起的,称之为共阳极八段数码管;另一种是八个发光二极管的阴极都连在一起的,称之为共阴极八段数码管。国信长天单片机使用的就是一个共阳的八段数码管。这也就是说,我们想要那一段点亮直接至0就好,那么我们就可以有:
字符 | 码段值 |
0 | 0xc0 |
1 | 0xf9 |
2 | 0xa4 |
3 | 0xb0 |
4 | 0x99 |
5 | 0x92 |
6 | 0x82 |
7 | 0xf8 |
8 | 0x80 |
9 | 0x90 |
- | 0xBF |
熄灭 | 0xFF |
那么,现在我们回到我们的单片机数码管来,这显然是一个数码管组合,而且他们的段选引脚(a,b,c,……g,dp)全部是接在一起的,但位选引脚(com1-com8)是分开的。由此,只要让选择不同的数码管位选,再给予数码管的段选就能够使得数码管显示不同或相同的数据。
既然如此,那又是如何做到数码管的动态刷新的呢?
数码管的动态刷新,其本质就是以较快的速度对数码管的每一位数据进行刷新,因为在这个速度下,我们肉眼是看不到数码管一次次的刷新,因此我们我们就能够看到他们是同时稳定刷新的。
接下来就让小编带大家看看小编自己数码管刷新思路吧:
小编使用的是定时器定时刷新。定时器定时刷新的好处是:在写程序时,不用再考虑延时、刷新顺序等因素,也不用担心数码管刷新时会不会因频率过大或过小导致数码管刷新时出现闪烁或有重影;
具体刷新的思路是,每次刷新时,首先使整个数码管都熄灭(消影),再分别进行段选与位选,最后再移动数码管的位选到下一个位置。
三、按键模块
国信长天单片机的按键分为两种,即矩阵按键(S4-S19)与独立按键(S4-S7),具体分布可见下图,这两者区别一是矩阵按键有16个,而独立按键只有4个;区别二是矩阵按键使用行列扫描法查找按键是否按下,而独立按键可以直接使用switch判断。
当按键被按下的时候,电路导通接地,I/0口为低电平;当按键未被下时,电路断开,I/0口保持高电平的。但一般的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,假如不加以处理,会导致按键被识别为按下多次。如下面这个按键按下的波形图:
为了不产生这种现象而作的措施就是按键消抖,下面就有一些常用的按键消抖措施:
方法A:使用延时。 即,检测出键闭合后执行一个延时程序,让前沿抖动消失后再一次检测按键的状态,如果仍保持闭合状态电平,则确认为真正有键按下。
方法B:检测多次。即,可以设定一个检测周期,如果在一个检测周期内,按键被检测为被按下达到了一定次数,则确认为真正被按下。
但是小编这里使用的就是状态机消抖,即定义三个状态,分别是按键按下状态、按键有效按下状态,按键弹起状态,相关转态装换如下图:
四、相关代码
config.h文件
#include "intrins.h"
#include "STC15F2K60S2.H"
#ifndef _CONFIG_H
#define _CONFIG_H
#define kbd_io P3
#define state_0 0 //是否按下
#define state_1 1 //是否是抖动
#define state_2 2 //判断是否弹起
#define kbd_maskrow 0x0f //屏蔽不需要的IO
#define u8 unsigned char
#define u16 unsigned int
sbit relay=P0^4;//
sbit buzzer=P0^6;//蜂鸣器的引脚
//以下三个变量虽然是全局变量,但是实际上定义的地方不在此处,编译时需要查找定义的地方
extern unsigned int key_num;//按键的数值
extern unsigned int segbuff[];//可以控制数码管显示的位置
extern unsigned int segtab[];//数码管显示的数字
extern unsigned int ledtab[];//led的位置
void Delay1us0();
void delay_us0(unsigned int us);
void scanbtn();//独立按键函数
void scankbd();//矩阵按键函数
void segs(void);//数码管新时函数
void SysInit(unsigned char x);//初始化,关闭LED,数码管段选位选,蜂鸣器,还可以控制蜂鸣器是否工作
void LED_Select(unsigned char n);//LED选择函数
#endif
config.c文件
//包括矩阵按键函数、独立按键函数、数码管显示函数、LED显示函数、初始化函数
#include "config.h"
//以下定义全部都是全局变量
unsigned int key_num=16;//有按键的位置得到的数值
//数码管显示位置 管1 管2 管3 管4 管5 管6 管7 管8
unsigned int segbuff[]={10, 10, 10, 10, 10, 10, 10, 10};//可以相当于数码管的位选
// 显示数值 0 1 2 3 4 5 6 7 8 9 - 熄灭
unsigned int segtab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xBF,0xFF,//共阳数码管段选带数值显示
//显示 0. 1. 2. 3. 4. 5. 6. 7. 8. 9. - 熄灭
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,0xBF,0xFF};//带小数点显示
//led显示位置 全灭 L1 L2 L3 L4 L5 L6 L7 L8 全亮
unsigned int ledtab[] = {0xff,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0x00};
//初始化关闭LED、蜂鸣器、数码管
void SysInit(unsigned char x)
{
//关闭LED
P2=P2&0x1f|0x80;P0=0xff;P2&=0x1f;//先将P2口高三位强制置0,再或上值,强制选择Y4C输出,后关闭所有的LED,再去除P2高三位的值
//关闭蜂鸣器
P2=P2&0x1f|0xa0;relay=0;buzzer=x;P2&=0x1f;//先将P2口高三位强制置0,再或上值,强制选择Y5C输出,
//后使P0^4有效,关闭蜂鸣器,再去除P2高三位的值
//关闭数码管的段选
P2=P2&0x1f|0xe0;P0=0xff;P2&=0x1f; //先将P2口高三位强制置0,再或上值,强制选择Y7C输出,
//后使P0口都有效,关闭共阳数码管段选,再去除P2高三位的值
//关闭数码管的位选
P2=P2&0x1f|0xc0;P0=0x00;P2&=0x1f;//先将P2口高三位强制置0,再或上值,强制选择Y6C输出,
//后使P0口都无效,关闭共阳数码管位选,再去除P2高三位的值
}
//延时
void Delay1us0() //@12.000MHz
{
_nop_();
_nop_();
_nop_();
_nop_();
}
//延时
void delay_us0(unsigned int us)
{
while(us--)
{
Delay1us0();
}
}
//LED选择闪烁
void LED_Select(unsigned char n)
{
P2 = ( P2& 0x1f ) | 0x80;//先将P2的高三位强制置0,后或上一个数强制选择Y4C口
P0 = n;//输入想要点亮的LED
P2 = P2 & 0x8e;
delay_us0(50000);//延时5毫秒
P2 = ( P2& 0x1f ) | 0x80;//先将P2的高三位强制置0,后或上一个数强制选择Y4C口
P0 = 0xff;//关闭所有LED灯
P2 = P2 & 0x8e;
delay_us0(50000);//延时
}
//矩阵按键
void scankbd()
{
static char kbd_state = 0;//定义的三个状态
unsigned char kbd_press;
char row;//判断按键的行
switch(kbd_state)
{
case state_0: //是否按下
kbd_io=0x0f; P42=0; P44=0;
kbd_press = kbd_io;
if(kbd_press != kbd_maskrow)
kbd_state = state_1;//说明key_press不等于0x0f,跳转到1状态
break;
case state_1: //是否是抖动 kbd_io-P3
kbd_press = kbd_io;
if(kbd_press != kbd_maskrow)
{//按键不是需要屏蔽的状态
//判断按键属于哪一行
if((kbd_io&0x08)==0) row=0;
if((kbd_io&0x04)==0) row=1;
if((kbd_io&0x02)==0) row=2;
if((kbd_io&0x01)==0) row=3;
kbd_io=0xf0; P42=1;P44=1; //将四列强制无效,再判断按键的列数
//判断按键属于哪一列
if(P44==0) key_num=row;
if(P42==0) key_num=row+4;
if((kbd_io&0x20)==0) key_num=row+8;
if((kbd_io&0x10)==0) key_num=row+12;
kbd_state = state_2;//跳转到2状态
}else{//按键被屏蔽,跳转0状态
kbd_state = state_0;
}
break;
case state_2: //判断是否弹起
kbd_io=0x0f; P42=0; P44=0;//将按键强制有效
kbd_press =kbd_io;//再获取端口的数值
if(kbd_press == 0x0f) kbd_state = state_0;//是需要被屏蔽的状态,需要跳转到0状态
break;
default: //屏蔽不需要的IO
break;
}
}
//数码管显示
void segs(void)
{
static unsigned char segaddr=0;
//首先全灭是为了消影
P2=(0x1f&P2)|0xe0;//先将P2口高三位置零,再强制选择Y7C,段选控制
P0=0xff;//将所有段选强制无效
P2=0x1f&P2;//消除P2口高三位的影响
P2=(0x1f&P2)|0xc0;//先将P2口高三位置零,再强制选择Y6C,位选控制
P0=1<<segaddr; //改变数码管现实的位置
P2=0x1f&P2;//消除P2口高三位的影响
P2=(0x1f&P2)|0xe0;//先将P2口高三位置零,再强制选择Y7C,段选控制
P0=segtab[segbuff[segaddr]];//显示需要显示的数值
P2=0x1f&P2;//消除数码管高三位的影响
if(++segaddr==8)
segaddr=0;//让数码管每次只显示在八个数码管上
}
//独立按键
void scanbtn(){
static char kbd_state = 0;//定义的三个状态
unsigned char kbd_press;//判断按键的行
switch(kbd_state)
{
case state_0: //是否按下
kbd_io=0x0f;
kbd_press =kbd_io;
if(kbd_press != kbd_maskrow)
kbd_state = state_1;//说明key_press不等于0x0f,跳转到1状态
break;
case state_1: //是否是抖动 kbd_io-P3
kbd_press =kbd_io;
if(kbd_press != kbd_maskrow)
{//按键不是需要屏蔽的状态
//判断按键属于哪一行
if((kbd_io&0x08)==0) key_num=0;
if((kbd_io&0x04)==0) key_num=1;
if((kbd_io&0x02)==0) key_num=2;
if((kbd_io&0x01)==0) key_num=3;
kbd_state = state_2;//跳转到2状态
}
else//按键被屏蔽,跳转0状态
kbd_state = state_0;
break;
case state_2: //判断是否弹起
kbd_io=0x0f;//将按键强制有效
kbd_press =kbd_io;//再获取端口的数值
if(kbd_press == 0x0f) kbd_state = state_0;//是需要被屏蔽的状态,需要跳转到0状态
break;
default: //屏蔽不需要的IO
break;
}
}
main.c文件
#include "config.h"
void Timer0Init(void);
void main()
{
SysInit(0);//关闭LED灯,数码管,蜂鸣器
Timer0Init();//开启定时器
while(1)
{
segbuff[0]=key_num/10;
segbuff[1]=key_num%10;
if(key_num%8 == 0) LED_Select(ledtab[1]);
if(key_num%8 == 1) LED_Select(ledtab[2]);
if(key_num%8 == 2) LED_Select(ledtab[3]);
if(key_num%8 == 3) LED_Select(ledtab[4]);
if(key_num%8 == 4) LED_Select(ledtab[5]);
if(key_num%8 == 5) LED_Select(ledtab[6]);
if(key_num%8 == 6) LED_Select(ledtab[7]);
if(key_num%8 == 7) LED_Select(ledtab[8]);
}
}
//中断1
void time0() interrupt 1
{
scankbd();
segs();
}
//定时器0
void Timer0Init(void) //1毫秒@12.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x18; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
EA = 1;
ET0 = 1;
}