【ESP32学习】驱动ws2812
说明
本片文章不做详细介绍,博主本身学习程度不高,没有系统学过FreeRtos操作系统,因此本文逻辑可能会有点混乱,而且不会进行原理的分析。本文主要是站在一个小白的角度,一步步从完全不了解FreeRtos到初步能在Rtos里面运行和修改例层。
先介绍WS2812b
ws2812b是一种能显示RGB三原色组合成的颜色显示灯,封装为5050,有四个引脚,分别是VCC、VSS、DIN、DOUT。引脚介绍如图(1)
电源供电范围规格书上描述为3.5-5.3V,但是实测3.3V也能驱动,逻辑电平为-0.5V-VDD-0.5V;这里DIN是通信接口的输入,RGB的数据就是从这里输入,而WS2812b具有级联功能,主要是通过DOUT实现,DIN输入的数据会被第一级WS2812截取24位,后面的数据会通过DOUT传输给下一个WS2812B,从而实现级联。如图(2)
上面是WS2812b的硬件接口,相对比较简单,只有一个数据口,逻辑也相对简单易懂,但是在时序方面则是相对比较严格。WS2812传输因为只有一跟数据线,所以只能传输0或1,因此在时序控制方面需要区分开1和0的时序。根据规格书,时序要求为图(3)
因此软件上可以通过IO口,输出高低电平来模拟时序。按照标准控制时序,传输一个位时间为1.25us,一个WS2812需要24位,即30us,假设100个WS2812组成矩阵,则一帧的时间为3ms,即可以实现333HZ的刷新率,对于30HZ刷新率最大可以控制1000个WS2812,即33*33.
RTOS理解学习
博主是第一次学习RTOS,并不懂RTOS的内部各种原理,所以一开始学习十分痛苦,RTOS函数与程序函数傻傻分不清,一些接口传递参数也分辨不清(特别是对于带有结构体、指针的接口,基本上看到就懵了,此处主要是C基础不扎实,因为都是爱好自学的,学得并不系统)
先说结论,通过看了一轮百度、博文的介绍后,一堆专业名词,到头来还是没看懂RTOS,最后看到一篇博文介绍API接口,想着先看看接口函数,再对RTOS理解。结果看了几遍RTOS的API接口函数之后,再对比源码,发现好像我并不需要了解RTOS的底层原理,我最主要能弄懂各个API接口以及各个接口的传递参数,其实就可以修改源码了。
**所以,如果不是为了学习底层,仅仅是为了使用以及修改代码的话,可以从API接口开始学习,会有奇效!!!**而在学习之处也不需要把所有API接口都搞懂,关于任务创建删除、消息列队、任务调度这几个先搞懂,就可以满足大部分需求了。
ESP32驱动WS2812代码
先贴源码
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "nvs_flash.h"
#include "esp_task_wdt.h"
#include "time.h"
#include "unistd.h"
#define BLINK_GPIO 2 //定义一个IO,用于WS2812通信
#define BLINK1_GPIO 0 //定义一个IO,用于呼吸灯
#define ws2812b_din_pin_set() gpio_set_level(BLINK_GPIO, 1) //WS2812宏定义,输出高电平
#define ws2812b_din_pin_rst() gpio_set_level(BLINK_GPIO, 0); //WS2812宏定义,输出低电平
TaskHandle_t start_task_handler; //创建test_task任务句柄
void delay_ns(int data) //假设是100ns,实际上时间不确定
{
int i;
for(i=0;i<(data*20);i++);
}
void ws2812b_rst(void)
{
ws2812b_din_pin_set();
//esp_rom_delay_us(300);
delay_ns(500); //50us
ws2812b_din_pin_rst();
}
void ws2812b_writebyte_byt(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(dat & 0x80)
{
ws2812b_din_pin_set();
//esp_rom_delay_us(780);
usleep(1);
ws2812b_din_pin_rst();
delay_ns(5);
}
else
{
ws2812b_din_pin_set();
delay_ns(3); //延时***ns
ws2812b_din_pin_rst();
usleep(1); //延时1us
}
dat <<= 1;
}
}
void ws2812b_write_rgb_byte(unsigned char r_data, unsigned char g_data, unsigned char b_data)
{
ws2812b_writebyte_byt(r_data);
ws2812b_writebyte_byt(g_data);
ws2812b_writebyte_byt(b_data);
}
void LED_Task(void *pvParameters)
{
char i=0;
while(1)
{
i=~i;
gpio_set_level(BLINK_GPIO, i);
delay();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void ws2812_task(void *pvParameters)
{
char i;
unsigned char kj;
unsigned char kk;
unsigned char kl;
while(1)
{
for(i=0;i<255;i++)
{
kj=random();
kk=random();
kl=random();
ws2812b_write_rgb_byte(kj,kk,kl);
ws2812b_rst();
printf("j=%d\r\n",kj);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
}
void delay_s(void *pvParameters)
{
int i;
while(1)
{
for(i=0;i<5000000;i++);
}
}
void start_task(void* pvParameters)
{
xTaskCreate(&ws2812_task,"ws2812_task",8192,NULL,4,NULL);
vTaskDelete(start_task_handler);
}
void app_main()
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
gpio_pad_select_gpio(BLINK_GPIO);
gpio_pad_select_gpio(BLINK1_GPIO);
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
gpio_set_direction(BLINK1_GPIO, GPIO_MODE_OUTPUT);
xTaskCreate(&start_task,"start_task",2048,NULL,5,start_task_handler);
esp_task_wdt_deinit();
}
上诉源码可以实现外接一个WS2812,通过Random函数随机产生RGB的组合颜色,其中控制WS2812的代码为
void ws2812b_writebyte_byt(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
if(dat & 0x80)
{
ws2812b_din_pin_set();
//esp_rom_delay_us(780);
usleep(1);
ws2812b_din_pin_rst();
delay_ns(5);
}
else
{
ws2812b_din_pin_set();
delay_ns(3); //延时***ns
ws2812b_din_pin_rst();
usleep(1); //延时1us
}
dat <<= 1;
}
}
实话说,这个delay_ns是博主通过实验估算的延时,单位为100ns,实际上似乎不是,因为没有仪器,所以测试不出实际的延时,然后配合usleep函数,延时1us,从而可以满足WS2812的时序要求,这份代码实际上是试出来的,因此1不具备指导性,只能作为参考。