add first re-try of C6

This commit is contained in:
Lukaswnd 2024-06-10 17:08:58 +02:00
parent 54ef607166
commit f0b4a6d081
4 changed files with 457 additions and 7 deletions

View file

@ -0,0 +1,309 @@
#include "dma_parallel_io.hpp"
#ifdef CONFIG_IDF_TARGET_ESP32C6
//First implementation might have a lot of bugs, especially on deleting and reloading
#pragma message "Compiling for ESP32-C6"
#ifdef ARDUINO_ARCH_ESP32
#include <Arduino.h>
#endif
#include "soc/parl_io_struct.h"
#include "soc/parl_io_reg.h"
#include "soc/parlio_periph.h"
#include "hal/parlio_hal.h"
#include "hal/parlio_ll.h"
#include "hal/parlio_types.h"
#include "esp_clk_tree.h"
#include "esp_private/periph_ctrl.h"
#include "esp_attr.h"
#include "esp_rom_sys.h"
#include "esp_rom_gpio.h"
#include "driver/gpio.h"
DRAM_ATTR volatile bool previousBufferFree = true;
// End-of-DMA-transfer callback
IRAM_ATTR bool gdma_on_trans_eof_callback(gdma_channel_handle_t dma_chan,
gdma_event_data_t *event_data, void *user_data)
{
//esp_rom_delay_us(100);
previousBufferFree = true;
return true;
}
// ------------------------------------------------------------------------------
void Bus_Parallel16::config(const config_t &cfg)
{
_cfg = cfg;
}
bool Bus_Parallel16::init(void)
{
periph_module_enable(PERIPH_PARLIO_MODULE);
periph_module_reset(PERIPH_PARLIO_MODULE);
// Reset LCD bus
parlio_ll_tx_reset_fifo(&PARL_IO);
esp_rom_delay_us(1000);
parlio_ll_clock_source_t clk_src = (parlio_ll_clock_source_t)PARLIO_CLK_SRC_DEFAULT;
uint32_t periph_src_clk_hz = 0;
esp_clk_tree_src_get_freq_hz((soc_module_clk_t)clk_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &periph_src_clk_hz);
parlio_ll_tx_set_clock_source(&PARL_IO, clk_src);
uint32_t div = (periph_src_clk_hz + _cfg.bus_freq - 1) / _cfg.bus_freq;
parlio_ll_tx_set_clock_div(&PARL_IO, div);
_cfg.bus_freq = periph_src_clk_hz / div;
ESP_LOGI("C6", "Clock divider is %d", (int)div);
ESP_LOGD("C6", "Resulting output clock frequency: %d Mhz", (int)(160000000L / _cfg.bus_freq));
// Allocate DMA channel and connect it to the LCD peripheral
static gdma_channel_alloc_config_t dma_chan_config = {
.sibling_chan = NULL,
.direction = GDMA_CHANNEL_DIRECTION_TX,
.flags = {
.reserve_sibling = 0}};
gdma_new_channel(&dma_chan_config, &dma_chan);
gdma_connect(dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0));
static gdma_strategy_config_t strategy_config = {
.owner_check = false,
.auto_update_desc = false};
gdma_apply_strategy(dma_chan, &strategy_config);
gdma_transfer_ability_t ability = {
.sram_trans_align = 32,
.psram_trans_align = 64,
};
gdma_set_transfer_ability(dma_chan, &ability);
// Enable DMA transfer callback
static gdma_tx_event_callbacks_t tx_cbs = {
.on_trans_eof = gdma_on_trans_eof_callback};
gdma_register_tx_event_callbacks(dma_chan, &tx_cbs, NULL);
parlio_ll_tx_reset_clock(&PARL_IO);
parlio_ll_tx_reset_fifo(&PARL_IO);
parlio_ll_tx_enable_clock(&PARL_IO, false);
parlio_ll_tx_enable_clock_gating(&PARL_IO, 0);
parlio_ll_tx_set_bus_width(&PARL_IO, 16);
parlio_ll_tx_treat_msb_as_valid(&PARL_IO, false);
auto sample_edge = _cfg.invert_pclk ? PARLIO_SAMPLE_EDGE_NEG : PARLIO_SAMPLE_EDGE_POS;
parlio_ll_tx_set_sample_clock_edge(&PARL_IO, sample_edge);
parlio_ll_clear_interrupt_status(&PARL_IO, PARLIO_LL_EVENT_TX_MASK);
int8_t *pins = _cfg.pin_data;
gpio_config_t gpio_conf = {
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
for (int i = 0; i < 16; i++)
{
if (pins[i] >= 0)
{ // -1 value will CRASH the ESP32!
gpio_conf.pin_bit_mask = BIT64(pins[i]);
gpio_config(&gpio_conf);
esp_rom_gpio_connect_out_signal(pins[i], parlio_periph_signals.groups[0].tx_units[0].data_sigs[i], false, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[pins[i]], PIN_FUNC_GPIO);
gpio_set_drive_capability((gpio_num_t)pins[i], (gpio_drive_cap_t)3);
}
}
// Clock
gpio_conf.pin_bit_mask = BIT64(_cfg.pin_wr);
gpio_config(&gpio_conf);
esp_rom_gpio_connect_out_signal(_cfg.pin_wr, parlio_periph_signals.groups[0].tx_units[0].clk_out_sig, _cfg.invert_pclk, false);
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[_cfg.pin_wr], PIN_FUNC_GPIO);
gpio_set_drive_capability((gpio_num_t)_cfg.pin_wr, (gpio_drive_cap_t)3);
parlio_ll_tx_set_idle_data_value(&PARL_IO, 0);
parlio_ll_tx_set_trans_bit_len(&PARL_IO, ((1<<16) -1) * 8);
return true; // no return val = illegal instruction
}
void Bus_Parallel16::release(void)
{
if (_dmadesc_a)
{
heap_caps_free(_dmadesc_a);
_dmadesc_a = nullptr;
}
if(_dmadesc_b){
heap_caps_free(_dmadesc_b);
_dmadesc_b = nullptr;
}
_dmadesc_count = 0;
}
void Bus_Parallel16::enable_double_dma_desc(void)
{
ESP_LOGI("C6", "Enabled support for secondary DMA buffer.");
_double_dma_buffer = true;
}
// Need this to work for double buffers etc.
bool Bus_Parallel16::allocate_dma_desc_memory(size_t len)
{
if (_dmadesc_a)
heap_caps_free(_dmadesc_a); // free all dma descrptios previously
_dmadesc_count = len;
ESP_LOGD("C6", "Allocating %d bytes memory for DMA descriptors.", (int)sizeof(HUB75_DMA_DESCRIPTOR_T) * len);
_dmadesc_a = (HUB75_DMA_DESCRIPTOR_T *)heap_caps_malloc(sizeof(HUB75_DMA_DESCRIPTOR_T) * len, MALLOC_CAP_DMA);
if (_dmadesc_a == nullptr)
{
ESP_LOGE("C6", "ERROR: Couldn't malloc _dmadesc_a. Not enough memory.");
return false;
}
if (_double_dma_buffer)
{
_dmadesc_b = (HUB75_DMA_DESCRIPTOR_T *)heap_caps_malloc(sizeof(HUB75_DMA_DESCRIPTOR_T) * len, MALLOC_CAP_DMA);
if (_dmadesc_b == nullptr)
{
ESP_LOGE("C6", "ERROR: Couldn't malloc _dmadesc_b. Not enough memory.");
_double_dma_buffer = false;
}
}
/// override static
_dmadesc_a_idx = 0;
_dmadesc_b_idx = 0;
return true;
}
void Bus_Parallel16::create_dma_desc_link(void *data, size_t size, bool dmadesc_b)
{
static constexpr size_t MAX_DMA_LEN = (4096 - 4);
if (size > MAX_DMA_LEN)
{
size = MAX_DMA_LEN;
ESP_LOGW("C6", "Creating DMA descriptor which links to payload with size greater than MAX_DMA_LEN!");
}
if (dmadesc_b == true)
{
_dmadesc_b[_dmadesc_b_idx].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
//_dmadesc_b[_dmadesc_b_idx].dw0.suc_eof = 0;
_dmadesc_b[_dmadesc_b_idx].dw0.suc_eof = (_dmadesc_b_idx == (_dmadesc_count - 1));
_dmadesc_b[_dmadesc_b_idx].dw0.size = _dmadesc_b[_dmadesc_b_idx].dw0.length = size; // sizeof(data);
_dmadesc_b[_dmadesc_b_idx].buffer = data; // data;
if (_dmadesc_b_idx == _dmadesc_count - 1)
{
_dmadesc_b[_dmadesc_b_idx].next = (dma_descriptor_t *)&_dmadesc_b[0];
}
else
{
_dmadesc_b[_dmadesc_b_idx].next = (dma_descriptor_t *)&_dmadesc_b[_dmadesc_b_idx + 1];
}
_dmadesc_b_idx++;
}
else
{
if (_dmadesc_a_idx >= _dmadesc_count)
{
ESP_LOGE("C6", "Attempted to create more DMA descriptors than allocated. Expecting max %u descriptors.", (unsigned int)_dmadesc_count);
return;
}
_dmadesc_a[_dmadesc_a_idx].dw0.owner = DMA_DESCRIPTOR_BUFFER_OWNER_DMA;
//_dmadesc_a[_dmadesc_a_idx].dw0.suc_eof = 0;
_dmadesc_a[_dmadesc_a_idx].dw0.suc_eof = (_dmadesc_a_idx == (_dmadesc_count - 1));
_dmadesc_a[_dmadesc_a_idx].dw0.size = _dmadesc_a[_dmadesc_a_idx].dw0.length = size; // sizeof(data);
_dmadesc_a[_dmadesc_a_idx].buffer = data; // data;
if (_dmadesc_a_idx == _dmadesc_count - 1)
{
_dmadesc_a[_dmadesc_a_idx].next = (dma_descriptor_t *)&_dmadesc_a[0];
}
else
{
_dmadesc_a[_dmadesc_a_idx].next = (dma_descriptor_t *)&_dmadesc_a[_dmadesc_a_idx + 1];
}
_dmadesc_a_idx++;
}
} // end create_dma_desc_link
void Bus_Parallel16::dma_transfer_start()
{
parlio_ll_tx_reset_fifo(&PARL_IO);
parlio_ll_tx_reset_clock(&PARL_IO);
gdma_start(dma_chan, (intptr_t)&_dmadesc_a[0]);
while (parlio_ll_tx_is_ready(&PARL_IO) == false);
parlio_ll_tx_start(&PARL_IO, true);
parlio_ll_tx_enable_clock(&PARL_IO, true);
} // end
void Bus_Parallel16::dma_transfer_stop()
{
gdma_stop(dma_chan);
} // end
void Bus_Parallel16::flip_dma_output_buffer(int back_buffer_id)
{
// if ( _double_dma_buffer == false) return;
if (back_buffer_id == 1) // change across to everything 'b''
{
_dmadesc_a[_dmadesc_count - 1].next = (dma_descriptor_t *)&_dmadesc_b[0];
_dmadesc_b[_dmadesc_count - 1].next = (dma_descriptor_t *)&_dmadesc_b[0];
}
else
{
_dmadesc_b[_dmadesc_count - 1].next = (dma_descriptor_t *)&_dmadesc_a[0];
_dmadesc_a[_dmadesc_count - 1].next = (dma_descriptor_t *)&_dmadesc_a[0];
}
// current_back_buffer_id ^= 1;
previousBufferFree = false;
// while (i2s_parallel_is_previous_buffer_free() == false) {}
while (!previousBufferFree)
;
} // end flip
#endif

