基于Freertos的ESP-IDF开发——7.WS2812B彩色灯循环

news2024/12/24 9:00:01

基于Freertos的ESP-IDF开发——7.WS2812B彩色灯循环

  • 0. 前言
  • 1. WS2812B简介
  • 2. 完整代码
  • 3. 演示效果
  • 4. 其他FreeRtos文章

0. 前言

本节使用WS2812B实现彩灯循环

开发环境:ESP-IDF 4.3
操作系统:Windows10 专业版
开发板:自制的ESP32-WROOM-32E

1. WS2812B简介

WS2812B彩灯是一种数字可编程LED灯,其中每个LED点都独立可编程控制灯光的颜色、亮度、饱和度、效果等多种参数,它的控制原理是基于控制芯片一般利用串行通讯协议和数据结构,实现对LED灯的控制。

WS2812B控制芯片内部集成了红、绿、蓝三个LED,在集成的控制电路中添加了DC-DC升压电路、正反器、调制驱动电路、信号整形电路、数据锁存控制电路等组成的数字控制电路,这些数字电路共同完成WS2812B的各项任务,其中信号整形电路主要用于提供时钟、数据的整形,实现输出数据的准确性。

通过设置不同的地址、端口和控制命令,可以实现对WS2812B灯的控制,其中控制命令是主控设备生成的,在通过PIN口发送时被WS2812B控制芯片使用,然后通过DC-DC升压电路将电流升压固定至5V,转换成直流电信号AP。在红、绿、蓝三个LED灯中,这些信号被分成三组,分别控制每个LED灯的亮度,改变灯的亮度,就可以实现不同的颜色、亮度、饱和度和效果的灯光表现。

根据以上原理,程序控制芯片就可以实现对灯的颜色、亮度、闪烁等控制,从而实现丰富的视觉效果和花样的变幻效果。

2. 完整代码

为了让你能够把它添加到main函数中,我没有使用编写库函数的方式。

#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <soc/rmt_struct.h>
#include <soc/dport_reg.h>
#include <driver/gpio.h>
#include <soc/gpio_sig_map.h>
#include <esp_intr_alloc.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <driver/rmt.h>
#include <esp_system.h>
#include <nvs_flash.h>
#include "esp_log.h"

#define TAG	"WS2812B"
#define delay_ms(ms) vTaskDelay((ms) / portTICK_RATE_MS)

#define WS2812_PIN				16			// WS2812 所连接的GPIO
#define DIVIDER					4			/* Above 4, timings start to deviate*/
#define DURATION				12.5		/* minimum time of a single RMT duration in nanoseconds based on clock */

// 逻辑0波形
#define PULSE_T0H	(  350 / (DURATION * DIVIDER));
#define PULSE_T0L	(  900 / (DURATION * DIVIDER));
// 逻辑1波形
#define PULSE_T1H	(  900 / (DURATION * DIVIDER));
#define PULSE_T1L	(  350 / (DURATION * DIVIDER));
#define PULSE_TRS	(50000 / (DURATION * DIVIDER));

#define MAX_PULSES	32			// 最大脉冲数
#define RMTCHANNEL	0			// RMT通道

typedef union {
	struct {
		uint32_t duration0:15;
		uint32_t level0:1;
		uint32_t duration1:15;
		uint32_t level1:1;
	};
	uint32_t val;
} rmtPulsePair;
// RMT脉冲对

#include <stdint.h>

typedef unsigned int	uint32_t;

typedef union {
  struct  {
    uint8_t r, g, b;
  };
  uint32_t num;
} rgbVal;

extern void WS2812_Init(void);
extern void WS2812_SetColors(unsigned int length, rgbVal *array);

inline rgbVal makeRGBVal(uint8_t r, uint8_t g, uint8_t b)
{
  rgbVal v;
  v.r = r;
  v.g = g;
  v.b = b;
  return v;
}

static uint8_t *ws2812_buffer = NULL;
static unsigned int ws2812_pos, ws2812_len, ws2812_half;
static xSemaphoreHandle ws2812_sem = NULL;
static intr_handle_t rmt_intr_handle = NULL;
static rmtPulsePair ws2812_bits[2];

