数码管根据连接方式分为共阴极和共阳极数码管,数码管的统一逻辑就是先位选再段选
1、静态数码管
/*头文件区域*/
#include <REGX52.H>
#include <intrins.h>
/*延时函数*/
void Delay(unsigned int xms) //@12.000MHz
{
while(xms--)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/*变量声明区域*/
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;
unsigned char Wela[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
unsigned char Dula[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
/*按键检测函数*/
unsigned char Key_Read()
{
unsigned char Temp = 0;
if(P3_4 == 0) Temp = 1 ;
if(P3_5 == 0) Temp = 2 ;
if(P3_6 == 0) Temp = 3 ;
if(P3_7 == 0) Temp = 4 ;
return Temp;
}
/*数码管显示函数*/
void Seg_Disp(unsigned char wela,dula)
{
P0 = 0;//消影-段码位码清零
P2_6 = 1;
P2_6 = 0;
P0 = Wela[wela];//位码赋值
P2_7 = 1;
P2_7 = 0;
P0 = Dula[dula];//段码赋值
P2_6 = 1;
P2_6 = 0;
}
/*Main*/
void main()
{
while(1)
{
Key_Val = Key_Read();//获取键码值
Key_Down = Key_Val & (Key_Val ^ Key_Old);//检测下降沿
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);//检测上升沿
Key_Old = Key_Val;//扫描获取的键码值
Seg_Disp(0,1);
}
}
上图是静态数码管的代码
上图是现象,不同的单片机代码可能稍微不同,但是总的逻辑来讲静态数码管显就是
(一)、消影
(二)、位选赋值
(三)、段选赋值
代码是记不完的,逻辑却是相通的,要举一反三
2、动态数码管
本次不采用江科大Delay延时函数进行处理,采用定时器跳转中断进行扫描
/*头文件区域*/
#include <REGX52.H>
#include <intrins.h>
/*延时函数*/
void Delay(unsigned int xms) //@12.000MHz
{
while(xms--)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/*变量声明区域*/
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;
unsigned char Seg_Wela[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};//数码管位码储存数据
unsigned char Seg_Dula[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};//数码管断码储存数据
unsigned char Seg_Pos;//中间变量,用于嵌套循环
unsigned char Seg_Buf[] = {1,2,3,4,5,6};//选择要显示的数字
/*按键检测函数*/
unsigned char Key_Read()
{
unsigned char Temp = 0;
if(P3_4 == 0) Temp = 1 ;
if(P3_5 == 0) Temp = 2 ;
if(P3_6 == 0) Temp = 3 ;
if(P3_7 == 0) Temp = 4 ;
return Temp;
}
/*数码管显示函数*/
void Seg_Disp(unsigned char wela,dula)
{
P0 = 0;//消影-段码位码清零
P2_6 = 1;
P2_6 = 0;
P0 = Seg_Wela[wela];//位码赋值
P2_7 = 1;
P2_7 = 0;
P0 = Seg_Dula[dula];//段码赋值
P2_6 = 1;
P2_6 = 0;
}
/*定时器初始化函数*/
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
/*定时器0中断服务函数*/
void Timer0_Serviver() interrupt 1
{
TL0 = 0x18;
TH0 = 0xFC; //给定时器重赋值保证能持续的进入中断函数
if(++Seg_Pos == 6) Seg_Pos = 0;//中间变量在0~5之间反复循环,i++是判断后再自增,++i是先自增再判断
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]);//嵌套,每次循环对应一个值
}
/*Main*/
void main()
{
Timer0Init();
while(1)
{
Key_Val = Key_Read();//获取键码值
Key_Down = Key_Val & (Key_Val ^ Key_Old);//检测下降沿
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);//检测上升沿
Key_Old = Key_Val;//扫描获取的键码值
}
}
代码如图,定时器寄存器的配置详见江科大
现象结果如上
3、动态数码管拓展
上电数码管显示三位数500,按键1按下一次加100,按键2按下一次加200;
/*头文件区域*/
#include <REGX52.H>
#include <intrins.h>
/*延时函数*/
void Delay(unsigned int xms) //@12.000MHz
{
while(xms--)
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/*变量声明区域*/
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;
unsigned char Seg_Wela[] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};//数码管位码储存数据
unsigned char Seg_Dula[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};//数码管断码储存数据
unsigned char Seg_Pos;//中间变量,用于嵌套循环
unsigned char Seg_Buf[] = {10,10,10,10,10,10};//选择不显示数字,不直接在这里更改是为了后续的模块化
unsigned int Time = 500;
bit System_Flag;
/*按键检测函数*/
unsigned char Key_Read()
{
unsigned char Temp = 0;
if(P3_4 == 0) Temp = 1 ;
if(P3_5 == 0) Temp = 2 ;
if(P3_6 == 0) Temp = 3 ;
if(P3_7 == 0) Temp = 4 ;
return Temp;
}
/*数码管显示函数*/
void Seg_Disp(unsigned char wela,dula)
{
P0 = 0;//消影-段码位码清零
P2_6 = 1;
P2_6 = 0;
P0 = Seg_Wela[wela];//位码赋值
P2_7 = 1;
P2_7 = 0;
P0 = Seg_Dula[dula];//段码赋值
P2_6 = 1;
P2_6 = 0;
}
/*定时器初始化函数*/
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
/*定时器0中断服务函数*/
void Timer0_Serviver() interrupt 1
{
TL0 = 0x18;
TH0 = 0xFC; //给定时器重赋值保证能持续的进入中断函数
if(++Seg_Pos == 6) Seg_Pos = 0;//中间变量在0~5之间反复循环,i++是判断后再自增,++i是先自增再判断
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]);//嵌套,每次循环对应一个值
}
/*Main*/
void main()
{
Timer0Init();
while(1)
{
Key_Val = Key_Read();//获取键码值
Key_Down = Key_Val & (Key_Val ^ Key_Old);//检测下降沿
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);//检测上升沿
Key_Old = Key_Val;//扫描获取的键码值
switch(Key_Down)
{
case 1:
Time +=100;//自增100
break;
case 2:
Time -=100;//自减100
break;
}
Seg_Buf[0] = Time/100%10;//百位
Seg_Buf[1] = Time/10%10;//十位
Seg_Buf[2] = Time/1%10;//个位
}
}
根据上一篇博客,代码如上,计算3位数的数字模板 ,无法理解就记住即可
4、函数封装
目前需要封装按键、数码管
1、按键函数
接下来就是写按键的函数了
本次封装的是矩阵键盘,按键对应的IO口默认高电平
参考这是普中的51单片机原理图,P1口默认高电平,矩阵键盘分为4行4列,可以采用逐行扫描或者逐列扫描,和数码高管的扫描本质一样,单片机无法同时处理,只能在极短的时间反复扫描。
代码如图,这是之前的按键函数
#include "Key.h"
unsigned char Key_Read()
{
unsigned char Temp = 0;//临时变量返回键码值
P3_0 = 0;P3_1 = 1;P3_2 = 1;P3_3 = 1;//第一行
if(P3_4 == 0) Temp = 1;
if(P3_5 == 0) Temp = 2;
if(P3_6 == 0) Temp = 3;
if(P3_7 == 0) Temp = 4;
P3_0 = 1;P3_1 = 0;P3_2 = 1;P3_3 = 1;//第二行
if(P3_4 == 0) Temp = 5;
if(P3_5 == 0) Temp = 6;
if(P3_6 == 0) Temp = 7;
if(P3_7 == 0) Temp = 8;
P3_0 = 1;P3_1 = 1;P3_2 = 0;P3_3 = 1;//第三行
if(P3_4 == 0) Temp = 9;
if(P3_5 == 0) Temp = 10;
if(P3_6 == 0) Temp = 11;
if(P3_7 == 0) Temp = 12;
P3_0 = 1;P3_1 = 1;P3_2 = 1;P3_3 = 0;//第四行
if(P3_4 == 0) Temp = 13;
if(P3_5 == 0) Temp = 14;
if(P3_6 == 0) Temp = 15;
if(P3_7 == 0) Temp = 16;
return Temp;
}
同理,创建key.h
在.h文件中声明
2、数码管函数
数码管函数在上一篇博客中已提,再以相同办法进行封装
封装完成后,还需要把文件加入keil的Drvier中
右键Drvier选择添加已存在的文件
返回上一级找到Driver
并选择显示所有文件,并添加按键和数码管,可以只选.c文件,我选择都添加
这样就添加成功了
5、模板
(一)头文件声明
(二)变量声明
(三)按键处理函数
(四)信息处理函数
(五)其他信息处理函数
(六)定时器初始化
(七)定时器终端服务函数
(八)Main函数
按照这个顺序写,遇到哪个就补充,不死记
/*头文件区域*/
#include <REGX52.H>
#include "key.h"
#include "seg.h"
/*变量声明区域*/
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;//按键检测变量
unsigned char Key_Slow_Down;//按键减速变量
unsigned char Seg_Slow_Down;//数码管减速变量
unsigned char Seg_Pos;//数码管遍历变量
unsigned char Seg_Buf[6] = {0,4,0,1,1,0};//数码管显示数据储存数组
/*按键处理函数*/
void Key_Proc()
{
if(Key_Slow_Down) return;
Key_Slow_Down = 1;//按键减速程序
Key_Val = Key_Read();//获取键码值
Key_Down = Key_Val & (Key_Val ^ Key_Old);//检测上升沿
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);///检测下降沿
Key_Old = Key_Val;//辅助扫描
/*常用三行消抖,上升沿不常用,这里写出来是加深记忆*/
}
/*信息处理函数*/
void Seg_Proc()
{
if(Seg_Slow_Down) return;
Seg_Slow_Down = 1;//数码管减速程序
}
/*其他信息处理函数*/
void Led_Proc()
{
}
/*定时器初始化函数*/
void Timer0Init(void) //1毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
/*定时器0中断服务函数*/
void Timer0Servier() interrupt 1
{
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//设置按键减速10ms
if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//设置数码管减速500ms
if(++Seg_Pos == 6) Seg_Pos = 0;//遍历
Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]);
}
/*Main*/
void main()
{
Timer0Init();
while(1)
{
Key_Proc();
Seg_Proc();
Led_Proc();
}
}
这里的减速程序弥补了之前三行消抖发现不灵敏的情况,因为单片机处理一行代码的时间很短,while里循环一遍也很短,不同设备接收的时间不同也比单片机更长,当第一个循环的信息还没处理完,第二个循环就来了,导致错乱。如按键一般接收10ms,想要收发一致就需要一个减速程序给单片机减速,在1~9s不执行,在第0s时为if语句为假,执行下面的程序,但是单片机处理一条语句是很快的,如果我们不手动给减速变量置1,那在0~1秒内会执行很多次按键检测程序。随着能力的提升,后面会使用调度器处理。
在代码中我们给了数码管一个数组用于检测模板是否正确
本次总结就到这里了,希望大家多多点赞
标签:P3,char,Temp,数码管,51,unsigned,Seg,单片机,Key From: https://blog.csdn.net/2401_86416551/article/details/144309783