【ESP32-S3的开发】| 2.ESP32S3 驱动 RGB-LED

【ESP32-S3的开发】


第二章 驱动 RGB-LED

  • 【ESP32-S3的开发】
  • 前言
  • 一、RGB-LED 硬件电路设计
  • 二、WS2812 LED驱动器
    • 1.遇见问题不要慌,先找一下SDK的example
    • 2.原理图的SK68xx和WS2812有什么关系?(为什么追溯到WS2812)
    • 3.led_strip为什么可以用 RMT 控制
  • 三、常用的几个API
    • 1.led_strip_init
    • 2.ws2812_set_pixel
  • 四、程序示例


前言

这里使用的是乐鑫的 ESP32-S3-DevKitC-1 开发板,搭载的是 Wi-Fi + Bluetooth® LE 模组 ESP32-S3-WROOM-1 ,内置芯片配置是 ESP32-S3FN16R8,开发板组件包含一下内容
【ESP32-S3的开发】| 2.ESP32S3 驱动 RGB-LED_第1张图片
接着给大家讲的是 esp-idf 对 RGB-LED的驱动


一、RGB-LED 硬件电路设计

参考来自官网的原理图纸:https://dl.espressif.com/dl/SCH_ESP32-S3-DEVKITC-1_V1_20210312C.pdf【ESP32-S3的开发】| 2.ESP32S3 驱动 RGB-LED_第2张图片
【ESP32-S3的开发】| 2.ESP32S3 驱动 RGB-LED_第3张图片
抽取关键部分对其进行分析,可以发现模组是通过 GPIO48 对 RGB-LED 实现控制,并且 RGB-LED 除了 DIN 还有 DOUT 引脚,有点奇怪。因为一般我们见的 RGB ,如果想产生不同的颜色,会有3个IO实现多种脉冲宽度调制,搭配产生多种颜色;按原理理解,肯定需要给不同的脉冲给 RGB,我们才能显示多颜色,那么这里的一个 DIN 数据,怎么能产生出多种脉冲宽度,猜测里面会有个串行数据转并行数据的 IC,帮我们把输入的数据并行传输到三个LED里。
我们可以通过搜索RGB型号,来找到这个RGB相应的驱动信息,SK68XXMINI-HS,以 SK6812 为例
【ESP32-S3的开发】| 2.ESP32S3 驱动 RGB-LED_第4张图片
到这里基本可以验证了我们的猜想

二、WS2812 LED驱动器

1.遇见问题不要慌,先找一下SDK的example

因为对于模组而言,这个驱动 RGB 的程序驱动,是属于外设,一般都会有例程支持,所以我们在 esp-idf 的 example/peripherals 中进行寻找,或者直接进行关键字 “led” 的搜索,可以发现和 led 控制有关的分别是 ledc 文件夹中的 ledc_basic 和 ledc_fade ,除此外还有 rmt 文件夹中的 led_strip

另外,结合官网手册理解这几个 example 分别是对于什么的应用,首先,我们找到的是 ledc(LED控制器)https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-reference/peripherals/ledc.html
【ESP32-S3的开发】| 2.ESP32S3 驱动 RGB-LED_第5张图片
那么在这个位置,我们可以发现,它确实能控制 RGB LED,但下面看到驱动时需要绑定相应的GPIO,看到这里就可以知道,它输出的脉冲应该是针对单个 IO 的脉冲宽度调制,而不是我们所说的串行数据转并行实现灯的控制。

现在我们来看一下 rmt 文件夹中的 led_strip。
led_strip 中文翻译成 LED 灯带,属于多个 LED 的同时控制。我们的原理图除了 DIN 管脚,还有一个 DOUT 管脚,虽然板子上没有接东西,但是这个管脚既然命名为 DOUT,说明它是属于数据输出,还可以接下一级控制,那么这个 led_strip 也就和我们当前需要的案例,更接近了。

2.原理图的SK68xx和WS2812有什么关系?(为什么追溯到WS2812)

通过查找资料得知,SK6812 内置的 IC 和 WS2812 内置 IC 型号不同,WS2812 比 SK6812 早推出市场,但 SK6812 做了防反接保护。使用上,程序控制部分是兼容的,测试的时候 SK6812 刷新率比 WS2812 稍微要好,传言两个厂家开始联合开发后,再分开用自己的技术进行生产。

  • SK68xx 的控制逻辑:
    DIN端接受从控制器传输过来的数据,首先送过来的24bit数据被第一个像素点提取后,送到像素点内部的数据锁存器,剩余的数据经过内部整形处理电路整形放大后通过DO端口开始转发输出给下一个级联的像素点,每经过一个像素点的传输,信号减少24bit。

3.led_strip为什么可以用 RMT 控制

RMT(远程控制)模块驱动程序可用于发送和接收红外远程控制信号。由于RMT模块的灵活性,驱动器还可以用来生成或接收许多其他类型的信号。

该信号由一系列脉冲组成,由RMT的发射机根据一系列值生成。这些值定义了脉冲持续时间和二进制电平。发射机还可以提供载波,并用提供的脉冲对其进行调制。

ESP32S3 这个 RMT 外设不仅可以灵活地映射到不同 IO、可以改变通道,还可以设置不同的时钟用于驱动,更改分频等灵活配置,只需要按 SK68xx 手册上描述的电平时序设定 RMT 外设的时钟以及分频值,做简单的数据传输简直易如反掌。

三、常用的几个API

1.led_strip_init