void ws2812_initRMTChannel(int rmtChannel)
{
	RMT.apb_conf.fifo_mask = 1;  //enable memory access, instead of FIFO mode.
	RMT.apb_conf.mem_tx_wrap_en = 1; //wrap around when hitting end of buffer
	RMT.conf_ch[rmtChannel].conf0.div_cnt = DIVIDER;
	RMT.conf_ch[rmtChannel].conf0.mem_size = 1;
	RMT.conf_ch[rmtChannel].conf0.carrier_en = 0;
	RMT.conf_ch[rmtChannel].conf0.carrier_out_lv = 1;
	RMT.conf_ch[rmtChannel].conf0.mem_pd = 0;
	RMT.conf_ch[rmtChannel].conf1.rx_en = 0;
	RMT.conf_ch[rmtChannel].conf1.mem_owner = 0;
	RMT.conf_ch[rmtChannel].conf1.tx_conti_mode = 0;    //loop back mode.
	RMT.conf_ch[rmtChannel].conf1.ref_always_on = 1;    // use apb clock: 80M
	RMT.conf_ch[rmtChannel].conf1.idle_out_en = 1;
	RMT.conf_ch[rmtChannel].conf1.idle_out_lv = 0;
}

// 将要发送的颜色信息
void ws2812_copy()
{
	unsigned int i, j, offset, len, bit;
	offset = ws2812_half * MAX_PULSES;
	ws2812_half = !ws2812_half;
	len = ws2812_len - ws2812_pos;
	if (len > (MAX_PULSES / 8)){
		len = (MAX_PULSES / 8);
	}
	if (!len) {
		for (i = 0; i < MAX_PULSES; i++)
		RMTMEM.chan[RMTCHANNEL].data32[i + offset].val = 0;
		return;
	}
	for (i = 0; i < len; i++) {
		bit = ws2812_buffer[i + ws2812_pos];
		for (j = 0; j < 8; j++, bit <<= 1) {
			RMTMEM.chan[RMTCHANNEL].data32[j + i * 8 + offset].val =
			ws2812_bits[(bit >> 7) & 0x01].val;
		}
		if (i + ws2812_pos == ws2812_len - 1){
			RMTMEM.chan[RMTCHANNEL].data32[7 + i * 8 + offset].duration1 = PULSE_TRS;
		}
	}
	for (i *= 8; i < MAX_PULSES; i++){
		RMTMEM.chan[RMTCHANNEL].data32[i + offset].val = 0;
	}
	ws2812_pos += len;
	return;
}

void ws2812_handleInterrupt(void *arg)
{
	portBASE_TYPE taskAwoken = 0;
	if (RMT.int_st.ch0_tx_thr_event) {				// 发送事件中断
		ws2812_copy();					// 
		RMT.int_clr.ch0_tx_thr_event = 1;			// 消除发送事件中断
	}else if (RMT.int_st.ch0_tx_end && ws2812_sem) {// 发送成功中断
		xSemaphoreGiveFromISR(ws2812_sem, &taskAwoken);
		RMT.int_clr.ch0_tx_end = 1;					// 清除发送完成中断
	}
	return;
}

void WS2812_Init(void)
{
	DPORT_SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_RMT_CLK_EN);	// 设置RMT时钟使能
	DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST);	// 清除RMT重启使能
	rmt_set_pin((rmt_channel_t)RMTCHANNEL, RMT_MODE_TX, (gpio_num_t)WS2812_PIN);// 设置RMT通道:0,模式:发送,引脚
	ws2812_initRMTChannel(RMTCHANNEL);					// 初始化RMT的0通道
	RMT.tx_lim_ch[RMTCHANNEL].limit = MAX_PULSES;		// 发送超过MAX_PULSES个脉冲会产生中断
	RMT.int_ena.ch0_tx_thr_event = 1;					// 发送事件标志位置1
	RMT.int_ena.ch0_tx_end = 1;							// 发送完成标志位置1
	// 配置ws2812的逻辑电平长度和定义
	ws2812_bits[0].level0 = 1;
	ws2812_bits[0].level1 = 0;
	ws2812_bits[0].duration0 = PULSE_T0H;
	ws2812_bits[0].duration1 = PULSE_T0L;
	ws2812_bits[1].level0 = 1;
	ws2812_bits[1].level1 = 0;
	ws2812_bits[1].duration0 = PULSE_T1H;
	ws2812_bits[1].duration1 = PULSE_T1L;

	// ESP分配中断(中断源,标志位,中断处理函数,传入参数,中断句柄)
	esp_intr_alloc(ETS_RMT_INTR_SOURCE, 0, ws2812_handleInterrupt, NULL, &rmt_intr_handle);
}

