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

描述

前言

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

硬件

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

编程

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

编程

实现方式之一

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

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

#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+1];
	ledIndex ++;
	
}


void main(void)
{
	LED_Init();

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

这样用查表法整理出来的的代码相对于第一种实现方式,代码行有所减短,编译后,查看.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 ++;
	
}


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++)
		{
			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 ++;
	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 ++;
	if(count == 500)
	{
		sta =1;
		count = 0;
	}
	
}

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

总结

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

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

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分