Files
ESPC3-wireless/app/drivers/ws2812_spi/ws2812_spi.c

281 lines
7.7 KiB
C
Raw Normal View History

2025-02-13 17:17:07 +08:00
/**
* @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 "ws2812_spi.h"
#include "drivers/pin_io/pin_io.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
2025-02-19 19:58:59 +08:00
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;
2025-02-13 17:17:07 +08:00
static struct
{
2025-02-19 19:58:59 +08:00
spi_host_device_t host_id;
spi_device_handle_t spi_hdl;
2025-02-13 17:17:07 +08:00
os_mutex_t mutex_hdl;
2025-02-19 19:58:59 +08:00
uint16_t spi_pin;
int max_transfer_sz;
2025-02-13 17:17:07 +08:00
} s_cm;
/**
* @brief SPI
*
2025-02-19 19:58:59 +08:00
* @param led_strip[out]
* @param host_id SPI peripheral that controls this bus @ref spi_host_device_t
2025-02-13 17:17:07 +08:00
* @param max_led_num
*/
2025-02-19 19:58:59 +08:00
void ws2812_spi_led_strip_init(uint8_t host_id, uint16_t max_led_num)
2025-02-13 17:17:07 +08:00
{
SYS_ASSERT(max_led_num > 0, "");
2025-02-19 19:58:59 +08:00
int max_transfer_sz = max_led_num * 24 + RESET_BIT_LENGTH * 2;
2025-02-13 17:17:07 +08:00
2025-02-19 19:58:59 +08:00
if (s_cm.max_transfer_sz > 0)
{
SYS_ASSERT(max_transfer_sz <= s_cm.max_transfer_sz, "");
2025-02-13 17:17:07 +08:00
}
2025-02-19 19:58:59 +08:00
if (s_cm.spi_hdl == NULL)
2025-02-13 17:17:07 +08:00
{
2025-02-19 19:58:59 +08:00
s_cm.max_transfer_sz = max_transfer_sz;
2025-02-13 17:17:07 +08:00
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
2025-02-19 19:58:59 +08:00
ret = spi_bus_initialize(host_id, &buscfg, SPI_DMA_CH_AUTO);
2025-02-13 17:17:07 +08:00
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
2025-02-19 19:58:59 +08:00
ret = spi_bus_add_device(host_id, &devcfg, &s_cm.spi_hdl);
2025-02-13 17:17:07 +08:00
ESP_ERROR_CHECK(ret);
2025-02-19 19:58:59 +08:00
s_cm.host_id = host_id;
s_cm.spi_pin = -1;
2025-02-13 17:17:07 +08:00
}
if (!os_mutex_is_valid(&s_cm.mutex_hdl))
{
os_mutex_create(&s_cm.mutex_hdl);
}
2025-02-19 19:58:59 +08:00
}
/**
* @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, "");
2025-02-13 17:17:07 +08:00
2025-02-19 19:58:59 +08:00
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);
2025-02-13 17:17:07 +08:00
led_strip->buffer_size = max_transfer_sz;
2025-02-19 19:58:59 +08:00
led_strip->max_led_num = leds;
2025-02-13 17:17:07 +08:00
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(led_strip);
2025-02-19 19:58:59 +08:00
return (ws2812_spi_led_buf_t *)led_strip;
2025-02-13 17:17:07 +08:00
}
/**
* @brief 使 ws2812_spi_led_strip_refresh()
*
2025-02-19 19:58:59 +08:00
* @param buf
2025-02-13 17:17:07 +08:00
*/
2025-02-19 19:58:59 +08:00
void ws2812_spi_led_strip_clear(ws2812_spi_led_buf_t *buf)
2025-02-13 17:17:07 +08:00
{
2025-02-19 19:58:59 +08:00
__ws2812_spi_led_buf_t *led_strip = (__ws2812_spi_led_buf_t *)buf;
if (led_strip == NULL)
2025-02-13 17:17:07 +08:00
{
SYS_LOG_WRN("led strip buffer is null");
return;
}
2025-02-19 19:58:59 +08:00
memset(led_strip->dma_buffer, WS_RESET, led_strip->buffer_size);
2025-02-13 17:17:07 +08:00
for (int i = 0; i < led_strip->max_led_num; i++)
{
ws2812_spi_led_strip_set_pixel(led_strip, i, 0, 0, 0);
}
}
/**
* @brief ws2812_spi_led_strip_refresh()
*
2025-02-19 19:58:59 +08:00
* @param buf
* @param index 0 .. led_strip->max_led_num - 1
* @param red 0 .. 255
* @param green 绿0 .. 255
* @param blue 0 .. 255
2025-02-13 17:17:07 +08:00
*/
2025-02-19 19:58:59 +08:00
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)
2025-02-13 17:17:07 +08:00
{
2025-02-19 19:58:59 +08:00
__ws2812_spi_led_buf_t *led_strip = (__ws2812_spi_led_buf_t *)buf;
if (led_strip == NULL)
2025-02-13 17:17:07 +08:00
{
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;
2025-02-19 19:58:59 +08:00
led_strip->dma_buffer[pos] = ((color << i) & 0x800000) ? WS_HIGH : WS_LOW;
2025-02-13 17:17:07 +08:00
}
}
/**
* @brief
*
2025-02-19 19:58:59 +08:00
* @param buf
* @param leds
* @param pin 使
2025-02-13 17:17:07 +08:00
* @retval 0
* @retval != 0
*/
2025-02-19 19:58:59 +08:00
int ws2812_spi_led_strip_refresh(ws2812_spi_led_buf_t *buf, uint32_t leds, uint8_t pin)
2025-02-13 17:17:07 +08:00
{
2025-02-19 19:58:59 +08:00
__ws2812_spi_led_buf_t *led_strip = (__ws2812_spi_led_buf_t *)buf;
if (led_strip == NULL || s_cm.spi_hdl == NULL)
2025-02-13 17:17:07 +08:00
{
SYS_LOG_ERR("led strip spi handler is null");
return -1;
}
if (leds > led_strip->max_led_num)
{
2025-02-19 19:58:59 +08:00
SYS_LOG_WRN("leds(%d) > led_strip->max_led_num(%d)", leds, led_strip->max_led_num);
2025-02-13 17:17:07 +08:00
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)
{
2025-02-19 19:58:59 +08:00
memset(&led_strip->dma_buffer[led_strip->buffer_size - empty_bytes], WS_RESET, empty_bytes);
2025-02-13 17:17:07 +08:00
}
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length = led_strip->buffer_size * 8 - empty_bytes; // length is in bits
2025-02-19 19:58:59 +08:00
t.tx_buffer = led_strip->dma_buffer;
2025-02-13 17:17:07 +08:00
2025-02-19 19:58:59 +08:00
if (s_cm.spi_pin != pin)
2025-02-13 17:17:07 +08:00
{
2025-02-19 19:58:59 +08:00
if (s_cm.spi_pin > 0)
2025-02-13 17:17:07 +08:00
{
2025-02-19 19:58:59 +08:00
cfg_board_pin_io_t cfg_pin = {.pin = s_cm.spi_pin, .en_lev = 1};
2025-02-13 17:17:07 +08:00
pin_cfg_output(&cfg_pin);
pin_set_valid(&cfg_pin, true);
}
if (pin > 0)
{
cfg_board_pin_io_t cfg_pin = {.pin = pin, .en_lev = 1};
pin_cfg_output(&cfg_pin);
gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT);
2025-02-19 19:58:59 +08:00
esp_rom_gpio_connect_out_signal(pin, spi_periph_signal[s_cm.host_id].spid_out, true, false);
2025-02-13 17:17:07 +08:00
}
2025-02-19 19:58:59 +08:00
s_cm.spi_pin = pin;
2025-02-13 17:17:07 +08:00
}
2025-02-19 19:58:59 +08:00
esp_err_t err = spi_device_transmit(s_cm.spi_hdl, &t);
2025-02-13 17:17:07 +08:00
os_mutex_unlock(&s_cm.mutex_hdl);
ESP_ERROR_CHECK(err);
return err;
}