开发者在入门点亮第一盏灯后,再深入一点就会用到流水灯。而如何实现流水灯又有好几种方式,我查询了一网上大神们的作品,无非有三种方式即查询法、位移法。这篇文章,我就如何实现流水灯开展讨论。
我以新定义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
总结一下这几种编程方式点用的空间:
序号 | data | xdata | const | code | 优点 | 缺点 |
---|---|---|---|---|---|---|
定义端口法 | 21.0 | 28 | 0 | 418 | 代码可读性高,直观 | 代码行数多,如何需要修改比较麻烦 |
数组查表法 | 40.0 | 0 | 0 | 454 | 代码较第一种整洁,容易修改 | 占用内存大 |
查表法优化 | 31.0 | 0 | 0 | 446 | 相比上一种减少了内存的占用 | 占用内存大 |
位移法之一 | 23.0 | 0 | 0 | 325 | 相比上面的数组查询占用内存小 | 实现代码复杂 |
非阻塞式位移 | 15.0 | 0 | 0 | 364 | 相比上面的,实现非阻塞式位移 | 代码理解需要一定基础 |
非阻塞进式位移二 | 15.0 | 0 | 0 | 359 | 代码更整法,占用空间小,后期实现功能简单方便 | 阅读理解代码,需要位移的基础知识 |
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !