mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/esp32: add LCD low-level parallel interface suppport
This commit is contained in:
parent
33d6281432
commit
70053c5284
@ -35,6 +35,7 @@ rsource "bootloader/Kconfig"
|
||||
rsource "esp-ble-nimble/Kconfig"
|
||||
rsource "esp-idf/Kconfig"
|
||||
rsource "esp-idf-api/Kconfig"
|
||||
rsource "esp-lcd/Kconfig"
|
||||
rsource "periph/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -29,6 +29,10 @@ ifneq (, $(filter esp_freertos, $(USEMODULE)))
|
||||
DIRS += freertos
|
||||
endif
|
||||
|
||||
ifneq (, $(filter esp_lcd, $(USEMODULE)))
|
||||
DIRS += esp-lcd
|
||||
endif
|
||||
|
||||
ifneq (, $(filter stdio_usb_serial_jtag, $(USEMODULE)))
|
||||
DIRS += stdio_usb_serial_jtag
|
||||
endif
|
||||
|
@ -128,6 +128,12 @@ ifneq (,$(filter esp_idf_heap,$(USEMODULE)))
|
||||
USEPKG += tlsf
|
||||
endif
|
||||
|
||||
ifneq (,$(filter lcd_parallel_ll_mcu,$(USEMODULE)))
|
||||
USEMODULE += esp_lcd
|
||||
USEMODULE += esp_idf_lcd
|
||||
USEMODULE += esp_idf_heap
|
||||
endif
|
||||
|
||||
ifneq (,$(filter mtd periph_flashpage,$(USEMODULE)))
|
||||
USEMODULE += esp_idf_spi_flash
|
||||
endif
|
||||
|
@ -113,6 +113,7 @@ PSEUDOMODULES += esp_hw_counter
|
||||
PSEUDOMODULES += esp_idf_gpio_hal
|
||||
PSEUDOMODULES += esp_i2c_hw
|
||||
PSEUDOMODULES += esp_jtag
|
||||
PSEUDOMODULES += esp_lcd_gpio
|
||||
PSEUDOMODULES += esp_rtc_timer_32k
|
||||
PSEUDOMODULES += esp_spi_ram
|
||||
PSEUDOMODULES += esp_spi_oct
|
||||
@ -167,6 +168,10 @@ ifneq (,$(filter esp_spi_ram,$(USEMODULE)))
|
||||
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_hw_support/include/soc/$(CPU_FAM)
|
||||
endif
|
||||
|
||||
ifneq (,$(filter esp_idf_lcd,$(USEMODULE)))
|
||||
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/include
|
||||
endif
|
||||
|
||||
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
|
||||
INCLUDES += -I$(ESP32_SDK_DIR)/components/spi_flash/include
|
||||
endif
|
||||
|
16
cpu/esp32/esp-idf/lcd/Kconfig
Normal file
16
cpu/esp32/esp-idf/lcd/Kconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2022 Gunar Schorcht
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
config MODULE_ESP_IDF_LCD
|
||||
bool
|
||||
depends on TEST_KCONFIG
|
||||
depends on MODULE_ESP_IDF && HAS_ESP_LCD
|
||||
|
||||
default y if MODULE_LCD_PARALLEL_LL_MCU
|
||||
|
||||
help
|
||||
ESP-IDF code for peripheral GPIO.
|
36
cpu/esp32/esp-idf/lcd/Makefile
Normal file
36
cpu/esp32/esp-idf/lcd/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
MODULE = esp_idf_lcd
|
||||
|
||||
# source files to be compiled for this module
|
||||
ESP32_SDK_SRC = \
|
||||
components/esp_lcd/src/esp_lcd_common.c \
|
||||
components/esp_lcd/src/esp_lcd_panel_io.c \
|
||||
components/esp_pm/pm_locks.c \
|
||||
components/soc/$(CPU_FAM)/lcd_periph.c \
|
||||
#
|
||||
|
||||
ifeq (esp32s3,$(CPU_FAM))
|
||||
ESP32_SDK_SRC += \
|
||||
components/driver/gdma.c \
|
||||
components/esp_lcd/src/esp_lcd_panel_io_i80.c \
|
||||
components/hal/gdma_hal.c \
|
||||
components/hal/lcd_hal.c \
|
||||
components/soc/$(CPU_FAM)/gdma_periph.c \
|
||||
#
|
||||
else ifneq (,$(filter esp32 esp32s2,$(CPU_FAM)))
|
||||
ESP32_SDK_SRC = \
|
||||
components/driver/i2s.c \
|
||||
components/esp_lcd/src/esp_lcd_panel_io_i2s.c \
|
||||
components/hal/i2s_hal.c \
|
||||
#
|
||||
endif
|
||||
|
||||
# additional include pathes required by this module
|
||||
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/include
|
||||
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/interface
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
||||
ESP32_SDK_BIN = $(BINDIR)/$(MODULE)
|
||||
|
||||
include ../esp_idf.mk
|
||||
include ../esp_idf_cflags.mk
|
54
cpu/esp32/esp-lcd/Kconfig
Normal file
54
cpu/esp32/esp-lcd/Kconfig
Normal file
@ -0,0 +1,54 @@
|
||||
# Copyright (c) 2023 Gunar Schorcht
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
menuconfig MODULE_ESP_LCD
|
||||
bool "Enable LCD low-level parallel interface driver"
|
||||
depends on MODULE_LCD
|
||||
default y if HAVE_LCD_PARALLEL_LL_MCU
|
||||
help
|
||||
Enabe the MCU-driven low-level MCU 8080 8-/16-bit parallel interface
|
||||
driver.
|
||||
|
||||
if MODULE_ESP_LCD
|
||||
|
||||
config MODULE_ESP_LCD_GPIO
|
||||
bool "GPIO-driven low-level parallel interface driver"
|
||||
depends on !CPU_FAM_ESP32 && !CPU_FAM_ESP32S2 && !CPU_FAM_ESP32S3
|
||||
default y
|
||||
help
|
||||
The ESP32x SoC variant used does not have a peripheral for the parallel
|
||||
low-level interface. However, it can be emulated with special low-level
|
||||
GPIO operations. It is faster than the GPIO-driven 8-/16-bit parallel
|
||||
interface implemented in the LCD driver, but requires 4 kByte RAM for
|
||||
8-bit data bus width and 8 kByte RAM for 16-bit data bus width.
|
||||
|
||||
config LCD_WRITE_CLOCK_MHZ
|
||||
int "LCD write clock rate in MHz"
|
||||
range 1 80
|
||||
depends on CPU_FAM_ESP32 || CPU_FAM_ESP32S2 || CPU_FAM_ESP32S3
|
||||
default 10 if CPU_FAM_ESP32
|
||||
default 40 if CPU_FAM_ESP32S2
|
||||
default 20 if CPU_FAM_ESP32S3
|
||||
help
|
||||
Defines the clock rate that is used for the LCD write signal. It
|
||||
depends on used ESP32x SoC variant and used display interface.
|
||||
|
||||
config LCD_DATA_BUF_SIZE
|
||||
int "LCD data buffer size in byte"
|
||||
depends on CPU_FAM_ESP32 || CPU_FAM_ESP32S2 || CPU_FAM_ESP32S3
|
||||
default 512
|
||||
help
|
||||
Defines the size of the buffers used to write data to the LCD
|
||||
screen. Since double buffering is used, there are two buffers
|
||||
of this size. One buffer is used first by the LCD driver to
|
||||
write the data that needs to be transferred to the LCD, and
|
||||
one buffer from which the DMA then transfers the data to the
|
||||
LCD peripherals. This allows data to be written before the
|
||||
DMA transfer is complete. The larger the buffers, the better
|
||||
the performance, but the higher the memory requirements.
|
||||
|
||||
endif
|
9
cpu/esp32/esp-lcd/Makefile
Normal file
9
cpu/esp32/esp-lcd/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
MODULE = esp_lcd
|
||||
|
||||
ifneq (,$(filter esp32 esp32s2 esp32s3,$(CPU_FAM)))
|
||||
SRC = esp_lcd_mcu.c
|
||||
else
|
||||
SRC = esp_lcd_gpio.c
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
269
cpu/esp32/esp-lcd/esp_lcd_gpio.c
Normal file
269
cpu/esp32/esp-lcd/esp_lcd_gpio.c
Normal file
@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_esp32
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief GPIO-driven low-Level parallel interface implementation for LCDs
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "periph/gpio.h"
|
||||
#include "lcd.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#if MODULE_ESP_LCD_GPIO
|
||||
|
||||
#include "soc/gpio_reg.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t set_mask_0; /* port 0 set mask */
|
||||
uint32_t set_mask_1; /* port 0 set mask */
|
||||
uint32_t clr_mask_0; /* port 1 clear mask */
|
||||
uint32_t clr_mask_1; /* port 1 clear mask */
|
||||
} _pin_mask_t;
|
||||
|
||||
static _pin_mask_t _low_byte_masks[256] = {};
|
||||
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
static _pin_mask_t _high_byte_masks[256] = {};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Following functions are not implemented by intention to let the
|
||||
* GPIO-driven low-level implementation handle the configuration
|
||||
* of the GPIOs. The function `_lcd_ll_mcu_set_data_dir` is used to
|
||||
* initialize the GPIO masks when the clear masks are completely 0.
|
||||
*/
|
||||
#if 0
|
||||
|
||||
static void _lcd_ll_mcu_init(lcd_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_cmd_start(lcd_t *dev, uint8_t cmd, bool cont)
|
||||
{
|
||||
(void)dev;
|
||||
(void)cmd;
|
||||
(void)cont;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void _lcd_ll_mcu_set_data_dir(lcd_t *dev, bool output)
|
||||
{
|
||||
DEBUG("[lcd_ll_mcu] %s %u\n", __func__, output);
|
||||
|
||||
/* sanity check to ensure that data pins can be handled as array */
|
||||
assert((&dev->params->d7_pin - &dev->params->d0_pin) == 7);
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
assert((&dev->params->d15_pin - &dev->params->d8_pin) == 7);
|
||||
#endif
|
||||
|
||||
if ((_low_byte_masks[0].clr_mask_0 == 0) &&
|
||||
(_low_byte_masks[0].clr_mask_1 == 0)) {
|
||||
/* initialize the mask array if it is not yet initialized */
|
||||
const gpio_t *pins = &dev->params->d0_pin;
|
||||
|
||||
for (unsigned data = 0; data < 256; data++) {
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
if (data & (1 << i)) {
|
||||
/* set mask */
|
||||
if (pins[i] < 32) {
|
||||
_low_byte_masks[data].set_mask_0 |= 1 << pins[i];
|
||||
}
|
||||
else {
|
||||
_low_byte_masks[data].set_mask_1 |= 1 << (pins[i] - 32);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* clear mask */
|
||||
if (pins[i] < 32) {
|
||||
_low_byte_masks[data].clr_mask_0 |= 1 << pins[i];
|
||||
}
|
||||
else {
|
||||
_low_byte_masks[data].clr_mask_1 |= 1 << (pins[i] - 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
pins = &dev->params->d8_pin;
|
||||
|
||||
for (unsigned data = 0; data < 256; data++) {
|
||||
for (unsigned i = 0; i < 8; i++) {
|
||||
if (data & (1 << i)) {
|
||||
/* set mask */
|
||||
if (pins[i] < 32) {
|
||||
_high_byte_masks[data].set_mask_0 |= 1 << pins[i];
|
||||
}
|
||||
else {
|
||||
_high_byte_masks[data].set_mask_1 |= 1 << (pins[i] - 32);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* clear mask */
|
||||
if (pins[i] < 32) {
|
||||
_high_byte_masks[data].clr_mask_0 |= 1 << pins[i];
|
||||
}
|
||||
else {
|
||||
_high_byte_masks[data].clr_mask_1 |= 1 << (pins[i] - 32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
gpio_init(dev->params->d0_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d1_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d2_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d3_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d4_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d5_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d6_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d7_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
gpio_init(dev->params->d8_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d9_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d10_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d11_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d12_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d13_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d14_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
gpio_init(dev->params->d15_pin, output ? GPIO_OUT : GPIO_IN);
|
||||
#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_write_data(lcd_t *dev, bool cont,
|
||||
uint16_t data, unsigned pin_num)
|
||||
{
|
||||
if (gpio_is_valid(dev->params->cs_pin)) {
|
||||
gpio_clear(dev->params->cs_pin);
|
||||
}
|
||||
|
||||
gpio_clear(dev->params->wrx_pin);
|
||||
|
||||
uint8_t _byte = data & 0xff;
|
||||
|
||||
uint32_t set_mask_0 = _low_byte_masks[_byte].set_mask_0;
|
||||
uint32_t clr_mask_0 = _low_byte_masks[_byte].clr_mask_0;
|
||||
uint32_t set_mask_1 = _low_byte_masks[_byte].set_mask_1;
|
||||
uint32_t clr_mask_1 = _low_byte_masks[_byte].clr_mask_1;
|
||||
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
_byte = data >> 8;
|
||||
|
||||
set_mask_0 |= _high_byte_masks[_byte].set_mask_0;
|
||||
clr_mask_0 |= _high_byte_masks[_byte].clr_mask_0;
|
||||
set_mask_1 |= _high_byte_masks[_byte].set_mask_1;
|
||||
clr_mask_1 |= _high_byte_masks[_byte].clr_mask_1;
|
||||
#endif
|
||||
|
||||
*((uint32_t *)GPIO_OUT_W1TS_REG) = set_mask_0;
|
||||
*((uint32_t *)GPIO_OUT_W1TC_REG) = clr_mask_0;
|
||||
*((uint32_t *)GPIO_OUT1_W1TS_REG) = set_mask_1;
|
||||
*((uint32_t *)GPIO_OUT1_W1TC_REG) = clr_mask_1;
|
||||
|
||||
gpio_set(dev->params->wrx_pin);
|
||||
|
||||
if (gpio_is_valid(dev->params->cs_pin) && !cont) {
|
||||
gpio_set(dev->params->cs_pin);
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t _lcd_ll_mcu_read_data(lcd_t *dev, bool cont, unsigned pin_num)
|
||||
{
|
||||
const gpio_t *pins = &dev->params->d0_pin;
|
||||
|
||||
if (gpio_is_valid(dev->params->cs_pin)) {
|
||||
gpio_clear(dev->params->cs_pin);
|
||||
}
|
||||
|
||||
gpio_clear(dev->params->rdx_pin);
|
||||
|
||||
uint32_t in_0 = *((uint32_t *)GPIO_IN_REG);
|
||||
uint32_t in_1 = *((uint32_t *)GPIO_IN1_REG);
|
||||
|
||||
gpio_set(dev->params->rdx_pin);
|
||||
|
||||
if (gpio_is_valid(dev->params->cs_pin) && !cont) {
|
||||
gpio_set(dev->params->cs_pin);
|
||||
};
|
||||
|
||||
uint16_t in = 0;
|
||||
|
||||
for (unsigned i = 0; i < pin_num; i++) {
|
||||
if (pins[i] < 32) {
|
||||
in |= in_0 & (1 << pins[i]) ? 1 : 0;
|
||||
}
|
||||
else {
|
||||
in |= in_1 & (1 << (pins[i] - 32)) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_write_byte(lcd_t *dev, bool cont, uint8_t out)
|
||||
{
|
||||
DEBUG("[lcd_ll_mcu] write byte: %02x\n", out);
|
||||
|
||||
_lcd_ll_mcu_write_data(dev, cont, out, 8);
|
||||
}
|
||||
|
||||
static uint8_t _lcd_ll_mcu_read_byte(lcd_t *dev, bool cont)
|
||||
{
|
||||
return _lcd_ll_mcu_read_data(dev, cont, 8);
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
|
||||
static void _lcd_ll_mcu_write_word(lcd_t *dev, bool cont, uint16_t out)
|
||||
{
|
||||
DEBUG("[lcd_ll_mcu] write word: %04x\n", out);
|
||||
|
||||
_lcd_ll_mcu_write_data(dev, cont, out, 16);
|
||||
}
|
||||
|
||||
static uint16_t _lcd_ll_mcu_read_word(lcd_t *dev, bool cont)
|
||||
{
|
||||
return _lcd_ll_mcu_read_data(dev, cont, 16);
|
||||
}
|
||||
|
||||
#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */
|
||||
|
||||
const lcd_ll_par_driver_t lcd_ll_par_driver = {
|
||||
.init = lcd_ll_par_gpio_init, /* GPIO-driven `init` is used */
|
||||
.set_data_dir = _lcd_ll_mcu_set_data_dir,
|
||||
.cmd_start = lcd_ll_par_gpio_cmd_start, /* GPIO-driven `cmd_start` is used */
|
||||
.write_byte = _lcd_ll_mcu_write_byte,
|
||||
.read_byte = _lcd_ll_mcu_read_byte,
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
.write_word = _lcd_ll_mcu_write_word,
|
||||
.read_word = _lcd_ll_mcu_read_word,
|
||||
#endif
|
||||
};
|
||||
|
||||
#else /* MODULE_ESP_LCD_GPIO */
|
||||
|
||||
/* the GPIO-driven low-level interface is not used */
|
||||
const lcd_ll_par_driver_t lcd_ll_par_driver = {
|
||||
};
|
||||
|
||||
#endif /* MODULE_ESP_LCD_GPIO */
|
260
cpu/esp32/esp-lcd/esp_lcd_mcu.c
Normal file
260
cpu/esp32/esp-lcd/esp_lcd_mcu.c
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_esp32
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Peripheral low-Level parallel interface implementation for LCDs
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lcd.h"
|
||||
#include "lcd_internal.h"
|
||||
#include "log.h"
|
||||
#include "macros/units.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "ztimer.h"
|
||||
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "soc/gpio_reg.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#if !defined(CPU_FAM_ESP32) && !defined(CPU_FAM_ESP32S2) && !defined(CPU_FAM_ESP32S3)
|
||||
#error "ESP32x SoC family not supported"
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LCD_WRITE_CLOCK_MHZ
|
||||
#if CONFIG_LCD_I80_COLOR_IN_PSRAM
|
||||
/* PCLK has to be low enough for SPI RAM */
|
||||
#define CONFIG_LCD_WRITE_CLOCK_MHZ 2
|
||||
#else
|
||||
|
||||
#if defined(CPU_FAM_ESP32S3)
|
||||
#define CONFIG_LCD_WRITE_CLOCK_MHZ 20
|
||||
#elif defined(CPU_FAM_ESP32S2)
|
||||
#define CONFIG_LCD_WRITE_CLOCK_MHZ 40
|
||||
#else /* ESP32 */
|
||||
#define CONFIG_LCD_WRITE_CLOCK_MHZ 10
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_LCD_I80_COLOR_IN_PSRAM */
|
||||
#endif /* CONFIG_LCD_WRITE_CLOCK_MHZ */
|
||||
|
||||
static_assert(CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE >= 32,
|
||||
"CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE mus be at least 32");
|
||||
|
||||
/* ESP32x SoCs support only one LCD peripheral so we can use single instances
|
||||
* of the following variables */
|
||||
|
||||
int _cmd = -1; /* means no command needed in ESP-IDF */
|
||||
|
||||
size_t _idx_bytes = 0;
|
||||
uint8_t _data_bytes[CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE];
|
||||
uint8_t _trans_bytes[CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE];
|
||||
|
||||
esp_lcd_i80_bus_handle_t _i80_bus_handle = NULL;
|
||||
esp_lcd_panel_io_handle_t _i80_io_handle = NULL;
|
||||
|
||||
/* indicates that a transfer of the data buffer is still in progress and must
|
||||
* not be overwritten */
|
||||
static bool _dma_transfer_in_progress = false;
|
||||
|
||||
static bool _dma_transfer_done(esp_lcd_panel_io_handle_t io_handle,
|
||||
esp_lcd_panel_io_event_data_t *io_event_data,
|
||||
void *user_ctx)
|
||||
{
|
||||
(void)io_handle;
|
||||
(void)io_event_data;
|
||||
(void)user_ctx;
|
||||
|
||||
_dma_transfer_in_progress = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_init(lcd_t *dev)
|
||||
{
|
||||
esp_lcd_i80_bus_config_t i80_bus_config = {
|
||||
.dc_gpio_num = dev->params->dcx_pin,
|
||||
.wr_gpio_num = dev->params->wrx_pin,
|
||||
.clk_src = LCD_CLK_SRC_PLL160M,
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
.data_gpio_nums = {
|
||||
dev->params->d0_pin,
|
||||
dev->params->d1_pin,
|
||||
dev->params->d2_pin,
|
||||
dev->params->d3_pin,
|
||||
dev->params->d4_pin,
|
||||
dev->params->d5_pin,
|
||||
dev->params->d6_pin,
|
||||
dev->params->d7_pin,
|
||||
dev->params->d8_pin,
|
||||
dev->params->d9_pin,
|
||||
dev->params->d10_pin,
|
||||
dev->params->d11_pin,
|
||||
dev->params->d12_pin,
|
||||
dev->params->d13_pin,
|
||||
dev->params->d14_pin,
|
||||
dev->params->d15_pin,
|
||||
},
|
||||
.bus_width = 16,
|
||||
#else
|
||||
.data_gpio_nums = {
|
||||
dev->params->d0_pin,
|
||||
dev->params->d1_pin,
|
||||
dev->params->d2_pin,
|
||||
dev->params->d3_pin,
|
||||
dev->params->d4_pin,
|
||||
dev->params->d5_pin,
|
||||
dev->params->d6_pin,
|
||||
dev->params->d7_pin,
|
||||
},
|
||||
.bus_width = 8,
|
||||
#endif
|
||||
.max_transfer_bytes = dev->params->rgb_channels * 40 * sizeof(uint16_t),
|
||||
};
|
||||
|
||||
esp_lcd_panel_io_i80_config_t i80_io_config = {
|
||||
.cs_gpio_num = gpio_is_valid(dev->params->cs_pin) ? (int)dev->params->cs_pin
|
||||
: -1,
|
||||
.pclk_hz = MHZ(CONFIG_LCD_WRITE_CLOCK_MHZ),
|
||||
.trans_queue_depth = 10,
|
||||
.dc_levels = {
|
||||
.dc_idle_level = 0,
|
||||
.dc_cmd_level = 0,
|
||||
.dc_dummy_level = 1,
|
||||
.dc_data_level = 1,
|
||||
},
|
||||
.on_color_trans_done = _dma_transfer_done,
|
||||
.user_ctx = NULL,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
};
|
||||
|
||||
esp_lcd_new_i80_bus(&i80_bus_config, &_i80_bus_handle);
|
||||
esp_lcd_new_panel_io_i80(_i80_bus_handle, &i80_io_config, &_i80_io_handle);
|
||||
|
||||
if (gpio_is_valid(dev->params->rdx_pin)) {
|
||||
gpio_init(dev->params->rdx_pin, GPIO_IN_PU);
|
||||
gpio_set(dev->params->rdx_pin);
|
||||
}
|
||||
|
||||
if (gpio_is_valid(dev->params->cs_pin)) {
|
||||
gpio_init(dev->params->cs_pin, GPIO_OUT);
|
||||
gpio_clear(dev->params->cs_pin);
|
||||
}
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_set_data_dir(lcd_t *dev, bool output)
|
||||
{
|
||||
(void)dev;
|
||||
(void)output;
|
||||
LOG_ERROR("[lcd_ll_mcu] set dir: %d is not supported\n", output);
|
||||
/* not supported yet */
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_cmd_start(lcd_t *dev, uint8_t cmd, bool cont)
|
||||
{
|
||||
DEBUG("[lcd_ll_mcu] write cmd: %02x\n", cmd);
|
||||
|
||||
if (!cont) {
|
||||
/* cmd without parameters */
|
||||
esp_lcd_panel_io_tx_param(_i80_io_handle, cmd, NULL, 0);
|
||||
_cmd = -1;
|
||||
}
|
||||
else {
|
||||
/* cmd with parameters */
|
||||
_cmd = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_transfer(lcd_t *dev, bool cont)
|
||||
{
|
||||
if (!cont) {
|
||||
/* if no further data follow, send the command with the data in the buffer */
|
||||
esp_lcd_panel_io_tx_param(_i80_io_handle, _cmd, _data_bytes, _idx_bytes);
|
||||
_cmd = -1;
|
||||
_idx_bytes = 0;
|
||||
}
|
||||
else if (_idx_bytes == CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE) {
|
||||
/* spin lock as long as a DMA data transfer is still in progress */
|
||||
while (_dma_transfer_in_progress) {}
|
||||
|
||||
/* copy data buffer to the DMA transfer buffer */
|
||||
memcpy(_trans_bytes, _data_bytes, _idx_bytes);
|
||||
/* start DMA data transfer */
|
||||
_dma_transfer_in_progress = true;
|
||||
esp_lcd_panel_io_tx_color(_i80_io_handle, _cmd, _data_bytes, _idx_bytes);
|
||||
|
||||
/* It should only be possible to follow more data than
|
||||
* CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE with the RAMWR command.
|
||||
* Transferring more data to continue the operation with cmd=-1 does
|
||||
* not seem to work. Therefore a RAMWRC generated in this
|
||||
* case for further data */
|
||||
_cmd = (_cmd == LCD_CMD_RAMWR) ? LCD_CMD_RAMWRC : _cmd;
|
||||
_idx_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void _lcd_ll_mcu_write_byte(lcd_t *dev, bool cont, uint8_t out)
|
||||
{
|
||||
DEBUG("[lcd_ll_mcu] write byte: %02x\n", out);
|
||||
|
||||
_data_bytes[_idx_bytes++] = out;
|
||||
/* transfer the data if necessary */
|
||||
_lcd_ll_mcu_transfer(dev, cont);
|
||||
}
|
||||
|
||||
static uint8_t _lcd_ll_mcu_read_byte(lcd_t *dev, bool cont)
|
||||
{
|
||||
LOG_ERROR("[lcd_ll_mcu] read from LCD is not supported\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
|
||||
static void _lcd_ll_mcu_write_word(lcd_t *dev, bool cont, uint16_t out)
|
||||
{
|
||||
DEBUG("[lcd_ll_mcu] write word: %04x\n", out);
|
||||
|
||||
/* out is given in BE order */
|
||||
_data_bytes[_idx_bytes++] = out >> 8;
|
||||
_data_bytes[_idx_bytes++] = out & 0xff;
|
||||
/* transfer the data if necessary */
|
||||
_lcd_ll_mcu_transfer(dev, cont);
|
||||
}
|
||||
|
||||
static uint16_t _lcd_ll_mcu_read_word(lcd_t *dev, bool cont)
|
||||
{
|
||||
LOG_ERROR("[lcd_ll_mcu] read from LCD is not supported\n");
|
||||
/* not supported yet */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */
|
||||
|
||||
const lcd_ll_par_driver_t lcd_ll_par_driver = {
|
||||
.init = _lcd_ll_mcu_init,
|
||||
.set_data_dir = _lcd_ll_mcu_set_data_dir,
|
||||
.cmd_start = _lcd_ll_mcu_cmd_start,
|
||||
.write_byte = _lcd_ll_mcu_write_byte,
|
||||
.read_byte = _lcd_ll_mcu_read_byte,
|
||||
#if IS_USED(MODULE_LCD_PARALLEL_16BIT)
|
||||
.write_word = _lcd_ll_mcu_write_word,
|
||||
.read_word = _lcd_ll_mcu_read_word,
|
||||
#endif
|
||||
};
|
@ -47,6 +47,7 @@ extern "C" {
|
||||
#define CPU_INUM_WDT 13 /**< Level interrupt with low priority 1 */
|
||||
#define CPU_INUM_SOFTWARE 17 /**< Level interrupt with low priority 1 */
|
||||
#define CPU_INUM_ETH 18 /**< Level interrupt with low priority 1 */
|
||||
#define CPU_INUM_LCD 18 /**< Level interrupt with low priority 1 */
|
||||
#define CPU_INUM_TIMER 19 /**< Level interrupt with medium priority 2 */
|
||||
#define CPU_INUM_FRC2 20 /**< Level interrupt with medium priority 2 */
|
||||
#define CPU_INUM_SYSTIMER 20 /**< Level interrupt with medium priority 2 */
|
||||
|
@ -233,6 +233,17 @@
|
||||
|
||||
#endif /* !CONFIG_ESP_FLASHPAGE_CAPACITY */
|
||||
|
||||
/**
|
||||
* LCD driver configuration
|
||||
*/
|
||||
#if MODULE_ESP_IDF_LCD
|
||||
#ifndef CONFIG_LCD_DATA_BUF_SIZE
|
||||
#define CONFIG_LCD_DATA_BUF_SIZE 512
|
||||
#endif
|
||||
|
||||
#define CONFIG_LCD_PANEL_IO_FORMAT_BUF_SIZE CONFIG_LCD_DATA_BUF_SIZE
|
||||
#endif
|
||||
|
||||
#endif /* DOXYGEN */
|
||||
|
||||
/**
|
||||
|
@ -87,10 +87,19 @@ static const struct intr_handle_data_t _irq_data_table[] = {
|
||||
{ ETS_USB_SERIAL_JTAG_INTR_SOURCE, CPU_INUM_SERIAL_JTAG, 1 },
|
||||
#endif
|
||||
{ ETS_RMT_INTR_SOURCE, CPU_INUM_RMT, 1 },
|
||||
#if defined(CPU_FAM_ESP32) || defined(CPU_FAM_ESP32S2)
|
||||
{ ETS_I2S0_INTR_SOURCE, CPU_INUM_LCD, 1 },
|
||||
#elif defined(CPU_FAM_ESP32S3)
|
||||
{ ETS_LCD_CAM_INTR_SOURCE, CPU_INUM_LCD, 1 },
|
||||
#endif
|
||||
};
|
||||
|
||||
#define IRQ_DATA_TABLE_SIZE ARRAY_SIZE(_irq_data_table)
|
||||
|
||||
#if defined(CPU_FAM_ESP32) && MODULE_ESP_LCD && MODULE_ESP_ETH
|
||||
#error "esp_eth and esp_lcd can't be used at the same time because of an interrupt conflict"
|
||||
#endif
|
||||
|
||||
void esp_irq_init(void)
|
||||
{
|
||||
#ifdef SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
@ -172,6 +181,17 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler,
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_intr_alloc_intrstatus(int source, int flags,
|
||||
uint32_t reg, uint32_t mask,
|
||||
intr_handler_t handler,
|
||||
void *arg, intr_handle_t *ret_handle)
|
||||
{
|
||||
/* TODO status register and status mask handling for shared interrupts */
|
||||
(void)reg;
|
||||
(void)mask;
|
||||
return esp_intr_alloc(source, flags, handler, arg, ret_handle);
|
||||
}
|
||||
|
||||
esp_err_t esp_intr_free(intr_handle_t handle)
|
||||
{
|
||||
return esp_intr_disable(handle);
|
||||
|
Loading…
Reference in New Issue
Block a user