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);
相关推荐
John_ToDebug4 小时前
判断力无法被 AI 替代,但它正在被你亲手扔掉
经验分享·笔记
sunshine22 girl5 小时前
Java学习二 基本语法2, 算术运算符
java·学习
柳鲲鹏6 小时前
运气学概论:提纲草案
笔记
衡石科技8 小时前
Data_Agent记忆机制与经验复用衡石分析智能体会话记忆与长期学习技术解析
人工智能·科技·学习·算法·企业级bi
传奇开心果编程8 小时前
【Xilem基础语法学与练】第7课:Xilem与Masonry底层构建技术解析
学习·rust·前端框架
疯狂打码的少年8 小时前
【计算机网络】子网掩码与子网划分计算(含CIDR表示法)
服务器·笔记·计算机网络·php
想要入门的程序猿9 小时前
回调函数学习
java·网络·学习
码上上班9 小时前
iptables学习; firewalld学习;ufw学习
学习
RisunJan9 小时前
丝连族谱 0452 — 功能拆解与使用手册
学习
老王爱玩车9 小时前
深入理解指针2
c语言·开发语言·学习