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_H
3、在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的的效果了。
附视频: