加入星計(jì)劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴(kuò)散
  • 作品版權(quán)保護(hù)
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長(zhǎng)期合作伙伴
立即加入
  • 正文
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

一個(gè)IO控制很多個(gè)LED,這個(gè)技能你get到了嗎

2021/01/21
629
  • 1評(píng)論
閱讀需 10 分鐘
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

實(shí)現(xiàn)目標(biāo)

  • 每隔一秒,點(diǎn)亮模塊上的一個(gè) LED點(diǎn)亮模塊上的所有 LED

 

所需工具及環(huán)境

 

本文源碼

后臺(tái)回復(fù)關(guān)鍵字“WS2812B”,獲取WS2812B模塊資料、數(shù)據(jù)手冊(cè)及工程源碼。

 

簡(jiǎn)介

多位(幾個(gè) LED 就是幾位)通過引腳級(jí)聯(lián),接一個(gè) LED 的 DOUT 引腳到另一個(gè) LED 的 DIN 引腳,通過這種級(jí)聯(lián)的方式,只需要使用一個(gè) IO 口(單片機(jī)引腳)就能控制盡可能多的 LED。

每個(gè) LED 都集成了一顆驅(qū)動(dòng)芯片在里面,讓我們的 LED 變得智能和尋址,每一個(gè)內(nèi)部都有恒流驅(qū)動(dòng),所以 LED 顏色非常一致,即使電壓有小幅抖動(dòng),電壓變化也是一樣的。

不需要外部電阻——限流電阻,使 LED 燈的布局設(shè)計(jì)變得簡(jiǎn)單。

單線通信,能夠最大限度的減少單片機(jī) IO 口的壓力,另外這款 RGB 燈使用了 WS2812B 驅(qū)動(dòng)芯片,讓外圍電路只需要一顆電容就能夠滿足電路需求,從而最大可能的讓電路變得簡(jiǎn)單優(yōu)美。

特點(diǎn)

  1. 智能反接保護(hù),電源反接不會(huì)損壞 IC;IC 控制電路與 LED 點(diǎn)光源公用一個(gè)電源;控制電路與 RGB 芯片集成在一個(gè) 5050 封裝元器件中,構(gòu)成一個(gè)外控像素點(diǎn);內(nèi)置信號(hào)整形電路,任何一個(gè)像素點(diǎn)收到信號(hào)后經(jīng)過波形整形再輸出,保證線路波形畸變不會(huì)累加;內(nèi)置上電復(fù)位和掉電復(fù)位電路;每個(gè)像素點(diǎn)的三基色顏色可實(shí)現(xiàn) 256 級(jí)亮度顯示,完成 16777216 種顏色的全真色彩顯示,掃描頻率不低于 400Hz;串行級(jí)聯(lián)接口,能通過一根信號(hào)線完成數(shù)據(jù)的接收與解碼;任意兩點(diǎn)傳輸距離在不超過 5 米時(shí),無需增加額外電路;當(dāng)刷新速率 30 幀 / 秒時(shí),級(jí)聯(lián)數(shù)不小于 1024 點(diǎn);數(shù)據(jù)發(fā)送速度可達(dá) 800Kbps;光的顏色高度一致,性價(jià)比高。

注意: 800Kbps,相當(dāng)于 1.25us 傳輸一比特?cái)?shù)據(jù)。

引腳圖

引腳功能描述:

NO. Symbol 功能描述
1 VDD LED 的供電電源,Vdd 范圍 +3.5~+5.3 V
2 DOUT 控制信號(hào)數(shù)據(jù)輸出引腳
3 VSS
4 DIN 控制信號(hào)數(shù)據(jù)輸入引腳

 

典型電路

串聯(lián)方法

 

原理圖

除了燈珠以外只需要額外增加一個(gè) 0.1uF 的電容即可。

硬件連接

STM32F103RET6 核心板 WS2812B 模塊
PA6 DIN
VCC +5V
GND GND

 

驅(qū)動(dòng)原理

數(shù)據(jù)協(xié)議采用單線歸零碼的通訊方式,像素點(diǎn)在上電復(fù)位以后,DIN 端接受從控制器傳輸過來的數(shù)據(jù),首先送過來的 24bit 數(shù)據(jù)被第一個(gè)像素點(diǎn)提取后,送到像素點(diǎn)內(nèi)部的數(shù)據(jù)鎖存器,剩余的數(shù)據(jù)經(jīng)過內(nèi)部整形處理電路整形放大后通過 DOUT 端口開始轉(zhuǎn)發(fā)輸出給下一個(gè)級(jí)聯(lián)的像素點(diǎn),每經(jīng)過一個(gè)像素點(diǎn)的傳輸,信號(hào)減少 24bit。

