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);
相关推荐
CCSBRIDGE2 小时前
Magento2项目部署笔记
笔记
爱吃西瓜的小菜鸡2 小时前
【C语言】判断回文
c语言·学习·算法
小A1593 小时前
STM32完全学习——SPI接口的FLASH(DMA模式)
stm32·嵌入式硬件·学习
亦枫Leonlew3 小时前
微积分复习笔记 Calculus Volume 2 - 5.1 Sequences
笔记·数学·微积分
岁岁岁平安3 小时前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA3 小时前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
qq_589568103 小时前
数据可视化echarts学习笔记
学习·信息可视化·echarts
爱码小白3 小时前
网络编程(王铭东老师)笔记
服务器·网络·笔记
LuH11244 小时前
【论文阅读笔记】Learning to sample
论文阅读·笔记·图形渲染·点云
兔C4 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序