使用ESP32驱动WS2812b
踩过的坑放在前面
通过阅读ws2812b的手册可以知道它使用的通信协议,高低电平的变化在ns这个级别,但是esp-idf库中提供的延时函数只有s和ms级别的。
#include <unistd.h>
sleep(1);// 延时1s
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
vTaskDelay(100 / portTICK_PERIOD_MS); // 延时100ms
之前在stm32的学习过程中,可以通过while循环获取一个ns级别的延时,方法如下:
/*
假设主频是72000000Hz,也就是单片机1s进行72M次运算,可以换算出
单片机100ns进行7.2此运算。
*/
void delay_ns(uint32_t ns)
{
ns*=7;
while(ns--);
}
之前我用这个办法在stm32上实现过驱动ws2812b,可是同样的方法搬到ESP32就不好使了。
使用Remote Control Transceiver (RMT)轻松驱动ws2812b
经过上面的一番折腾,没能成功驱动,在查找资料的过程中偶然看到了官方教程😶(挺让人无语的,我不知道有这个外设)
乐鑫科技官方驱动ws2812b教程
看完教程后先去官方文档中了解了一下这个外设
官方文档对RMT的介绍
然后参考着例程源码,把关键代码copy过来成功驱动!
#include <stdio.h>
#include <unistd.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "driver/rmt_tx.h"
#define LED_PIN 2
#define LED_NUM 20
typedef struct ledcolor
{
uint8_t G;
uint8_t R;
uint8_t B;
} ledcolor;
void app_main(void)
{
// 初始化RMT的一个通道
printf("New RMT_CH...\n");
rmt_channel_handle_t LED_CH = NULL; // LED的通道
rmt_tx_channel_config_t LED_CH_CONFIG =
{
.clk_src = RMT_CLK_SRC_DEFAULT, // 选择时钟资源:默认资源
.gpio_num = LED_PIN, // 配置rmt输出引脚
.mem_block_symbols = 64, // 增大块的大小可以使 LED 减少闪烁?
.resolution_hz = 10000000, // 频率为10MHz,1个tick是0.1us
.trans_queue_depth = 4, // 设置后台待处理的事务数量?
};
rmt_new_tx_channel(&LED_CH_CONFIG, &LED_CH);
// 初始化一个RMT编码器
rmt_encoder_handle_t LED_ENCODE = NULL; // LED的编码器
rmt_bytes_encoder_config_t LED_ENCODE_CONFIG = {
.bit0 = {
.level0 = 1,
.duration0 = 3, // T0H=0.3us
.level1 = 0,
.duration1 = 9, // T0L=0.9us
},
.bit1 = {
.level0 = 1,
.duration0 = 9, // T1H=0.9us
.level1 = 0,
.duration1 = 3, // T1L=0.3us
},
.flags.msb_first = 1 // 大端先发
};
rmt_new_bytes_encoder(&LED_ENCODE_CONFIG, &LED_ENCODE);
// 使能RMT
rmt_enable(LED_CH);
// 配置发送
rmt_transmit_config_t RMT_TRANSMIT_CONFIG = {.loop_count = 0}; // 不循环发送
// 主循环
while (1)
{
// 发送数据
ledcolor led_buffer[LED_NUM] = {0};
ledcolor *sel = led_buffer;
for (int i = 0; i < LED_NUM; i++)
{
sel->R = 0xFF;
sel->G = 0;
sel->B = 0;
rmt_transmit(LED_CH, LED_ENCODE, &led_buffer, sizeof(led_buffer), &RMT_TRANSMIT_CONFIG);
rmt_tx_wait_all_done(LED_CH, 0xFF);
vTaskDelay(100 / portTICK_PERIOD_MS); // 延时100ms
sel->R = 0;
sel->G = 0;
sel->B = 0;
if (sel == &led_buffer[19])
{
sel = led_buffer;
continue;
}
sel++;
}
}
}
写在最后的碎碎念
esp-idf的文档很大一部分都是英文的,例程里的注释也是,阅读起来比较缓慢,但是也能理解。
今天浪费了大量时间去实现ns级别的延时函数,看来得去熟悉一下ESP32上已经有的所有外设,以防再次踩坑。