像素點(diǎn)采用自動(dòng)整形轉(zhuǎn)發(fā)技術(shù),使得像素點(diǎn)的級(jí)聯(lián)個(gè)數(shù)不受信號(hào)傳送的限制,僅僅受限信號(hào)傳輸速度要求。

因?yàn)閿?shù)據(jù)被內(nèi)部鎖存,所以只要不改變顏色值(模塊持續(xù)供電),顏色是不會(huì)發(fā)生改變的,設(shè)置顏色的脈沖也不需要持續(xù)提供(單片機(jī)發(fā)生復(fù)位也無影響),只需要在修改顏色值的時(shí)候,發(fā)送一遍即可。

0 和 1 的區(qū)分

Treset:復(fù)位時(shí)間

由上圖可知,我們要發(fā)送 '0' ,需要將 GPIO 引腳置高并持續(xù) 0.4 us(400 ns),然后 GPIO 置低并持續(xù) 0.85 us(850 ns),此過程即完成0 code的發(fā)送,具體代碼實(shí)現(xiàn)如下:

void send_0(void)
{
    IN_H;
    Wait400ns;
    IN_L;
    Wait850ns;
}

我們要發(fā)送 '1' ,需要將 GPIO 引腳置高并持續(xù) 0.85 us(850 ns),然后 GPIO 置低并持續(xù) 0.4 us(400 ns),此過程即完成1 code的發(fā)送,具體代碼實(shí)現(xiàn)如下:

void send_1(void)
{
    IN_H;
    Wait850ns;
    IN_L;
    Wait400ns;
}

所以本程序的難點(diǎn)即是求取 400 ns 和 850 ns 相對(duì)精確的延時(shí)時(shí)間。

延時(shí)函數(shù)的實(shí)現(xiàn)

單片機(jī)里的延時(shí)函數(shù)一般通過執(zhí)行一些無意義的循環(huán)進(jìn)行延時(shí),比如定義如下函數(shù):

void delay(unsigned char i)
{
    while(--i);
}

我們這里需要的延時(shí)周期很小,才 1.25us,因?yàn)楹瘮?shù)的調(diào)用,需要入棧和出棧,所以如果使用上面的延時(shí)函數(shù)的方式的話,那么一進(jìn)一出就接近幾百 ns 的時(shí)間就沒了,所以為了精確控制,我們這里延時(shí)函數(shù)的定義如下:

#define    Wait10nop        {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
#define    Wait250ns        {__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();}
#define    Wait400ns        {Wait250ns;Wait10nop;}    //388
#define    Wait850ns        {Wait250ns;Wait10nop;Wait10nop;Wait10nop;Wait10nop;__NOP();__NOP();__NOP();__NOP();__NOP();}    //860

我們?cè)?main 函數(shù)中使用如下方式測(cè)試這個(gè)延時(shí)函數(shù):

while(1)
{
    IN_H;
    //Wait250ns;
    Wait850ns;
    IN_L;
    //Wait250ns;
    Wait850ns;
}

然后用示波器觀察與模塊 DIN 引腳相連的 GPIO 輸出的脈沖信號(hào),查看其高電平是否與咱們預(yù)定義的一致,如果不一致,增加或減少空指令進(jìn)行調(diào)整。

注意:一個(gè) __NOP(); 空指令的耗時(shí)大約:1000/72 ≈ 14 ns 的時(shí)間,自己可以在上面定義的基礎(chǔ)上,根據(jù)需要隨意增加或者減少 __NOP(); 空指令的個(gè)數(shù)。

注意空指令前面是兩個(gè)“_”。

經(jīng)過示波器測(cè)量,不斷調(diào)整,上面定義的 Wait250ns 宏定義的耗時(shí)如下圖所示。

經(jīng)過示波器測(cè)試,上面的 Wait400ns 耗時(shí)為 388 ns , Wait850ns 耗時(shí)為 860 ns,滿足上面"0"和"1"的時(shí)間區(qū)間范圍。

24 bit 數(shù)據(jù)的組成

注意: 數(shù)據(jù)傳輸順序按 GRB 順序傳輸,并且高位在前。

void ws2812_rgb(u8 ws_num,u8 ws_r,u8 ws_g,u8 ws_b) 
{
    ws_data[(ws_num-1)*3]=ws_g;
    ws_data[(ws_num-1)*3+1]=ws_r;
    ws_data[(ws_num-1)*3+2]=ws_b;
}

ws_data[] 數(shù)組用于記錄待傳輸?shù)?RGB 數(shù)據(jù),每一個(gè)燈珠的顏色占用三個(gè)字節(jié),因?yàn)閿?shù)據(jù)傳輸順序按 GRB 的順序傳輸,所以賦值的時(shí)候注意先后順序,上面函數(shù)是設(shè)置某一個(gè)燈珠的顏色值。

