ESP32学习笔记:WS2812B驱动

WS2812B是一款贴片RGB灯。由于采用了单总线通讯,所以需要特别关注下它的通讯时序。

调试细节:

本来以为会是一个比较简单的调试,结果还是花了很长时间才调试完成。

首先是关于ESP32的纳秒级延时确定,当时按照空指令始终调试不出来。之前在STM32平台上的nop()函数也不知道怎么用。

后来发掘出了一个比较简单的办法。就是一个个试,然后在main函数中按照1S的频率打印调试信息来倒推ns级别的延时是否可靠。

注意:在ESP32不能使用空语句加;来进行空指令延时,需要使用操作语句。

复制代码
unsigned long ns_delay_value = 0;

void delay_100_ns(int data)
{
    unsigned char i;

    ns_delay_value = 0;

    for(i = 0; i < data; i++)
    {
        ns_delay_value++;
    }
}

void delay_1_us()
{
    delay_100_ns(10);
}

void delay_1_ms()
{
    long i;
    for(i = 0; i < 1000; i++)
    {
        delay_1_us();
    }
}

void delay_1_s()
{
    long i;
    for(i = 0; i < 1000; i++)
    {
        delay_1_ms();
    }
}

然后在main函数中按照1S的频率打印调试信息:

复制代码
while(1){

        printf("ws2812B demo system run ...\n");
//        vTaskDelay(1000 / portTICK_PERIOD_MS);
        delay_1_s();
    }

基本确定了ns级别延时后,就可以按照时序来写ws2812的驱动函数啦。

复制代码
*WS2812B Drive*/
#define WS2812B_GPIO               8
#define WS2812B_GPIO_ACTIVE_LEVEL  1
#define ws2812b_pin_set()          gpio_set_level(WS2812B_GPIO, 1)
#define ws2812b_pin_rst()          gpio_set_level(WS2812B_GPIO, 0)

void ws2812b_writebyte(unsigned char data)
{
    unsigned char i;

    for(i = 0; i < 8; i++)
    {
        if(data & 0X80)
        {
            ws2812b_pin_set();
            delay_100_ns(3);
            ws2812b_pin_rst();
            delay_100_ns(3);
        }else
        {
            ws2812b_pin_set();
            delay_100_ns(1);
            ws2812b_pin_rst();
            delay_100_ns(3);
        }
        data <<= 1;
    }
}

void ws2812b_write_rgb(unsigned char red_value, unsigned char green_value, unsigned char blue_value)
{
    ws2812b_writebyte(red_value);
    ws2812b_writebyte(green_value);
    ws2812b_writebyte(blue_value);
}

后来发现依然无法驱动,到了晚上才发现自己犯了一个低级错误。ESP32的IO口没有进行初始化配置!

复制代码
void ws2812b_gpio_init(void)
{
    gpio_config_t gpio_conf;

    gpio_conf.intr_type = GPIO_INTR_DISABLE;
    gpio_conf.mode = GPIO_MODE_OUTPUT;

    gpio_conf.pin_bit_mask = (1ULL << WS2812B_GPIO);

    if (WS2812B_GPIO_ACTIVE_LEVEL) {
        gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
        gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
    } else {
        gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
        gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
    }

    gpio_config(&gpio_conf);
}

配置完IO口就可以进行WS2812B驱动啦。不过还是出现了一些小bug,比如初始化第一次点灯,绿色灯珠总是会不受控制地自动点亮!后来发现在IO初始化函数前预先执行一次点亮指令就可以消除这个bug!Nice!

复制代码
void app_main(void)
{

    ws2812b_write_rgb(0, 255, 0);
    ws2812b_gpio_init();

    delay_1_s();
    ws2812b_write_rgb(20, 20, 20);

    while(1){

        printf("ws2812B demo system run ...\n");
//        vTaskDelay(1000 / portTICK_PERIOD_MS);
        delay_1_s();
    }
}

至此,ESP32对于WS2812B的驱动函数就调试完成啦。

对于这个说起来简单但是异常曲折的小demo项目积累了如下经验:

1.ESP32的IO也是需要进行初始化配置的。

2.MCU的单指令确实能够进行粗略的ns级延时,为后续调试一些芯片时序提供了新的方法。

3.ESP32的GPIO8需要使用一个10K电阻上拉3.3V,否则无法进行程序下载。

2023-05-04 细节补充

1.在后续demo项目完善该驱动时,发现会出现初始化后,在别处点灯时,依然会出现亮出绿灯现象。

后来增加了灯珠的复位函数:

复制代码
void ws2812b_write_reset(void)
{
    unsigned int i = 0;

    ws2812b_pin_rst();

    for(i = 0; i < 300; i++)
    {
        delay_1_us();
    }
   
}

然后初始化变更为:

复制代码
ws2812b_write_rgb(0, 255, 0);
ws2812b_gpio_init();
ws2812b_write_reset();
ws2812b_write_rgb(20, 20, 20);

如果需要在别的函数位置电灯,择执行以下函数即可,亲测有效。

复制代码
ws2812b_write_rgb(0, 0, 0);
ws2812b_write_reset();
ws2812b_write_rgb(0, 0, 20);
相关推荐
Bin二叉4 分钟前
南京大学cpp复习——第二部分(继承)
开发语言·c++·笔记·学习
代码游侠6 分钟前
数据结构——线性表
linux·c语言·数据结构·学习·算法
人邮异步社区7 分钟前
完全没接触过AI/NLP,如何系统学习大模型?
人工智能·学习·自然语言处理·大模型
。TAT。8 分钟前
进程间通信-对匿名管道的学习
linux·学习
empti_8 分钟前
《大规模 3D 城市布局的语义与结构引导可控生成》翻译
论文阅读·笔记
QiZhang | UESTC15 分钟前
学习日记day37
学习
YJlio15 分钟前
SDelete 学习笔记(9.9):安全擦除原理、SSD 场景与企业合规实战
笔记·学习·安全
弘毅 失败的 mian20 分钟前
Linux 基本工具详解
linux·运维·服务器·经验分享·笔记
摇滚侠20 分钟前
零基础小白自学 Git_Github 教程,DeskTop 进阶操作,笔记12
笔记·git·github
弘毅 失败的 mian20 分钟前
Linux 编程第一个小程序:进度条
linux·经验分享·笔记·小程序·apache