void WS2812_SetColors(unsigned int length, rgbVal *array)
{
	unsigned int i;
	ws2812_len = (length * 3) * sizeof(uint8_t);	// (颜色值长度3*8,三个字节) * 灯数量
	ws2812_buffer = malloc(ws2812_len);				// 申请(灯数*颜色*3)字节内存
	for (i = 0; i < length; i++) {					// 把N个灯的RGB颜色按顺序填入
		ws2812_buffer[0 + i * 3] = array[i].g;
		ws2812_buffer[1 + i * 3] = array[i].r;
		ws2812_buffer[2 + i * 3] = array[i].b;
	}
	ws2812_pos = 0;
	ws2812_half = 0;
	ws2812_copy();
	if (ws2812_pos < ws2812_len){
		ws2812_copy();
	}
	ws2812_sem = xSemaphoreCreateBinary();			// 创建一个二值信号量
	RMT.conf_ch[RMTCHANNEL].conf1.mem_rd_rst = 1;	// 设置此位,重置读取内存地址
	RMT.conf_ch[RMTCHANNEL].conf1.tx_start = 1;		// 设置此位,开始发送数据
	xSemaphoreTake(ws2812_sem, portMAX_DELAY);		// 获取二值信号量等待发送完成(ws2812_handleInterrupt)
	vSemaphoreDelete(ws2812_sem);					// 删除二值信号量
	ws2812_sem = NULL;								// 二值信号量设置空
	free(ws2812_buffer);							// 删除颜色值缓存
	return;
}



typedef enum {
	GreenAdd,		// 绿色值+
	RedMinus,		// 红色值-
	BlueAdd,		// 蓝色值+
	GreenMinus,		// 绿色值-
	RedAdd,			// 红色值+
	BlueMinus,		// 蓝色值-
} color_change_t;


// RGB灯彩虹效果,如果有多个灯串联可以看到彩虹效果
void WS2812_Rainbow_Task(void *pvParameters)
{
	const uint8_t anim_step = 10;		// 颜色值步进,0-255,每次变化1
	const uint8_t anim_max = 250;		// 最大值
	const uint8_t pixel_count = 6;		// 灯的数量(开发板只有一个,WS2812支持单总线串联控制)
	const uint8_t delay = 20;			// 单次变化间隔延时
	WS2812_Init();						// 初始化WS2812

	rgbVal color = makeRGBVal(anim_max, 0, 0);
	uint8_t step = 0;
	rgbVal color2 = makeRGBVal(anim_max, 0, 0);
	uint8_t step2 = 0;
	rgbVal *pixels;
	pixels = malloc(sizeof(rgbVal) * pixel_count);
	while (1) {
		color = color2;
		step = step2;
		for (uint8_t i = 0; i < pixel_count; i++) {
			pixels[i] = color;
			if (i == 1) {
				color2 = color;
				step2 = step;
			}
			switch (step) {
				case GreenAdd:
					color.g += anim_step;
					if (color.g >= anim_max)
						step++;
				break;
				case RedMinus:
					color.r -= anim_step;
					if (color.r == 0)
						step++;
				break;
				case BlueAdd:
					color.b += anim_step;
					if (color.b >= anim_max)
						step++;
				break;
				case GreenMinus:
					color.g -= anim_step;
					if (color.g == 0)
						step++;
				break;
				case RedAdd:
					color.r += anim_step;
					if (color.r >= anim_max)
						step++;
				break;
				case BlueMinus:
					color.b -= anim_step;
					if (color.b == 0)
						step = 0;
				break;
			}
		}
		WS2812_SetColors(pixel_count, pixels);// 写入颜色(灯数量,颜色值数组)
		ESP_LOGI(TAG, "Color Value R:%d G:%d B:%d",pixels[0].r,pixels[0].g,pixels[0].b);
		ESP_LOGI(TAG, "Color Value R:%d G:%d B:%d",pixels[1].r,pixels[1].g,pixels[1].b);
		ESP_LOGI(TAG, "Color Value R:%d G:%d B:%d",pixels[2].r,pixels[2].g,pixels[2].b);

		delay_ms(delay);
	}
}

void app_main()
{
	xTaskCreate(WS2812_Rainbow_Task, "WS2812_Rainbow_Task", 4096, NULL, 10, NULL);
	return;
}

3. 演示效果

