【STM32】硬件SPI+DMA驱动WS2812灯珠,基于HAL库

本文详细介绍了如何在STM32平台上使用硬件SPI配合DMA,通过Cubemx配置和自定义驱动程序,实现WS2812灯珠的色彩控制,包括模块连接、SPI设置、色彩数据编码和RGB流水灯效果的创建。
摘要由CSDN通过智能技术生成

一、简单介绍

二、模块与接线

笔者使用的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每种颜色用8bit24位表示一个颜色,分给一个灯珠

当想表示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

七、工程文件源码

STM32硬件SPI配合DMA驱动WS2812流水灯,基于HAL库资源-CSDN文库

  • 36
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值