mirror of
https://github.com/Threnklyn/esphome-dev.git
synced 2026-06-01 10:38:27 +02:00
fix rp2040_pio_led flicker and proper multi-strip support (#6194)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#include <hardware/clocks.h>
|
||||
#include <hardware/dma.h>
|
||||
#include <hardware/pio.h>
|
||||
#include <pico/stdlib.h>
|
||||
|
||||
@@ -14,6 +15,15 @@ namespace rp2040_pio_led_strip {
|
||||
|
||||
static const char *TAG = "rp2040_pio_led_strip";
|
||||
|
||||
static uint8_t num_instance_[2] = {0, 0};
|
||||
static std::map<Chipset, uint> chipset_offsets_ = {
|
||||
{CHIPSET_WS2812, 0}, {CHIPSET_WS2812B, 0}, {CHIPSET_SK6812, 0}, {CHIPSET_SM16703, 0}, {CHIPSET_CUSTOM, 0},
|
||||
};
|
||||
static std::map<Chipset, bool> conf_count_ = {
|
||||
{CHIPSET_WS2812, false}, {CHIPSET_WS2812B, false}, {CHIPSET_SK6812, false},
|
||||
{CHIPSET_SM16703, false}, {CHIPSET_CUSTOM, false},
|
||||
};
|
||||
|
||||
void RP2040PIOLEDStripLightOutput::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up RP2040 LED Strip...");
|
||||
|
||||
@@ -34,24 +44,71 @@ void RP2040PIOLEDStripLightOutput::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the PIO program
|
||||
|
||||
// Select PIO instance to use (0 or 1)
|
||||
this->pio_ = pio0;
|
||||
if (this->pio_ == nullptr) {
|
||||
ESP_LOGE(TAG, "Failed to claim PIO instance");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the assembled program into the PIO and get its location in the PIO's instruction memory
|
||||
uint offset = pio_add_program(this->pio_, this->program_);
|
||||
// if there are multiple strips, we can reuse the same PIO program and save space
|
||||
// but there are only 4 state machines on each PIO so we can only have 4 strips per PIO
|
||||
uint offset = 0;
|
||||
|
||||
if (num_instance_[this->pio_ == pio0 ? 0 : 1] > 4) {
|
||||
ESP_LOGE(TAG, "Too many instances of PIO program");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
// keep track of how many instances of the PIO program are running on each PIO
|
||||
num_instance_[this->pio_ == pio0 ? 0 : 1]++;
|
||||
|
||||
// if there are multiple strips of the same chipset, we can reuse the same PIO program and save space
|
||||
if (this->conf_count_[this->chipset_]) {
|
||||
offset = chipset_offsets_[this->chipset_];
|
||||
} else {
|
||||
// Load the assembled program into the PIO and get its location in the PIO's instruction memory and save it
|
||||
offset = pio_add_program(this->pio_, this->program_);
|
||||
chipset_offsets_[this->chipset_] = offset;
|
||||
conf_count_[this->chipset_] = true;
|
||||
}
|
||||
|
||||
// Configure the state machine's PIO, and start it
|
||||
this->sm_ = pio_claim_unused_sm(this->pio_, true);
|
||||
if (this->sm_ < 0) {
|
||||
// in theory this code should never be reached
|
||||
ESP_LOGE(TAG, "Failed to claim PIO state machine");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initalize the DMA channel (Note: There are 12 DMA channels and 8 state machines so we won't run out)
|
||||
|
||||
this->dma_chan_ = dma_claim_unused_channel(true);
|
||||
if (this->dma_chan_ < 0) {
|
||||
ESP_LOGE(TAG, "Failed to claim DMA channel");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->dma_config_ = dma_channel_get_default_config(this->dma_chan_);
|
||||
channel_config_set_transfer_data_size(
|
||||
&this->dma_config_,
|
||||
DMA_SIZE_8); // 8 bit transfers (could be 32 but the pio program would need to be changed to handle junk data)
|
||||
channel_config_set_read_increment(&this->dma_config_, true); // increment the read address
|
||||
channel_config_set_write_increment(&this->dma_config_, false); // don't increment the write address
|
||||
channel_config_set_dreq(&this->dma_config_,
|
||||
pio_get_dreq(this->pio_, this->sm_, true)); // set the DREQ to the state machine's TX FIFO
|
||||
|
||||
dma_channel_configure(this->dma_chan_, &this->dma_config_,
|
||||
&this->pio_->txf[this->sm_], // write to the state machine's TX FIFO
|
||||
this->buf_, // read from memory
|
||||
this->is_rgbw_ ? num_leds_ * 4 : num_leds_ * 3, // number of bytes to transfer
|
||||
false // don't start yet
|
||||
);
|
||||
|
||||
this->init_(this->pio_, this->sm_, offset, this->pin_, this->max_refresh_rate_);
|
||||
}
|
||||
|
||||
@@ -68,16 +125,8 @@ void RP2040PIOLEDStripLightOutput::write_state(light::LightState *state) {
|
||||
return;
|
||||
}
|
||||
|
||||
// assemble bits in buffer to 32 bit words with ex for GBR: 0bGGGGGGGGRRRRRRRRBBBBBBBB00000000
|
||||
for (int i = 0; i < this->num_leds_; i++) {
|
||||
uint8_t multiplier = this->is_rgbw_ ? 4 : 3;
|
||||
uint8_t c1 = this->buf_[(i * multiplier) + 0];
|
||||
uint8_t c2 = this->buf_[(i * multiplier) + 1];
|
||||
uint8_t c3 = this->buf_[(i * multiplier) + 2];
|
||||
uint8_t w = this->is_rgbw_ ? this->buf_[(i * 4) + 3] : 0;
|
||||
uint32_t color = encode_uint32(c1, c2, c3, w);
|
||||
pio_sm_put_blocking(this->pio_, this->sm_, color);
|
||||
}
|
||||
// the bits are already in the correct order for the pio program so we can just copy the buffer using DMA
|
||||
dma_channel_transfer_from_buffer_now(this->dma_chan_, this->buf_, this->get_buffer_size_());
|
||||
}
|
||||
|
||||
light::ESPColorView RP2040PIOLEDStripLightOutput::get_view_internal(int32_t index) const {
|
||||
|
||||
Reference in New Issue
Block a user