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-ble-nimble/Kconfig"
|
||||||
rsource "esp-idf/Kconfig"
|
rsource "esp-idf/Kconfig"
|
||||||
rsource "esp-idf-api/Kconfig"
|
rsource "esp-idf-api/Kconfig"
|
||||||
|
rsource "esp-lcd/Kconfig"
|
||||||
rsource "periph/Kconfig"
|
rsource "periph/Kconfig"
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
@ -29,6 +29,10 @@ ifneq (, $(filter esp_freertos, $(USEMODULE)))
|
|||||||
DIRS += freertos
|
DIRS += freertos
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (, $(filter esp_lcd, $(USEMODULE)))
|
||||||
|
DIRS += esp-lcd
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (, $(filter stdio_usb_serial_jtag, $(USEMODULE)))
|
ifneq (, $(filter stdio_usb_serial_jtag, $(USEMODULE)))
|
||||||
DIRS += stdio_usb_serial_jtag
|
DIRS += stdio_usb_serial_jtag
|
||||||
endif
|
endif
|
||||||
|
@ -128,6 +128,12 @@ ifneq (,$(filter esp_idf_heap,$(USEMODULE)))
|
|||||||
USEPKG += tlsf
|
USEPKG += tlsf
|
||||||
endif
|
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)))
|
ifneq (,$(filter mtd periph_flashpage,$(USEMODULE)))
|
||||||
USEMODULE += esp_idf_spi_flash
|
USEMODULE += esp_idf_spi_flash
|
||||||
endif
|
endif
|
||||||
|
@ -113,6 +113,7 @@ PSEUDOMODULES += esp_hw_counter
|
|||||||
PSEUDOMODULES += esp_idf_gpio_hal
|
PSEUDOMODULES += esp_idf_gpio_hal
|
||||||
PSEUDOMODULES += esp_i2c_hw
|
PSEUDOMODULES += esp_i2c_hw
|
||||||
PSEUDOMODULES += esp_jtag
|
PSEUDOMODULES += esp_jtag
|
||||||
|
PSEUDOMODULES += esp_lcd_gpio
|
||||||
PSEUDOMODULES += esp_rtc_timer_32k
|
PSEUDOMODULES += esp_rtc_timer_32k
|
||||||
PSEUDOMODULES += esp_spi_ram
|
PSEUDOMODULES += esp_spi_ram
|
||||||
PSEUDOMODULES += esp_spi_oct
|
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)
|
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_hw_support/include/soc/$(CPU_FAM)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter esp_idf_lcd,$(USEMODULE)))
|
||||||
|
INCLUDES += -I$(ESP32_SDK_DIR)/components/esp_lcd/include
|
||||||
|
endif
|
||||||
|
|
||||||
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
|
ifneq (,$(filter esp_idf_spi_flash,$(USEMODULE)))
|
||||||
INCLUDES += -I$(ESP32_SDK_DIR)/components/spi_flash/include
|
INCLUDES += -I$(ESP32_SDK_DIR)/components/spi_flash/include
|
||||||
endif
|
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_WDT 13 /**< Level interrupt with low priority 1 */
|
||||||
#define CPU_INUM_SOFTWARE 17 /**< 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_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_TIMER 19 /**< Level interrupt with medium priority 2 */
|
||||||
#define CPU_INUM_FRC2 20 /**< 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 */
|
#define CPU_INUM_SYSTIMER 20 /**< Level interrupt with medium priority 2 */
|
||||||
|
@ -233,6 +233,17 @@
|
|||||||
|
|
||||||
#endif /* !CONFIG_ESP_FLASHPAGE_CAPACITY */
|
#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 */
|
#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 },
|
{ ETS_USB_SERIAL_JTAG_INTR_SOURCE, CPU_INUM_SERIAL_JTAG, 1 },
|
||||||
#endif
|
#endif
|
||||||
{ ETS_RMT_INTR_SOURCE, CPU_INUM_RMT, 1 },
|
{ 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)
|
#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)
|
void esp_irq_init(void)
|
||||||
{
|
{
|
||||||
#ifdef SOC_CPU_HAS_FLEXIBLE_INTC
|
#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;
|
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)
|
esp_err_t esp_intr_free(intr_handle_t handle)
|
||||||
{
|
{
|
||||||
return esp_intr_disable(handle);
|
return esp_intr_disable(handle);
|
||||||
|
Loading…
Reference in New Issue
Block a user