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:
parent
229b0d874c
commit
4f5fcf0399
6 changed files with 115 additions and 69 deletions
|
@ -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]);
|
||||
|
||||
// Setup I2S
|
||||
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
|
||||
|
|
|
@ -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,14 +526,22 @@ 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;
|
||||
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) { }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,17 +58,25 @@ 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
|
||||
|
||||
// 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();
|
||||
|
||||
#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;
|
||||
|
||||
// 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
|
||||
|
@ -314,7 +335,7 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
|
|||
|
||||
|
||||
// Device Reset
|
||||
dev_reset(dev);
|
||||
dev_reset(dev);
|
||||
dev->conf1.val = 0;
|
||||
dev->conf1.tx_stop_en = 0;
|
||||
|
||||
|
@ -329,25 +350,27 @@ 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;
|
||||
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
|
@ -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. |
|
||||
|
|
Loading…
Reference in a new issue