Files
ESPC3-wireless/app/drivers/ws2812_spi/ws2812_spi.c
LokLiang e9b5e42ef2 重构项目配置并添加对 SBDEMO 的支持
更新多个 ESP32 芯片(C2、C3、S3)
的版本配置为 SBDEMO
产品添加新的配置文件实现板卡和应用程序配置模块添加 CRC 实用函数修改系统日志以支持动态日志控制更新 Kconfig 和 sdkconfig 的默认设置以适应新产品
2025-02-21 10:38:22 +08:00

277 lines
7.6 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file ws2812_spi.c
* @author your name (you@domain.com)
* @brief 通过 SPI 硬件模拟 PWM 信号,驱动 WS2812 的程序
* @version 0.1
* @date 2023-10-05
*
* @copyright Copyright (c) 2023
*
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_attr.h"
#include "esp_rom_gpio.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "soc/spi_periph.h"
#include "soc/gpio_periph.h"
#include "config/board_config.h"
#include "ws2812_spi.h"
#include "os/os.h"
#define CONFIG_SYS_LOG_LEVEL SYS_LOG_LEVEL_INF
#define SYS_LOG_DOMAIN "LED_STRIP_SPI"
#include "sys_log.h"
#define SPI_CLK_HZ 8000000
#define WS_HIGH 0x3
#define WS_LOW 0x3F
#define WS_RESET ~0
#define RESET_TIME_US 50
#define RESET_BIT_LENGTH (SPI_CLK_HZ * (RESET_TIME_US) / 1000000)
#define _POW_Y 1.3
typedef struct ws2812_spi_led_strip_s
{
uint32_t buffer_size;
uint16_t pow_tbl[0x100];
uint32_t max_led_num;
uint8_t dma_buffer[0];
} __ws2812_spi_led_buf_t;
static struct
{
spi_host_device_t host_id;
spi_device_handle_t spi_hdl;
os_mutex_t mutex_hdl;
uint16_t spi_pin;
int max_transfer_sz;
} s_cm;
/**
* @brief 初始化工作环境。这将初始化 SPI 驱动并申请显示缓存到对象中。可重初始化
*
* @param led_strip[out] 灯带驱动缓存
* @param host_id SPI peripheral that controls this bus @ref spi_host_device_t
* @param max_led_num 最大可驱动灯条共多少个灯珠
*/
void ws2812_spi_led_strip_init(uint8_t host_id, uint16_t max_led_num)
{
SYS_ASSERT(max_led_num > 0, "");
int max_transfer_sz = max_led_num * 24 + RESET_BIT_LENGTH * 2;
if (s_cm.max_transfer_sz > 0)
{
SYS_ASSERT(max_transfer_sz <= s_cm.max_transfer_sz, "");
}
if (s_cm.spi_hdl == NULL)
{
s_cm.max_transfer_sz = max_transfer_sz;
esp_err_t ret;
spi_bus_config_t buscfg = {
.miso_io_num = -1,
.mosi_io_num = -1,
.sclk_io_num = -1,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = max_transfer_sz};
// Initialize the SPI bus
ret = spi_bus_initialize(host_id, &buscfg, SPI_DMA_CH_AUTO);
ESP_ERROR_CHECK(ret);
spi_device_interface_config_t devcfg = {
.clock_speed_hz = SPI_CLK_HZ,
.mode = 0,
.spics_io_num = -1,
.queue_size = 1,
.pre_cb = NULL,
.command_bits = 0,
.address_bits = 0};
// Attach the LCD to the SPI bus
ret = spi_bus_add_device(host_id, &devcfg, &s_cm.spi_hdl);
ESP_ERROR_CHECK(ret);
s_cm.host_id = host_id;
s_cm.spi_pin = -1;
}
if (!os_mutex_is_valid(&s_cm.mutex_hdl))
{
os_mutex_create(&s_cm.mutex_hdl);
}
}
/**
* @brief 创建灯带驱动缓存
*
* @param led_strip[out] 灯带驱动缓存
* @param leds 可驱动灯条共多少个灯珠
*/
ws2812_spi_led_buf_t *ws2812_spi_led_new_buf(uint16_t leds)
{
__ws2812_spi_led_buf_t *led_strip;
SYS_ASSERT(leds > 0, "");
int max_transfer_sz = leds * 24 + RESET_BIT_LENGTH * 2;
if (s_cm.max_transfer_sz > 0)
{
SYS_ASSERT(max_transfer_sz <= s_cm.max_transfer_sz, "");
}
led_strip = heap_caps_malloc(sizeof(__ws2812_spi_led_buf_t) + max_transfer_sz, MALLOC_CAP_DMA); // Critical to be DMA memory.
memset(led_strip->dma_buffer, WS_RESET, max_transfer_sz);
led_strip->buffer_size = max_transfer_sz;
led_strip->max_led_num = leds;
float max = pow(0x100, _POW_Y) / 0x100;
for (int i = 0; i < 0x100; i++)
{
led_strip->pow_tbl[i] = (int)(pow(i, _POW_Y) / max) + 1;
if (led_strip->pow_tbl[i] > 0xFF)
{
led_strip->pow_tbl[i] = 0xFF;
}
}
led_strip->pow_tbl[0] = 0;
/* 使所有数据合法化 */
ws2812_spi_led_strip_clear((ws2812_spi_led_buf_t *)led_strip);
return (ws2812_spi_led_buf_t *)led_strip;
}
/**
* @brief 使所有显存清零。注意需要通过 ws2812_spi_led_strip_refresh() 方可把数据刷新到灯带中。
*
* @param buf 灯带驱动缓存
*/
void ws2812_spi_led_strip_clear(ws2812_spi_led_buf_t *buf)
{
__ws2812_spi_led_buf_t *led_strip = (__ws2812_spi_led_buf_t *)buf;
if (led_strip == NULL)
{
SYS_LOG_WRN("led strip buffer is null");
return;
}
memset(led_strip->dma_buffer, WS_RESET, led_strip->buffer_size);
for (int i = 0; i < led_strip->max_led_num; i++)
{
ws2812_spi_led_strip_set_pixel((ws2812_spi_led_buf_t *)led_strip, i, 0, 0, 0);
}
}
/**
* @brief 设一个灯珠为一个像素,设置单个像素的颜色。注意需要通过 ws2812_spi_led_strip_refresh() 方可把数据刷新到灯带中。
*
* @param buf 灯带驱动缓存
* @param index 灯像素序号0 .. led_strip->max_led_num - 1
* @param red 红基色亮度0 .. 255
* @param green 绿基色亮度0 .. 255
* @param blue 蓝基色亮度0 .. 255
*/
void ws2812_spi_led_strip_set_pixel(ws2812_spi_led_buf_t *buf, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
{
__ws2812_spi_led_buf_t *led_strip = (__ws2812_spi_led_buf_t *)buf;
if (led_strip == NULL)
{
SYS_LOG_WRN("led strip buffer is null");
return;
}
if (index > led_strip->max_led_num)
{
SYS_LOG_WRN("index: %u > led_strip->max_led_num: %u", index, led_strip->max_led_num);
return;
}
if (red > 255 || green > 255 || blue > 255)
{
SYS_LOG_WRN("param over range");
return;
}
/* 套指数表 */
red = led_strip->pow_tbl[red];
green = led_strip->pow_tbl[green];
blue = led_strip->pow_tbl[blue];
uint32_t color = (green & 0xFF) << 16 | (red & 0xFF) << 8 | (blue & 0xFF);
/* PWM 编码 */
for (int i = 0; i < 24; i++)
{
int pos = RESET_BIT_LENGTH + index * 24 + i;
led_strip->dma_buffer[pos] = ((color << i) & 0x800000) ? WS_HIGH : WS_LOW;
}
}
/**
* @brief 执行把显示缓存刷新到灯带
*
* @param buf 灯带驱动缓存
* @param leds 实际刷新的灯珠数
* @param pin 使用的引脚
* @retval 0 操作成功
* @retval != 0 操作失败
*/
int ws2812_spi_led_strip_refresh(ws2812_spi_led_buf_t *buf, uint32_t leds, uint8_t pin)
{
__ws2812_spi_led_buf_t *led_strip = (__ws2812_spi_led_buf_t *)buf;
if (led_strip == NULL || s_cm.spi_hdl == NULL)
{
SYS_LOG_ERR("led strip spi handler is null");
return -1;
}
if (leds > led_strip->max_led_num)
{
SYS_LOG_WRN("leds(%d) > led_strip->max_led_num(%d)", leds, led_strip->max_led_num);
leds = led_strip->max_led_num;
}
os_mutex_lock(&s_cm.mutex_hdl, OS_WAIT_FOREVER);
uint32_t empty_bytes = (led_strip->max_led_num - leds) * 24;
if (empty_bytes)
{
memset(&led_strip->dma_buffer[led_strip->buffer_size - empty_bytes], WS_RESET, empty_bytes);
}
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = led_strip->buffer_size * 8 - empty_bytes; // length is in bits
t.tx_buffer = led_strip->dma_buffer;
if (s_cm.spi_pin != pin)
{
if (s_cm.spi_pin > 0)
{
drv_gpio_pin_configure(s_cm.spi_pin, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP);
}
if (pin > 0)
{
drv_gpio_pin_configure(pin, _GPIO_DIR_OUT, _GPIO_PUD_PULL_UP);
esp_rom_gpio_connect_out_signal(pin, spi_periph_signal[s_cm.host_id].spid_out, true, false);
}
s_cm.spi_pin = pin;
}
esp_err_t err = spi_device_transmit(s_cm.spi_hdl, &t);
os_mutex_unlock(&s_cm.mutex_hdl);
ESP_ERROR_CHECK(err);
return err;
}