初学51单片机点阵LED,数码管,流水灯同时工作的程序实现

这是笔者关于51单片机学习的第5篇学习笔记了,通过对51单片机各个基础模块的学习终于到了小结的时候了。

先上结果视频

三个模块同时工作_哔哩哔哩_bilibili

#include <reg52.h>

sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;

unsigned char code LedChar[] = {  //数码管显示字符转换表 0 1 2 3 4 5 6 7 8 9 A B C D E F
    0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
    0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};
unsigned char LedBuff[6] = {  //数码管显示缓冲区,初值0xFF确保启动时都不亮
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
//unsigned char i = 0;   //动态扫描的索引
//unsigned int cnt = 0;  //记录T0中断次数
unsigned char flag1s = 0;  //1秒定时标志
unsigned char code image[] = 
	{ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,   //点阵
		0x99,0x99,0x99,0x99,0x99,0x81,0xC3,0xFF,
    0x99,0x00,0x00,0x00,0x81,0xC3,0xE7,0xFF,
		0xC3,0xE7,0xE7,0xE7,0xE7,0xE7,0xE7,0xC3,
    0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,

};


void main()
{
    char j;  //循环变量
    unsigned long sec = 0;  //记录经过的秒数
    unsigned char buf[6];   //中间转换缓冲区
	  EA = 1; //中断打开
	  TMOD = 0x11;  //设置T1和T0都为模式1
	
    TH0 = 0xFC;  //定时器0赋值 定时2ms
    TL0 = 0x67;	
	  ET0 = 1;    //定时器0中断使能(启动T0中断)
	  TR0 = 1;   // 定时器0使能(启动T0)

    TH1  = 0xFC;  //为T0赋初值0xFC67,定时1ms
    TL1  = 0x67;
    ET1  = 1;     //使能T1中断
    TR1 = 1;     //启动T1
    
    while (1)
    {
        if (flag1s == 1)  //判断1秒定时标志
        {
            flag1s = 0;   //1秒定时标志清零
            sec++;        //秒计数自加1
            //将sec按十进制位从低到高依次提取到buf数组中
            buf[0] = sec%10;
            buf[1] = sec/10%10;
            buf[2] = sec/100%10;
            buf[3] = sec/1000%10;
            buf[4] = sec/10000%10;
            buf[5] = sec/100000%10;
            //从最高为开始,遇到0不显示,遇到非0退出循环
         for (j=5; j>=1; j--)             //共6个数码管用下标表示就是0-5,最高位数码管的下标是5
           {
               if (buf[j] == 0)           //从最高位j=5的时候开始倒序判断缓冲区的值,遇到非零跳出函数
                    LedBuff[j] = 0xFF;   //假设sec为123,通过取值函数我们知道buf[] = {3,2,1,0,0,0} buf[3] buf[4] buf[5] 都为0代表LedBuff[5],LedBuff[4],LedBuff[3]
               else                      //被赋值位0xFF.0xff即数码管LED关闭什么都不显示
                  break;                 //buf[2]不为0进入else函数随后跳出,这种的j值就是2
           }
            //将剩余的有效数字位如实转换
           for ( ; j>=0; j--)    //for()起始未对j操作,j即保持上个循环结束时的值,j的初始值由上个for函数提供
           {
                LedBuff[j] = LedChar[buf[j]];  // 函数循环执行第一次结果是 LedBuff[2] = LedChar[buf[2]]=LedChar[1]=0xF9 这个值是1
            }                                 // 	同理LedBuff[1]=2 ledBuff[0] =3 对于中断里的扫描函数 P0 = LedBuff[] = {3,2,1,0xff,0xff,0xff}
        }                                       //只显示3位高位置0xff不显示达成
    }
}
/* 定时器0中断服务函数 */
void InterruptTimer1() interrupt 3 //定时器1
{ 
	static unsigned char j = 0;
	static unsigned char i = 0;   //动态扫描的索引
  static unsigned int cnt = 0;  //记录T0中断次数
	static unsigned int shift1 = 0x01; //(LED1)shift1在最左边端口1那么一开始会左移
  static unsigned int shift2 = 0x80;// (LED2)shift2在最右边端口8那么一开始会右移
	static unsigned char dir1 = 0; 
	  P0 = 0xFF;    //该句必须放在最ENLED和ADDR3的上面,如果放在原先的位置任然会有鬼影的出现
	  ENLED = 0;    //使能U3
    ADDR3 = 1;    //因为需要动态改变ADDR0-2的值,所以不需要再初始化了
    TH1 = 0xFC;  //重新加载初值
    TL1 = 0x67;
    cnt++;       //中断次数计数值加1
	  j++;
    if (cnt >= 1000)  //中断1000次即1秒
    {
        cnt = 0;      //清零计数值以重新开始下1秒计时
        flag1s = 1;   //设置1秒定时标志为1
    }
    //以下代码完成数码管动态扫描刷新
   // P0 = 0xFF;   //显示消隐这句放在这会产生鬼影
    switch (i)
    {
        case 0: ADDR2=0; ADDR1=0; ADDR0=0; i++; P0=LedBuff[0]; break;  //数码管扫描
        case 1: ADDR2=0; ADDR1=0; ADDR0=1; i++; P0=LedBuff[1]; break;
        case 2: ADDR2=0; ADDR1=1; ADDR0=0; i++; P0=LedBuff[2]; break;
        case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;
        case 4: ADDR2=1; ADDR1=0; ADDR0=0; i++; P0=LedBuff[4]; break;
        case 5: ADDR2=1; ADDR1=0; ADDR0=1; i++; P0=LedBuff[5]; break;
			  case 6: ADDR2=1; ADDR1=1; ADDR0=0; i = 0;                            //LED扫描
			
     	                    
  	  P0 = ~(shift1+shift2); //P0等于循环变量取反	 

	if( j>= 200) //延时200ms
		{
		   j = 0;		 				
		  if(shift1 == 0x08 && dir1 == 0)	//判断是否 shit1运动到端口4并且是向左移动
	    {
		  shift1 = shift1<<1;	//shift1在端口4 0x08的位置往左移一位
		  shift2 = shift2>>1;	//shift2在端口5	0x10的位置往右移一位,
		  }						//  因为是相对运动的因此判断了shift1的方向shift2 的方向就确认了
	    if(shift1 == 0x10 && dir1 == 1)	//判断是否 shift1运动到端口5并且是向右运动
		    {
		  shift1 = shift1>>1;  // shift1在端口5 0x10的位置并且向右移动一位
		  shift2 = shift2<<1;  // shift2在端口4 0x08的位置往左移一位
        }	
	    if(dir1 == 0 ) //判断是否 shift1往左移动
		    {
			   {shift1 = shift1<<1; //循环变量1左移1位
			    shift2 = shift2>>1;
			    }	//循环变量2右移1位
			    if(shift1 ==0x80 && shift2 == 0x01)	//循环变量都移到最左端和最右端
			    {
				   dir1 = 1;		 //换向
				   break ; //跳出switch语句
				   }
			   } 

       if(dir1 == 1)  // 判断是否 shift1往右移动
	      {
		      {
	  	     shift1 = shift1>>1 ;	//换向后循环变量1右移
		       shift2 = shift2<<1 ;	//换向后循环变量2左移
	        }
	         if(shift1 == 0x01 && shift2 == 0x80)  //判断循环变量1到最右端且循环变量2到最 左端
		      {
		       dir1 = 0;	   //又开始换向换回到原先的方向
			 
			
		      }
			 
		     }
		  }
			       
			  break;
        default: break;
    }
		
}

 void TtrTimer0 () interrupt 1   //定时器0
{
   static unsigned char k = 0;
	 static unsigned char index = 39;
	 static unsigned char tmr = 0;
	 P0 = 0xFF;  该句必须放在ENLED和ADDR3上面,如果放在原先的位置就会有鬼影的出现
   ENLED = 0;  //38译码器U4使能
   ADDR3 = 0; //38译码器U4使能
	 TH0 = 0xFC;  //定时器0重新赋值
	 TL0 = 0x67;
	
	// P0 = 0xFF;  //这句放在这会产生鬼影
	 switch(k)
	 {
		 case 7: ADDR2 = 0; ADDR1 = 0; ADDR0 = 0; k = 0; P0 = image[index-7]; break;   //点阵扫描
		 case 6: ADDR2 = 0; ADDR1 = 0; ADDR0 = 1; k++; P0 = image[index-6]; break;
		 case 5: ADDR2 = 0; ADDR1 = 1; ADDR0 = 0; k++; P0 = image[index-5]; break;
		 case 4: ADDR2 = 0; ADDR1 = 1; ADDR0 = 1; k++; P0 = image[index-4]; break;
		 case 3: ADDR2 = 1; ADDR1 = 0; ADDR0 = 0; k++; P0 = image[index-3]; break;
		 case 2: ADDR2 = 1; ADDR1 = 0; ADDR0 = 1; k++; P0 = image[index-2]; break;
		 case 1: ADDR2 = 1; ADDR1 = 1; ADDR0 = 0; k++; P0 = image[index-1]; break;
		 case 0: ADDR2 = 1; ADDR1 = 1; ADDR0 = 1; k++; P0 = image[index-0]; break;
		 default: break;
     }
	 //P0 = 0xFF;
   tmr++;
		 if(tmr >= 250)
		 {
		   tmr = 0;
			 index--;
			 if(index <= 8)
			 {
			   index = 39;
			 }
		 
		 }

程序已经实现了三个模块的同时工作,该篇的程序前文都有放出,笔者只是把各个功能模块整合在了一起,在实现这个程序的时候笔者遇到了一个问题导致了鬼影的产生。后来通过逻辑的分析排除了,因此关于电路的程序模块的使用时候要注意逻辑的关系。

先看下鬼影的产生视频

三个模块同时工作有鬼影_哔哩哔哩_bilibili

这个产生的原因就是消除鬼影的语句 P0 = 0xFF; 之前的博文都是放在刷新语句的前面了,现在是放在中断函数里ADDR3和ENLED的前面,因为这两句是切换38译码器U3和U4的语句,

举个例子:上图是定时器0的中断函数程序,假设在进入该中断前已经执行了中断1的刷新语句的case3语句,

定时器1中断的case3语句是

case 3: ADDR2=0; ADDR1=1; ADDR0=1; i++; P0=LedBuff[3]; break;,接着我们进入定时器0的中断,如果一开始不加上 P0 = 0xFF;这句,执行了ENLED = 0;   ADDR3 = 0; 语句,三八译码器从U3切换到U4了,这时点阵LED上的就会显示图象,显示的图像就由前个中断刷新语句ADDR2=0; ADDR1=1; ADDR0=1这句产生。

前篇博文我们说到若想点阵LED某个灯点亮,P0口最少要有一个端口输出低电平,

ENLED = 0;   ADDR3 = 0;使U4使能,

可以看出只要U4使能后,ADDR0  ADDR1 ADDR2无论如何取值,它的输出断必然有一个低电平。

因此在U3与U4切换的时候,把P0的所有端口全部置1,即短暂关闭点阵LED,就可以避免鬼影的产生。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值