Skip to content

WS2812是单线通信,0码和1码都是由高低电平组成,只不过高低电平占用时间有区别

发送第一个24位数据时,第一个WS2812会记录并且锁存起来,再发第二个的24位的时候,就会把数据直接传输给第二个WS2812并记录,再发第三个24位数据,第前两个因为已经记录了数据,因此都会将数据流转到下一个,最后也就是第三个会收到数据。如此类推,直到我们发送一个RESET信号,本次传输才结束

添加控制灯的组件库

led_strip

https://components.espressif.com/components/espressif/led_strip/versions/2.5.5

控制台输入

bash
idf.py add-dependency "espressif/led_strip^3.0.0"

然后编译会生成组件目录

RMT

使用 ESP RMT 库进行驱动

官方关于RMT的介绍:RMT(红外遥控器)是一个红外发送/接收控制器, 其特殊设计支持生成各类信号。红外遥控发射器从内置的 RAM(随机存取存储器)区中读取连续的脉冲码, 并对输出信号进行载波调制。尽管RMT外设主要用于远程红外应用,由于其灵活的数据格式,RMT同样可支持其它协议。WS2812是集成了驱动电路和单线控制的数字RGB LED,其协议数据格式兼容于RMT外设。

驱动WS2812要求的时序很高,我们用IO口模拟控制比较困难,用SPI或者I2C时序也对不上,因此红外是最优选择

RMT初始化

c
// 使用默认参数填入rmt_config_t结构体
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(CONFIG_EXAMPLE_RMT_TX_GPIO, RMT_TX_CHANNEL);
// 将时钟设置为40MHz
config.clk_div = 2;
 
// 调用两个函数完成初始化
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

WS2812初始化

c
// install ws2812 driver 安装ws2812驱动
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(CONFIG_EXAMPLE_STRIP_LED_NUMBER, (led_strip_dev_t)config.channel);
led_strip_t *strip = led_strip_new_rmt_ws2812(&strip_config);
if (!strip) {
    ESP_LOGE(TAG, "install WS2812 driver failed");
}
// Clear LED strip (turn off all LEDs)关闭所有ws2812
ESP_ERROR_CHECK(strip->clear(strip, 100));

示例代码:

ws2812.c

点击展开代码
c
#include "ws2812.h"

#include <stdlib.h>

#include "driver/gpio.h"
#include "driver/rmt.h"
#include "esp_mac.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "led_strip.h"
static led_strip_t *strip = NULL;

/********************************************************************************
 * @brief: ws2812 初始化
 * @return {*}
 ********************************************************************************/
void ws2812_init() {
  rmt_config_t config = RMT_DEFAULT_CONFIG_TX(GPIO_LED_PIN, RMT_TX_CHANNEL);
  config.clk_div = 2;  // 时钟分频,设置为2可以提高时序精度
  ESP_ERROR_CHECK(rmt_config(&config));
  ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

  // 使用 LED Strip 库
  led_strip_config_t strip_config =
      LED_STRIP_DEFAULT_CONFIG(LED_NUM, (led_strip_dev_t)config.channel);
  strip = led_strip_new_rmt_ws2812(&strip_config);
  if (!strip) {
    printf("Failed to install WS2812 driver\n");
  }
  // 清空 LED
  ESP_ERROR_CHECK(strip->clear(strip, 100));
  vTaskDelay(pdMS_TO_TICKS(100));
  // 设置固定颜色
  set_static_color(255, 0, 0);
  vTaskDelay(pdMS_TO_TICKS(100));

  // // 呼吸灯效果(蓝色)
  // xTaskCreate(breathing_effect, "breathing_effect", 2048,
  //             (void *){0, 0, 255, 20}, 5, NULL);

  // // 彩虹渐变效果
  // xTaskCreate(rainbow_effect, "rainbow_effect", 2048, (void *)20, 5, NULL);

  // 跑马灯效果
  // xTaskCreate(chasing_effect, "chasing_effect", 2048, NULL, 5, NULL);
}

/********************************************************************************
 * @brief: 固定颜色
 * @param {uint8_t} red
 * @param {uint8_t} green
 * @param {uint8_t} blue
 * @return {*}
 ********************************************************************************/
void set_static_color(uint8_t red, uint8_t green, uint8_t blue) {
  for (int i = 0; i < LED_NUM; i++) {
    strip->set_pixel(strip, i, red, green, blue);
  }
  strip->refresh(strip, 100);
}

/********************************************************************************
 * @brief: 彩虹渐变
 * @param {uint8_t} wait_ms
 * @return {*}
 ********************************************************************************/