View file

@ -0,0 +1,117 @@
/*
Simple example of using the ESP32-C6's parallel IO peripheral for general-purpose
parallel data output with DMA.
Credits to ESPRESSIF them selfe, they made the first example:
https://github.com/espressif/esp-idf/tree/release/v5.1/examples/peripherals/parlio/simple_rgb_led_matrix
And Credits to the guy who implemented the S3 version of this library.
There is a lot of resusable code
*/
#pragma once
#include <sdkconfig.h>
#ifdef CONFIG_IDF_TARGET_ESP32C6
#include "driver/parlio_tx.h"
#include <esp_private/gdma.h>
#include <hal/dma_types.h>
#include <hal/gpio_hal.h>
#define DMA_MAX (4096-4)
#define HUB75_DMA_DESCRIPTOR_T dma_descriptor_t
class Bus_Parallel16
{
public:
Bus_Parallel16()
{
}
struct config_t
{
// LCD_CAM peripheral number. No need to change (only 0 for ESP32-S3.)
//int port = 0;
// max 40MHz (when in 16 bit / 2 byte mode)
uint32_t bus_freq = 10000000;
int8_t pin_wr = -1;
//int8_t pin_rd = -1;
//int8_t pin_rs = -1; // D/C
bool invert_pclk = false;
//bool psram_clk_override = false;
union
{
int8_t pin_data[16];
struct
{
int8_t pin_d0;
int8_t pin_d1;
int8_t pin_d2;
int8_t pin_d3;
int8_t pin_d4;
int8_t pin_d5;
int8_t pin_d6;
int8_t pin_d7;
int8_t pin_d8;
int8_t pin_d9;
int8_t pin_d10;
int8_t pin_d11;
int8_t pin_d12;
int8_t pin_d13;
int8_t pin_d14;
int8_t pin_d15;
};
};
};
const config_t& config(void) const { return _cfg; }
void config(const config_t& config);
bool init(void) ;
void release(void) ;
void enable_double_dma_desc();
bool allocate_dma_desc_memory(size_t len);
void create_dma_desc_link(void *memory, size_t size, bool dmadesc_b = false);
void dma_transfer_start();
void dma_transfer_stop();
void flip_dma_output_buffer(int back_buffer_id);
private:
config_t _cfg;
gdma_channel_handle_t dma_chan;
uint32_t _dmadesc_count = 0; // number of dma decriptors
uint32_t _dmadesc_a_idx = 0;
uint32_t _dmadesc_b_idx = 0;
HUB75_DMA_DESCRIPTOR_T* _dmadesc_a = nullptr;
HUB75_DMA_DESCRIPTOR_T* _dmadesc_b = nullptr;
bool _double_dma_buffer = false;
//bool _dmadesc_a_active = true;
};
#endif