/*****************************************************************************************
Function Description : 初始化 led 灯串
Function name : led_strip_t *led_strip_init(uint8_t channel, uint8_t gpio, uint16_t led_num);
Function Parameter :
	@Para1 : channel-RMT 通道
	@Para2 : gpio-实现功能的gpio
	@Para3 : led_num-led灯珠数量
eg : pUserLED_Strip = led_strip_init(RMT_TX_CHANNEL, RMT_TX_NUM, LED_STRIP_NUM);
*****************************************************************************************/
led_strip_t *led_strip_init(uint8_t channel, uint8_t gpio, uint16_t led_num)
{
        static led_strip_t *pStrip;

        rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, channel);
        // set counter clock to 40MHz
        config.clk_div = 2;

        ESP_ERROR_CHECK(rmt_config(&config));
        ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));

        // install ws2812 driver
        led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(led_num, (led_strip_dev_t)config.channel);

        pStrip = led_strip_new_rmt_ws2812(&strip_config);

        if (!pStrip)
        {
                ESP_LOGE(TAG, "install WS2812 driver failed");
                return NULL;
        }

        // Clear LED strip (turn off all LEDs)
        ESP_ERROR_CHECK(pStrip->clear(pStrip, 100));

        return pStrip;
}

2.ws2812_set_pixel

函数原型

/*****************************************************************************************
Function Description : 设置 led 灯珠颜色
Function name : static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
Function Parameter :
	@Para1 : strip-led 控制句柄
	@Para2 : index-当前控制灯珠所属下标
	@Para3 : red-R 值
	@Para4 : green-G 值
	@Para5 : blue-B 值
*****************************************************************************************/
static esp_err_t ws2812_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)

由于函数原型是static类型,作用域只在当前文件,所以用户无法直接调用它进行控制;但是在 led_strip_rmt_ws2812.c 源文件里,实现了对应用 API 的抽象管理,单独用函数指针实现指向,用户直接使用函数指针进行调用,可以不去涉及原函数的操作,另外,将来有更方便的 IC 出现的时候,可以继续增加对底层驱动的管理,重新实现指针指向后可以更少地修改应用层逻辑。

led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config)
{
        led_strip_t *ret = NULL;
        STRIP_CHECK(config, "configuration can't be null", err, NULL);

        // 24 bits per led
        uint32_t ws2812_size = sizeof(ws2812_t) + config->max_leds * 3;
        ws2812_t *ws2812 = calloc(1, ws2812_size);
        STRIP_CHECK(ws2812, "request memory for ws2812 failed", err, NULL);

        uint32_t counter_clk_hz = 0;
        STRIP_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev, &counter_clk_hz) == ESP_OK, "get rmt counter clock failed", err, NULL);
        // ns -> ticks
        float ratio = (float)counter_clk_hz / 1e9;
        ws2812_t0h_ticks = (uint32_t)(ratio * WS2812_T0H_NS);
        ws2812_t0l_ticks = (uint32_t)(ratio * WS2812_T0L_NS);
        ws2812_t1h_ticks = (uint32_t)(ratio * WS2812_T1H_NS);
        ws2812_t1l_ticks = (uint32_t)(ratio * WS2812_T1L_NS);

        // set ws2812 to rmt adapter
        rmt_translator_init((rmt_channel_t)config->dev, ws2812_rmt_adapter);

        ws2812->rmt_channel = (rmt_channel_t)config->dev;
        ws2812->strip_len = config->max_leds;

        ws2812->parent.set_pixel = ws2812_set_pixel;
        ws2812->parent.refresh = ws2812_refresh;
        ws2812->parent.clear = ws2812_clear;
        ws2812->parent.del = ws2812_del;

        return &ws2812->parent;
err:
        return ret;
}

四、程序示例

#include "driver/gpio.h"
#include "esp_err.h"
#include "led_strip.h"
#include "rmt_types.h"

led_strip_t *pUserLED_Strip;

#define LED_ON                                                                                                                                                                                \
        {                                                                                                                                                                                              \
                pUserLED_Strip->set_pixel(pUserLED_Strip, 0, 0xFF, 0x00, 0x00);                                                                                                                        \
                pUserLED_Strip->refresh(pUserLED_Strip, 10);                                                                                                                                           \
        }

#define LED_OFF                                                                                                                                                                                   \
        {                                                                                                                                                                                              \
                pUserLED_Strip->clear(pUserLED_Strip, 50);                                                                                                                                             \
                vTaskDelay(50 / portTICK_PERIOD_MS);                                                                                                                                                   \
        }
        

void LED_Init(void)
{
    pUserLED_Strip = led_strip_init(RMT_TX_CHANNEL, RMT_TX_NUM, LED_STRIP_NUM);
}

/******************************************************
****** Function	   :	void LED_START_Indicate(void)
****** Detail IO   :
****** Description :	LED 开机指示
****** Step		   :
*******************************************************/
void LED_START_Indicate(void)
{
    LED_ON;
    vTaskDelay(500 / portTICK_PERIOD_MS);
    LED_OFF;
    vTaskDelay(500 / portTICK_PERIOD_MS);
    LED_ON;
    vTaskDelay(500 / portTICK_PERIOD_MS);
    LED_OFF;
    vTaskDelay(500 / portTICK_PERIOD_MS);
    LED_ON;
    vTaskDelay(500 / portTICK_PERIOD_MS);
    LED_OFF;
    vTaskDelay(1000 / portTICK_PERIOD_MS);
}

你可能感兴趣的:(ESP32S3,单片机,物联网,嵌入式硬件)