void rainbow_effect(uint8_t wait_ms) {
  uint8_t hue = 0;
  while (1) {
    for (int i = 0; i < LED_NUM; i++) {
      uint8_t red = 0, green = 0, blue = 0;
      hsv2rgb(hue + i * 10, 255, 50, &red, &green, &blue);  // HSV 转 RGB
      strip->set_pixel(strip, i, red, green, blue);
    }
    strip->refresh(strip, 100);
    hue += 5;  // 控制颜色变化速度
    vTaskDelay(pdMS_TO_TICKS(wait_ms));
  }
}

/********************************************************************************
 * @brief: 呼吸灯效果
 * @param {uint8_t} red
 * @param {uint8_t} green
 * @param {uint8_t} blue
 * @param {uint8_t} wait_ms
 * @return {*}
 ********************************************************************************/
void breathing_effect(uint8_t red, uint8_t green, uint8_t blue,
                      uint8_t wait_ms) {
  while (1) {
    for (int brightness = 0; brightness < 256; brightness += 5) {
      for (int i = 0; i < LED_NUM; i++) {
        strip->set_pixel(strip, i, (red * brightness) / 255,
                         (green * brightness) / 255, (blue * brightness) / 255);
      }
      strip->refresh(strip, 100);
      vTaskDelay(pdMS_TO_TICKS(wait_ms));
    }
    for (int brightness = 255; brightness >= 0; brightness -= 5) {
      for (int i = 0; i < LED_NUM; i++) {
        strip->set_pixel(strip, i, (red * brightness) / 255,
                         (green * brightness) / 255, (blue * brightness) / 255);
      }
      strip->refresh(strip, 100);
      vTaskDelay(pdMS_TO_TICKS(wait_ms));
    }
  }
}

/********************************************************************************
 * @brief: 跑马灯效果
 * @param {uint8_t} red
 * @param {uint8_t} green
 * @param {uint8_t} blue
 * @param {uint8_t} wait_ms
 * @return {*}
 ********************************************************************************/
void chasing_effect() {
  int led_pos = 0;  // 跑马灯的当前位置

  while (true) {
    // 清除所有 LED
    strip->clear(strip, 50);
    vTaskDelay(pdMS_TO_TICKS(1000));  // 延时 100 毫秒
    // 设置跑马灯效果
    strip->set_pixel(strip, led_pos, 255, 0, 0);  // 设置当前 LED 为红色
    strip->refresh(strip, 100);                   // 刷新 LED 状态
    // 更新跑马灯位置
    led_pos = (led_pos + 1) % LED_NUM;

    // vTaskDelay(pdMS_TO_TICKS(100));  // 延时 100 毫秒
  }
}

/********************************************************************************
 * @brief: HSV 转 RGB 函数
 * @param {uint8_t} hue
 * @param {uint8_t} sat
 * @param {uint8_t} val
 * @param {uint8_t} *r
 * @param {uint8_t} *g
 * @param {uint8_t} *b
 * @return {*}
 ********************************************************************************/
void hsv2rgb(uint8_t hue, uint8_t sat, uint8_t val, uint8_t *r, uint8_t *g,
             uint8_t *b) {
  uint8_t region, remainder, p, q, t;

  if (sat == 0) {
    *r = val;
    *g = val;
    *b = val;
    return;
  }

  region = hue / 43;
  remainder = (hue - (region * 43)) * 6;

  p = (val * (255 - sat)) >> 8;
  q = (val * (255 - ((sat * remainder) >> 8))) >> 8;
  t = (val * (255 - ((sat * (255 - remainder)) >> 8))) >> 8;

  switch (region) {
    case 0:
      *r = val;
      *g = t;
      *b = p;
      break;
    case 1:
      *r = q;
      *g = val;
      *b = p;
      break;
    case 2:
      *r = p;
      *g = val;
      *b = t;
      break;
    case 3:
      *r = p;
      *g = q;
      *b = val;
      break;
    case 4:
      *r = t;
      *g = p;
      *b = val;
      break;
    default:
      *r = val;
      *g = p;
      *b = q;
      break;
  }
}

ws2812.h

点击展开代码
c
#ifndef WS2812_H
#define WS2812_H

#include <stdio.h>

// RMT 发送通道
#define RMT_TX_CHANNEL RMT_CHANNEL_0
// LED 数据端口
#define GPIO_LED_PIN 41
// LED 数量
#define LED_NUM 2

void ws2812_init();

void set_static_color(uint8_t red, uint8_t green, uint8_t blue);

void rainbow_effect(uint8_t wait_ms);

void breathing_effect(uint8_t red, uint8_t green, uint8_t blue,
                      uint8_t wait_ms);

void chasing_effect();

void hsv2rgb(uint8_t hue, uint8_t sat, uint8_t val, uint8_t *r, uint8_t *g,
             uint8_t *b);

#endif

Released under the GPL License.