深圳市百诺芯科技有限公司

(非本站正式会员)

深圳市百诺芯科技有限公司

营业执照:已审核经营模式:贸易/代理/分销所在地区:广东 深圳

收藏本公司 人气:105228

企业档案

  • 相关证件:营业执照已审核 
  • 会员类型:普通会员
  •  
  • 张先生 QQ:1296701637
  • 电话:18576232501
  • 手机:18929337441
  • 张先生 QQ:1034259340
  • 电话:18929337441
  • 手机:18576232501

您的当前位置:

深圳市百诺芯科技有限公司 > 新闻动态 > 51单片机LED流水灯多种驱动方式

51单片机LED流水灯多种驱动方式

发布时间: 2023/4/23 14:39:43 | 96 次阅读

前言

开发者在入门点亮di一盏灯后,再深入一点就会用到流水灯。而如何实现流水灯又有好几种方式,我查询了一网上大神们的作品,无非有三种方式即查询法、位移法。这篇文章,我就如何实现流水灯开展讨论。

硬件

我以新定义TBK-RD8T3x_v1.0开发板,为实验条件。

image.png

板载了8个流水灯。原理图如下:

image.png

实现方式之一

从原理图上看,这8个灯不是接在1个P口上,分别接到了P3的第1-4,与P4的0-3端口之,按网上的教材位移方法都是不适用的。

于是我写下了di一种方法那就是直接对每一个灯进行写来实现:

#include "rd8.h"

 #define ON 1 #define OFF 0

sbit LED0 = P4^0;
sbit LED1 = P4^1;
sbit LED2 = P4^2;
sbit LED3 = P4^3;
sbit LED4 = P3^1;
sbit LED5 = P3^2;
sbit LED6 = P3^3;
sbit LED7 = P3^4;
 void  delay(uint32_t xms)   //延时约xms毫秒
{
    uint32_t  i,j;
    for(i=xms*2;i>0;i--)
    for(j=112;j>0;j--);    //分号代表跑空,for语句不需要分号,112次表示一毫秒
}
 void LED_Init(void)
{
	P3CON |= 0x1E; //P3 0b0001 1110 输出
	P4CON |= 0x0F; //P4 0b0000 1111
}


 void main(void)
{
	LED_Init();
 while(1)
	{
		LED0 = ON;
		delay(500);
		
		LED0 = OFF;
		LED1 = ON;
		delay(500);
		
		LED1 = OFF;
		LED2 = ON;
		delay(500);
		
		LED2 = OFF;
		LED3 = ON;
		delay(500);
		
		LED3 = OFF;
		LED4 = ON;
		delay(500);
		
		LED4 = OFF;
		LED5 = ON;
		delay(500);
		
		LED5 = OFF;
		LED6 = ON;
		delay(500);
		
		LED6 = OFF;
		LED7 = ON;
		delay(500);
		
		LED7 = OFF;
		delay(500);
	}
} 

这样的编程实现了流水灯,优点是直观,缺点是编写起来麻烦,代码比较长。经查看map文件编译结果为:Program Size: data=21.0 xdata=28 const=0 code=418

实现方式之二

用数组法来实现,我们用数据来定义了P4,P3两组显示状态,共组组了9对分别表示8个灯的显示状态:

#include "rd8.h"
 //P40 P41 P42 P43  //P31 P32 P33 P34   //定义LED 状态数组 
 static uint8_t LEDs[]={0x00,0x00,0x01,0x00, 0x02,0x00,0x04,0x00,0x08,0x00, 0x00,0x02, 0x00,0x04, 0x00,0x08,0x00,0x10};  

 void LED_Init(void) {
	P3CON |= 0x1E; //P3 0b0001 1110 输出
	P4CON |= 0x0F; //P4 0b0000 1111
}

 void delay(uint32_t xms)   //延时约xms毫秒 {
    uint32_t  i,j;
    for(i=xms*2;i>0;i--)
    for(j=112;j>0;j--);    //分号代表跑空,for语句不需要分号,112次表示一毫秒
}
 void LED_Flash(void) {
 static uint8_t ledIndex = 0;
 if(ledIndex == 9)
		ledIndex = 0;
	P4 = LEDs[ledIndex*2];
	P3 = LEDs[ledIndex*2%2B1];
	ledIndex %2B%2B;
	
}

 void main(void) {
 LED_Init();

 while(1)
	{
	 LED_Flash();
	 delay(500);
	}
} 