View file

@ -0,0 +1,18 @@
#pragma once
// Avoid and QSPI pins
#define R1_PIN_DEFAULT 7
#define G1_PIN_DEFAULT 4
#define B1_PIN_DEFAULT 1
#define R2_PIN_DEFAULT 6
#define G2_PIN_DEFAULT 3
#define B2_PIN_DEFAULT 0
#define A_PIN_DEFAULT 20
#define B_PIN_DEFAULT 21
#define C_PIN_DEFAULT 22
#define D_PIN_DEFAULT 23
#define E_PIN_DEFAULT -1 // required for 1/32 scan panels, like 64x64. Any available pin would do, i.e. IO32
#define LAT_PIN_DEFAULT 5
#define OE_PIN_DEFAULT 2
#define CLK_PIN_DEFAULT 10

View file

@ -22,11 +22,7 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#include <sdkconfig.h>
#if defined (CONFIG_IDF_TARGET_ESP32C3)
#error "ERROR: ESP32C3 not supported."
#elif defined (CONFIG_IDF_TARGET_ESP32S2)
#if defined (CONFIG_IDF_TARGET_ESP32S2)
//#pragma message "Compiling for ESP32-S2"
#include "esp32/esp32_i2s_parallel_dma.hpp"
@ -47,9 +43,14 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#define NO_FAST_FUNCTIONS 1
#endif
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32H2)
#elif defined(CONFIG_IDF_TARGET_ESP32C6)
#error "ESP32 RISC-V devices do not have an LCD interface and are therefore not supported by this library."
#include "esp32c6/dma_parallel_io.hpp"
#include "esp32c6/esp32c6-default-pins.hpp"
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
#pragma message "you are ahead of your time. ESP32P4 Support is planned"
#elif defined (CONFIG_IDF_TARGET_ESP32) || defined(ESP32)
@ -63,6 +64,11 @@ Modified heavily for the ESP32 HUB75 DMA library by:
#include "esp32/esp32_i2s_parallel_dma.hpp"
#include "esp32/esp32-default-pins.hpp"
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32H2)
#error "ESP32 C2 C3 and H2 devices are not supported by this library."
#else
#error "Unknown platform."