實現(xiàn)目標
- 每隔一秒,點亮模塊上的一個 LED點亮模塊上的所有 LED
所需工具及環(huán)境
本文源碼
后臺回復(fù)關(guān)鍵字“WS2812B”,獲取WS2812B模塊資料、數(shù)據(jù)手冊及工程源碼。
簡介
多位(幾個 LED 就是幾位)通過引腳級聯(lián),接一個 LED 的 DOUT 引腳到另一個 LED 的 DIN 引腳,通過這種級聯(lián)的方式,只需要使用一個 IO 口(單片機引腳)就能控制盡可能多的 LED。
每個 LED 都集成了一顆驅(qū)動芯片在里面,讓我們的 LED 變得智能和尋址,每一個內(nèi)部都有恒流驅(qū)動,所以 LED 顏色非常一致,即使電壓有小幅抖動,電壓變化也是一樣的。
不需要外部電阻——限流電阻,使 LED 燈的布局設(shè)計變得簡單。
單線通信,能夠最大限度的減少單片機 IO 口的壓力,另外這款 RGB 燈使用了 WS2812B 驅(qū)動芯片,讓外圍電路只需要一顆電容就能夠滿足電路需求,從而最大可能的讓電路變得簡單優(yōu)美。
特點
- 智能反接保護,電源反接不會損壞 IC;IC 控制電路與 LED 點光源公用一個電源;控制電路與 RGB 芯片集成在一個 5050 封裝的元器件中,構(gòu)成一個外控像素點;內(nèi)置信號整形電路,任何一個像素點收到信號后經(jīng)過波形整形再輸出,保證線路波形畸變不會累加;內(nèi)置上電復(fù)位和掉電復(fù)位電路;每個像素點的三基色顏色可實現(xiàn) 256 級亮度顯示,完成 16777216 種顏色的全真色彩顯示,掃描頻率不低于 400Hz;串行級聯(lián)接口,能通過一根信號線完成數(shù)據(jù)的接收與解碼;任意兩點傳輸距離在不超過 5 米時,無需增加額外電路;當刷新速率 30 幀 / 秒時,級聯(lián)數(shù)不小于 1024 點;數(shù)據(jù)發(fā)送速度可達 800Kbps;光的顏色高度一致,性價比高。
注意: 800Kbps,相當于 1.25us 傳輸一比特數(shù)據(jù)。
引腳圖
引腳功能描述:
NO. | Symbol | 功能描述 |
---|---|---|
1 | VDD | LED 的供電電源,Vdd 范圍 +3.5~+5.3 V |
2 | DOUT | 控制信號數(shù)據(jù)輸出引腳 |
3 | VSS | 地 |
4 | DIN | 控制信號數(shù)據(jù)輸入引腳 |
典型電路
串聯(lián)方法
原理圖
除了燈珠以外只需要額外增加一個 0.1uF 的電容即可。
硬件連接
STM32F103RET6 核心板 | WS2812B 模塊 |
---|---|
PA6 | DIN |
VCC | +5V |
GND | GND |
驅(qū)動原理
數(shù)據(jù)協(xié)議采用單線歸零碼的通訊方式,像素點在上電復(fù)位以后,DIN 端接受從控制器傳輸過來的數(shù)據(jù),首先送過來的 24bit 數(shù)據(jù)被第一個像素點提取后,送到像素點內(nèi)部的數(shù)據(jù)鎖存器,剩余的數(shù)據(jù)經(jīng)過內(nèi)部整形處理電路整形放大后通過 DOUT 端口開始轉(zhuǎn)發(fā)輸出給下一個級聯(lián)的像素點,每經(jīng)過一個像素點的傳輸,信號減少 24bit。
像素點采用自動整形轉(zhuǎn)發(fā)技術(shù),使得像素點的級聯(lián)個數(shù)不受信號傳送的限制,僅僅受限信號傳輸速度要求。
因為數(shù)據(jù)被內(nèi)部鎖存,所以只要不改變顏色值(模塊持續(xù)供電),顏色是不會發(fā)生改變的,設(shè)置顏色的脈沖也不需要持續(xù)提供(單片機發(fā)生復(fù)位也無影響),只需要在修改顏色值的時候,發(fā)送一遍即可。
0 和 1 的區(qū)分
Treset:復(fù)位時間
由上圖可知,我們要發(fā)送 '0' ,需要將 GPIO 引腳置高并持續(xù) 0.4 us(400 ns),然后 GPIO 置低并持續(xù) 0.85 us(850 ns),此過程即完成0 code的發(fā)送,具體代碼實現(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ā)送,具體代碼實現(xiàn)如下:
void send_1(void)
{
IN_H;
Wait850ns;
IN_L;
Wait400ns;
}
所以本程序的難點即是求取 400 ns 和 850 ns 相對精確的延時時間。
延時函數(shù)的實現(xiàn)
單片機里的延時函數(shù)一般通過執(zhí)行一些無意義的循環(huán)進行延時,比如定義如下函數(shù):
void delay(unsigned char i)
{
while(--i);
}
我們這里需要的延時周期很小,才 1.25us,因為函數(shù)的調(diào)用,需要入棧和出棧,所以如果使用上面的延時函數(shù)的方式的話,那么一進一出就接近幾百 ns 的時間就沒了,所以為了精確控制,我們這里延時函數(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
我們在 main 函數(shù)中使用如下方式測試這個延時函數(shù):
while(1)
{
IN_H;
//Wait250ns;
Wait850ns;
IN_L;
//Wait250ns;
Wait850ns;
}
然后用示波器觀察與模塊 DIN 引腳相連的 GPIO 輸出的脈沖信號,查看其高電平是否與咱們預(yù)定義的一致,如果不一致,增加或減少空指令進行調(diào)整。
注意:一個 __NOP(); 空指令的耗時大約:1000/72 ≈ 14 ns 的時間,自己可以在上面定義的基礎(chǔ)上,根據(jù)需要隨意增加或者減少 __NOP(); 空指令的個數(shù)。
“
注意空指令前面是兩個“_”。
經(jīng)過示波器測量,不斷調(diào)整,上面定義的 Wait250ns 宏定義的耗時如下圖所示。
經(jīng)過示波器測試,上面的 Wait400ns 耗時為 388 ns , Wait850ns 耗時為 860 ns,滿足上面"0"和"1"的時間區(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ù),每一個燈珠的顏色占用三個字節(jié),因為數(shù)據(jù)傳輸順序按 GRB 的順序傳輸,所以賦值的時候注意先后順序,上面函數(shù)是設(shè)置某一個燈珠的顏色值。
ws_data[] 數(shù)組中顏色值設(shè)置完畢之后,就要把這個數(shù)組的數(shù)據(jù)發(fā)送到模塊中,具體的實現(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();
}
// 延時一段時間
ws2812_reset();
}
ws_data[] 數(shù)組中的每一個字節(jié)按位發(fā)送,因為高位在前,所以先發(fā)送每個字節(jié)的高位,獲取最高位的值的方法為:ws_data[ws_ri]&0x80 。
數(shù)據(jù)傳輸方法
N 位的模塊,一次就要發(fā)送 N * 3 字節(jié)的數(shù)據(jù)。
注意: 上圖中 D1 的數(shù)據(jù)是通過單片機發(fā)送,D2,D3,D4 通過像素內(nèi)重塑放大傳輸。
main 函數(shù)實現(xiàn)
main 函數(shù)中,每隔 1S,點亮一個 LED,當 8 個 LED 都點亮一次之后,所有 LED 點亮一次,然后再開啟下一次循環(huán)。
main 函數(shù)的具體實現(xiàn)如下所示:
int main(void)
{
int times = 0;
// 初始化
// 延時函數(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)注公眾號,第一時間獲得技術(shù)干貨,掃描加我微信,拉你進入技術(shù)交流群。