定时器扫描8位数码管(含滚动显示)


前言

  本次编程实验以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可调时钟(按键短按/长按)中提供了其中一种方法。

  有任何问题和补充,欢迎评论区交流。

  • 27
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值