一、简单介绍
二、模块与接线
笔者使用的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的时候,需要高电平持续200~300多ns,然后是580ns~1us的低电平
当想表示1的时候,需要高电平持续580ns~1us,然后是580ns~1us的低电平
笔者设置SPI为6MHz,一个时钟周期大概是160ns(1/6MHz)
那么想表示0,就需要大约2个时钟的高电平(160*2=320ns)紧接着6个时钟的低电平(6*160=960ns),就是由SPI发出0xC0
想表示1,就需要大约4个时钟的高电平(4*160=640ns)紧接着4个时钟的低电平(4*160=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位的数据
/**
create 24 byte sent by SPI using RGB values.
*/
static 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中
/**
cook the whole buffer made by many(16 pieces) RGB_BIT_Buffers.
*/
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();
}
}
RGB流水灯函数
根据介绍里的描述,ws2812可以显示256*256*256种颜色,但常用的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);
}
主函数
/**
* @brief The application entry point.
* @retval int
*/
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);
}
/**
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();
}
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;
/**
turn off all leds
*/
void WS2812_TurnOff();
void WS2812_Show_Wheel();
void WS2812_Update();
#endif