之前一篇文章寫了使用IO控制WS2812b操作原理,但是由於IO的輸出比較慢,所以現在改用了硬體SPI控制WS2812b燈條
把SPI的mosi線接到ws2812b的資料線,SPI的速率可達十幾Mbit/s,如此高的傳輸速率,我們可以使用一個(uint8_t)型別的資料代表一個碼1或者碼0;
也就是說,本來控制一個燈珠的資料由3個Byte(24位)變成了24Byte,每個bit轉換成一個Byte;
具體看時鐘如何配置的。
SPI配置:
在上一篇文章可以看到控制一個碼的週期在1.25us±300ns之間,現在把時鐘配成9Mbit/s,這個的話每個碼的週期大概就在889ns,和LED的週期略有誤差;
從規格書上可以看到碼1和碼0的佔空比是大致分配一下1個byte的8個bit;
我這裡分配的是
碼1:0xF8;
碼0:0xE0;
可以根據自己的具體情況取修改碼0和碼1的分配,下面是驅動ws2812b的程式碼:
ws2812b.h
#ifndef __WS2812B_H__
#define __WS2812B_H__
#include <stdint.h>
// 編碼 0 : 11100000
#define CODE_0 0xE0
// 編碼 1 : 11111000
#define CODE_1 0xF8
/*ws2812b燈珠數量*/
#define WS2812B_AMOUNT 8
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
} tWs2812bCache_TypeDef;
extern tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT];
void WS2812b_Set(uint16_t Ws2b812b_NUM, uint8_t r,uint8_t g,uint8_t b);
void WS2812B_Task(void);
#endif
ws2812b.c
#include "ws2812b.h"
#include "spi.h"
//燈條視訊記憶體SPI資料快取
uint8_t gWs2812bDat_SPI[WS2812B_AMOUNT * 24] = {0};
//燈條視訊記憶體
tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT] = {
//R G B
0X0F, 0X0F, 0X0F, //0
0X0F, 0X0F, 0X0F, //1
0X0F, 0X0F, 0X0F, //2
0X0F, 0X0F, 0X0F, //3
0X0F, 0X0F, 0X0F, //4
0X0F, 0X0F, 0X0F, //5
0X0F, 0X0F, 0X0F, //6
0X0F, 0X0F, 0X0F, //7
};
void WS2812b_Set(uint16_t Ws2b812b_NUM, uint8_t r,uint8_t g,uint8_t b)
{
uint8_t *pR = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24 + 8];
uint8_t *pG = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24];
uint8_t *pB = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24 + 16];
for(uint8_t i = 0; i < 8; i++) {
if(g & 0x80) {
*pG = CODE_1;
}
else {
*pG = CODE_0;
}
if(r & 0x80) {
*pR = CODE_1;
}
else {
*pR = CODE_0;
}
if(b & 0x80) {
*pB = CODE_1;
}
else {
*pB = CODE_0;
}
r <<= 1;
g <<= 1;
b <<= 1;
pR++;
pG++;
pB++;
}
}
void WS2812B_Task(void)
{
uint8_t dat = 0;
//將gWs2812bDat資料解析成SPI資料
for(uint8_t iLED = 0; iLED < WS2812B_AMOUNT; iLED++)
{
WS2812b_Set(iLED, gWs2812bDat[iLED].R, gWs2812bDat[iLED].G, gWs2812bDat[iLED].B);
}
//匯流排輸出資料
HAL_SPI_Transmit(&hspi1, gWs2812bDat_SPI, sizeof(gWs2812bDat_SPI), 100);
//使匯流排輸出低電平
HAL_SPI_Transmit(&hspi1, &dat, 1, 100);
//幀訊號:一個大於50us的低電平
HAL_Delay(1);
}
直接在main()裡面呼叫WS2812B_Task()就可以驅動燈條,另外直接修改陣列gWs2812bDat就可改變燈條的顏色。