所需材料

  • 开发板:STM32F103C8T6,我用的是B站博主keysking

  • 材料:WS2812灯带,杜邦线

  • 相关配置:GPIO,DMA,TIM

  • 软件:STM32CubeIDE

步骤

  • 打开软件,新建工程

  • 设置 RCC 和 SYS

  • 设置TIM2

  • Clock Source  选择 Internal Clock,开启 TIM3 的内部时钟源,引脚使用 PA15,Prescaler 配置为 0,Counter Period配置为90-1。

设置DMA

点击DMA Setting,添加DMA通道

设置GPIO

  • 时钟树将HCLK频率配置为 72 MHz

  • 单独生成.c / .h 文件

  • 按下Ctrl + S 生成工程

  • 添加WS2812.c 和 WS2812.h 文件

注意在  void ws2812_update(void)函数中的HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, ,如果你不是TIM2,channel 1,在这里修改

#include "ws2812.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>

// LED颜色
uint32_t ws2812_color[WS2812_NUM] = {0};

// 当前LED颜色
static uint32_t _ws2812_color_current[WS2812_NUM];

/**
 * @brief  直接更新LED颜色
 */
void ws2812_update(void)
{
	// 数据缓冲,每个LED占用24个字节,共10个LED,前100个字节用于复位信号
	static uint16_t ws2812_data[RST_PERIOD_NUM + WS2812_NUM * 24];

	for (uint8_t led_id = 0; led_id < WS2812_NUM; led_id++)
	{
		_ws2812_color_current[led_id] = ws2812_color[led_id];
		static uint8_t r, g, b;
		color_to_rgb(_ws2812_color_current[led_id], &r, &g, &b);
		uint16_t *p = ws2812_data + RST_PERIOD_NUM + led_id * 24;
		for (uint8_t i = 0; i < 8; i++)
		{
			p[i] = (r << i) & (0x80) ? CODE_ONE_DUTY : CODE_ZERO_DUTY;
			p[i + 8] = (g << i) & (0x80) ? CODE_ONE_DUTY : CODE_ZERO_DUTY;
			p[i + 16] = (b << i) & (0x80) ? CODE_ONE_DUTY : CODE_ZERO_DUTY;
		}
	}
	HAL_TIM_PWM_Start_DMA(&htim2, TIM_CHANNEL_1, (uint32_t *)ws2812_data,
						  RST_PERIOD_NUM + WS2812_NUM * 24);
}

/**
 * @brief  通过渐变方式更新LED颜色(线性插值)
 * @param  steps: 渐变步数
 * @param  delay_ms: 每步之间的延迟时间(毫秒)
 */
void ws2812_gradient(uint8_t steps, uint16_t delay_ms)
{
	static uint8_t start_r[WS2812_NUM], start_g[WS2812_NUM], start_b[WS2812_NUM];
	static float r_step[WS2812_NUM], g_step[WS2812_NUM], b_step[WS2812_NUM];

	// 提取初始颜色,并计算每步的渐变步长
	for (uint8_t i = 0; i < WS2812_NUM; i++)
	{
		color_to_rgb(_ws2812_color_current[i], &start_r[i], &start_g[i], &start_b[i]);
		uint8_t target_r, target_g, target_b;
		color_to_rgb(ws2812_color[i], &target_r, &target_g, &target_b);

		r_step[i] = (float)(target_r - start_r[i]) / steps;
		g_step[i] = (float)(target_g - start_g[i]) / steps;
		b_step[i] = (float)(target_b - start_b[i]) / steps;
	}

	// 逐步渐变
	for (uint8_t step = 1; step <= steps; step++)
	{
		for (uint8_t led_id = 0; led_id < WS2812_NUM; led_id++)
		{
			// 计算当前步的颜色
			uint8_t r = (uint8_t)(start_r[led_id] + r_step[led_id] * step);
			uint8_t g = (uint8_t)(start_g[led_id] + g_step[led_id] * step);
			uint8_t b = (uint8_t)(start_b[led_id] + b_step[led_id] * step);

			ws2812_set_rgb(led_id, r, g, b);
		}

		ws2812_update();
		HAL_Delay(delay_ms);
	}
}

