單片機學習(二)開發板LED燈的控制


開發板上LED燈相關的電路圖

這是P2相關7個引腳的電路圖,在默認情況下它是直接接着VCC的,即默認為高電平。

可以看到,8個LED燈分別是和單片機上P20~P27這8個引腳聯系起來的,即一端是VCC,另一端是單片機上的端口,這樣我們只需要將引腳端電平置為低電平即可將對應的LED點亮。

點燈

這里我們嘗試先點亮從左到右數第一個LED燈,從電路圖上看我們只需要將P20引腳的輸出置為0(即設為低電平)即可,代碼如下:

#include<REG52.H>

sbit led = P2^0;

int main() {
    while (1) {
        led = 0;
    }
}

代碼說明:首先我們需要引入單片機的頭文件REG52.H,里面包含了許多寄存器的地址:

我們可以使用類似sbit led = P2^0;來指定P2寄存器的某一位,例如這里我們指定P2寄存器的第一個bit。然后我們在main函數中將其的值設置為0,即可看到第一個LED燈點亮:

LED閃爍

我們先設置一個延時函數deley()

void deley(u16 x) {
    while (x--) {}
}

即在這里面一直執行x--操作,按照這個型號單片機的執行速度,大概一個基本語句的執行為10μs,則若輸入參數為x,則該函數可以延時\(10μs*x=x*10^{-5}s\),故若我們希望延時0.5s,我們需要填入50000。

代碼(約1s閃爍一次):

#include<REG52.H>

typedef unsigned int u16;
typedef unsigned char u8;

sbit led = P2^0;

void deley(u16 x) {
    while (x--) {}
}

int main() {
    while (1) {
        led = 0;
        deley(50000);
        led = 1;
        deley(50000);
    }
}

運行結果:
在這里插入圖片描述

tips: 推薦一個GIF制作網站:https://www.tutieshi.com/video/,免費制作GIF,為良心網站點贊!

LED流水燈

#include<REG52.H>
#include<INTRINS.H>

typedef unsigned int u16;
typedef unsigned char u8;

#define LED P2

void deley(u16 x) {
    while (x--) {}
}

void defaultDeley() {
    deley(50000);
}

int main() {
    u8 i = 0;
    LED = 0xfe;     // 1111 1110
    defaultDeley();
    while (1) {
        for(i=0;i<7;i++){
            LED = LED << 1;
            LED+=1;
            defaultDeley();
        }
        // 0111 1111
        for(i=0;i<7;i++){
            LED >>= 1;
            LED+=0x80;
            defaultDeley();
        }
        // 1111 1110
    }
}

這里我們直接操作P2寄存器,我們希望一開始第一個燈點亮,因此設置P2的值為0b1111 1110,即0xfe,然后我們可以進行移位操作使0位不斷變化,即:
1111 1110
1111 1101
1111 1011
...
所以循環的代碼應為:

LED <<= 1;
LED += 1;

這樣即可實現向左點燈。

向右點燈也是類似的,循環代碼為:

LED >>= 1;
LED+=0x80;

於是這樣就完全實現了流水燈。

運行效果:
在這里插入圖片描述
(后面還有幾秒鍾,但GIF要超過csdn的限定大小了。。。)

其他效果

燈光二進制計數器

核心代碼:

int main() {
    u8 full = 0xff;
    u8 cnt;
    while (1) {
        cnt = -1;
        while (1) {        
        	// cnt: 0000 0000 -> 0000 0001
        	// LED: 1111 1111 -> 1111 1110
            cnt +=1;
            LED = full - cnt;
            defaultDeley();
            if (cnt == 0xff) {
                break;
            }
        }
    }
}

思路是讓一個變量從0開始計數到0xff,LED對應的bit是0發光1熄滅的,這剛好是和我們的計數變量的二進制位模式相反,因此使用LED = 0xff - cnt進行取反,進而使LED顯示與計數變量的變化對應起來。

運行效果:
在這里插入圖片描述
但是此時的燈光計數器最低位在最左邊,看起來不太舒服,因此希望修改最低位在最右邊

那么我們仍然使用cnt作為計數變量,它將從0x00變化到0xff,然后我們再思考建立cnt到LED的映射關系:
看前面的變化規律:
cnt: 0000 0000 -> 0000 0001 -> 0000 0010
LED: 1111 1111 -> 0111 1111 -> 1011 1111
可以發現,LED的值應為cnt的二進制位模式反轉后的補碼,
LED = 0xff - reverse(cnt);

這樣,我們很容易就可以寫出控制代碼:

int main() {
    u8 full = 0xff;
    u8 cnt = 0;
    while (1) {
        cnt = -1;
        while (1) {
            // cnt: 0000 0000 -> 0000 0001 -> 0000 0010
	        // LED: 1111 1111 -> 0111 1111 -> 1011 1111
	        // LED = full - (reverse(cnt));
            cnt +=1;
            LED = full - (reverse(cnt));;
            defaultDeley();
            if (cnt == 0xff) {
                break;
            }
        }
    }
}

其中二進制位模式的翻轉函數為(輸入一個數,返回它二進制位模式反轉值):

u8 reverse(u8 x) {
    int cnt = 8;
    int res = 0;
    while (cnt--) {
        res <<= 1;
        res += x % 2;
        x >>= 1;
    }
    return res;
}

運行效果:
在這里插入圖片描述

進階版流水燈

效果:
在這里插入圖片描述
思路:原來的最基本的流水燈部分我們已經建立了bit為1時對應的燈亮的映射,即LED = 0xff - val;,然后為了實現這個效果,我們的val變量的變化情況應該如下所示:

0000 0000
0000 0001
0000 0010
...
1000 0000
1000 0001
1000 0010
...
...
1111 1111

這樣我們操作時可以看做是兩個變化的東西,一個是左邊的“1”,代表已經到達終點的1,另一個是右邊不斷左移的“1”,因此這兩個部分可以放在兩個變量中分別存儲,這里設為leftPart和rightPart。

這樣我們設計外層循環中leftPart每次的變化為:leftPart>>=1; leftPart+=0x80;,而內層循環中rightPart變化,為:rightPart<<=1;,且循環次數用變量leftMoveTimes進行存儲,一開始值為7,代表可以左移7次,然后每次外循環后-=1。

核心代碼:

void showAndDeley(u8 res) {
    LED = 0xff - res;
    defaultDeley();
}

int main() {
    u8 rightPart , res, i;
    u8 leftMoveTimes;
    u8 leftPart;
start:
    LED = 0xff;
    
    leftPart = 0;
    leftMoveTimes = 7;
    rightPart = 0;
    res = leftPart | rightPart ;
    showAndDeley(res);
    while (1) {								// 外循環
        rightPart = 1;
        res = leftPart | rightPart ;
        showAndDeley(res);
        for(i = 0; i< leftMoveTimes; i++){ 	// 內循環
            rightPart <<= 1;
            res = leftPart | rightPart ;
            showAndDeley(res);
        }
        if (leftMoveTimes ==0) {
            goto start;						// 重新開始運行
        }
        leftPart>>=1;
        leftPart+=0x80;
        leftMoveTimes--;
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM