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);
相关推荐
im_AMBER3 小时前
React 17
前端·javascript·笔记·学习·react.js·前端框架
报错小能手4 小时前
C++笔记——STL map
c++·笔记
谷歌开发者4 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (六)
前端·chrome·学习
lkbhua莱克瓦245 小时前
Java基础——集合进阶3
java·开发语言·笔记
QT 小鲜肉6 小时前
【QT/C++】Qt定时器QTimer类的实现方法详解(超详细)
开发语言·数据库·c++·笔记·qt·学习
MeowKnight9586 小时前
【Qt】Qt实践记录3——UDP通信
笔记·qt
REDcker6 小时前
前端打包工具 - Rollup 打包工具笔记
前端·笔记
lkbhua莱克瓦246 小时前
Java基础——集合进阶用到的数据结构知识点1
java·数据结构·笔记·github
Mr.Jessy7 小时前
Web APIs 学习第五天:日期对象与DOM节点
开发语言·前端·javascript·学习·html
进化中的码农7 小时前
Go中的泛型编程和reflect(反射)
开发语言·笔记·golang