
一、适用场景
适用场景:炫彩灯效(跑马、流水、渐变、呼吸)、状态指示、可穿戴灯光、舞台/装饰灯带、视觉化数值显示(温度/音频)以及练习精确时序、DMA+定时器应用与实时动画算法。
二、器材清单
WS2812(Neopixel)LED 灯带
stm32f103(或其它 STM32)开发板 ×1
若干杜邦线(母对母/公对母)×1组
稳定电源(3.3V 或 5V,按模块标注)×1
三、工作原理(要点)

WS2812 使用单线串行协议,时序严格(常见 800kHz):每个 LED 接收 24 位数据(按 GRB 顺序),每个位占用 约 1.25µs(Tbit)。
逻辑 0:T_H ≈ 0.35µs,T_L ≈ 0.9µs
逻辑 1:T_H ≈ 0.7µs,T_L ≈ 0.6µs
复位(latch)脉冲:拉低 ≥ 50µs,LED 把接收的 24×N 位数据写入并显示。
因时序严格,常用两种驱动方式:
软件 bit-bang(阻塞):屏蔽中断、严格微秒延时逐位输出 ------ 实现简单但会阻塞 CPU,适合少量 LED / 教学。
硬件 PWM + DMA:用定时器 PWM 输出固定周期(例如 1.25µs 分 30 个计数),把"0/1"映射为不同的 CCR 值,通过 DMA 把整个比特流送入 CCR 寄存器,精度高且 CPU 空闲,适合中大量灯带(我使用的也是这种方法)。
母头是输入信号,公头是输出,注意这里的公母头看的是接口的公母,不是外壳上的公母

