本章目标是把屏幕点亮。
老规矩,我们先来看看WS2812的原理和时序。
WS2812最牛的地方,就是实现单根信号线即可实现所有灯的控制。简单点理解,就是每个灯都截取头部数据,然后把其他数据往后传递。
好啦,我们把项目搭建起来。用的最新版STM32CubeIDE。
1、项目配置
前文说了,我们灯珠比较多,分了三组,所以SPI1/2/3全部用上,DMA也要启用。
2、要使用DMA发送数据,所以要两组缓存,一组为发送数据用,另一组为绘图用。
#define WS2812_NUM_LEDS1 768
#define WS2812_NUM_LEDS2 768
#define WS2812_NUM_LEDS3 512
#define WS2812_RESET_PULSE 60
#define WS2812_BUFFER_SIZE1 (WS2812_NUM_LEDS1 * 24 + WS2812_RESET_PULSE)
#define WS2812_BUFFER_SIZE2 (WS2812_NUM_LEDS2 * 24 + WS2812_RESET_PULSE)
#define WS2812_BUFFER_SIZE3 (WS2812_NUM_LEDS3 * 24 + WS2812_RESET_PULSE)
uint8_t ws2812_buffer1[WS2812_BUFFER_SIZE1];
uint8_t ws2812_buffer2[WS2812_BUFFER_SIZE2];
uint8_t ws2812_buffer3[WS2812_BUFFER_SIZE3];
RGBColor_TypeDef show_buffer1[WS2812_NUM_LEDS1];
RGBColor_TypeDef show_buffer2[WS2812_NUM_LEDS2];
RGBColor_TypeDef show_buffer3[WS2812_NUM_LEDS3];
定义一个宏,用于填充发送数据的BUFFER
#define WS2812_FILL_BUFFER(COLOR) \
for (uint8_t mask = 0x80; mask; mask >>= 1) { \
if ((COLOR) & mask) { \
*ptr++ = 0xfc; \
} else { \
*ptr++ = 0x80; \
} \
}
在show方法中,填充BUFF并通过DMA发送
void ws2812_show() {
// 判断上次DMA有没有传输完成
while (HAL_DMA_GetState(&hdma_spi1_tx) != HAL_DMA_STATE_READY
|| HAL_DMA_GetState(&hdma_spi2_tx) != HAL_DMA_STATE_READY
|| HAL_DMA_GetState(&hdma_spi3_tx) != HAL_DMA_STATE_READY)
;
uint8_t *ptr;
uint16_t i;
for (i = 0; i < WS2812_NUM_LEDS1; i++) {
ptr = &ws2812_buffer1[i * 24];
WS2812_FILL_BUFFER(show_buffer1[i].G);
WS2812_FILL_BUFFER(show_buffer1[i].R);
WS2812_FILL_BUFFER(show_buffer1[i].B);
}
for (i = 0; i < WS2812_NUM_LEDS2; i++) {
ptr = &ws2812_buffer2[i * 24];
WS2812_FILL_BUFFER(show_buffer2[i].G);
WS2812_FILL_BUFFER(show_buffer2[i].R);
WS2812_FILL_BUFFER(show_buffer2[i].B);
}
for (i = 0; i < WS2812_NUM_LEDS3; i++) {
ptr = &ws2812_buffer3