Christmas DMA core tweaks

Bring back support to change the I2S_NUM_X as a define.

Attempt to reduce buffer swap flicker with some additional checks.
This commit is contained in:
mrfaptastic 2021-12-21 22:19:59 +00:00
parent 229b0d874c
commit 4f5fcf0399
6 changed files with 115 additions and 69 deletions

View file

@ -419,7 +419,7 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
Serial.println(F("Performing I2S setup:"));
#endif
i2s_parallel_config_t cfg = {
i2s_parallel_config_t dma_cfg = {
.gpio_bus={_cfg.gpio.r1, _cfg.gpio.g1, _cfg.gpio.b1, _cfg.gpio.r2, _cfg.gpio.g2, _cfg.gpio.b2, _cfg.gpio.lat, _cfg.gpio.oe, _cfg.gpio.a, _cfg.gpio.b, _cfg.gpio.c, _cfg.gpio.d, _cfg.gpio.e, -1, -1, -1},
.gpio_clk=_cfg.gpio.clk,
.sample_rate=_cfg.i2sspeed,
@ -428,18 +428,16 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
.lldesc_a=dmadesc_a,
.desccount_b=desccount,
.lldesc_b=dmadesc_b,
.clkphase=_cfg.clkphase
.clkphase=_cfg.clkphase,
.int_ena_out_eof=_cfg.double_buff
};
// Setup I2S
i2s_parallel_driver_install(I2S_NUM_0, &cfg);
//i2s_parallel_setup_without_malloc(&I2S1, &cfg);
// Start DMA Output
i2s_parallel_send_dma(I2S_NUM_0, &dmadesc_a[0]);
i2s_parallel_driver_install(ESP32_I2S_DEVICE, &dma_cfg);
i2s_parallel_send_dma(ESP32_I2S_DEVICE, &dmadesc_a[0]);
#if SERIAL_DEBUG
Serial.println(F("configureDMA(): DMA setup completed on I2S_NUM_0."));
Serial.println(F("configureDMA(): DMA setup completed on ESP32_I2S_DEVICE."));
#endif
} // end initMatrixDMABuff

View file

