问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

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

创作时间:
作者:
@小白创作中心

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

引用
CSDN
1.
https://blog.csdn.net/weixin_56719449/article/details/135886089

本文将详细介绍如何使用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
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号