STM32硬件SPI+DMA驱动WS2812灯珠,基于HAL库
STM32硬件SPI+DMA驱动WS2812灯珠,基于HAL库
本文将详细介绍如何使用STM32的硬件SPI和DMA驱动WS2812灯珠。通过本文,你将学习到硬件连接、Cubemx配置以及驱动程序的编写方法,并获得完整的代码示例。
一、工程链接
STM32硬件SPI配合DMA驱动WS2812流水灯,基于HAL库资源-CSDN文库
二、简单介绍
WS2812是一种常见的LED灯珠,具有高亮度和丰富的色彩表现力。本文将介绍如何使用STM32的硬件SPI和DMA来驱动WS2812灯珠,实现流水灯效果。
三、模块与接线
使用的WS2812灯环如下图所示:
留出四个接口:DOUT、DIN、VCC、GND。实际使用中只需要连接DIN、VCC和GND。
- 推荐使用5V供电,即VCC连接至单片机的5V引脚
- DIN作为数据接收端,需要连接至单片机SPI的MOSI引脚,这里选择开启SPI1,因此连接到PA7
- GND连接至单片机的GND引脚
四、Cubemx配置
时钟配置为48MHz:
设置SPI:
开启DMA。
五、驱动编写
数据格式说明
在数据包与数据包之间需要大于280us的延时,一个数据包有好几个数据帧,取决于想点亮几个灯珠。
数据帧格式如上,G R B每种颜色用8bit,24位表示一个颜色,分给一个灯珠。当表示0时,需要高电平持续200300多ns,然后是580ns1us的低电平;当表示1时,需要高电平持续580ns1us,然后是580ns1us的低电平。
设置SPI为6MHz,一个时钟周期大概是160ns(1/6MHz)。
那么表示0,就需要大约2个时钟的高电平(1602=320ns)紧接着6个时钟的低电平(6160=960ns),就是由SPI发出0xC0。
表示1,就需要大约4个时钟的高电平(4160=640ns)紧接着4个时钟的低电平(4160=640ns),就是由SPI发出0xF0。
定义结构体和存放信息的数组和宏定义:
#define WS2812_0 0xC0
#define WS2812_1 0xF0
#define WS2812_RST 0x00
#define LED_NUMS 16
#define RGB_BIT 24
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
} LEDType;
volatile uint8_t RGB_BIT_Buffer[RGB_BIT];
volatile uint8_t buffer[RGB_BIT * LED_NUMS];
volatile LEDType LED[LED_NUMS];
这里的RGB_BIT_Buffer是一个含有24个字节的数组,用来存放一个LED灯的色彩信息;buffer用来存放模块的16个灯珠的所有色彩信息。
创建RGB_BIT_Buffer
这个函数非常关键,用于将色彩转化为24位的数据:
static void WS2812_CreatData(uint8_t R, uint8_t G, uint8_t B)
{
uint8_t temp[RGB_BIT] = {0};
for (uint8_t i = 0; i < 8; i++)
{
temp[7 - i] = (G & 0x01) ? WS2812_1 : WS2812_0;
G = G >> 1;
}
for (uint8_t i = 0; i < 8; i++)
{
temp[15 - i] = (R & 0x01) ? WS2812_1 : WS2812_0;
R = R >> 1;
}
for (uint8_t i = 0; i < 8; i++)
{
temp[23 - i] = (B & 0x01) ? WS2812_1 : WS2812_0;
B = B >> 1;
}
memcpy(RGB_BIT_Buffer, temp, RGB_BIT);
}
处理buffer
将16个灯珠的色彩进行处理,变成一个大的数组,存入buffer中:
void WS2812_MakeBuffer()
{
for (uint16_t i = 0; i < LED_NUMS; i++)
{
WS2812_CreatData(LED[i].R, LED[i].G, LED[i].B);
memcpy(buffer + i * RGB_BIT, RGB_BIT_Buffer, RGB_BIT);
}
}
关灯函数
void WS2812_TurnOff()
{
for (uint16_t i = 0; i < LED_NUMS * 24; i++)
{
buffer[i] = WS2812_0;
}
}
点灯函数
这个函数用来使某个灯珠呈现出设定的色彩:
static void WS2812_Color_Pos(uint32_t color, uint16_t Pos)
{
uint8_t R, G, B;
uint16_t i;
R = (color >> 16) & 0x00FF;
G = (color >> 8) & 0x0000FF;
B = (color) & 0x0000FF;
WS2812_CreatData(R, G, B);
if (Pos < LED_NUMS && Pos >= 0)
{
memcpy(buffer + RGB_BIT * Pos, RGB_BIT_Buffer, RGB_BIT);
}
else
{
WS2812_TurnOff();
}
}
RGB流水灯函数
根据介绍里的描述,ws2812可以显示256256256种颜色,但常用的RGB值其实并不多,我们只需要显示其中一些就能呈现出RGB变幻的效果了:
void WS2812_Show_Wheel()
{
static uint16_t i = 0;
i++;
WS2812_Color_Pos(0xFF0000, (i) % 16);
WS2812_Color_Pos(0XFF7F00, (i + 1) % 16);
WS2812_Color_Pos(0XFFFF00, (i + 2) % 16);
WS2812_Color_Pos(0X7FFF00, (i + 3) % 16);
WS2812_Color_Pos(0X00FF00, (i + 4) % 16);
WS2812_Color_Pos(0X00FF7F, (i + 5) % 16);
WS2812_Color_Pos(0X00FFFF, (i + 6) % 16);
WS2812_Color_Pos(0X007FFF, (i + 7) % 16);
WS2812_Color_Pos(0X0000FF, (i + 8) % 16);
WS2812_Color_Pos(0X7F00FF, (i + 9) % 16);
WS2812_Color_Pos(0XFF00FF, (i + 10) % 16);
WS2812_Color_Pos(0XFF007F, (i + 11) % 16);
WS2812_Color_Pos(0XFF0000, (i + 12) % 16);
WS2812_Color_Pos(0XFF7F00, (i + 13) % 16);
WS2812_Color_Pos(0XFFFF00, (i + 14) % 16);
WS2812_Color_Pos(0X7FFF00, (i + 15) % 16);
WS2812_Update();
}
刷新函数
void WS2812_Update()
{
HAL_SPI_Transmit_DMA(&hspi1, buffer, RGB_BIT * LED_NUMS);
}
主函数
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
WS2812_Show_Wheel();
HAL_Delay(30);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
六、效果展示
(此处应有实际效果展示图片或视频)
七、驱动附录
ws2812.c
#include "ws2812.h"
volatile uint8_t RGB_BIT_Buffer[RGB_BIT];
volatile uint8_t buffer[RGB_BIT * LED_NUMS];
volatile LEDType LED[LED_NUMS];
void WS2812_Update()
{
HAL_SPI_Transmit_DMA(&hspi1, buffer, RGB_BIT * LED_NUMS);
}
static void WS2812_CreatData(uint8_t R, uint8_t G, uint8_t B)
{
uint8_t temp[RGB_BIT] = {0};
for (uint8_t i = 0; i < 8; i++)
{
temp[7 - i] = (G & 0x01) ? WS2812_1 : WS2812_0;
G = G >> 1;
}
for (uint8_t i = 0; i < 8; i++)
{
temp[15 - i] = (R & 0x01) ? WS2812_1 : WS2812_0;
R = R >> 1;
}
for (uint8_t i = 0; i < 8; i++)
{
temp[23 - i] = (B & 0x01) ? WS2812_1 : WS2812_0;
B = B >> 1;
}
memcpy(RGB_BIT_Buffer, temp, RGB_BIT);
}
static void WS2812_MakeBuffer()
{
for (uint16_t i = 0; i < LED_NUMS; i++)
{
WS2812_CreatData(LED[i].R, LED[i].G, LED[i].B);
memcpy(buffer + i * RGB_BIT, RGB_BIT_Buffer, RGB_BIT);
}
}
void WS2812_TurnOff()
{
for (uint16_t i = 0; i < LED_NUMS * 24; i++)
{
buffer[i] = WS2812_0;
}
}
static void WS2812_Color_Pos(uint32_t color, uint16_t Pos)
{
uint8_t R, G, B;
uint16_t i;
R = (color >> 16) & 0x00FF;
G = (color >> 8) & 0x0000FF;
B = (color) & 0x0000FF;
WS2812_CreatData(R, G, B);
if (Pos < LED_NUMS && Pos >= 0)
{
memcpy(buffer + RGB_BIT * Pos, RGB_BIT_Buffer, RGB_BIT);
}
else
{
WS2812_TurnOff();
}
}
void WS2812_Show_Wheel()
{
static uint16_t i = 0;
i++;
WS2812_Color_Pos(0xFF0000, (i) % 16);
WS2812_Color_Pos(0XFF7F00, (i + 1) % 16);
WS2812_Color_Pos(0XFFFF00, (i + 2) % 16);
WS2812_Color_Pos(0X7FFF00, (i + 3) % 16);
WS2812_Color_Pos(0X00FF00, (i + 4) % 16);
WS2812_Color_Pos(0X00FF7F, (i + 5) % 16);
WS2812_Color_Pos(0X00FFFF, (i + 6) % 16);
WS2812_Color_Pos(0X007FFF, (i + 7) % 16);
WS2812_Color_Pos(0X0000FF, (i + 8) % 16);
WS2812_Color_Pos(0X7F00FF, (i + 9) % 16);
WS2812_Color_Pos(0XFF00FF, (i + 10) % 16);
WS2812_Color_Pos(0XFF007F, (i + 11) % 16);
WS2812_Color_Pos(0XFF0000, (i + 12) % 16);
WS2812_Color_Pos(0XFF7F00, (i + 13) % 16);
WS2812_Color_Pos(0XFFFF00, (i + 14) % 16);
WS2812_Color_Pos(0X7FFF00, (i + 15) % 16);
WS2812_Update();
}
ws2812.h
#ifndef WS2812_H
#define WS2812_H
#include "string.h"
#include "main.h"
#include "spi.h"
#define WS2812_0 0xC0
#define WS2812_1 0xF0
#define WS2812_RST 0x00
#define LED_NUMS 16
#define RGB_BIT 24
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
} LEDType;
void WS2812_TurnOff();
void WS2812_Show_Wheel();
void WS2812_Update();
#endif