灯带型号

淘宝购买此灯带,5V-2.7MM-60灯每米,1米:
img_v3_0211f_c3fba5fa-e687-4cc5-ab82-0f3183199a0g
img_v3_0211f_2938c58f-c053-4bc2-ab32-5821e481415g
灯带引出三根线,红线是电源正极(5V),白线是电源负极(GND,与单片机共地),绿线是PWM信号线(STM32 GPIO引出)。

硬件配置

使用STM32F405的PB0引脚做PWM,推挽输出无上拉:

void HAL_TIM_MspPostInit(TIM_HandleTypeDef* timHandle)
{

 GPIO_InitTypeDef GPIO_InitStruct = {0};
 if(timHandle->Instance==TIM3)
 {
 /* USER CODE BEGIN TIM3_MspPostInit 0 */

 /* USER CODE END TIM3_MspPostInit 0 */
   __HAL_RCC_GPIOB_CLK_ENABLE();
   /**TIM3 GPIO Configuration
   PB0     ------> TIM3_CH3
   */
   GPIO_InitStruct.Pin = GPIO_PIN_0;
   GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
   GPIO_InitStruct.Pull = GPIO_NOPULL;
   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
   GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
 }

 else if(timHandle->Instance==TIM4)
 {
 /* USER CODE BEGIN TIM4_MspPostInit 0 */

 /* USER CODE END TIM4_MspPostInit 0 */

 /* USER CODE BEGIN TIM4_MspPostInit 1 */

 /* USER CODE END TIM4_MspPostInit 1 */
 }

}

未经验证的驱动能力增强硬件接法:硬件用一5V正极接上4.7kΩ电阻连接PB0(FT),并开漏配置“GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;”,“ GPIO_InitStruct.Pull = GPIO_NOPULL;”。

根据数据手册
https://docs.keysking.com/assets/files/WS2812数据手册-c2fb4796e75b0ce7d513045045c06b2a.pdf
时序特性:
image

打开定时器3并配置DMA

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */

  /* USER CODE END TIM3_MspInit 0 */
    /* TIM3 clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();

    /* TIM3 DMA Init */
    /* TIM3_CH3 Init */
    hdma_tim3_ch3.Instance = DMA1_Stream7;
    hdma_tim3_ch3.Init.Channel = DMA_CHANNEL_5;
    hdma_tim3_ch3.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_tim3_ch3.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim3_ch3.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim3_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_tim3_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_tim3_ch3.Init.Mode = DMA_CIRCULAR;
    hdma_tim3_ch3.Init.Priority = DMA_PRIORITY_LOW;
    hdma_tim3_ch3.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_tim3_ch3) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(tim_baseHandle,hdma[TIM_DMA_ID_CC3],hdma_tim3_ch3);

  /* USER CODE BEGIN TIM3_MspInit 1 */

  /* USER CODE END TIM3_MspInit 1 */
  }

配置定时器3频率为800kHz:

/* TIM3 init function */
void MX_TIM3_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 209;  // 168MHz/210 = 800KHz,符合WS2812标准

  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */
  HAL_TIM_MspPostInit(&htim3);

}

软件驱动模块

再根据数据手册:
image

rgb_drive.h

#ifndef __RGBDRIVE_H
#define __RGBDRIVE_H

#include "stm32f4xx_hal.h"
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

#define WS2812_LED_NUM      60U

void WS2812_Init(void);
void WS2812_Set(uint16_t index, uint8_t r, uint8_t g, uint8_t b);
void WS2812_Clear(void);
void WS2812_Show(void);

#ifdef __cplusplus
}
#endif

#endif

rgb_drive.c

#include "rgb_drive.h"
#include <string.h>

extern TIM_HandleTypeDef htim3;

#define WS2812_BITS_PER_LED   24U
#define WS2812_RESET_SLOTS    120U
#define WS2812_BUF_LEN        (WS2812_LED_NUM * WS2812_BITS_PER_LED + WS2812_RESET_SLOTS)

/* TIM3: 168MHz / 210 = 800kHz, 1 bit = 1.25us */
#define WS2812_PERIOD_TICKS   209U

/* 约 0.4us 和 0.85us, 适配 800kHz 周期 */
#define WS2812_T0H_TICKS      67U
#define WS2812_T1H_TICKS      143U

static uint8_t  s_led_rgb[WS2812_LED_NUM][3];   /* [R, G, B] */
static uint32_t s_pwm_buf[WS2812_BUF_LEN];

static void WS2812_BuildBuffer(void)
{
    uint32_t idx = 0;

    for (uint16_t led = 0; led < WS2812_LED_NUM; led++)
    {
        uint8_t r = s_led_rgb[led][0];
        uint8_t g = s_led_rgb[led][1];
        uint8_t b = s_led_rgb[led][2];

        /* WS2812 默认 GRB 顺序 */
        for (int8_t bit = 7; bit >= 0; bit--)
        {
            s_pwm_buf[idx++] = (g & (1U << bit)) ? WS2812_T1H_TICKS : WS2812_T0H_TICKS;
        }
        for (int8_t bit = 7; bit >= 0; bit--)
        {
            s_pwm_buf[idx++] = (r & (1U << bit)) ? WS2812_T1H_TICKS : WS2812_T0H_TICKS;
        }
        for (int8_t bit = 7; bit >= 0; bit--)
        {
            s_pwm_buf[idx++] = (b & (1U << bit)) ? WS2812_T1H_TICKS : WS2812_T0H_TICKS;
        }
    }

    /* 末尾补足低电平, 满足 reset 时间 */
    for (; idx < WS2812_BUF_LEN; idx++)
    {
        s_pwm_buf[idx] = 0;
    }
}

//void WS2812_Init(void)
//{
//    memset(s_led_rgb, 0, sizeof(s_led_rgb));
//    memset(s_pwm_buf, 0, sizeof(s_pwm_buf));

//    HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_3);
//    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, 0);
//}


void WS2812_Init(void)
{
    /* 上电全暖橘色: R=255, G=100, B=0 */
    for (uint16_t i = 0; i < WS2812_LED_NUM; i++)
    {
        s_led_rgb[i][0] = 255;  /* R */
        s_led_rgb[i][1] = 100;  /* G */
        s_led_rgb[i][2] = 0;    /* B */
    }

    memset(s_pwm_buf, 0, sizeof(s_pwm_buf));

    HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_3);
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, 0);

    WS2812_Show();  /* 立即显示暖橘色 */
}


void WS2812_Set(uint16_t index, uint8_t r, uint8_t g, uint8_t b)
{
    if (index >= WS2812_LED_NUM)
    {
        return;
    }

    s_led_rgb[index][0] = r;
    s_led_rgb[index][1] = g;
    s_led_rgb[index][2] = b;
}

void WS2812_Clear(void)
{
    memset(s_led_rgb, 0, sizeof(s_led_rgb));
}

void WS2812_Show(void)
{
    WS2812_BuildBuffer();

    HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_3);

    if (HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_3, s_pwm_buf, WS2812_BUF_LEN) != HAL_OK)
    {
        return;
    }

    /* 有 DMA 就等 DMA 完成, 没有就按总时长等一小段时间 */
    if (htim3.hdma[TIM_DMA_ID_CC3] != NULL)
    {
        while (HAL_DMA_GetState(htim3.hdma[TIM_DMA_ID_CC3]) != HAL_DMA_STATE_READY)
        {
        }
    }
    else
    {
        HAL_Delay(2);
    }

    HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_3);
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, 0);
}

posted on 2026-05-07 13:20  快乐的乙炔  阅读(27)  评论(0)    收藏  举报