效果如下,这颗灯珠一如既往的刺眼。
在这里插入图片描述

4. 其他FreeRtos文章

基于Freertos的ESP-IDF开发——0.Windows下espidf的环境搭建
基于Freertos的ESP-IDF开发——1.HelloWorld
基于Freertos的ESP-IDF开发——2.点亮一颗LED
基于Freertos的ESP-IDF开发——3.使用任务(上)
基于Freertos的ESP-IDF开发——3.使用任务(中)
基于Freertos的ESP-IDF开发——3.使用任务(下)
基于Freertos的ESP-IDF开发——4.使用任务的方式来点亮LED灯
基于Freertos的ESP-IDF开发——5.使用按键[不带消抖、带消抖、长按短按识别]
基于Freertos的ESP-IDF开发——6.使用DHT1温湿度传感器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/549927.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【软考中级】软件设计师选择题题集(一)

海明校验码是在n个数据位之外增设k个校验位,从而形成一个k+n位的新的码字, 使新的码字的码距比较均匀地拉大。n与k的关系是(1)。 (1)A.2k - 1≥n + k  B.2n - 1≤ n + k   C.n = k  D.n-1≤k 【答案】A 【解析】 【答案】B A 【解析】 在采用结构化方法进行系统分析时,…

MySQL高级篇——索引失效的11种情况

导航&#xff1a; 【黑马Java笔记踩坑汇总】Java基础进阶JavaWebSSMSpringBoot瑞吉外卖SpringCloud黑马旅游谷粒商城学成在线设计模式牛客面试题 目录 1. 索引优化思路 2. 索引失效的11种情况 2.0. 数据准备 2.1 要尽量满足全值匹配 2.2 要满足最佳左前缀法则 2.3 主键插…

详解Jetpack Compose中的Modifier修饰符

前言 本文将会介绍Jetpack Compose中的Modifier。在谷歌官方文档中它的描述是这么一句话&#xff1a;Modifier元素是一个有序、不可变的集合&#xff0c;它可以往Jetpack Compose UI元素中添加修饰或者各种行为。例如&#xff0c;背景、填充和单击事件监听器装饰或添加行为到文…

依次对数组中的元素进行逻辑非和异或判断numpy.logical_not()numpy.logical_xor()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 依次对数组中的元素进行逻辑非和异或判断 numpy.logical_not() numpy.logical_xor() [太阳]选择题 下列代码中np.logical_xor(A, B)输出的结果是&#xff1f; import numpy as np A [True, …

苹果新增了28个以AI为重点的职位,希望利用这种AI技术改变iPhone和iPad

&#x1f680; 苹果新增了28个以AI为重点的职位&#xff0c;希望利用这种AI技术改变iPhone和iPad 苹果公司限制了使用ChatGPT等外部AI工具&#xff0c;正在开发自主的生成式AI技术。 苹果此前新增了28个以AI为重点的职位&#xff0c;希望利用这种AI技术改变iPhone和iPad。 此…

IntelliJ 导出和导入配置文件

导出&#xff1a; File->Manage IDE Setting->Export Settings…->选择导出目录&#xff0c;可以得到一个 settings.zip 的文件 导入方法&#xff1a; File->Manage IDE Setting->Import Settings…->选择需要导入的文件即可&#xff0c;导入之后重启IDE。

13-2-CSS3-渐变色、2D转换、3D转换

一、渐变色 CSS渐变色&#xff08;Gradient&#xff09;是指在元素背景中使用两种或多种不同的颜色进行过渡&#xff0c;超过两个颜色可以形成更为细腻的渐变效果。常见的CSS渐变色有线性渐变和径向渐变。 1. 线性渐变&#xff1a;Linear Gradients 向下/向上/向左/向右/对角…

CyberLink的屏幕录制软件Screen Recorder Deluxe 4.3版本在win10系统的下载与安装配置教程

目录 前言一、Screen Recorder Deluxe安装二、使用配置总结 前言 Screen Recorder Deluxe是由CyberLink公司开发的一款高效的屏幕录制工具&#xff0c;该软件提供了全面而易于使用的功能&#xff0c;可以满足广泛的屏幕录制需求。 Screen Recorder Deluxe的一些主要特点&…

代码随想录算法训练营 Day 46 | 139.单词拆分,关于多重背包,你该了解这些!,背包问题总结篇!

