前言
開發者在入門點亮第一盞燈后,再深入一點就會用到流水燈。而如何實現流水燈又有好幾種方式,我查詢了一網上大神們的作品,無非有三種方式即查詢法、位移法。這篇文章,我就如何實現流水燈開展討論。
硬件
我以新定義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 | 代碼更整法,占用空間小,后期實現功能簡單方便 | 閱讀理解代碼,需要位移的基礎知識 |
審核編輯 黃宇
-
單片機
+關注
關注
6050文章
44687瀏覽量
641171 -
編程
+關注
關注
88文章
3649瀏覽量
94344 -
定時器
+關注
關注
23文章
3259瀏覽量
115884 -
流水燈
+關注
關注
21文章
433瀏覽量
59957
發布評論請先 登錄
相關推薦
基于AT89C51的單片機簡易流水燈設計(C語言)

單片機led燈閃爍實驗總結_51單片機實現LED流水燈

單片機51的流水燈

評論