定时器控制数码管的动态流水显示

实验要求

用单片机实现4个数码管的动态数字显示,让4个数码管能够一次显示1、2、3、4,切换初始时间为1秒,按下独立按键S1,显示数字切换加快0.1秒,持续按下独立按键S1,显示数字切换速度持续加快;按独立按键S2,显示数字切换变慢0.1秒,持续按独立按键S2,显示数组切换速度持续变慢;按独立按键S3,显示数组调换为4、3、2、1。

软件操作

LED数码管的静态显示与动态显示(Keil+Proteus)_proteus数码管显示-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/134101256?spm=1001.2014.3001.5501

实验思路

首先对实验进行分析,可以拆分成以下的几个部分,数码管的显示、每1秒进行切换、独立按键的响应。

数码管的显示

LED数码管的静态显示与动态显示(Keil+Proteus)_proteus数码管显示-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/134101256?spm=1001.2014.3001.5501元器件科普之LED数码管的原理和应用 - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/37804311/我这里采用的是共阳极的接法,所以就是通过输出对应字符的段码,来实现数码管的显示,我这里也是提前将输出位置和数字先储存在数组当中。

每1秒的延迟

定时器/计数器的应用-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/134601236?spm=1001.2014.3001.5501【MCS-51】内部定时和计数器 - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/617219210这个每1秒切换的话,用循环其实也差不多,但是不准确,题目要求是要1秒这种具体的时间还是采用了定时器来控制,通过设置晶体振荡器的频率以及TH,TL的初值让他5ms中断一次,循环20次就是0.1记录一次(因为之后增加和减少都是0.1s的形式)。然后就是将延迟里面的循环替换成定时器的即可,我是设置了一个标记,当flag=1的时候表示他经历了0.1s了,然后函数传递的形参是以10为基准,循环10次就表示1s。

定时时间=(65536-X)*12/晶体振荡器频率(定时时间单位um,晶体振荡器频率单位MHz,X为计时器初值。)

独立按键的响应

独立键盘接口设计(Keil+Proteus)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/134253686?spm=1001.2014.3001.5501四、51单片机控制独立按键_独立按键工作原理_朱嘉鼎的博客-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_42727214/article/details/12791306851单片机独立按键和矩阵按键实现 - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/161715453这个我相信大家也知道原理了,这里主要讲一下函数的实现,我这里是又设置了两个标志,一个用来判断是输出1234还是输出4321,一个是用来控制他的延迟时间,前面提到是以10作为基准来进行加减来控制数码管切换的时间。

题目要求的持续按下依旧可以增加,我理解的应该是按下两次会有两次,不是一直按着一直响应,不然假设我按下S1,加快0.1s,我一直按着一下就切换到0s了,根本看不到现象,所以我这里增加了一个防止一直按着一直响应的部分,当某个按键持续按下时他会保持着停在那个地方不会往下面执行。

#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int

//采用的是共阳极的接法
uchar code dis_code1[]={0xF9,0xA4,0xB0,0x99};//1234
uchar code dis_code2[]={0x99,0xB0,0xA4,0xF9};//4321
uchar code wei_code[]={0x01,0x02,0x04,0x08};//对应输出的位置

sbit K1=P3^1;//将S1位定义为P1.0引脚
sbit K2=P3^0;//将S2位定义为P1.1引脚
sbit K3=P3^2;//将S3位定义为P1.2引脚
 
uchar keyval;//定义键值储存变量单元
uchar zheng=1;//用来标记是正向输出还是反向输出
uchar timer=0;//记录中断次数
uint sudu=10;//延迟时间
uchar flag=1;//判断是否结束延迟

void key_scan(void);//扫描键盘
void kuai(void);//数码管输出加快0.1秒
void man(void);//数码管输出减慢0.1一秒
void fan(void);//数码管输出反过来

void delay(uint t){
	uint i;
	for(i=0;i<t;i++){
		TH0=0xee;//向TH0写入初值的高8位
		TL0=0x00;//向TL0写入初值的低8位,定时5ms
		flag=0;//不结束
		while(flag==0);
	}
}
 