139.单词拆分 讲解链接&#xff1a;代码随想录-139.单词拆分 确定 dp 数组以及下标的含义&#xff1a;dp[i] : 字符串长度为 i 的话&#xff0c;dp[i]为 true&#xff0c;表示可以拆分为一个或多个在字典中出现的单词。 确定递推公式&#xff1a;如果确定 dp[j] 是 true&…

PyTorch-Dataset

Dataset类&#xff1a; 如何获取数据及标签。 Dataloader类&#xff1a;为之后的网络提供不同的数据形式。 1. 数据文件夹表示&#xff1a; from torch.utils.data import Dataset from PIL import Image import osclass MyData(Dataset):def __init__(self, root_dir, label_d…

三十七、雪崩问题、Sentinel、簇点链路、流控模式

1、初识Sentinel 1.1雪崩问题 微服务调用链路中的某个服务故障&#xff0c;引起整个链路中的所有微服务都不可用&#xff0c;这就是雪崩。 解决雪崩问题的常见方式有四种&#xff1a; 超时处理&#xff1a;设定超时时间&#xff0c;请求超过一定时间没有响应就返回错误信息&am…

Rust每日一练(Leetday0006) 三数之和、字母组合、四数之和

目录 16. 最接近的三数之和 3Sum Closest &#x1f31f;&#x1f31f; 17. 电话号码的字母组合 Letter-combinations-of-a-phone-number &#x1f31f;&#x1f31f; 18. 四数之和 4Sum &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Rust…

肝一肝设计模式【九】-- 享元模式

系列文章目录 肝一肝设计模式【一】-- 单例模式 传送门 肝一肝设计模式【二】-- 工厂模式 传送门 肝一肝设计模式【三】-- 原型模式 传送门 肝一肝设计模式【四】-- 建造者模式 传送门 肝一肝设计模式【五】-- 适配器模式 传送门 肝一肝设计模式【六】-- 装饰器模式 传送门 肝…

java枚举类解读

目录 为什么需要枚举类 枚举类的使用 枚举类的实现 枚举类的属性 自定义枚举类 使用enum定义枚举类 常见enum的使用场景 switch 向枚举中添加新方法 覆盖枚举的方法 Enum类的主要方法 实现接口的枚举类 为什么需要枚举类 类的对象只有有限个&#xff0c;确定的。…

如何将map与对象进行转换

Spring Boot内置了一个强大的JSON转换器Jackson&#xff0c;可以实现将JSON字符串或Map类型的数据转换成Java对象。以下是将Map类型的数据转换成Java对象的示例代码&#xff1a; import com.fasterxml.jackson.databind.ObjectMapper;//定义Java对象 public class User {priva…

oracle客户端的安装教程

文章目录 一、安装前的准备工作 1.1、百度网盘安装包的连接 1.2、百度网盘oracle11g软件包 二、oracle数据库客户端的安装与数据的准备 安装步骤 前言 本文主要讲解oracle客户端的安装与简单使用过程 一、安装前的准备工作 1.1、百度网盘安装包的连接 客户端的软件包 …

STM32 FMC篇-SDRAM(IS42S16400J)

IS42S16400J 这个东西太常见啦&#xff0c;长方形的。不会过多解释&#xff0c;详细请阅读它的数据手册。 IS42S16400J是一种高速同步动态随机存储器(SDRAM)&#xff0c;64Mb的存储容量&#xff0c;采用4个bank&#xff0c;每个bank大小为16Mb&#xff0c;总线宽度为16位&…

【CW32开发】00 开发环境搭建和示例代码运行

1.下载MDK 2.下载芯片相关的固件库 我用的是芯片是CW32F030系列&#xff0c;所以下载相应的固件库 下载地址&#xff1a;https://www.whxy.com/support/filelist/13 3.安装固件库 解压下载的文件&#xff0c;并在cw32f030-stdperiph-lib\IdeSupport\MDK路径下安装固件库 …

064:cesium设置点划线材质(material-8)

第064个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置点划线材质,请参考源代码,了解PolylineDashMaterialProperty的应用。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)相关API参考:专栏…

CBFS Vault 2022 for .NET Crack

将多个文件打包到一个 Vault - 一个“文件中的文件系统”&#xff0c;完成每个文件的压缩、透明加密和随机读/写访问。 亮点包括新的日记选项、用于更好地控制和跟踪的新事件&#xff0c;以及一系列核心性能和可用性改进 [了解更多]。 CBFS保险库 在任何地方存储一个完整的文件…