mirror of
https://github.com/esphome/esphome.git
synced 2024-11-28 12:46:22 +01:00
Add beken_spi_led_strip component (#6515)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
773951d85e
commit
46eee4a4f0
@ -52,6 +52,7 @@ esphome/components/bedjet/* @jhansche
|
|||||||
esphome/components/bedjet/climate/* @jhansche
|
esphome/components/bedjet/climate/* @jhansche
|
||||||
esphome/components/bedjet/fan/* @jhansche
|
esphome/components/bedjet/fan/* @jhansche
|
||||||
esphome/components/bedjet/sensor/* @javawizard @jhansche
|
esphome/components/bedjet/sensor/* @javawizard @jhansche
|
||||||
|
esphome/components/beken_spi_led_strip/* @Mat931
|
||||||
esphome/components/bh1750/* @OttoWinter
|
esphome/components/bh1750/* @OttoWinter
|
||||||
esphome/components/binary_sensor/* @esphome/core
|
esphome/components/binary_sensor/* @esphome/core
|
||||||
esphome/components/bk72xx/* @kuba2k2
|
esphome/components/bk72xx/* @kuba2k2
|
||||||
|
0
esphome/components/beken_spi_led_strip/__init__.py
Normal file
0
esphome/components/beken_spi_led_strip/__init__.py
Normal file
384
esphome/components/beken_spi_led_strip/led_strip.cpp
Normal file
384
esphome/components/beken_spi_led_strip/led_strip.cpp
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
#include "led_strip.h"
|
||||||
|
|
||||||
|
#ifdef USE_BK72XX
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "rtos_pub.h"
|
||||||
|
#include "spi.h"
|
||||||
|
#include "arm_arch.h"
|
||||||
|
#include "general_dma_pub.h"
|
||||||
|
#include "gpio_pub.h"
|
||||||
|
#include "icu_pub.h"
|
||||||
|
#undef SPI_DAT
|
||||||
|
#undef SPI_BASE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint32_t SPI_TX_DMA_CHANNEL = GDMA_CHANNEL_3;
|
||||||
|
|
||||||
|
// TODO: Check if SPI_PERI_CLK_DCO depends on the chip variant
|
||||||
|
static const uint32_t SPI_PERI_CLK_26M = 26000000;
|
||||||
|
static const uint32_t SPI_PERI_CLK_DCO = 120000000;
|
||||||
|
|
||||||
|
static const uint32_t SPI_BASE = 0x00802700;
|
||||||
|
static const uint32_t SPI_DAT = SPI_BASE + 3 * 4;
|
||||||
|
static const uint32_t SPI_CONFIG = SPI_BASE + 1 * 4;
|
||||||
|
|
||||||
|
static const uint32_t SPI_TX_EN = 1 << 0;
|
||||||
|
static const uint32_t CTRL_NSSMD_3 = 1 << 17;
|
||||||
|
static const uint32_t SPI_TX_FINISH_EN = 1 << 2;
|
||||||
|
static const uint32_t SPI_RX_FINISH_EN = 1 << 3;
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace beken_spi_led_strip {
|
||||||
|
|
||||||
|
static const char *const TAG = "beken_spi_led_strip";
|
||||||
|
|
||||||
|
struct spi_data_t {
|
||||||
|
SemaphoreHandle_t dma_tx_semaphore;
|
||||||
|
volatile bool tx_in_progress;
|
||||||
|
bool first_run;
|
||||||
|
};
|
||||||
|
|
||||||
|
static spi_data_t *spi_data = nullptr;
|
||||||
|
|
||||||
|
static void set_spi_ctrl_register(unsigned long bit, bool val) {
|
||||||
|
uint32_t value = REG_READ(SPI_CTRL);
|
||||||
|
if (val == 0) {
|
||||||
|
value &= ~bit;
|
||||||
|
} else if (val == 1) {
|
||||||
|
value |= bit;
|
||||||
|
}
|
||||||
|
REG_WRITE(SPI_CTRL, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_spi_config_register(unsigned long bit, bool val) {
|
||||||
|
uint32_t value = REG_READ(SPI_CONFIG);
|
||||||
|
if (val == 0) {
|
||||||
|
value &= ~bit;
|
||||||
|
} else if (val == 1) {
|
||||||
|
value |= bit;
|
||||||
|
}
|
||||||
|
REG_WRITE(SPI_CONFIG, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_dma_tx_enable(bool enable) {
|
||||||
|
GDMA_CFG_ST en_cfg;
|
||||||
|
set_spi_config_register(SPI_TX_EN, enable ? 1 : 0);
|
||||||
|
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||||
|
en_cfg.param = enable ? 1 : 0;
|
||||||
|
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_DMA_ENABLE, &en_cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spi_set_clock(uint32_t max_hz) {
|
||||||
|
int source_clk = 0;
|
||||||
|
int spi_clk = 0;
|
||||||
|
int div = 0;
|
||||||
|
uint32_t param;
|
||||||
|
if (max_hz > 4333000) {
|
||||||
|
if (max_hz > 30000000) {
|
||||||
|
spi_clk = 30000000;
|
||||||
|
} else {
|
||||||
|
spi_clk = max_hz;
|
||||||
|
}
|
||||||
|
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_DOWN, ¶m);
|
||||||
|
source_clk = SPI_PERI_CLK_DCO;
|
||||||
|
param = PCLK_POSI_SPI;
|
||||||
|
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_DCO, ¶m);
|
||||||
|
param = PWD_SPI_CLK_BIT;
|
||||||
|
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, ¶m);
|
||||||
|
} else {
|
||||||
|
spi_clk = max_hz;
|
||||||
|
#if CFG_XTAL_FREQUENCE
|
||||||
|
source_clk = CFG_XTAL_FREQUENCE;
|
||||||
|
#else
|
||||||
|
source_clk = SPI_PERI_CLK_26M;
|
||||||
|
#endif
|
||||||
|
param = PCLK_POSI_SPI;
|
||||||
|
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, ¶m);
|
||||||
|
}
|
||||||
|
div = ((source_clk >> 1) / spi_clk);
|
||||||
|
if (div < 2) {
|
||||||
|
div = 2;
|
||||||
|
} else if (div >= 255) {
|
||||||
|
div = 255;
|
||||||
|
}
|
||||||
|
param = REG_READ(SPI_CTRL);
|
||||||
|
param &= ~(SPI_CKR_MASK << SPI_CKR_POSI);
|
||||||
|
param |= (div << SPI_CKR_POSI);
|
||||||
|
REG_WRITE(SPI_CTRL, param);
|
||||||
|
ESP_LOGD(TAG, "target frequency: %d, actual frequency: %d", max_hz, source_clk / 2 / div);
|
||||||
|
}
|
||||||
|
|
||||||
|
void spi_dma_tx_finish_callback(unsigned int param) {
|
||||||
|
spi_data->tx_in_progress = false;
|
||||||
|
xSemaphoreGive(spi_data->dma_tx_semaphore);
|
||||||
|
spi_dma_tx_enable(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BekenSPILEDStripLightOutput::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up Beken SPI LED Strip...");
|
||||||
|
|
||||||
|
size_t buffer_size = this->get_buffer_size_();
|
||||||
|
size_t dma_buffer_size = (buffer_size * 8) + (2 * 64);
|
||||||
|
|
||||||
|
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
||||||
|
this->buf_ = allocator.allocate(buffer_size);
|
||||||
|
if (this->buf_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Cannot allocate LED buffer!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->effect_data_ = allocator.allocate(this->num_leds_);
|
||||||
|
if (this->effect_data_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Cannot allocate effect data!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->dma_buf_ = allocator.allocate(dma_buffer_size);
|
||||||
|
if (this->dma_buf_ == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Cannot allocate DMA buffer!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(this->buf_, 0, buffer_size);
|
||||||
|
memset(this->effect_data_, 0, this->num_leds_);
|
||||||
|
memset(this->dma_buf_, 0, dma_buffer_size);
|
||||||
|
|
||||||
|
uint32_t value = PCLK_POSI_SPI;
|
||||||
|
sddev_control(ICU_DEV_NAME, CMD_CONF_PCLK_26M, &value);
|
||||||
|
|
||||||
|
value = PWD_SPI_CLK_BIT;
|
||||||
|
sddev_control(ICU_DEV_NAME, CMD_CLK_PWR_UP, &value);
|
||||||
|
|
||||||
|
if (spi_data != nullptr) {
|
||||||
|
ESP_LOGE(TAG, "SPI device already initialized!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_data = (spi_data_t *) calloc(1, sizeof(spi_data_t));
|
||||||
|
if (spi_data == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "Cannot allocate spi_data!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_data->dma_tx_semaphore = xSemaphoreCreateBinary();
|
||||||
|
if (spi_data->dma_tx_semaphore == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "TX Semaphore init faild!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_data->first_run = true;
|
||||||
|
|
||||||
|
set_spi_ctrl_register(MSTEN, 0);
|
||||||
|
set_spi_ctrl_register(BIT_WDTH, 0);
|
||||||
|
spi_set_clock(this->spi_frequency_);
|
||||||
|
set_spi_ctrl_register(CKPOL, 0);
|
||||||
|
set_spi_ctrl_register(CKPHA, 0);
|
||||||
|
set_spi_ctrl_register(MSTEN, 1);
|
||||||
|
set_spi_ctrl_register(SPIEN, 1);
|
||||||
|
|
||||||
|
set_spi_ctrl_register(TXINT_EN, 0);
|
||||||
|
set_spi_ctrl_register(RXINT_EN, 0);
|
||||||
|
set_spi_config_register(SPI_TX_FINISH_EN, 1);
|
||||||
|
set_spi_config_register(SPI_RX_FINISH_EN, 1);
|
||||||
|
set_spi_ctrl_register(RXOVR_EN, 0);
|
||||||
|
set_spi_ctrl_register(TXOVR_EN, 0);
|
||||||
|
|
||||||
|
value = REG_READ(SPI_CTRL);
|
||||||
|
value &= ~CTRL_NSSMD_3;
|
||||||
|
value |= (1 << 17);
|
||||||
|
REG_WRITE(SPI_CTRL, value);
|
||||||
|
|
||||||
|
value = GFUNC_MODE_SPI_DMA;
|
||||||
|
sddev_control(GPIO_DEV_NAME, CMD_GPIO_ENABLE_SECOND, &value);
|
||||||
|
set_spi_ctrl_register(SPI_S_CS_UP_INT_EN, 0);
|
||||||
|
|
||||||
|
GDMA_CFG_ST en_cfg;
|
||||||
|
GDMACFG_TPYES_ST init_cfg;
|
||||||
|
memset(&init_cfg, 0, sizeof(GDMACFG_TPYES_ST));
|
||||||
|
|
||||||
|
init_cfg.dstdat_width = 8;
|
||||||
|
init_cfg.srcdat_width = 32;
|
||||||
|
init_cfg.dstptr_incr = 0;
|
||||||
|
init_cfg.srcptr_incr = 1;
|
||||||
|
init_cfg.src_start_addr = this->dma_buf_;
|
||||||
|
init_cfg.dst_start_addr = (void *) SPI_DAT; // SPI_DMA_REG4_TXFIFO
|
||||||
|
init_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||||
|
init_cfg.prio = 0; // 10
|
||||||
|
init_cfg.u.type4.src_loop_start_addr = this->dma_buf_;
|
||||||
|
init_cfg.u.type4.src_loop_end_addr = this->dma_buf_ + dma_buffer_size;
|
||||||
|
init_cfg.half_fin_handler = nullptr;
|
||||||
|
init_cfg.fin_handler = spi_dma_tx_finish_callback;
|
||||||
|
init_cfg.src_module = GDMA_X_SRC_DTCM_RD_REQ;
|
||||||
|
init_cfg.dst_module = GDMA_X_DST_GSPI_TX_REQ; // GDMA_X_DST_HSSPI_TX_REQ
|
||||||
|
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_TYPE4, (void *) &init_cfg);
|
||||||
|
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||||
|
en_cfg.param = dma_buffer_size;
|
||||||
|
sddev_control(GDMA_DEV_NAME, CMD_GDMA_SET_TRANS_LENGTH, (void *) &en_cfg);
|
||||||
|
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||||
|
en_cfg.param = 0;
|
||||||
|
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_WORK_MODE, (void *) &en_cfg);
|
||||||
|
en_cfg.channel = SPI_TX_DMA_CHANNEL;
|
||||||
|
en_cfg.param = 0;
|
||||||
|
sddev_control(GDMA_DEV_NAME, CMD_GDMA_CFG_SRCADDR_LOOP, &en_cfg);
|
||||||
|
|
||||||
|
spi_dma_tx_enable(0);
|
||||||
|
|
||||||
|
value = REG_READ(SPI_CONFIG);
|
||||||
|
value &= ~(0xFFF << 8);
|
||||||
|
value |= ((dma_buffer_size & 0xFFF) << 8);
|
||||||
|
REG_WRITE(SPI_CONFIG, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BekenSPILEDStripLightOutput::set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency) {
|
||||||
|
this->bit0_ = bit0;
|
||||||
|
this->bit1_ = bit1;
|
||||||
|
this->spi_frequency_ = spi_frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BekenSPILEDStripLightOutput::write_state(light::LightState *state) {
|
||||||
|
// protect from refreshing too often
|
||||||
|
uint32_t now = micros();
|
||||||
|
if (*this->max_refresh_rate_ != 0 && (now - this->last_refresh_) < *this->max_refresh_rate_) {
|
||||||
|
// try again next loop iteration, so that this change won't get lost
|
||||||
|
this->schedule_show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->last_refresh_ = now;
|
||||||
|
this->mark_shown_();
|
||||||
|
|
||||||
|
ESP_LOGVV(TAG, "Writing RGB values to bus...");
|
||||||
|
|
||||||
|
if (spi_data == nullptr) {
|
||||||
|
ESP_LOGE(TAG, "SPI not initialized");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!spi_data->first_run && !xSemaphoreTake(spi_data->dma_tx_semaphore, 10 / portTICK_PERIOD_MS)) {
|
||||||
|
ESP_LOGE(TAG, "Timed out waiting for semaphore");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spi_data->tx_in_progress) {
|
||||||
|
ESP_LOGE(TAG, "tx_in_progress is set");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_data->tx_in_progress = true;
|
||||||
|
|
||||||
|
size_t buffer_size = this->get_buffer_size_();
|
||||||
|
size_t size = 0;
|
||||||
|
uint8_t *psrc = this->buf_;
|
||||||
|
uint8_t *pdest = this->dma_buf_ + 64;
|
||||||
|
// The 64 byte padding is a workaround for a SPI DMA bug where the
|
||||||
|
// output doesn't exactly start at the beginning of dma_buf_
|
||||||
|
|
||||||
|
while (size < buffer_size) {
|
||||||
|
uint8_t b = *psrc;
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
*pdest++ = b & (1 << (7 - i)) ? this->bit1_ : this->bit0_;
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
psrc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_data->first_run = false;
|
||||||
|
spi_dma_tx_enable(1);
|
||||||
|
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
light::ESPColorView BekenSPILEDStripLightOutput::get_view_internal(int32_t index) const {
|
||||||
|
int32_t r = 0, g = 0, b = 0;
|
||||||
|
switch (this->rgb_order_) {
|
||||||
|
case ORDER_RGB:
|
||||||
|
r = 0;
|
||||||
|
g = 1;
|
||||||
|
b = 2;
|
||||||
|
break;
|
||||||
|
case ORDER_RBG:
|
||||||
|
r = 0;
|
||||||
|
g = 2;
|
||||||
|
b = 1;
|
||||||
|
break;
|
||||||
|
case ORDER_GRB:
|
||||||
|
r = 1;
|
||||||
|
g = 0;
|
||||||
|
b = 2;
|
||||||
|
break;
|
||||||
|
case ORDER_GBR:
|
||||||
|
r = 2;
|
||||||
|
g = 0;
|
||||||
|
b = 1;
|
||||||
|
break;
|
||||||
|
case ORDER_BGR:
|
||||||
|
r = 2;
|
||||||
|
g = 1;
|
||||||
|
b = 0;
|
||||||
|
break;
|
||||||
|
case ORDER_BRG:
|
||||||
|
r = 1;
|
||||||
|
g = 2;
|
||||||
|
b = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint8_t multiplier = this->is_rgbw_ || this->is_wrgb_ ? 4 : 3;
|
||||||
|
uint8_t white = this->is_wrgb_ ? 0 : 3;
|
||||||
|
|
||||||
|
return {this->buf_ + (index * multiplier) + r + this->is_wrgb_,
|
||||||
|
this->buf_ + (index * multiplier) + g + this->is_wrgb_,
|
||||||
|
this->buf_ + (index * multiplier) + b + this->is_wrgb_,
|
||||||
|
this->is_rgbw_ || this->is_wrgb_ ? this->buf_ + (index * multiplier) + white : nullptr,
|
||||||
|
&this->effect_data_[index],
|
||||||
|
&this->correction_};
|
||||||
|
}
|
||||||
|
|
||||||
|
void BekenSPILEDStripLightOutput::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Beken SPI LED Strip:");
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||||
|
const char *rgb_order;
|
||||||
|
switch (this->rgb_order_) {
|
||||||
|
case ORDER_RGB:
|
||||||
|
rgb_order = "RGB";
|
||||||
|
break;
|
||||||
|
case ORDER_RBG:
|
||||||
|
rgb_order = "RBG";
|
||||||
|
break;
|
||||||
|
case ORDER_GRB:
|
||||||
|
rgb_order = "GRB";
|
||||||
|
break;
|
||||||
|
case ORDER_GBR:
|
||||||
|
rgb_order = "GBR";
|
||||||
|
break;
|
||||||
|
case ORDER_BGR:
|
||||||
|
rgb_order = "BGR";
|
||||||
|
break;
|
||||||
|
case ORDER_BRG:
|
||||||
|
rgb_order = "BRG";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rgb_order = "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ESP_LOGCONFIG(TAG, " RGB Order: %s", rgb_order);
|
||||||
|
ESP_LOGCONFIG(TAG, " Max refresh rate: %" PRIu32, *this->max_refresh_rate_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Number of LEDs: %u", this->num_leds_);
|
||||||
|
}
|
||||||
|
|
||||||
|
float BekenSPILEDStripLightOutput::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
|
} // namespace beken_spi_led_strip
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_BK72XX
|
85
esphome/components/beken_spi_led_strip/led_strip.h
Normal file
85
esphome/components/beken_spi_led_strip/led_strip.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_BK72XX
|
||||||
|
|
||||||
|
#include "esphome/components/light/addressable_light.h"
|
||||||
|
#include "esphome/components/light/light_output.h"
|
||||||
|
#include "esphome/core/color.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace beken_spi_led_strip {
|
||||||
|
|
||||||
|
enum RGBOrder : uint8_t {
|
||||||
|
ORDER_RGB,
|
||||||
|
ORDER_RBG,
|
||||||
|
ORDER_GRB,
|
||||||
|
ORDER_GBR,
|
||||||
|
ORDER_BGR,
|
||||||
|
ORDER_BRG,
|
||||||
|
};
|
||||||
|
|
||||||
|
class BekenSPILEDStripLightOutput : public light::AddressableLight {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void write_state(light::LightState *state) override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
int32_t size() const override { return this->num_leds_; }
|
||||||
|
light::LightTraits get_traits() override {
|
||||||
|
auto traits = light::LightTraits();
|
||||||
|
if (this->is_rgbw_ || this->is_wrgb_) {
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB_WHITE, light::ColorMode::WHITE});
|
||||||
|
} else {
|
||||||
|
traits.set_supported_color_modes({light::ColorMode::RGB});
|
||||||
|
}
|
||||||
|
return traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
||||||
|
void set_num_leds(uint16_t num_leds) { this->num_leds_ = num_leds; }
|
||||||
|
void set_is_rgbw(bool is_rgbw) { this->is_rgbw_ = is_rgbw; }
|
||||||
|
void set_is_wrgb(bool is_wrgb) { this->is_wrgb_ = is_wrgb; }
|
||||||
|
|
||||||
|
/// Set a maximum refresh rate in µs as some lights do not like being updated too often.
|
||||||
|
void set_max_refresh_rate(uint32_t interval_us) { this->max_refresh_rate_ = interval_us; }
|
||||||
|
|
||||||
|
void set_led_params(uint8_t bit0, uint8_t bit1, uint32_t spi_frequency);
|
||||||
|
|
||||||
|
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
|
||||||
|
|
||||||
|
void clear_effect_data() override {
|
||||||
|
for (int i = 0; i < this->size(); i++)
|
||||||
|
this->effect_data_[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
light::ESPColorView get_view_internal(int32_t index) const override;
|
||||||
|
|
||||||
|
size_t get_buffer_size_() const { return this->num_leds_ * (this->is_rgbw_ || this->is_wrgb_ ? 4 : 3); }
|
||||||
|
|
||||||
|
uint8_t *buf_{nullptr};
|
||||||
|
uint8_t *effect_data_{nullptr};
|
||||||
|
uint8_t *dma_buf_{nullptr};
|
||||||
|
|
||||||
|
uint8_t pin_;
|
||||||
|
uint16_t num_leds_;
|
||||||
|
bool is_rgbw_;
|
||||||
|
bool is_wrgb_;
|
||||||
|
|
||||||
|
uint32_t spi_frequency_{6666666};
|
||||||
|
uint8_t bit0_{0xE0};
|
||||||
|
uint8_t bit1_{0xFC};
|
||||||
|
RGBOrder rgb_order_;
|
||||||
|
|
||||||
|
uint32_t last_refresh_{0};
|
||||||
|
optional<uint32_t> max_refresh_rate_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace beken_spi_led_strip
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_BK72XX
|
134
esphome/components/beken_spi_led_strip/light.py
Normal file
134
esphome/components/beken_spi_led_strip/light.py
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import pins
|
||||||
|
from esphome.components import libretiny, light
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_CHIPSET,
|
||||||
|
CONF_IS_RGBW,
|
||||||
|
CONF_MAX_REFRESH_RATE,
|
||||||
|
CONF_NUM_LEDS,
|
||||||
|
CONF_OUTPUT_ID,
|
||||||
|
CONF_PIN,
|
||||||
|
CONF_RGB_ORDER,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@Mat931"]
|
||||||
|
DEPENDENCIES = ["libretiny"]
|
||||||
|
|
||||||
|
beken_spi_led_strip_ns = cg.esphome_ns.namespace("beken_spi_led_strip")
|
||||||
|
BekenSPILEDStripLightOutput = beken_spi_led_strip_ns.class_(
|
||||||
|
"BekenSPILEDStripLightOutput", light.AddressableLight
|
||||||
|
)
|
||||||
|
|
||||||
|
RGBOrder = beken_spi_led_strip_ns.enum("RGBOrder")
|
||||||
|
|
||||||
|
RGB_ORDERS = {
|
||||||
|
"RGB": RGBOrder.ORDER_RGB,
|
||||||
|
"RBG": RGBOrder.ORDER_RBG,
|
||||||
|
"GRB": RGBOrder.ORDER_GRB,
|
||||||
|
"GBR": RGBOrder.ORDER_GBR,
|
||||||
|
"BGR": RGBOrder.ORDER_BGR,
|
||||||
|
"BRG": RGBOrder.ORDER_BRG,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class LEDStripTimings:
|
||||||
|
bit0: int
|
||||||
|
bit1: int
|
||||||
|
spi_frequency: int
|
||||||
|
|
||||||
|
|
||||||
|
CHIPSETS = {
|
||||||
|
"WS2812": LEDStripTimings(
|
||||||
|
0b11100000, 0b11111100, 6666666
|
||||||
|
), # Clock divider: 9, Bit time: 1350ns
|
||||||
|
"SK6812": LEDStripTimings(
|
||||||
|
0b11000000, 0b11111000, 7500000
|
||||||
|
), # Clock divider: 8, Bit time: 1200ns
|
||||||
|
"APA106": LEDStripTimings(
|
||||||
|
0b11000000, 0b11111110, 5454545
|
||||||
|
), # Clock divider: 11, Bit time: 1650ns
|
||||||
|
"SM16703": LEDStripTimings(
|
||||||
|
0b11000000, 0b11111110, 7500000
|
||||||
|
), # Clock divider: 8, Bit time: 1200ns
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CONF_IS_WRGB = "is_wrgb"
|
||||||
|
|
||||||
|
SUPPORTED_PINS = {
|
||||||
|
libretiny.const.FAMILY_BK7231N: [16],
|
||||||
|
libretiny.const.FAMILY_BK7231T: [16],
|
||||||
|
libretiny.const.FAMILY_BK7251: [16],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_pin(value):
|
||||||
|
family = libretiny.get_libretiny_family()
|
||||||
|
if family not in SUPPORTED_PINS:
|
||||||
|
raise cv.Invalid(f"Chip family {family} is not supported.")
|
||||||
|
if value not in SUPPORTED_PINS[family]:
|
||||||
|
supported_pin_info = ", ".join(f"{x}" for x in SUPPORTED_PINS[family])
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Pin {value} is not supported on the {family}. Supported pins: {supported_pin_info}"
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_num_leds(value):
|
||||||
|
max_num_leds = 165 # 170
|
||||||
|
if value[CONF_IS_RGBW] or value[CONF_IS_WRGB]:
|
||||||
|
max_num_leds = 123 # 127
|
||||||
|
if value[CONF_NUM_LEDS] > max_num_leds:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"The maximum number of LEDs for this configuration is {max_num_leds}.",
|
||||||
|
path=CONF_NUM_LEDS,
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BekenSPILEDStripLightOutput),
|
||||||
|
cv.Required(CONF_PIN): cv.All(
|
||||||
|
pins.internal_gpio_output_pin_number, _validate_pin
|
||||||
|
),
|
||||||
|
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||||
|
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||||
|
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||||
|
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
||||||
|
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
||||||
|
cv.Optional(CONF_IS_WRGB, default=False): cv.boolean,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
_validate_num_leds,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||||
|
await light.register_light(var, config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_num_leds(config[CONF_NUM_LEDS]))
|
||||||
|
cg.add(var.set_pin(config[CONF_PIN]))
|
||||||
|
|
||||||
|
if CONF_MAX_REFRESH_RATE in config:
|
||||||
|
cg.add(var.set_max_refresh_rate(config[CONF_MAX_REFRESH_RATE]))
|
||||||
|
|
||||||
|
chipset = CHIPSETS[config[CONF_CHIPSET]]
|
||||||
|
cg.add(
|
||||||
|
var.set_led_params(
|
||||||
|
chipset.bit0,
|
||||||
|
chipset.bit1,
|
||||||
|
chipset.spi_frequency,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
cg.add(var.set_rgb_order(config[CONF_RGB_ORDER]))
|
||||||
|
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
|
||||||
|
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
|
@ -6,6 +6,7 @@ from esphome import pins
|
|||||||
from esphome.components import esp32_rmt, light
|
from esphome.components import esp32_rmt, light
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CHIPSET,
|
CONF_CHIPSET,
|
||||||
|
CONF_IS_RGBW,
|
||||||
CONF_MAX_REFRESH_RATE,
|
CONF_MAX_REFRESH_RATE,
|
||||||
CONF_NUM_LEDS,
|
CONF_NUM_LEDS,
|
||||||
CONF_OUTPUT_ID,
|
CONF_OUTPUT_ID,
|
||||||
@ -52,7 +53,6 @@ CHIPSETS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CONF_IS_RGBW = "is_rgbw"
|
|
||||||
CONF_IS_WRGB = "is_wrgb"
|
CONF_IS_WRGB = "is_wrgb"
|
||||||
CONF_BIT0_HIGH = "bit0_high"
|
CONF_BIT0_HIGH = "bit0_high"
|
||||||
CONF_BIT0_LOW = "bit0_low"
|
CONF_BIT0_LOW = "bit0_low"
|
||||||
|
@ -5,6 +5,7 @@ from esphome.components import light, rp2040
|
|||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CHIPSET,
|
CONF_CHIPSET,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_IS_RGBW,
|
||||||
CONF_NUM_LEDS,
|
CONF_NUM_LEDS,
|
||||||
CONF_OUTPUT_ID,
|
CONF_OUTPUT_ID,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
@ -165,7 +166,6 @@ CHIPSETS = {
|
|||||||
"SM16703": LEDStripTimings(17, 52, 52, 17),
|
"SM16703": LEDStripTimings(17, 52, 52, 17),
|
||||||
}
|
}
|
||||||
|
|
||||||
CONF_IS_RGBW = "is_rgbw"
|
|
||||||
CONF_BIT0_HIGH = "bit0_high"
|
CONF_BIT0_HIGH = "bit0_high"
|
||||||
CONF_BIT0_LOW = "bit0_low"
|
CONF_BIT0_LOW = "bit0_low"
|
||||||
CONF_BIT1_HIGH = "bit1_high"
|
CONF_BIT1_HIGH = "bit1_high"
|
||||||
|
@ -399,6 +399,7 @@ CONF_INVERT_COLORS = "invert_colors"
|
|||||||
CONF_INVERTED = "inverted"
|
CONF_INVERTED = "inverted"
|
||||||
CONF_IP_ADDRESS = "ip_address"
|
CONF_IP_ADDRESS = "ip_address"
|
||||||
CONF_IRQ_PIN = "irq_pin"
|
CONF_IRQ_PIN = "irq_pin"
|
||||||
|
CONF_IS_RGBW = "is_rgbw"
|
||||||
CONF_JS_INCLUDE = "js_include"
|
CONF_JS_INCLUDE = "js_include"
|
||||||
CONF_JS_URL = "js_url"
|
CONF_JS_URL = "js_url"
|
||||||
CONF_JVC = "jvc"
|
CONF_JVC = "jvc"
|
||||||
|
7
tests/components/beken_spi_led_strip/test.bk72xx.yaml
Normal file
7
tests/components/beken_spi_led_strip/test.bk72xx.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
light:
|
||||||
|
- platform: beken_spi_led_strip
|
||||||
|
rgb_order: GRB
|
||||||
|
pin: P16
|
||||||
|
num_leds: 30
|
||||||
|
chipset: ws2812
|
||||||
|
name: "My Light"
|
@ -3,7 +3,7 @@ esphome:
|
|||||||
friendly_name: $component_name
|
friendly_name: $component_name
|
||||||
|
|
||||||
bk72xx:
|
bk72xx:
|
||||||
board: cb3s
|
board: generic-bk7231n-qfn32-tuya
|
||||||
|
|
||||||
logger:
|
logger:
|
||||||
level: VERY_VERBOSE
|
level: VERY_VERBOSE
|
||||||
|
Loading…
Reference in New Issue
Block a user