void main(){
	uchar i;
	TMOD=0x01;//定时器T0方式1定时模式
	ET0=1;//允许定时器T0中断
	EA=1;//总中断允许
	TH0=0xee;//向TH0写入初值的高8位
	TL0=0x00;//向TL0写入初值的低8位,定时5ms
	TR0=1;//启动定时器T0
	while(1){
		if(zheng==1){
			//正向输出
			for(i=0;i<4;i++){
				key_scan();//键盘扫描
				switch(keyval){
					case 1:kuai();break;//键值为1,数码管输出加快0.1秒
					case 2:man();break;//键值为2,数码管输出减慢0.1一秒
					case 3:fan();break;//键值为3,数码管输出反过来
				}
				while((P3&0x0F)!=0x0F);//避免持续按下
				keyval=0;//每次清零
				P0=dis_code1[i];//P0口输出段码
				P2=wei_code[i];//P2口输入位控码
				delay(sudu);//延时
				if(zheng==0)break;
			}	
		}else{
			//反向输出
			for(i=0;i<4;i++){
				key_scan();//键盘扫描
				switch(keyval){
					case 1:kuai();break;//键值为1,数码管输出加快0.1秒
					case 2:man();break;//键值为2,数码管输出减慢0.1一秒
					case 3:fan();break;//键值为3,数码管输出减慢0.1一秒
				}
				while((P3&0x0F)!=0x0F);//避免持续按下
				keyval=0;//每次清零
				P0=dis_code2[i];//P0口输出段码
				P2=wei_code[i];//P2口输入位控码
				delay(sudu);//延时
			}
		}
	}
}
	
	
//键盘扫描(P3口)
void key_scan(void){
	P1=0xFF;
	if((P3&0x0F)!=0x0F){
		delay(1);
		if(K1==0)keyval=1;//按键K1被按下
		if(K2==0)keyval=2;//按键K1被按下
		if(K3==0)keyval=3;//按键K1被按下
	}
}

//数码管反向输出
void fan(){
	zheng=0;
}

//数码管输出加快0.1秒
void kuai(void){
	sudu--;
	//防止溢出
	if(sudu==0){
		sudu=0;
	}
}

//数码管输出减慢0.1一秒
void man(void){
	sudu++;
	if(sudu==0xFFFF){
		sudu=0xFFFF;
	}
}

//定时器T0中断函数0.1中断一次
void timer0(void) interrupt 1{
	TR0=0;//停止计时,避免带来误差
	TH0=0xee;//设置定时器的初值,让中断时间为5ms
	TL0=0x00;
	timer++;
	if(timer==20){//中断20次,就是20*5ms=0.1s,让程序0.1秒,数码管更新一次
		timer=0;//中断清零(为下一个0.1秒重新累计)
		flag=1;//结束延迟
	}
	TR0=1;//启动定时器继续计时
}

【51单片机】七段数码管显示实验+详细讲解-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/cumtLeibnizDavid/article/details/115969981 这个就可以不需要采用之前的有8位七段数码管了,当然想用也不是不可以。

4位七段数码管7SEG-MPX4-CA(共阳极),其他的器件在之前的实验中都有出现过,这里就不再赘述。

 运行结果

修改仿真

想实现仿真还需要对电路图进行修改,结果发现要修改的地方还挺多。

独立按键的位置,因为之前就注意了所以这个不用修改。

 数码管动态显示的位置,这个在之前是直接通过引脚控制的,现在是用4HC138译码器而且之前的引脚也不对,单片机中数码管也是采用共阴极的接法,所以段码也需要更改。

 

 之前的程序晶振的频率是11.0592,单片机中是12MHz,所以这个计算也需要修改。

12MHz延迟0.1s, (65536-X)*12/12=100000,可以求出X=60535(0x3EC77),不能直接是0.1,s,因为0.1s换成us太大了。

对原始程序进行修改

  •  修改TH0和TL的初值,让他保持中断一次是5ms
  • 将共阳极接法的段码修改成共阴极接法的段码
  • 因为键盘放抖动的延迟我是设置成了0.1s,所以数码管切换的初值应该修改为9(0.9s+0.1s)
  • 将数码管输出位置的数组修改成函数,之前考虑是对那三位进行处理,现在发现也可以直接赋值,也就是封装成数组也行
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int

//采用的是共阴极的接法
uchar code dis_code[]={0x06,0x5B,0x4F,0x66};//1234

void wei_code(uchar wei){
	switch(wei){
		case 0:
			//P2_4=1;P2_3=1;P2_2=1;break;//7 LED8
			P2=0xFF;break;//111	111 111 11
		case 1:
			P2=0xFB;break;//110	111 110 11
		case 2:
			P2=0xF7;break;//101 111 101 11
		case 3:
			P2=0xF3;break;//100	111 100 11
	}
}

sbit K1=P3^1;//将S1位定义为P1.0引脚
sbit K2=P3^0;//将S2位定义为P1.1引脚
sbit K3=P3^2;//将S3位定义为P1.2引脚
 
