一個(gè)IO控制很多個(gè)LED,這個(gè)技能你Get到了嗎
簡(jiǎn)介
多位(幾個(gè)LED就是幾位)通過(guò)引腳級(jí)聯(lián),接一個(gè)LED的DOUT引腳到另一個(gè)LED的DIN引腳,通過(guò)這種級(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)
- 智能反接保護(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)過(guò)波形整形再輸出,保證線路波形畸變不會(huì)累加;
- 內(nèi)置上電復(fù)位和掉電復(fù)位電路;
- 每個(gè)像素點(diǎn)的三基色顏色可實(shí)現(xiàn)256級(jí)亮度顯示,完成16777216種顏色的全真色彩顯示,掃描頻率不低于400Hz;
- 串行級(jí)聯(lián)接口,能通過(guò)一根信號(hào)線完成數(shù)據(jù)的接收與解碼;
- 任意兩點(diǎn)傳輸距離在不超過(guò)5米時(shí),無(wú)需增加額外電路;
- 當(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端接受從控制器傳輸過(guò)來(lái)的數(shù)據(jù),首先送過(guò)來(lái)的24bit數(shù)據(jù)被第一個(gè)像素點(diǎn)提取后,送到像素點(diǎn)內(nèi)部的數(shù)據(jù)鎖存器,剩余的數(shù)據(jù)經(jīng)過(guò)內(nèi)部整形處理電路整形放大后通過(guò)DOUT端口開(kāi)始轉(zhuǎn)發(fā)輸出給下一個(gè)級(jí)聯(lián)的像素點(diǎn),每經(jīng)過(guò)一個(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ù)位也無(wú)影響),只需要在修改顏色值的時(shí)候,發(fā)送一遍即可。
0和1的區(qū)分
Treset:復(fù)位時(shí)間
由上圖可知,我們要發(fā)送 '0' ,需要將GPIO引腳置高并持續(xù)0.4 us(400 ns),然后GPIO置低并持續(xù)0.85 us(850 ns),此過(guò)程即完成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),此過(guò)程即完成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ù)一般通過(guò)執(zhí)行一些無(wú)意義的循環(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í)間就沒(méi)了,所以為了精確控制,我們這里延時(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è)趍ain函數(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)過(guò)示波器測(cè)量,不斷調(diào)整,上面定義的 Wait250ns 宏定義的耗時(shí)如下圖所示。
經(jīng)過(guò)示波器測(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ù)腞GB數(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<ws_count*3;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ù)是通過(guò)單片機(jī)發(fā)送,D2,D3,D4通過(guò)像素內(nèi)重塑放大傳輸。
main函數(shù)實(shí)現(xiàn)
main函數(shù)中,每隔1S,點(diǎn)亮一個(gè)LED,當(dāng)8個(gè)LED都點(diǎn)亮一次之后,所有LED點(diǎn)亮一次,然后再開(kāi)啟下一次循環(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 ...\r\n");
- 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ù)自己的喜歡,隨意替換顏色。
本文轉(zhuǎn)載自微信公眾號(hào)「 嵌入式從0到1」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 嵌入式從0到1公眾號(hào)。