Merge pull request #88 from mrfaptastic/clkphase
Clock phase toggling option + DMA stop function
This commit is contained in:
commit
a3068c8934
5 changed files with 153 additions and 84 deletions
|
@ -421,7 +421,8 @@ void MatrixPanel_I2S_DMA::configureDMA(const HUB75_I2S_CFG& _cfg)
|
|||
.desccount_a=desccount,
|
||||
.lldesc_a=dmadesc_a,
|
||||
.desccount_b=desccount,
|
||||
.lldesc_b=dmadesc_b
|
||||
.lldesc_b=dmadesc_b,
|
||||
.clkphase=_cfg.clkphase
|
||||
};
|
||||
|
||||
// Setup I2S
|
||||
|
@ -589,82 +590,6 @@ void MatrixPanel_I2S_DMA::updateMatrixDMABuffer(uint8_t red, uint8_t green, uint
|
|||
} // colour depth loop (8)
|
||||
} // updateMatrixDMABuffer (full frame paint)
|
||||
|
||||
|
||||
#define CLK_PULSE digitalWrite(_cfg.gpio.clk, HIGH); digitalWrite(_cfg.gpio.clk, LOW);
|
||||
|
||||
/**
|
||||
* pre-init procedures for specific drivers
|
||||
*
|
||||
*/
|
||||
void MatrixPanel_I2S_DMA::shiftDriver(const HUB75_I2S_CFG& _cfg){
|
||||
switch (_cfg.driver){
|
||||
case HUB75_I2S_CFG::ICN2038S:
|
||||
case HUB75_I2S_CFG::FM6124:
|
||||
case HUB75_I2S_CFG::FM6126A:
|
||||
{
|
||||
#if SERIAL_DEBUG
|
||||
Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver..."));
|
||||
#endif
|
||||
bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0}; // this sets global matrix brightness power
|
||||
bool REG2[16] = {0,0,0,0,0, 0,0,0,0,1,0, 0,0,0,0,0}; // a single bit enables the matrix output
|
||||
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2, _cfg.gpio.clk, _cfg.gpio.lat, _cfg.gpio.oe}){
|
||||
pinMode(_pin, OUTPUT);
|
||||
digitalWrite(_pin, LOW);
|
||||
}
|
||||
|
||||
digitalWrite(_cfg.gpio.oe, HIGH); // Disable Display
|
||||
|
||||
// Send Data to control register REG1
|
||||
// this sets the matrix brightness actually
|
||||
for (int l = 0; l < PIXELS_PER_ROW; l++){
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
|
||||
digitalWrite(_pin, REG1[l%16]); // we have 16 bits shifters and write the same value all over the matrix array
|
||||
|
||||
if (l > PIXELS_PER_ROW - 12){ // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value
|
||||
digitalWrite(_cfg.gpio.lat, HIGH);
|
||||
}
|
||||
CLK_PULSE
|
||||
}
|
||||
|
||||
// drop the latch and save data to the REG1 all over the FM6124 chips
|
||||
digitalWrite(_cfg.gpio.lat, LOW);
|
||||
|
||||
// Send Data to control register REG2 (enable LED output)
|
||||
for (int l = 0; l < PIXELS_PER_ROW; l++){
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
|
||||
digitalWrite(_pin, REG2[l%16]); // we have 16 bits shifters and we write the same value all over the matrix array
|
||||
|
||||
if (l > PIXELS_PER_ROW - 13){ // pull the latch 12 clocks before the end of matrix so that reg2 stars counting to save the value
|
||||
digitalWrite(_cfg.gpio.lat, HIGH);
|
||||
}
|
||||
CLK_PULSE
|
||||
}
|
||||
|
||||
// drop the latch and save data to the REG1 all over the FM6126 chips
|
||||
digitalWrite(_cfg.gpio.lat, LOW);
|
||||
|
||||
// blank data regs to keep matrix clear after manipulations
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
|
||||
digitalWrite(_pin, LOW);
|
||||
|
||||
for (int l = 0; l < PIXELS_PER_ROW; ++l){
|
||||
CLK_PULSE
|
||||
}
|
||||
|
||||
digitalWrite(_cfg.gpio.lat, HIGH);
|
||||
CLK_PULSE
|
||||
digitalWrite(_cfg.gpio.lat, LOW);
|
||||
digitalWrite(_cfg.gpio.oe, LOW); // Enable Display
|
||||
CLK_PULSE
|
||||
}
|
||||
break;
|
||||
case HUB75_I2S_CFG::SHIFT:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clear screen to black and reset all service bits
|
||||
*/
|
||||
|
|
|
@ -227,7 +227,7 @@ struct HUB75_I2S_CFG {
|
|||
* Enumeration of hardware-specific chips
|
||||
* used to drive matrix modules
|
||||
*/
|
||||
enum shift_driver {SHIFT=0, FM6124, FM6126A, ICN2038S};
|
||||
enum shift_driver {SHIFTREG=0, FM6124, FM6126A, ICN2038S, MBI5124};
|
||||
|
||||
/**
|
||||
* I2S clock speed selector
|
||||
|
@ -258,6 +258,20 @@ struct HUB75_I2S_CFG {
|
|||
// How many clock cycles to blank OE before/after LAT signal change, default is 1 clock
|
||||
uint8_t latch_blanking;
|
||||
|
||||
/**
|
||||
* I2S clock phase
|
||||
* 0 (default) - data lines are clocked with negative edge
|
||||
* Clk /¯\_/¯\_/
|
||||
* LAT __/¯¯¯\__
|
||||
* EO ¯¯¯¯¯¯\___
|
||||
*
|
||||
* 1 - data lines are clocked with positive edge
|
||||
* Clk \_/¯\_/¯\
|
||||
* LAT __/¯¯¯\__
|
||||
* EO ¯¯¯¯¯¯\__
|
||||
*
|
||||
*/
|
||||
bool clkphase;
|
||||
|
||||
// struct constructor
|
||||
HUB75_I2S_CFG (
|
||||
|
@ -268,17 +282,19 @@ struct HUB75_I2S_CFG {
|
|||
R1_PIN_DEFAULT, G1_PIN_DEFAULT, B1_PIN_DEFAULT, R2_PIN_DEFAULT, G2_PIN_DEFAULT, B2_PIN_DEFAULT,
|
||||
A_PIN_DEFAULT, B_PIN_DEFAULT, C_PIN_DEFAULT, D_PIN_DEFAULT, E_PIN_DEFAULT,
|
||||
LAT_PIN_DEFAULT, OE_PIN_DEFAULT, CLK_PIN_DEFAULT },
|
||||
shift_driver _drv = SHIFT,
|
||||
shift_driver _drv = SHIFTREG,
|
||||
bool _dbuff = false,
|
||||
clk_speed _i2sspeed = HZ_10M,
|
||||
uint16_t _latblk = 1
|
||||
uint16_t _latblk = 1,
|
||||
bool _clockphase = false
|
||||
) : mx_width(_w),
|
||||
mx_height(_h),
|
||||
chain_length(_chain),
|
||||
gpio(_pinmap),
|
||||
driver(_drv), i2sspeed(_i2sspeed),
|
||||
double_buff(_dbuff),
|
||||
latch_blanking(_latblk) {}
|
||||
latch_blanking(_latblk),
|
||||
clkphase(_clockphase) {}
|
||||
}; // end of structure HUB75_I2S_CFG
|
||||
|
||||
|
||||
|
@ -522,6 +538,16 @@ class MatrixPanel_I2S_DMA {
|
|||
*
|
||||
*/
|
||||
const HUB75_I2S_CFG& getCfg() const {return m_cfg;};
|
||||
|
||||
|
||||
/**
|
||||
* Stop the ESP32 DMA Engine. Screen will forever be black until next ESP reboot.
|
||||
*/
|
||||
void stopDMAoutput() {
|
||||
clearScreen();
|
||||
i2s_parallel_stop_dma(I2S_NUM_1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------- PROTECTED -------
|
||||
|
@ -576,7 +602,6 @@ class MatrixPanel_I2S_DMA {
|
|||
void fillRectDMA(int16_t x_coord, int16_t y_coord, int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b);
|
||||
#endif
|
||||
|
||||
|
||||
// ------- PRIVATE -------
|
||||
private:
|
||||
|
||||
|
@ -625,6 +650,11 @@ class MatrixPanel_I2S_DMA {
|
|||
*/
|
||||
void shiftDriver(const HUB75_I2S_CFG& opts);
|
||||
|
||||
/**
|
||||
* @brief - FM6124-family chips initialization routine
|
||||
*/
|
||||
void fm6124init(const HUB75_I2S_CFG& _cfg);
|
||||
|
||||
/**
|
||||
* @brief - reset OE bits in DMA buffer in a way to control brightness
|
||||
* @param brt - brightness level from 0 to row_width
|
||||
|
|
94
ESP32-HUB75-MatrixPanel-leddrivers.cpp
Normal file
94
ESP32-HUB75-MatrixPanel-leddrivers.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Various LED Driver chips might need some specific code for initialisation/control logic
|
||||
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
|
||||
|
||||
#define CLK_PULSE digitalWrite(_cfg.gpio.clk, HIGH); digitalWrite(_cfg.gpio.clk, LOW);
|
||||
|
||||
/**
|
||||
* @brief - pre-init procedures for specific led-drivers
|
||||
* this method is called before DMA/I2S setup while GPIOs
|
||||
* aint yet assigned for DMA operation
|
||||
*
|
||||
*/
|
||||
void MatrixPanel_I2S_DMA::shiftDriver(const HUB75_I2S_CFG& _cfg){
|
||||
switch (_cfg.driver){
|
||||
case HUB75_I2S_CFG::ICN2038S:
|
||||
case HUB75_I2S_CFG::FM6124:
|
||||
case HUB75_I2S_CFG::FM6126A:
|
||||
fm6124init(_cfg);
|
||||
break;
|
||||
case HUB75_I2S_CFG::MBI5124:
|
||||
/* MBI5124 chips must be clocked with positive-edge, since it's LAT signal
|
||||
* resets on clock's rising edge while high
|
||||
* https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/files/5952216/5a542453754da.pdf
|
||||
*/
|
||||
m_cfg.clkphase=true;
|
||||
break;
|
||||
case HUB75_I2S_CFG::SHIFTREG:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MatrixPanel_I2S_DMA::fm6124init(const HUB75_I2S_CFG& _cfg){
|
||||
#if SERIAL_DEBUG
|
||||
Serial.println( F("MatrixPanel_I2S_DMA - initializing FM6124 driver..."));
|
||||
#endif
|
||||
bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0}; // this sets global matrix brightness power
|
||||
bool REG2[16] = {0,0,0,0,0, 0,0,0,0,1,0, 0,0,0,0,0}; // a single bit enables the matrix output
|
||||
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2, _cfg.gpio.clk, _cfg.gpio.lat, _cfg.gpio.oe}){
|
||||
pinMode(_pin, OUTPUT);
|
||||
digitalWrite(_pin, LOW);
|
||||
}
|
||||
|
||||
digitalWrite(_cfg.gpio.oe, HIGH); // Disable Display
|
||||
|
||||
// Send Data to control register REG1
|
||||
// this sets the matrix brightness actually
|
||||
for (int l = 0; l < PIXELS_PER_ROW; l++){
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
|
||||
digitalWrite(_pin, REG1[l%16]); // we have 16 bits shifters and write the same value all over the matrix array
|
||||
|
||||
if (l > PIXELS_PER_ROW - 12){ // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value
|
||||
digitalWrite(_cfg.gpio.lat, HIGH);
|
||||
}
|
||||
CLK_PULSE
|
||||
}
|
||||
|
||||
// drop the latch and save data to the REG1 all over the FM6124 chips
|
||||
digitalWrite(_cfg.gpio.lat, LOW);
|
||||
|
||||
// Send Data to control register REG2 (enable LED output)
|
||||
for (int l = 0; l < PIXELS_PER_ROW; l++){
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
|
||||
digitalWrite(_pin, REG2[l%16]); // we have 16 bits shifters and we write the same value all over the matrix array
|
||||
|
||||
if (l > PIXELS_PER_ROW - 13){ // pull the latch 12 clocks before the end of matrix so that reg2 stars counting to save the value
|
||||
digitalWrite(_cfg.gpio.lat, HIGH);
|
||||
}
|
||||
CLK_PULSE
|
||||
}
|
||||
|
||||
// drop the latch and save data to the REG1 all over the FM6126 chips
|
||||
digitalWrite(_cfg.gpio.lat, LOW);
|
||||
|
||||
// blank data regs to keep matrix clear after manipulations
|
||||
for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
|
||||
digitalWrite(_pin, LOW);
|
||||
|
||||
for (int l = 0; l < PIXELS_PER_ROW; ++l){
|
||||
CLK_PULSE
|
||||
}
|
||||
|
||||
digitalWrite(_cfg.gpio.lat, HIGH);
|
||||
CLK_PULSE
|
||||
digitalWrite(_cfg.gpio.lat, LOW);
|
||||
digitalWrite(_cfg.gpio.oe, LOW); // Enable Display
|
||||
CLK_PULSE
|
||||
}
|
|
@ -37,7 +37,6 @@ void setShiftCompleteCallback(callback f) {
|
|||
volatile bool previousBufferFree = true;
|
||||
|
||||
static void IRAM_ATTR irq_hndlr(void* arg) { // if we use I2S1 (default)
|
||||
// REG_WRITE(I2S_INT_CLR_REG(1), (REG_READ(I2S_INT_RAW_REG(1)) & 0xffffffc0) | 0x3f);
|
||||
|
||||
if ( (*(i2s_port_t*)arg) == I2S_NUM_1 ) { // https://www.bogotobogo.com/cplusplus/pointers2_voidpointers_arrays.php
|
||||
//For I2S1
|
||||
|
@ -297,11 +296,30 @@ esp_err_t i2s_parallel_driver_install(i2s_port_t port, i2s_parallel_config_t* co
|
|||
for(int i = 0; i < bus_width; i++) {
|
||||
iomux_set_signal(conf->gpio_bus[i], iomux_signal_base + i);
|
||||
}
|
||||
iomux_set_signal(conf->gpio_clk, iomux_clock);
|
||||
iomux_set_signal(conf->gpio_clk, iomux_clock);
|
||||
// invert clock phase if required
|
||||
if (conf->clkphase)
|
||||
GPIO.func_out_sel_cfg[conf->gpio_clk].inv_sel = 1;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t i2s_parallel_stop_dma(i2s_port_t port) {
|
||||
if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
i2s_dev_t* dev = I2S[port];
|
||||
|
||||
// Stop all ongoing DMA operations
|
||||
dev->out_link.stop = 1;
|
||||
dev->out_link.start = 0;
|
||||
dev->conf.tx_start = 0;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t i2s_parallel_send_dma(i2s_port_t port, lldesc_t* dma_descriptor) {
|
||||
if(port < I2S_NUM_0 || port >= I2S_NUM_MAX) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef struct {
|
|||
lldesc_t * lldesc_a;
|
||||
int desccount_b; // only used with double buffering
|
||||
lldesc_t * lldesc_b; // only used with double buffering
|
||||
bool clkphase; // Clock signal phase
|
||||
} i2s_parallel_config_t;
|
||||
|
||||
static inline int i2s_parallel_get_memory_width(i2s_port_t port, i2s_parallel_cfg_bits_t width) {
|
||||
|
@ -65,6 +66,7 @@ void link_dma_desc(volatile lldesc_t *dmadesc, volatile lldesc_t *prevdmadesc, v
|
|||
// I2S DMA Peripheral Setup Functions
|
||||
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);
|
||||
|
||||
// For frame buffer flipping / double buffering
|
||||
|
|
Loading…
Reference in a new issue