uchar keyval;//定义键值储存变量单元
uchar zheng=1;//用来标记是正向输出还是反向输出
uchar timer=0;//记录中断次数
uint sudu=9;//延迟时间
uchar flag=1;//判断是否结束延迟

void key_scan(void);//扫描键盘
void kuai(void);//数码管输出加快0.1秒
void man(void);//数码管输出减慢0.1一秒
void fan(void);//数码管输出反过来

void delay(uint t){
	uint i;
	for(i=0;i<t;i++){
		TH0=0xEC;//向TH0写入初值的高8位
		TL0=0x77;//向TL0写入初值的低8位,定时5ms
		flag=0;//不结束
		while(flag==0);
	}
}

void main(){
	uchar i;
	TMOD=0x01;//定时器T0方式1定时模式
	ET0=1;//允许定时器T0中断
	EA=1;//总中断允许
	TH0=0xEC;//向TH0写入初值的高8位
	TL0=0x77;//向TL0写入初值的低8位,定时5ms
	TR0=1;//启动定时器T0
	while(1){
		if(zheng==1){
			//正向输出
			for(i=0;i<4;i++){
				key_scan();//键盘扫描
				switch(keyval){
					case 1:kuai();break;//键值为1,数码管输出加快0.1秒
					case 2:man();break;//键值为2,数码管输出减慢0.1一秒
					case 3:fan();break;//键值为3,数码管输出反过来
				}
				while((P3&0x0F)!=0x0F);//避免持续按下
				keyval=0;//每次清零
				wei_code(i);//P2口输入位控码
				P0=dis_code[i];//P0口输出段码
				delay(sudu);//延时
				if(zheng==0)break;
			}	
		}else{
			//反向输出
			for(i=0;i<4;i++){
				key_scan();//键盘扫描
				switch(keyval){
					case 1:kuai();break;//键值为1,数码管输出加快0.1秒
					case 2:man();break;//键值为2,数码管输出减慢0.1一秒
					case 3:fan();break;//键值为3,数码管输出反过来
				}
				while((P3&0x0F)!=0x0F);//避免持续按下
				keyval=0;//每次清零
				wei_code(i);//P2口输入位控码
				P0=dis_code[3-i];//P0口输出段码	
				delay(sudu);//延时
			}
		}
	}
}
	
	
//键盘扫描(P3口)
void key_scan(void){
	P1=0xFF;
	if((P3&0x0F)!=0x0F){
		delay(1);
		if(K1==0)keyval=1;//按键K1被按下
		if(K2==0)keyval=2;//按键K1被按下
		if(K3==0)keyval=3;//按键K1被按下
	}
}

//数码管反向输出
void fan(){
	zheng=0;
}

//数码管输出加快0.1秒
void kuai(void){
	sudu--;
	//防止溢出
	if(sudu==0){
		sudu=0;
	}
}

//数码管输出减慢0.1一秒
void man(void){
	sudu++;
	if(sudu==0xFFFF){
		sudu=0xFFFF;
	}
}

//定时器T0中断函数0.1中断一次
void timer0(void) interrupt 1{
	TR0=0;//停止计时,避免带来误差
	TH0=0xEC;//设置定时器的初值,让中断时间为5ms
	TL0=0x77;
	timer++;
	if(timer==20){//中断20次,就是20*5ms=0.1s,让程序0.1秒,数码管更新一次
		timer=0;//中断清零(为下一个0.1秒重新累计)
		flag=1;//结束延迟
	}
	TR0=1;//启动定时器继续计时
}

对原始原理图进行修改

  • 修改4位的七段数码管,将7SEG-MPX4-CA改成7SEG-MPX4-CC
  • 增加一个38译码器74HC138来控制输出的位置

运行结果图

总结 

用原理图和实物的感觉是不太一样的,实物的连接都是固定的,需要看原理图来画出对应的电路图,这样才能得到一样的效果,仿真就更加自由一点。程序还有很多不足的地方,对独立键盘的处理不是很好,导致键盘不是灵敏,需要按稍微按久一点才能正确判断,这个确实是不足的地方,目前考虑的就是让数码管切换的时间量化,所以刚好就是设置成0.1s,尽管对0.1靠感觉感受不出来。

由于本人也是刚学单片机不久,有不足的地方希望有大佬能够指正,感觉有用的话希望各位小伙伴能够点赞多多支持一下。

猜你喜欢

转载自blog.csdn.net/weixin_64066303/article/details/134864987