WS2812 是一款集成了控制电路和 RGB 发光芯片的智能全彩 LED 器件,属于 “像素级可编程 LED”。其核心特点如下:单颗集成:每个 LED 内部包含驱动芯片和 RGB 三色灯珠,支持独立控制颜色和亮度。串行通信:采用单线归零码(Single-Wire Non-Return-to-Zero, NRZ)通信协议,数据可通过一根线级联传输。
WS2812可以通过spi或者PWM方式来驱动。此次采集spi来驱动。
【工程配置】
我选择STM32H755的SPI2的MOSI来驱动,选择的IO为PC3
在STM32CubeMX中配置SPI2为单发送的方式。
其配置如下:

2、打开DMA,配置为字节传输,转输方向为从内存到外设:

3、打开DMA中断

最后生成工程。
【代码移植】
创建sk6812_spi2.c到项目中(注:本来我买的是sk6812,结果我用sk6812来驱动,发现有问题,后来改为ws2812驱动就OK了,我看这不良商家是不是给我发错货了。)
其内容如下:
#include "sk6812_spi2.h"
#include "string.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(&hspi2,buffer,RGB_BIT*LED_NUMS);
}
/**
create 24 byte sent by SPI using RGB values.
*/
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);
}
/**
cook the whole buffer made by many(16 pieces) RGB_BIT_Buffers.
*/
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;
}
}
/**
an led of position will bright with color value
pos is [0 , max-1]
*/
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();
}
// 流水灯灯效果参数定义
#define RUNNING_LIGHT_COLOR 0xFF0000 // 流水灯颜色(蓝绿色)
#define RUNNING_LIGHT_LENGTH 4 // 流水灯长度
#define RUNNING_LIGHT_SPEED 1 // 流水灯速度(数值越大越慢)
/**
* 流水灯效果实现
* 一个或多个连续的LED组成的光点沿着灯带移动
* 传入参数:color - 流水灯颜色
* length - 流水灯长度
*
*/
void WS2812_Show_RunningLight(uint32_t color, uint16_t length)
{
static uint16_t pos = 0; // 当前流水灯位置
static uint8_t speed_counter = 0; // 速度控制计数器
// 先关闭所有LED
WS2812_TurnOff();
if(length > LED_NUMS) length = LED_NUMS;
if(length == 0) length = 1;
// 点亮当前位置及后续几个LED形成流水效果
for(uint16_t i = 0; i < length; i++)
{
uint16_t current_pos = (pos + i) % LED_NUMS;
WS2812_Color_Pos(color, current_pos);
}
// 更新位置,循环移动
pos = (pos + 1) % LED_NUMS;
// 发送数据更新显示
WS2812_Update();
}2、sk6812_spi2.h
#ifndef SK6812_SPI2_H
#define SK6812_SPI2_H
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
#include "spi.h"
#define WS2812_0 0xC0
#define WS2812_1 0xF0
#define WS2812_RST 0x00
#define LED_NUMS 60
#define RGB_BIT 24
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
}LEDType;
/**
turn off all leds
*/
void WS2812_TurnOff();
void WS2812_Show_Wheel();
void WS2812_Update();
void WS2812_Show_RunningLight(uint32_t color, uint16_t length);
#ifdef __cplusplus
}
#endif
#endif // SK6812_SPI2_H3、在freertos.c中添加测试代码,测试代码如下:
// 定义LED控制参数结构体
typedef struct {
uint32_t color;
uint32_t length;
uint32_t delay;
} led_control_params_t;
// 默认参数
static led_control_params_t default_led_params = {0xFF0000, 4, 100};
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
for(;;)
{
WS2812_Show_RunningLight(default_led_params.color, default_led_params.length);
osDelay(default_led_params.delay);
osDelay(1);
}
/* USER CODE END StartDefaultTask */
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
// 供外部调用的函数,用于更新LED参数
void UpdateLedParams(uint32_t color, uint32_t length, uint32_t delay)
{
default_led_params.color = color;
default_led_params.length = length;
default_led_params.delay = delay;
}
// 单独更新颜色
void UpdateLedColor(uint32_t color)
{
default_led_params.color = color;
}
// 单独更新长度
void UpdateLedLength(uint32_t length)
{
default_led_params.length = length;
}
// 单独更新延迟时间
void UpdateLedDelay(uint32_t delay)
{
if(delay > 0) {
default_led_params.delay = delay;
}
}下载到开发板后,就可以实现动态点亮WS2812的的效果了。
附视频:
我要赚赏金
