STM32F103C8T6使用SPI介面驅動WS2812b燈條

曾經夢想少年發表於2021-09-19

之前一篇文章寫了使用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就可改變燈條的顏色。

相關文章