这样用查表法整理出来的的代码相对于di一种实现方式,代码行有所减短,编译后,查看.map结果为:Program Size: data=40.0 xdata=0 const=0 code=454

实现方式之三

实现方式2,主要是查表的数组还是比较点内存,这里优化一下。

#include "rd8.h"
 //P40 P41 P42 P43  //P31 P32 P33 P34   //定义LED 状态数组 
 //static uint8_t LEDs[]={0x00,0x00,0x01,0x00, 0x02,0x00,0x04,0x00,0x08,0x00, 0x00,0x02, 0x00,0x04, 0x00,0x08,0x00,0x10};   //                            高四位代表P4 低四位代表P3 由于P3 为1-4,我们右移了一位,在显示时,我们需要左移一位 static uint8_t LEDs[]={0x00,// 0b 0000 00000
											 0x10,// 0b 0001 00000 
                       0x20,
                       0x40,
                       0x80,
                       0x01,//0b 0000 0001 
                       0x02, 0x04,0x08,};  void LED_Init(void) {
	P3CON |= 0x1E; //P3 0b0001 1110 输出
	P4CON |= 0x0F; //P4 0b0000 1111
}

 void delay(uint32_t xms)   //延时约xms毫秒 {
    uint32_t  i,j;
    for(i=xms*2;i>0;i--)
    for(j=112;j>0;j--);    //分号代表跑空,for语句不需要分号,112次表示一毫秒
}
 void LED_Flash(void) {
 static uint8_t ledIndex = 0;
 if(ledIndex == 9)
		ledIndex = 0;
	P4 = (LEDs[ledIndex] & 0xF0)>>4;
	P3 = (LEDs[ledIndex] & 0x0F)<<1;
	ledIndex %2B%2B;
	
}

 void main(void) {
 LED_Init();

 while(1)
	{
	 LED_Flash();
	 delay(500);
	}
} 

这样优化后,点用内存有所减少:Program Size: data=31.0 xdata=0 const=0 code=446

实现方式之四

在方式2、方式3,我们定义了数组,利用查表法来实现流水灯。这一节我用利用位移来实现。

#include "rd8.h"
 void LED_Init(void) {
	P3CON |= 0x1E; //P3 0b0001 1110 输出
	P4CON |= 0x0F; //P4 0b0000 1111
}

 void delay(uint32_t xms)   //延时约xms毫秒 {
    uint32_t  i,j;
    for(i=xms*2;i>0;i--)
    for(j=112;j>0;j--);    //分号代表跑空,for语句不需要分号,112次表示一毫秒
}
 void LED_Flash(uint8_t led_data) {
	P3 = (led_data & 0xF0)>>3;  //由于P3从1开始,所以只右移3位
	P4 = (led_data & 0x0F);
}

 void main(void) {
 uint8_t LED_DATA;
 uint8_t i;
 LED_Init();
	
 while(1)
	{
		LED_DATA = 0x00;
	 LED_Flash(LED_DATA); // 这里开始是熄灭所有的灯
	 delay(500);
		LED_DATA = 0x01;      //初始值
	 for(i=0;i<9;i%2B%2B)
		{
		 LED_Flash(LED_DATA);
			LED_DATA = LED_DATA << 1;
		 delay(500);		
		}
	}
} 

这样我也实现了流水灯,这次位移的实现,我们的代码量变化为:Program Size: data=23.0 xdata=0 const=0 code=325

实现方式之五

上面所有的流水灯是阻塞式的,我们如果需要处理其的事任,那就得修改为非阻塞式,这里我们增加了定时器来实现,代码如下:

#include "rd8.h"
 uint8_t sta;   uint32_t count = 0;
 void LED_Init(void) {
	P3CON |= 0x1E; //P3 0b0001 1110 输出
	P4CON |= 0x0F; //P4 0b0000 1111
}
 void Timer0Iint(void) {
	TMOD |= 0x01;  // 配置定时器0为 16位定时器,  TH0、TL0全用 
	TH0 =(65536-1000)/256;   //1000us定时,即1毫秒溢出产生中断
	TL0 =(65536-1000)%256;  //1000us定时,即1毫秒溢出产生中断
	ET0 = 1;								 //开启定时器0中断
	EA = 1;									 //开启全局中断
	TR0 = 1;								 //定时器0开始计数;
}
 void LED_Flash(void) {
 static uint8_t led_data = 0x00;

	P3 = (led_data & 0xF0)>>3;  //由于P3从1开始,所以只右移3位
	P4 = (led_data & 0x0F);
	led_data = led_data<<1;
 if (led_data == 0x00)
		led_data = led_data |= 0x01;
}

 void main(void) {
 Timer0Iint();
 LED_Init();
	
 while(1)
	{
	 if(sta == 1)
		{
			sta = 0;
		 LED_Flash();		
		}
	}
}
 void Timer0() interrupt 1 {
 //每次产生中断后初始化定时器初值, 1ms秒产生1次中断
	TH0=(65536-1000)/256;
	TL0=(65536-1000)%256;
 //500毫秒执行次LED1反转
	count %2B%2B;
 if(count == 500)
	{
		sta =1;
		count = 0;
	}
	
} 

经过修改,这一版是基于非阻塞式的实现。编译后的.map,代码尺寸如下:Program Size: data=15.0 xdata=0 const=0 code=364

实现方式之六

这里再增加一种位移的方面代码如下,这种方式更加简洁:

#include "rd8.h" #include  uint8_t sta;   uint32_t count = 0;
 void LED_Init(void) {
	P3CON |= 0x1E; //P3 0b0001 1110 输出
	P4CON |= 0x0F; //P4 0b0000 1111
}
 void Timer0Iint(void) {
	TMOD |= 0x01;  // 配置定时器0为 16位定时器,  TH0、TL0全用 
	TH0 =(65536-1000)/256;   //1000us定时,即1毫秒溢出产生中断
	TL0 =(65536-1000)%256;  //1000us定时,即1毫秒溢出产生中断
	ET0 = 1;								 //开启定时器0中断
	EA = 1;									 //开启全局中断
	TR0 = 1;								 //定时器0开始计数;
}
 void LED_Flash(void) {
 static uint8_t led_data = 0x01;
	led_data = _crol_(led_data,1);
	P3 = (led_data & 0xF0)>>3;  //由于P3从1开始,所以只右移3位
	P4 = (led_data & 0x0F); //	led_data = led_data<<1; //	if (led_data == 0x00) //		led_data = led_data |= 0x01;
}

 void main(void) {
 Timer0Iint();
 LED_Init();
	
 while(1)
	{
	 if(sta == 1)
		{
			sta = 0;
		 LED_Flash();		
		}
	}
}
 void Timer0() interrupt 1 {
 //每次产生中断后初始化定时器初值, 1ms秒产生1次中断
	TH0=(65536-1000)/256;
	TL0=(65536-1000)%256;
 //500毫秒执行次LED1反转
	count %2B%2B;
 if(count == 500)
	{
		sta =1;
		count = 0;
	}
	
} 

此次修改后的.map文件显示为:Program Size: data=15.0 xdata=0 const=0 code=359

总结

总结一下这几种编程方式点用的空间:

序号dataxdataconstcode优点缺点
定义端口法21.0280418代码可读性高,直观代码行数多,如何需要修改比较麻烦
数组查表法40.000454代码较di一种整洁,容易修改占用内存大
查表法优化31.000446相比上一种减少了内存的占用占用内存大
位移法之一23.000325相比上面的数组查询占用内存小实现代码复杂
非阻塞式位移15.000364相比上面的,实现非阻塞式位移代码理解需要一定基础
非阻塞进式位移二15.000359代码更整法,占用空间小,后期实现功能简单方便阅读理解代码,需要位移的基础知识