@ -129,9 +129,10 @@
/***************************************************************************************/
/* Definitions below should NOT be ever changed without rewriting library logic */
#define ESP32_I2S_DMA_MODE I2S_PARALLEL_WIDTH_16 // From esp32_i2s_parallel_v2.h = 16 bits in parallel
#define ESP32_I2S_DMA_MODE I2S_PARALLEL_WIDTH_16 // From esp32_i2s_parallel_v2.h = 16 bits in parallel
#define ESP32_I2S_DMA_STORAGE_TYPE uint16_t // DMA output of one uint16_t at a time.
#define CLKS_DURING_LATCH 0 // Not (yet) used.
#define CLKS_DURING_LATCH 0 // Not (yet) used.
#define ESP32_I2S_DEVICE I2S_NUM_1
// Panel Upper half RGB (numbering according to order in DMA gpio_bus configuration)
#define BITS_RGB1_OFFSET 0 // Start point of RGB_X1 bits
@ -316,7 +317,7 @@ struct HUB75_I2S_CFG {
LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT },
shift_driver _drv = SHIFTREG,
bool _dbuff = false,
clk_speed _i2sspeed = HZ_20M,
clk_speed _i2sspeed = HZ_10M,
uint8_t _latblk = 1, // Anything > 1 seems to cause artefacts on ICS panels
bool _clockphase = true,
uint8_t _min_refresh_rate = 85
@ -525,15 +526,23 @@ class MatrixPanel_I2S_DMA {
Serial.printf_P(PSTR("Set back buffer to: %d\n"), back_buffer_id);
#endif
i2s_parallel_flip_to_buffer(I2S_NUM_0, back_buffer_id);
i2s_parallel_set_previous_buffer_not_free();
// Wait before we allow any writing to the buffer. Stop flicker.
while(i2s_parallel_is_previous_buffer_free() == false) { }
i2s_parallel_flip_to_buffer(ESP32_I2S_DEVICE, back_buffer_id);
// Flip to other buffer as the backbuffer.
// i.e. Graphic changes happen to this buffer, but aren't displayed until flipDMABuffer() is called again.
back_buffer_id ^= 1;
i2s_parallel_set_previous_buffer_not_free();
// Wait before we allow any writing to the buffer. Stop flicker.
while(i2s_parallel_is_previous_buffer_free() == false) { }
}
inline void setPanelBrightness(int b)
@ -585,7 +594,7 @@ class MatrixPanel_I2S_DMA {
*/
void stopDMAoutput() {
resetbuffers();
i2s_parallel_stop_dma(I2S_NUM_0);
i2s_parallel_stop_dma(ESP32_I2S_DEVICE);
}

View file

@ -13,6 +13,7 @@
// Header
#include "esp32_i2s_parallel_dma.h"
#include "esp32_i2s_parallel_mcu_def.h"
#include <stdbool.h>
#include <stdint.h>
@ -25,22 +26,28 @@
#include <soc/gpio_sig_map.h>
// For I2S state management.
static i2s_parallel_state_t *i2s_state = NULL;
static i2s_parallel_state_t *i2s_state = NULL;
// ESP32-S2,S3,C3 only has IS20
// Original ESP32 has two I2S's, but we'll stick with the lowest common denominator.
static i2s_dev_t* I2S = &I2S0; // Device to use for this library, change if you want.
#ifdef ESP32_ORIG
static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0, &I2S1};
#else
static i2s_dev_t* I2S[I2S_NUM_MAX] = {&I2S0};
#endif
callback shiftCompleteCallback;
void setShiftCompleteCallback(callback f) {
shiftCompleteCallback = f;
}
volatile bool previousBufferFree = true;
volatile int previousBufferOutputLoopCount = 0;
volatile bool previousBufferFree = true;
static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
#ifdef ESP_ORIG
#ifdef ESP32_ORIG
if ( (*(i2s_port_t*)arg) == I2S_NUM_1 ) { // https://www.bogotobogo.com/cplusplus/pointers2_voidpointers_arrays.php
//For I2S1
@ -51,16 +58,24 @@ static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
}
#else
// Other ESP32 MCU's only have one I2S
// Other ESP32 MCU's only have one I2S
SET_PERI_REG_BITS(I2S_INT_CLR_REG(0), I2S_OUT_EOF_INT_CLR_V, 1, I2S_OUT_EOF_INT_CLR_S);
#endif
/*
if ( previousBufferOutputLoopCount == 1)
{
// at this point, the previously active buffer is free, go ahead and write to it
previousBufferFree = true;
////previousBufferOutputLoopCount = 0;
//i2s_parallel_set_previous_buffer_not_free();
}
else { previousBufferOutputLoopCount++; }
*/
previousBufferFree = true;
// at this point, the previously active buffer is free, go ahead and write to it
previousBufferFree = true;
if(shiftCompleteCallback) // we've defined a callback function ?
shiftCompleteCallback();
// if(shiftCompleteCallback) // we've defined a callback function ?
// shiftCompleteCallback();
} // end irq_hndlr
@ -155,7 +170,7 @@ void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, v
esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* conf) {
port = I2S_NUM_0; /// override.
//port = I2S_NUM_0; /// override.
if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) {
return ESP_ERR_INVALID_ARG;
@ -176,7 +191,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
int irq_source;
// Initialize I2S0 peripheral
//if (port == I2S_NUM_0) {
if (port == I2S_NUM_0) {
periph_module_reset(PERIPH_I2S0_MODULE);
periph_module_enable(PERIPH_I2S0_MODULE);
iomux_clock = I2S0O_WS_OUT_IDX;
@ -193,8 +208,12 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
case I2S_PARALLEL_WIDTH_MAX:
return ESP_ERR_INVALID_ARG;
}
/*
} else {
}
#ifdef ESP32_ORIG
// Can't compile if I2S1 if it doesn't exist with that hardware's IDF....
else {
// I2S = &I2S1;
periph_module_reset(PERIPH_I2S1_MODULE);
periph_module_enable(PERIPH_I2S1_MODULE);
iomux_clock = I2S1O_WS_OUT_IDX;
@ -212,12 +231,14 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
return ESP_ERR_INVALID_ARG;
}
}
*/
#endif
// Setup GPIOs
int bus_width = get_bus_width(conf->sample_width);
// Setup I2S peripheral
i2s_dev_t* dev = I2S;
i2s_dev_t* dev = I2S[port];
//dev_reset(dev);
// Setup GPIO's
@ -245,7 +266,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
#ifdef ESP32_SXXX
dev->clkm_conf.clk_sel = 2; // esp32-s2 only
dev->clkm_conf.clk_en = 1;
dev->clkm_conf.clk_en = 1;
#endif
#ifdef ESP32_ORIG
@ -329,26 +350,28 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
state->dmadesc_b = conf->lldesc_b;
state->i2s_interrupt_port_arg = port; // need to keep this somewhere in static memory for the ISR
// Get ISR setup
esp_err_t err = esp_intr_alloc(irq_source,
(int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1),
irq_hndlr,
&state->i2s_interrupt_port_arg, NULL);
if(err) {
return err;
}
dev->timing.val = 0;
// We using the double buffering switch logic?
if (conf->int_ena_out_eof)
{
// Get ISR setup
esp_err_t err = esp_intr_alloc(irq_source,
(int)(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL1),
irq_hndlr,
&state->i2s_interrupt_port_arg, NULL);
// Setup interrupt handler which is focussed only on the (page 322 of Tech. Ref. Manual)
// "I2S_OUT_EOF_INT: Triggered when rxlink has finished sending a packet"
// ... whatever the hell that is supposed to mean... One massive linked list.
dev->int_ena.out_eof = 1;
if(err) {
return err;
}
// Setup interrupt handler which is focussed only on the (page 322 of Tech. Ref. Manual)
// "I2S_OUT_EOF_INT: Triggered when rxlink has finished sending a packet"
// ... whatever the hell that is supposed to mean... One massive linked list? So all pixels in the chain?
dev->int_ena.out_eof = 1;
}
return ESP_OK;
}
@ -357,7 +380,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
return ESP_ERR_INVALID_ARG;
}
i2s_dev_t* dev = I2S;
i2s_dev_t* dev = I2S[port];
// Stop all ongoing DMA operations
dev->out_link.stop = 1;
@ -373,7 +396,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
return ESP_ERR_INVALID_ARG;
}
i2s_dev_t* dev = I2S;
i2s_dev_t* dev = I2S[port];
// Configure DMA burst mode
@ -390,16 +413,23 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
return ESP_OK;
}
/*
i2s_dev_t* i2s_parallel_get_dev(i2s_port_t port) {
if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) {
return NULL;
}
return I2S; // HARCODE THIS TO RETURN &I2S0
}
#ifdef ESP32_ORIG
if (port == I2S_NUM_1)
return &I2S1;
#endif
return I2S0; // HARCODE THIS TO RETURN &I2S0
}
*/
// Double buffering flipping
// Flip to a buffer: 0 for bufa, 1 for bufb
// dmadesc_a and dmadesc_b point to the same memory if double buffering isn't enabled.
void i2s_parallel_flip_to_buffer(i2s_port_t port, int buffer_id) {
if (i2s_state == NULL) {
@ -418,9 +448,16 @@ void i2s_parallel_flip_to_buffer(i2s_port_t port, int buffer_id) {
i2s_state->dmadesc_b[i2s_state->desccount_b-1].qe.stqe_next = active_dma_chain;
// we're still shifting out the buffer, so it shouldn't be written to yet.
previousBufferFree = false;
//previousBufferFree = false;
i2s_parallel_set_previous_buffer_not_free();
}
bool i2s_parallel_is_previous_buffer_free() {
return previousBufferFree;
}
void i2s_parallel_set_previous_buffer_not_free() {
previousBufferFree = false;
previousBufferOutputLoopCount = 0;
}

View file

@ -41,6 +41,7 @@ typedef struct {
int desccount_b; // only used with double buffering
lldesc_t * lldesc_b; // only used with double buffering
bool clkphase; // Clock signal phase
bool int_ena_out_eof; // Do we raise an interrupt every time the DMA output loops? Don't do this unless we're doing double buffering!
} i2s_parallel_config_t;
static inline int i2s_parallel_get_memory_width(i2s_port_t port, i2s_parallel_cfg_bits_t width) {
@ -72,7 +73,7 @@ void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, v
esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* conf);
esp_err_t i2s_parallel_send_dma(i2s_port_t port, lldesc_t* dma_descriptor);
esp_err_t i2s_parallel_stop_dma(i2s_port_t port);
i2s_dev_t* i2s_parallel_get_dev(i2s_port_t port);
//i2s_dev_t* i2s_parallel_get_dev(i2s_port_t port);
// For frame buffer flipping / double buffering
typedef struct {
@ -83,6 +84,7 @@ typedef struct {
void i2s_parallel_flip_to_buffer(i2s_port_t port, int bufid);
bool i2s_parallel_is_previous_buffer_free();
void i2s_parallel_set_previous_buffer_not_free();
// Callback function for when whole length of DMA chain has been sent out.
typedef void (*callback)(void);

View file

@ -143,11 +143,11 @@ uint8_t wheelval = 0;
void loop() {
// animate by going through the colour wheel for the first two lines
//drawText(wheelval);
//wheelval +=1;
//delay(20);
drawText(wheelval);
wheelval +=1;
delay(20);
/*
drawText(0);
delay(2000);
dma_display->clearScreen();
@ -161,5 +161,6 @@ void loop() {
delay(2000);
dma_display->fillScreen(myWHITE);
dma_display->clearScreen();
*/
}

View file

@ -8,7 +8,6 @@
|BitmapIcons |Simple example of how to display a bitmap image to the display. |
|ChainedPanels |Popular example on how to use the 'VirtualDisplay' class to chain multiple LED Matrix Panels to form a much bigger display! Refer to the README within this example's folder! |
|ChainedPanelsAuroraDemo |As above, but showing a large trippy plasma animation. |
|Smooth Double Buffer |Example of using a back-buffer (double buffering). Not useful in 99.9% of use-cases of this library. Uses double the SRAM as well. |
|One_Quarter_1_4_ScanPanel |Using this library with a 32w x 16h 1/4 Scan LED Matrix Panel. Custom co-ordinate remapping logic required. |
|One_Eighth_1_8_ScanPanel |Using this library with a 64w x 32h 1/8 Scan LED Matrix Panel. Custom co-ordinate remapping logic required.
|One_Quarter_1_4_ScanPanel |Using this library with a 32w x 16h 1/4 Scan LED Matrix Panel. Custom co-ordinate remapping logic required. |
|One_Eighth_1_8_ScanPanel |Using this library with a 64w x 32h 1/8 Scan LED Matrix Panel. Custom co-ordinate remapping logic required.
|PIO_TestPatterns |Non-Arduino example of how to display basic shapes. |