ws_data[] 數(shù)組中顏色值設(shè)置完畢之后,就要把這個(gè)數(shù)組的數(shù)據(jù)發(fā)送到模塊中,具體的實(shí)現(xiàn)函數(shù)如下:

void ws2812_refresh(u8 ws_count)
{
    u8 ws_ri=0;
    
    for(;ws_ri    {
        if((ws_data[ws_ri]&0x80)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x40)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x20)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x10)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x08)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x04)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x02)==0) send_0(); else send_1();
        if((ws_data[ws_ri]&0x01)==0) send_0(); else send_1();
    }
    
    // 延時(shí)一段時(shí)間
    ws2812_reset();
}

ws_data[] 數(shù)組中的每一個(gè)字節(jié)按位發(fā)送,因?yàn)楦呶辉谇?,所以先發(fā)送每個(gè)字節(jié)的高位,獲取最高位的值的方法為:ws_data[ws_ri]&0x80 。

數(shù)據(jù)傳輸方法

N 位的模塊,一次就要發(fā)送 N * 3 字節(jié)的數(shù)據(jù)。

注意: 上圖中 D1 的數(shù)據(jù)是通過單片機(jī)發(fā)送,D2,D3,D4 通過像素內(nèi)重塑放大傳輸。

main 函數(shù)實(shí)現(xiàn)

main 函數(shù)中,每隔 1S,點(diǎn)亮一個(gè) LED,當(dāng) 8 個(gè) LED 都點(diǎn)亮一次之后,所有 LED 點(diǎn)亮一次,然后再開啟下一次循環(huán)。

main 函數(shù)的具體實(shí)現(xiàn)如下所示:

int main(void)  
{ 
    int times = 0; 
    
    // 初始化
    // 延時(shí)函數(shù)初始化   
    delay_init();
    
    uart_init(115200);    // 串口 1:Debug,初始化為 115200 

    ws2812_init();
    
    printf("System Init OK ...rn");
 
    while(1) 
    {  
        times++; 

        if(times > 8)
            times = 0;
        
        switch(times)
        {
            case 0:
                ws2812_rgb(1, WS_RED);
                ws2812_rgb(2, WS_GREEN);
                ws2812_rgb(3, WS_BLUE);
                ws2812_rgb(4, WS_WHITE);
                ws2812_rgb(5, WS_PURPLE);
                ws2812_rgb(6, WS_YELLOW);
                ws2812_rgb(7, WS_BROWN);
                ws2812_rgb(8, WS_BLUE);
                ws2812_refresh(8);
                break;
            case 1:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(1, WS_RED);
                ws2812_refresh(8);
                break;
            case 2:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(2, WS_GREEN);
                ws2812_refresh(8);
                break;
            case 3:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(3, WS_BLUE);
                ws2812_refresh(8);
                break;
            case 4:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(4, WS_WHITE);
                ws2812_refresh(8);
                break;
            case 5:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(5, WS_PURPLE);
                ws2812_refresh(8);
                break;
            case 6:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(6, WS_YELLOW);
                ws2812_refresh(8);
                break;
            case 7:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(7, WS_BROWN);
                ws2812_refresh(8);
                break;
            case 8:
                memset(ws_data,0,WS_ARRAY_SIZE*sizeof(u8)); 
                ws2812_rgb(8, WS_BLUE);
                ws2812_refresh(8);
                break;
        }
        
        delay_ms(1000);        
    }  
}

 

顏色 RGB 值查詢

顏色的 RGB 值和名稱可以參考下面鏈接:

https://code.ziqiangxuetang.com/try/color.py

程序中顏色預(yù)定義如下:

#define WS_DARK  0,0,0
#define WS_WHITE  255,255,255
#define WS_RED   255,0,0
#define WS_GREEN  0,255,0
#define WS_BLUE  0,0,255
#define WS_YELLOW  255,255,0
#define WS_PURPLE   255,0,255
#define WS_CYAN  0,255,255
#define WS_BROWN    165,42,42

大家可以根據(jù)自己的喜歡,隨意替換顏色。

結(jié)果展示

歡迎關(guān)注

關(guān)注公眾號(hào),第一時(shí)間獲得技術(shù)干貨,掃描加我微信,拉你進(jìn)入技術(shù)交流群。

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

公眾號(hào)『嵌入式從0到1』,號(hào)主:程序員小哈,是一個(gè)軟硬件全棧開發(fā)工程師(12年工作經(jīng)驗(yàn)的老司機(jī)),電子發(fā)燒友論壇鴻蒙版塊版主,公眾號(hào)內(nèi)容專注于嵌入式學(xué)習(xí)。堅(jiān)持原創(chuàng),寫有圖、有視頻的保姆級(jí)教程文章,篇篇有干貨。做一個(gè)講清楚,說明白,大家學(xué)得會(huì)的交流平臺(tái)。