前言
本次编程实验以IAP15F2K61S2为单片机主控芯片,其编程使用与STC15F2K60S2完全相同,头文件为STC15F2K60S2.H
。若用于51系列单片机,以reg52.h
为头文件,则需将后文中timer0.c中void Timer0_Init(void)
函数中的AUXR &= 0x7F;
一行删除(后文有提示)。程序中涉及的定时器初始化程序和LED亮灭程序和数码管显示程序,读者可根据自身所用单片机原理图和手册自行修改。
一、为什么使用定时器扫描数码管?
在对主程序执行效率要求低、项目工程复杂度低的情况下,通常我们对数码管位选、段选后用delay延时函数进行延时,利用人眼视觉暂留效应实现8位数码管的静态、动态显示。但数码管延时时间,以2ms为例,相比机器周期微秒级别还是太长。为了提高主程序执行效率,故使用定时器扫描数码管的方式。
二、定时器扫描数码管的原理及代码
定时器定时1ms,每隔2ms刷新1位数码管的显示,从而代替了delay延时函数。
#include <STC15F2K60S2.H>
typedef unsigned char u8;
#define outputp0(y,x) P0=x,P2&=0x1f,P2|=y,P2&=0x1f; //P2高三位用于74HC138译码器选择锁存器,P0由于发送数据
int shownum=123; //数码管显示数值
unsigned char Seg_sy[]={10,10,10,10,10,10,10,10}; //数码管段选索引数组,上电默认数码管全灭
unsigned char code Seg_Table[]={
0xc0, //共阳数码管
0xf9,
0xa4,
0xb0,
0x99,
0x92,
0x82,
0xf8,
0x80,
0x90,
0xff //8段全灭
};
void Timer0_Init(void) //1毫秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式,若以reg52.h为头文件时删除这行代码
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA=1; //使能总中断
}
/*
* @brief 数码管显示函数
* @param pos:位选;dat:显示数字;dot:小数点选择位,1有0无
* @reval
* @note:
*/
void showbit(unsigned char pos,dat,dot) //数码管显示函数
{
outputp0(0xc0,0x01<<pos); //位选
outputp0(0xe0,Seg_Table[10]); //段选,这两行用于消影
outputp0(0xc0,0x01<<pos); //位选
outputp0(0xe0,Seg_Table[dat]+0x80*dot); //段选
}
/*
* @brief 8位数码管显示函数
* @param
* @reval
* @note:
*/
void showloop()
{
static unsigned char sy=0; //sy用于选择位选和位选对应的段选索引
showbit(sy,Seg_sy[sy],0); //刷新1位数码管
sy++; //每执行一次,sy加一,从而下次执行时刷新下一位数码管
sy%=8; //8位数码管,因此sy不超过8
}
void main()
{
Timer0_Init();
Seg_sy[0]=shownum/100; //赋初值
Seg_sy[1]=shownum/10%10;
Seg_sy[2]=shownum%10;
while(1)
{
}
}
void Timer0_Isr(void) interrupt 1
{
static u8 tsmg=0; //用于数码管扫描时间计数
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
tsmg++;
if(tsmg>=2) //每2ms扫描一次数码管
{
tsmg=0; //清零
showloop(); //刷新数码管
}
}
编译下载程序后,数码管正常显示123。
三、滚动显示
有时候我们希望数码管显示像滚动屏一样,数据能够从左向右或从右向左进入然后从另一端消失。以显示"HELLO",从右向左,每1s滚动一次为例,首先写出对应的段码表,将上文代码中的unsigned char code Seg_Table[]
修改为如下值:
unsigned char code Seg_Table[]={ //数码管段选数组
0x89, //H
0x86, //E
0xc7, //L
0xc7, //L
0xc0, //O
0xff //0xff,用于熄灭1位数码管
};
为了实现滚动显示,数码管段选索引数组应该每1秒向左移动一次。当”HELLO“全部移出数码管后,此时数码管应该全灭,下一秒再将”H“移入数码管,从而在数码管段选数组中需要8个连续0xff用于全灭8位数码管,即在数码管段选索引数组中有8个连续5。将上文代码中的unsigned char Seg_sy[]
修改为如下值:
unsigned char Seg_sy[]={5,5,5,5,5,5,5,5,0,1,2,3,4};
完整代码如下:
#include <STC15F2K60S2.H>
typedef unsigned char u8;
#define outputp0(y,x) P0=x,P2&=0x1f,P2|=y,P2&=0x1f; //P2高三位用于74HC138译码器选择锁存器,P0由于发送数据
unsigned char Seg_sy[]={5,5,5,5,5,5,5,5,0,1,2,3,4}; //数码管段选索引数组
unsigned char code Seg_Table[]={ //数码管段选数组
0x89, //H
0x86, //E
0xc7, //L
0xc7, //L
0xc0, //O
0xff //0xff,用于熄灭1位数码管
};
void Timer0_Init(void) //1毫秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式,若以reg52.h为头文件时删除这行代码
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
EA=1; //使能总中断
}
/*
* @brief 数码管显示函数
* @param pos:位选;dat:显示数字;dot:小数点选择位,1有0无
* @reval
* @note:
*/
void showbit(unsigned char pos,dat,dot) //数码管显示函数
{
outputp0(0xc0,0x01<<pos); //位选
outputp0(0xe0,Seg_Table[10]); //段选,这两行用于消影
outputp0(0xc0,0x01<<pos); //位选
outputp0(0xe0,Seg_Table[dat]+0x80*dot); //段选
}
/*
* @brief 8位数码管显示函数
* @param
* @reval
* @note:
*/
void showloop()
{
static unsigned char sy=0; //sy用于选择位选和位选对应的段选索引
showbit(sy,Seg_sy[sy],0); //刷新1位数码管
sy++; //每执行一次,sy加一,从而下次执行时刷新下一位数码管
sy%=8; //8位数码管,因此sy不超过8
}
void dongtaishaomiao()
{
u8 i=0,temp=0;
temp=Seg_sy[0]; //temp用于保存数码管段选索引数组第0位
for(i=0;i<sizeof(Seg_sy)-1;i++)
Seg_sy[i]=Seg_sy[i+1]; //数码管段选索引数组循环向左移动,每次移动一位
Seg_sy[sizeof(Seg_sy)-1]=temp; //数码管段选索引数组第0位向左移动一位后变成第12位
}
void main()
{
outputp0(0x80,0xff);
outputp0(0xa0,0x00);
Timer0_Init();
while(1)
{
}
}
void Timer0_Isr(void) interrupt 1
{
static u8 tsmg=0; //用于数码管扫描时间计数
static int dtaismg=0;
TL0 = 0x66; //设置定时初始值
TH0 = 0xFC; //设置定时初始值
tsmg++;
if(tsmg>=2) //每2ms扫描一次数码管
{
tsmg=0; //清零
showloop(); //刷新数码管
}
dtaismg++;
if(dtaismg>=1000) //每1秒
{
dtaismg=0;
dongtaishaomiao(); //将数码管段选索引数组循环向左移动,每次移动一位
}
}
编译下载程序,数码管滚动显示"HELLO"。
总结
通过上述程序,可以实现定时器扫描数码管(含滚动显示)以提高主程序执行效率。通过类比,也可以实现定时器扫描独立按键和矩阵按键,详情见定时器扫描按键(短按/长按)和定时器扫描矩阵键盘简易代码(短按/长按)
此外,由于定时器每1ms进入一次中断,对于具有IIC和One-Wire通信等具有严格时序要求的项目来说,容易使通信被干扰,因此不建议使用定时器扫描的方式。当然,通过一些方法也可以实现二者兼容并存,在DS1302可调时钟(按键短按/长按)中提供了其中一种方法。
有任何问题和补充,欢迎评论区交流。