问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

ESP32-C3使用RMT模块控制SK6812全彩RGB LED灯详解

创作时间:
作者:
@小白创作中心

ESP32-C3使用RMT模块控制SK6812全彩RGB LED灯详解

引用
CSDN
1.
https://blog.csdn.net/2401_85015050/article/details/138977770

本文将详细介绍如何使用ESP32-C3的RMT模块控制SK6812全彩RGB LED灯。文章将从SK6812 LED的基础介绍、控制原理、控制方案,以及ESP32-C3 RMT模块的详细介绍和使用示例等方面进行讲解。

一、SK6812 LED基础介绍

SK6812灯珠集成了控制电路与发光电路与一体的智能外控LED光源。外形与5050 LED灯珠是一样的。但是与普通的LED不同的是,他不是简单的通过高低电平来控制亮灭,它通过单线就能控制RGB三色的亮灭,采用了一个叫单极性归零码数据协议的通讯方式。

1.1 SK6812控制原理

对于使用者来说,我们需要知道的主要是理解这个协议,然后实现手册中提到的“0”码和“1”码,然后每一个灯珠,是由24 bit的数据结构组成。

(注意这里说的24 bit是数据结构,举个例子,如果我们使用SPI实现“0”、“1”码,SPI总线发送一个字节,只是实现了一个码,只是上面24 bit数据结构中的1bit!后面会更加详细的说明这一点)

我们把对应需要了解的参数都截图说明(根据自己选用的产品规格书来确定具体参数):

额外添加点说明,不同比例的三原色光相加得到彩色称为相加混色,比如:

  • 红+绿=黄
  • 红+蓝=紫
  • 蓝+绿=青
  • 红+蓝+绿=白

举个例子:显示黄色,其RGB值为255, 255, 0。那么上面的24bit数据结构为:

1111 1111 1111 1111 0000 0000(其中的1和0是上面说的“1”码和“0”码)

综合上面,不管使用哪一种方式,其中都是需要在DIN端给出符合时间要求的高低电平,才能正确的控制SK6812:

1.2 SK6812控制方案

知道了SK6812的控制原理,在开发板上面,我们使用一个IO口连接DIN端,作为信号的输入端,那么就需要实现这个IO口实现符合时序的高低电平,那么有哪些实现方式呢?

1.2.1 GPIO翻转

最终目的是需要实现规定时间的高低电平,那么最直接想到的就是直接把GPIO翻转速度设置成最大,然后直接置位复位GPIO实现高低电平。==!

但是仔细想一下上面的时间要求是us级别的,那么对于不同的芯片,不仅是因为主频不同,IO口的翻转速度当然也会不同,在网络上查看到(仅供参考):

1、STM32

  • 看到一篇博文直接操作寄存器222ns采用库函数指令会延迟300ns左右,参考博文无聊测一下IO口翻转速度STM32F103RCT6
  • 以主频为72MHz为例,指令控制GPIO翻转,最高可达18MHz。
  • 直接操作寄存器,单指令周期的,407超频200M的时候,IO口刚好100M

上述事件自己通过公式算一下即可:f=1/T。(T的单位是秒(s),f的单位是赫兹(Hz))。

2、ESP32

  • ESP32的IO口速度,简单查找没有找到说明。

3、ESP8266

  • ESP8266的GPIO有效翻转大约须要2.5us(0.4MHz)
  • ESP8266的GPIO0的翻转速度最快,配合寄存器操作可以实现

算下来,目前来说MCU的发展,还是有能够直接控制IO口电平实现的条件,这种简单粗暴的方式需要经过反复的测试调整,因为对于时间的控制还需要考虑很多因素。所以这里介绍一下,不过多探究。

1.2.2 SPI方式

SPI方式,SPI通讯的速度目前器件可以达到大几十Mbps,一般情况下,SPI模块的最大时钟频率为系统时钟频率的1/2。

SPI的基础知识网上很多,在我博文《总线协议记录》也有记录。

所以通过SPI总线发送是比较可行的一种方式。只要将SPI的时钟调整为8MHz左右(小于等于8Mhz),这样不同的MCU下,都可以实现。

采用8Mhz SPI,发送一个字节所需时间1.25us,满足上面SK6812的24bit数据结构一个bit的时间:

再根据“1”码<1us && >0.6us的高电平,>0.2us的低电平,得出,SPI发送一个字节11111100b即表示“1”码,0XF0。

同理可得,SPI发送字节11000000b即表示“0”码,0xC0。

那么还是按照上面原理部分举的例子,显示黄色的24bit数据结构为:1111 1111 1111 1111 0000 0000

那么SPI总线发送如下的24个字节数据(十六进制),就能使得LED显示为黄色:

F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 C0 C0 C0 C0 C0 C0 C0 C0

1.2.2 PWM方式

PWM方式也是一种常见的控制高低电平的方式,通过上面我们得知,“1”码和“0”码的高低电平比例为3:1.那么就是调节占空比,“1”码的占空比为75%,“0”码的占空比为25%。那么剩下的只需要把PWM的周期设置成SK6812的码元周期>1.2us左右。注意占空比多少,可以根据实际情况调整。

给出2个网上的结论,仅供参考:

  • cycle(周期)=1.2us,占空比=50%为1,占空比=30%为0;
    (833Khz,感觉还行)
  • 周期设置为3MHz,占空比=66%为1,占空比=33%为0;
    (0.33us周期,估计写错了,1MHZ还差不多,1us)

关于ESP32 -C3使用PWM方式,不确定可不可以用,毕竟ESP32 -C3的PWM按照我们的博文流程我们还没有学习测试,下一篇博文写一下ESP32 -C3的PWM学习测试记录。

1.2.3 RMT方式(ESP32)

RMT,是ESP32系列特有的一个红外发送和接收控制器,红外协议转化为信号,体现在IO上也就是高低电平。

将上面讲到的“1”码和“0”码当成红外信号,也可以实现SK6812的控制。下面就先来了解下ESP32-C3的RMT。

二、ESP32-C3 RMT介绍

2.1 RMT基础介绍

在乐鑫官方ESP32-C3芯片手册《esp32-c3_technical_reference_manual_cn》文档中对于RMT有详细的介绍:

在官方网站也有关于RMT相关API的详细介绍:乐鑫官方ESP32-C3 RMT部分说明

所以详细的资料还是可以通过上面的途径查看,这里我们需要关注的一点就是:

RMT是如何控制SK6812的?

通过前面的SK6812控制原理我们知道了,控制SK2812就是实现符合时间规定的高低电平,那么在ESP32-C3芯片手册中,有提到RMT是如何实现此功能的,对于部分如下图:(当然如果要了解更深还是要好好查看官方的资料)

结合官网图片就能更容易理解:

2.2 RMT使用介绍(API相关)

RMT的使用基本步骤如下,但是本文我们是需要控制SK6812,所以只需要了解发送相关的配置及使用:

首先要了解的是一个结构体,发送配置的结构体

rmt_tx_config_t:

上述结构体内容依次是:RMT载波频率、RMT输出的电平、空闲电平状态、占空比、最大循环计数、载波使能、循环发送使能、空闲电平输出使能。

通过初始化结构体的示例,可以更好的理解:

RMT输出结构体默认配置如下:

对于控制SK6812,目前了解到RMT的输入配置就可以了。

三、RMT示例测试

3.1 IDF示例测试

在IDF示例程序中,官方提供了控制WS2812的示例

RMT Transmit Example -- LED Strip:

程序的过程比较简单,SK6812的驱动和ws2812的驱动是一样的,相关的代码在

components/led_strip/src/led_strip_rmt_ws2812.c

文件中。

针对自己的开发板,然后对于示例工程,简单修改一下既可以看到效果,因为示例大家都一样,这里就使用截图表示需要修改的地方:

在示例中

EXAMPLE_CHASE_SPEED_MS

太快了,闪得我眼睛有点花,把这个时间改成了300:

#define EXAMPLE_CHASE_SPEED_MS (300)//

在我的开发板上面,本来确实是只有一个LED,但是为了测试,我飞线焊接了一个:

测试结果,示例的现象就是,LED不同颜色的交替闪烁,并没有渐变效果,这里上几张图勉强看看:

3.2 示例改渐变效果

最开始也没有一点一点去分析驱动代码,示例代码也就看看RMT的配置,后面的SK6812驱动部分并没有仔细研究,所以测试是闪烁效果,后来想想还是不得劲,不渐变闪烁,这不得亮瞎眼= =!

所以还是得改改,所以看了看示例,其实也就是简单的修改(最后一个

vTaskDelay(50)

不需要,这里是以前改过的代码忘了去掉了):

根据上面图示的说明,把所有时间改成如下,是基于例程基础最平滑最快速的渐变了:

没视频看不到= =!上张图勉强应付一下:

四、SK6812驱动代码说明

最近ESP32-C3的学习博主已经更新完了蓝牙GATT篇章,正准备写一篇蓝牙的小应用,计划要通过手机与开发板进行蓝牙连接,控制板子上的灯,能够渐变当然是最好了,忽然发现SK6812的驱动函数忘了怎么用了……

在官方示例中,给了最原始的驱动,但是感觉当时没有理解透彻,所以回过头来重新看一看。

4.1 驱动函数简析

我们在使用中,需要定义一个LED变量,比如:

static led_strip_t *strip;

我们来看一看

led_strip_t

,他是

led_strip_s

结构体类型:

struct led_strip_s {
    /**
     * 设置灯的颜色
     */
    esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
    /**
     * 更新灯的颜色
     */
    esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
    /**
     * 清除灯的颜色
     */
    esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
    /**
     * 删除灯这个对象
     */
    esp_err_t (*del)(led_strip_t *strip);
};

在示例中我们都使用到了这几个函数,简单记录一下这几个函数的说明:

设置灯的颜色:

/**
 * 参数含义:
 * 灯的句柄,我们开始定义的变量
 * 需要设置的灯的位置下标,从0开始,如果有很多灯,一般都是使用for循环赋值
 * 红色的值
 * 绿色的值
 * 蓝色的值
 */
static esp_err_t ws2812_set_pixel(led_strip_t *strip, 
                                 uint32_t index, 
                                 uint32_t red, 
                                 uint32_t green, 
                                 uint32_t blue)

更新灯的值:

使用上面函数设置完LED颜色值后,需要调用

ws2812_refresh

将颜色更新到灯条:

static esp_err_t ws2812_refresh(led_strip_t *strip, uint32_t timeout_ms)

清除灯的颜色:

static esp_err_t ws2812_clear(led_strip_t *strip, uint32_t timeout_ms)

删除灯这个对象:

static esp_err_t ws2812_del(led_strip_t *strip)
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号