四、接线示意
白色线 → GND
红色线 → 5V/3.3V电源
另外还有两根补压线(一根5V,一根GND,当LED灯带超过300个LED灯或灯带长5米时最后进行一个补压操作,实际补压时最好大于5V)暂时不使用
标准库
绿色线 → PB6
HAL库
绿色线 → PA8
五、示例代码
标准库
cpp
#include "stm32f10x.h"
#include "stdio.h"
#include "bsp_SysTick.h"
#include "bsp_usart.h"
#include "oled.h"
#define led_num 10
uint32_t ws2812b_buf[led_num];
uint16_t ws2812b_bit[24*led_num+1];
uint16_t showflag=0,showtime=0;
int z;
char show[20];
int flag;
void WS2812B_IRQHandler(void)//自定义的中断,是当DMA搬运完一次数据后才会触发的
{
TIM_SetCompare1(TIM4,0);//设置pwm的比较值为0
TIM_Cmd(TIM4,DISABLE);//关闭定时器
flag=1;
// for(z=0;z<100000;z++);
}
void WS2812B_ClearBuf(void)
{
uint8_t i;
for(i=0;i<led_num;i++)
{
ws2812b_buf[i]=0x000000;//给每个led写入24位的0,即清除颜色
}
}
void WS2812B_Init(void)
{
Dma1_SetIRQHandler(WS2812B_IRQHandler);//指定DMA中断后执行的中断函数,并不是用系统的函数
Dma1_Configuare((uint32_t)(&ws2812b_bit));//指定DMA帮运的数组
Remote_Init();//进行TIM的初始化
}
void WS2812B_SetBuf(uint32_t color)
{
uint8_t i;
for(i=0;i<led_num;i++)
{
ws2812b_buf[i]=color;//循环给每个led赋值24位的颜色数据
}
}
void WS2812B_UpdateBuf(void)//将所有灯的颜色数据进行全部更新
{
uint8_t i,j;
for(j=0;j<led_num;j++)//遍历每个灯
{
for(i=0;i<24;i++)//遍历每个灯的颜色位
{
if(ws2812b_buf[j]&(0x800000>>i))//判断每个灯的24位颜色数据,如果为0则定义其对应的PWM数据为30,反之为60
ws2812b_bit[j*24+i]=60;
else
ws2812b_bit[j*24+i]=30;
}
Dma1_Configuare((uint32_t)(&ws2812b_bit));//将更新好的数组重新赋值到DMA初始化中
Dma1_start(24*led_num);//启动DMA并将一次要搬运的数据个数指明
Time4_run(ENABLE);//开启定时器
while(flag==0);//等待数据搬运完成
flag=0;
}
}
void init_ALL(void)
{
WS2812B_Init();
WS2812B_ClearBuf();
tim2_Init();
}
void showbreath()
{
static uint8_t i,color;
showtime=6;//设置运行时间
if(i==0)WS2812B_SetBuf((color));
if(i==1)WS2812B_SetBuf((255-color));
if(i==2)WS2812B_SetBuf((color)<<8);
if(i==3)WS2812B_SetBuf((255-color)<<8);
if(i==4)WS2812B_SetBuf((color)<<16);
if(i==5)WS2812B_SetBuf((255-color)<<16);
if(i==6)WS2812B_SetBuf((color)|(color)<<8);
if(i==7)WS2812B_SetBuf((255-color)|(255-color)<<8);
if(i==8)WS2812B_SetBuf((color)|(color)<<16);
if(i==9)WS2812B_SetBuf((255-color)|(255-color)<<16);
if(i==10)WS2812B_SetBuf(((color)<<8)|((color)<<16));
if(i==11)WS2812B_SetBuf(((255-color)<<8)|((255-color)<<16));
if(i==12)WS2812B_SetBuf(((color))|((color)<<8)|((color)<<16));
if(i==13)WS2812B_SetBuf(((255-color))|((255-color)<<8)|((255-color)<<16));
color++;
if(color==0)
{
i++;
i%=14;
}
}
void startshow()
{
uint8_t i,num;
uint32_t R,G,B;
static uint8_t j;
showtime=20;
for(i=led_num;i>0;i--)
{
ws2812b_buf[i]=ws2812b_buf[i-1];
}
if(j==0)
{
num=rand()%7;
if(num==0)ws2812b_buf[0]=0x0000ff;
if(num==1)ws2812b_buf[0]=0x00ff00;
if(num==2)ws2812b_buf[0]=0xff0000;
if(num==3)ws2812b_buf[0]=0x00ffff;
if(num==4)ws2812b_buf[0]=0xff00ff;
if(num==5)ws2812b_buf[0]=0xffff00;
if(num==6)ws2812b_buf[0]=0xffffff;
}else if(j<15)
{
R=ws2812b_buf[1]/0X100%0x100;
G=ws2812b_buf[1]/0X10000%0x100;
B=ws2812b_buf[1]%0x100;
if(G>20) G-=20;
if(R>20) R-=20;
if(B>20) B-=20;
ws2812b_buf[0]=(G<<16)|(R<<8)|B;
}else{
ws2812b_buf[0]=0;
}
j++;
j%=50;
}
int main(void)
{
init_ALL();
USART_Config();
while(1)
{
if(showflag == 1)//定时一段时间后会自动开启颜色设置
{
showflag=0;
startshow();//将颜色数据写入数组中
// showbreath();//将颜色数据写入数组中
WS2812B_UpdateBuf();//将数组数据更新到DMA中,有DMA自行去将数据写入到LED灯带中
}
}
}
#include "bsp_SysTick.h"
extern uint16_t showflag,showtime;
void Remote_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE); //使能PORTB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE); //TIM4 时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //PB9 输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //上拉输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// GPIO_SetBits(GPIOB,GPIO_Pin_9); //初始化GPIOB.9
TIM_TimeBaseStructure.TIM_Period = 90-1; //设定计数器自动重装值 最大10ms溢出
TIM_TimeBaseStructure.TIM_Prescaler = 1-1; //配置输入分频,不分频,即72MHz,约等于1/72MHz=0.00000001s=10ns
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // 选择输入端 IC4映射到TI4上
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //上升沿捕获
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 0; //配置输入分频,不分频
TIM_OC1Init(TIM4, &TIM_OCInitStruct);//初始化定时器输入捕获通道
TIM_DMAConfig(TIM4,TIM_DMABase_CCR1,TIM_DMABurstLength_1Transfer);
TIM_DMACmd(TIM4,TIM_DMA_Update,ENABLE);
TIM_Cmd(TIM4,DISABLE);
}
void tim2_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //TIM2 时钟使能
TIM_TimeBaseStructure.TIM_Period = 1000; //设定计数器自动重装值 最大1ms溢出
TIM_TimeBaseStructure.TIM_Prescaler =(72-1); //预分频器,1M的计数频率,1us加1.
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseStructure.TIM_RepetitionCounter= 0 ;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动把更新中断标志位清除一下,解决刚初始化完就进入中断计数从1开始的问题
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM2,ENABLE ); //使能定时器2
}
void Time4_run(FunctionalState NewState)//控制TIM的运行
{
TIM_Cmd(TIM4,NewState);
}
void Time4_SetCompare(uint16_t Compare1)//控制TIM的比较值
{
TIM_SetCompare1(TIM4,Compare1);
}
void TIM2_IRQHandler(void)
{
static uint16_t rupttime;
if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)//计时器更新中断
{
rupttime++;
if(rupttime >= showtime)//超时打开标志位去设置led的数据并打开灯
{
showflag=1;
rupttime=0;
}
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
HAL库






cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "more-led.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim2;
DMA_HandleTypeDef hdma_tim1_ch1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM1_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern uint16_t showflag;
/* USER CODE END 0 */
/**
* @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_TIM1_Init();
MX_TIM2_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
init_ALL();
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(showflag == 1)//定时一段时间后会自动开启颜色设置
{
showflag=0;
// startshow();//将颜色数据写入数组中
showbreath();//将颜色数据写入数组中
WS2812B_UpdateBuf();//将数组数据更新到DMA中,有DMA自行去将数据写入到LED灯带中
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief TIM1 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 89;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
HAL_TIM_MspPostInit(&htim1);
}
/**
* @brief TIM2 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* Enable DMA controller clock
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel2_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
#include "more-led.h"
DMA_HandleTypeDef hdma_tim4_ch1;
extern TIM_HandleTypeDef htim1;
extern TIM_HandleTypeDef htim2;
extern DMA_HandleTypeDef hdma_tim4_up;
volatile uint32_t ADC_ConvertedValue[2]={0};
/* 颜色位缓冲区和实际 LED 数据缓冲 */
uint32_t ws2812b_buf[LED_NUM];
uint16_t ws2812b_bit[LED_NUM * 24];
/* 传输完成标志与回调指针 */
volatile uint8_t flag = 0;
uint16_t showtime;
uint8_t showflag;
/* 存放用户回调 */
static DMA_CallbackTypeDef user_dma_cb = NULL;
//static void WS2812B_IRQHandler(void)
//{
// /* 关闭 PWM 输出------恢复为零电平 */
// __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_1, 0);
// /* 停止 TIM4 PWM */
// HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_1);
//
// /* 通知上层数据已全部发送完 */
// flag = 1;
//}
/*------------------------------------------------------------------------------*/
/* 2. 清空 LED 数据缓冲,所有颜色置黑 */
void WS2812B_ClearBuf(void)
{
for (uint8_t i = 0; i < LED_NUM; i++)
{
ws2812b_buf[i] = 0x000000;
}
}
/*------------------------------------------------------------------------------*/
/* 4. 设置所有 LED 同一个颜色 */
void WS2812B_SetBuf(uint32_t color)
{
for (uint8_t i = 0; i < LED_NUM; i++)
{
ws2812b_buf[i] = color;
}
}
/*------------------------------------------------------------------------------*/
/* 5. 更新并发送整个 LED 缓冲区 */
void WS2812B_UpdateBuf(void)
{
for (uint8_t j = 0; j < LED_NUM; j++)
{
uint32_t c = ws2812b_buf[j];
/* 逐 LED 展开为 24bit PWM 数据流 */
for (uint8_t i = 0; i < 24; i++)
{
ws2812b_bit[j*24 + i] =
(c & (1UL << (23 - i))) ? 60 : 30;
// if (ws2812b_buf[j] & (0x800000 >> i))
// ws2812b_bit[j * 24 + i] = 60; /* "1" 对应高电平时长 */
// else
// ws2812b_bit[j * 24 + i] = 30; /* "0" 对应高电平时长 */
}
}
Time4_run(ENABLE); // 启动 TIM4 + DMA
while (flag == 0) { }
flag = 0;
}
/*------------------------------------------------------------------------------*/
/* 6. 系统初始化入口,清缓冲并启动周期中断(TIM2) */
void init_ALL(void)
{
WS2812B_ClearBuf();
/* TIM2 中断由 CubeMX 的 HAL_TIM_Base_Start_IT(&htim2) 在 main() 中启动 */
}
/*------------------------------------------------------------------------------*/
/* 7. 呼吸灯效果驱动,每次调用更新一次显示缓冲和定时参数 */
void showbreath(void)
{
static uint8_t idx = 0, color = 0;
showtime = 4; /* 由 TIM2 中断控制刷新周期 */
switch (idx)
{
case 0: WS2812B_SetBuf( color); break;
case 1: WS2812B_SetBuf((255 - color)); break;
case 2: WS2812B_SetBuf( color << 8); break;
case 3: WS2812B_SetBuf((255 - color) << 8); break;
case 4: WS2812B_SetBuf( color << 16); break;
case 5: WS2812B_SetBuf((255 - color) << 16); break;
case 6: WS2812B_SetBuf(( color) | ( color << 8)); break;
case 7: WS2812B_SetBuf(((255 - color)) | ((255 - color) << 8)); break;
case 8: WS2812B_SetBuf(( color) | ( color << 16)); break;
case 9: WS2812B_SetBuf(((255 - color)) | ((255 - color) << 16)); break;
case 10: WS2812B_SetBuf(( color << 8) | ( color << 16)); break;
case 11: WS2812B_SetBuf(((255 - color) << 8) | ((255 - color) << 16)); break;
case 12: WS2812B_SetBuf(( color) | ( color << 8) | ( color << 16)); break;
case 13: WS2812B_SetBuf(((255 - color)) | ((255 - color) << 8) | ((255 - color) << 16)); break;
}
color++;
if (color == 0)
{
idx = (idx + 1) % 14;
}
}
/*------------------------------------------------------------------------------*/
/* 8. 滚动色带/随机色效果 */
void startshow(void)
{
static uint8_t j = 0;
uint8_t num;
uint32_t R, G, B;
showtime = 20;
/* 数据右移一位,为最新色腾出位置 */
for (uint8_t i = LED_NUM - 1; i > 0; i--)
{
ws2812b_buf[i] = ws2812b_buf[i - 1];
}
if (j == 0)
{
num = rand() % 7;
const uint32_t colors[7] = {
0x0000FF, 0x00FF00, 0xFF0000,
0x00FFFF, 0xFF00FF, 0xFFFF00, 0xFFFFFF
};
ws2812b_buf[0] = colors[num];
}
else if (j < 15)
{
R=ws2812b_buf[1]/0X100%0x100;
G=ws2812b_buf[1]/0X10000%0x100;
B=ws2812b_buf[1]%0x100;
if (G > 20) G -= 20;
if (R > 20) R -= 20;
if (B > 20) B -= 20;
ws2812b_buf[0] = (G << 16) | (R << 8) | B;
}
else
{
ws2812b_buf[0] = 0;
}
j = (j + 1) % 50;
}
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
HAL_TIM_PWM_Stop_DMA(&htim1,TIM_CHANNEL_1);
flag = 1;
}
/**
* @brief 启停 TIM4 PWM / DMA
* @param NewState: ENABLE 或 DISABLE
*/
void Time4_run(FunctionalState NewState)
{
if (NewState) {
HAL_TIM_PWM_Start_DMA(&htim1,TIM_CHANNEL_1,(uint32_t *)ws2812b_bit,LED_NUM * 24);
} else {
HAL_TIM_PWM_Stop_DMA(&htim1, TIM_CHANNEL_1);
}
}
/**
* @brief 更新 TIM4 的比较寄存器 CCR1 值,用于输出PWM控制LED
* @param Compare1: 新的比较值
*/
void Time4_SetCompare(uint16_t Compare1)
{
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, Compare1);
}
/**
* @brief TIM 中断定时到达回调(弱定义,可被重载)
* @param htim: 产生中断的定时器句柄
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint16_t rupttime = 0;
if (htim->Instance == TIM2)
{
rupttime++;
if (rupttime >= showtime)
{
showflag = 1;
rupttime = 0;
}
}
}
#ifndef _ADC_TIME_H
#define _ADC_TIME_H
#include "main.h"
#define LED_NUM 10
/* 用户用来接收 DMA 完成回调的函数指针类型 */
typedef void (*DMA_CallbackTypeDef)(void);
static void WS2812B_IRQHandler(void);
void WS2812B_ClearBuf(void);
void WS2812B_Init(void);
void WS2812B_SetBuf(uint32_t color);
void WS2812B_UpdateBuf(void);
void init_ALL(void);
void showbreath(void);
void startshow(void);
void Dma1_SetIRQHandler(DMA_CallbackTypeDef cb);
void Dma1_start(uint16_t DataNumber);
void Time4_run(FunctionalState NewState);
void Time4_SetCompare(uint16_t Compare1);
void Dma1_Configuare(uint32_t MemoryBaseAddr);
#endif
六、讲解视频