/**
 * @brief  设置LED颜色(RGB格式)
 * @param  led_id: LED编号(学习板一共有10个LED,编号范围0-9)
 * @param  r: 红色亮度(0-255)
 * @param  g: 绿色亮度(0-255)
 * @param  b: 蓝色亮度(0-255)
 */
void ws2812_set_rgb(uint8_t led_id, uint8_t r, uint8_t g, uint8_t b)
{
	ws2812_color[led_id] = rgb_to_color(r, g, b);
}

/**
 * @brief  设置LED颜色(24bit颜色格式)
 * @param  led_id: LED编号(学习板一共有10个LED,编号范围0-9)
 * @param  color: 24bit颜色
 */
void ws2812_set(uint8_t led_id, uint32_t color)
{
	ws2812_color[led_id] = color;
}

/**
 * @brief  设置所有LED颜色(24bit颜色格式)
 * @param  color: 24bit颜色
 */
void ws2812_set_all(uint32_t color)
{
	for (uint8_t led_id = 0; led_id < WS2812_NUM; led_id++)
	{
		ws2812_color[led_id] = color;
	}
}

/**
 * @brief  RGB转换为24bit颜色
 * @param  r: 红色亮度(0-255)
 * @param  g: 绿色亮度(0-255)
 * @param  b: 蓝色亮度(0-255)
 * @retval 24bit颜色
 */
uint32_t rgb_to_color(uint8_t r, uint8_t g, uint8_t b)
{
	return (r << 16) | (g << 8) | b;
}

/**
 * @brief  24bit颜色转换为RGB
 * @param  color: 24bit颜色
 * @param  r: 红色亮度(0-255)
 * @param  g: 绿色亮度(0-255)
 * @param  b: 蓝色亮度(0-255)
 */
void color_to_rgb(uint32_t color, uint8_t *r, uint8_t *g, uint8_t *b)
{
	*r = (color >> 16) & 0xFF;
	*g = (color >> 8) & 0xFF;
	*b = color & 0xFF;
}

注意  #define WS2812_NUM ,这是灯带LED的数量

#ifndef __WS2812_H__
#define __WS2812_H__

#include "main.h"
#include "gpio.h"
#include "tim.h"
#include "stdio.h"
#include "string.h"

#define CODE_ONE_DUTY 66
#define CODE_ZERO_DUTY 21
// #define CODE_ONE_DUTY	90
// #define CODE_ZERO_DUTY	32
#define RST_PERIOD_NUM 100
#define WS2812_NUM 60 //灯带LED灯数量

extern uint32_t ws2812_color[WS2812_NUM];

// 将颜色数组直接更新到 LED,不使用渐变过渡
void ws2812_update(void);

// 渐变的更新LED颜色
void ws2812_gradient(uint8_t steps, uint16_t delay_ms);

// 设置LED颜色(24bit颜色)
void ws2812_set(uint8_t led_id, uint32_t color);

// 设置LED颜色(RGB)
void ws2812_set_rgb(uint8_t led_id, uint8_t r, uint8_t g, uint8_t b);

// 设置所有LED颜色
void ws2812_set_all(uint32_t color);

// RGB转换为24bit颜色
uint32_t rgb_to_color(uint8_t r, uint8_t g, uint8_t b);

// 24bit颜色转换为RGB
void color_to_rgb(uint32_t color, uint8_t *r, uint8_t *g, uint8_t *b);

#endif
  • 最后在主函数 while (1) 循环添加代码

ws2812_set_all(0xFFFFFF); // 纯白色
ws2812_gradient(200, 3);
ws2812_update();

点击查看更多颜色代码

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