diff --git a/drivers/include/lcd.h b/drivers/include/lcd.h index 605c248fa6..f1c03e314d 100644 --- a/drivers/include/lcd.h +++ b/drivers/include/lcd.h @@ -256,8 +256,8 @@ void lcd_ll_release(lcd_t *dev); * * @param[in] dev device descriptor * @param[in] cmd command code - * @param[in] data command data to the device - * @param[in] len length of the command data + * @param[in] data command data to the device or NULL for commands without data + * @param[in] len length of the command data or 0 for commands without data */ void lcd_ll_write_cmd(lcd_t *dev, uint8_t cmd, const uint8_t *data, size_t len); @@ -350,8 +350,8 @@ void lcd_pixmap(lcd_t *dev, uint16_t x1, uint16_t x2, uint16_t y1, * * @param[in] dev device descriptor * @param[in] cmd command code - * @param[in] data command data to the device - * @param[in] len length of the command data + * @param[in] data command data to the device or NULL for commands without data + * @param[in] len length of the command data or 0 for commands without data */ void lcd_write_cmd(lcd_t *dev, uint8_t cmd, const uint8_t *data, size_t len); @@ -387,6 +387,109 @@ void lcd_invert_on(lcd_t *dev); void lcd_invert_off(lcd_t *dev); /** @} */ +#if MODULE_LCD_PARALLEL || DOXYGEN +/** + * @name Low-level MCU 8080 8-/16-bit parallel interface + * + * The low-level MCU 8080 8-/16-bit parallel interface (low-level parallel + * interface for short) is used when the LCD device is connected via a parallel + * interface. Either the GPIO-driven low-level parallel interface provided by + * this LCD driver or a low-level parallel interface implemented by the MCU, + * such as the STM32 FMC peripheral, can be used. If the MCU provides its + * own implementation of the low-level parallel interface, it can be used + * by implementing the following low-level parallel interface driver functions, + * enabling the `lcd_parallel_ll_mcu` module and defining the + * @ref lcd_ll_par_driver variable of type @ref lcd_ll_par_driver_t. + * + * @{ + */ + +/** + * @brief Low-level MCU 8080 8-/16-bit parallel interface driver + * + * If the MCU-driven low-level parallel interface is enabled by + * module `lcd_ll_parallel_mcu`, the implementation of the MCU low-level + * parallel interface has to define a variable @ref lcd_ll_par_driver of this + * type. All or a set of members have to point to the low-level parallel + * interface functions implemented by the MCU. For functions that are not + * implemented by the MCU, the members have to be set to the corresponding + * GPIO-driven low-level parallel interface functions provided by the LCD + * driver. + */ +typedef struct { + /** + * @brief Initialize the MCU-driven low-level parallel interface + * + * @param[in] dev device descriptor + */ + void (*init)(lcd_t *dev); + + /** + * @brief Set the data direction of the low-level parallel interface + * + * @param[in] dev device descriptor + * @param[in] output set to output mode if true and to input mode otherwise + */ + void (*set_data_dir)(lcd_t *dev, bool output); + + /** + * @brief Write command using the MCU-driven low-level parallel interface + * + * @param[in] dev device descriptor + * @param[in] cmd command + * @param[in] cont operation is continued + */ + void (*cmd_start)(lcd_t *dev, uint8_t cmd, bool cont); + + /** + * @brief Write a byte using the MCU-driven low-level parallel interface + * + * @param[in] dev device descriptor + * @param[in] cont operation is continued + * @param[in] out byte to be written + */ + void (*write_byte)(lcd_t *dev, bool cont, uint8_t out); + + /** + * @brief Read a byte using the MCU-driven low-level parallel interface + * + * @param[in] dev device descriptor + * @param[in] cont operation is continued + * + * @return byte read + */ + uint8_t (*read_byte)(lcd_t *dev, bool cont); + +#if MODULE_LCD_PARALLEL_16BIT || DOXYGEN + /** + * @brief Write a word using the MCU-driven low-level parallel interface + * + * @param[in] dev device descriptor + * @param[in] cont operation is continued + * @param[in] out word to be written + */ + void (*write_word)(lcd_t *dev, bool cont, uint16_t out); + + /** + * @brief Read a word using the MCU-driven low-level parallel interface + * + * @param[in] dev device descriptor + * @param[in] cont operation is continued + * + * @return word read + */ + uint16_t (*read_word)(lcd_t *dev, bool cont); +#endif +} lcd_ll_par_driver_t; + +/** + * @brief Low-level parallel interface driver instance + */ +extern const lcd_ll_par_driver_t lcd_ll_par_driver; + +/** @} */ +#endif + #ifdef __cplusplus } #endif diff --git a/drivers/lcd/Kconfig b/drivers/lcd/Kconfig index 05626f433c..64ce2a5571 100644 --- a/drivers/lcd/Kconfig +++ b/drivers/lcd/Kconfig @@ -11,6 +11,8 @@ config MODULE_LCD depends on TEST_KCONFIG select MODULE_PERIPH_GPIO +if MODULE_LCD + config MODULE_LCD_MULTI_CNTRL bool help @@ -21,7 +23,6 @@ config MODULE_LCD_SPI default y if !MODULE_LCD_PARALLEL && !MODULE_LCD_PARALLEL_16BIT default y if HAVE_LCD_SPI depends on HAS_PERIPH_SPI - depends on MODULE_LCD select MODULE_PERIPH_SPI help SPI serial interface is used @@ -29,17 +30,24 @@ config MODULE_LCD_SPI config MODULE_LCD_PARALLEL bool default y if HAVE_LCD_PARALLEL || HAVE_LCD_PARALLEL_16BIT - depends on MODULE_LCD help MCU 8080 8-/16-bit parallel interface is used config MODULE_LCD_PARALLEL_16BIT bool default y if HAVE_LCD_PARALLEL_16BIT - depends on MODULE_LCD help MCU 8080 16-bit paralell interface is used +config MODULE_LCD_PARALLEL_LL_MCU + bool + default y if HAVE_LCD_PARALLEL_LL_MCU + depends on MODULE_LCD_PARALLEL + help + MCU 8080 8-/16-bit low-level parallel interface is provided by the MCU. + +endif + config HAVE_LCD_SPI bool help @@ -58,6 +66,12 @@ config HAVE_LCD_PARALLEL_16BIT Indicates that a display with MCU 8080 16-bit parallel interface is present +config HAVE_LCD_PARALLEL_LL_MCU + bool + help + Indicates that the MCU provides the MCU 8080 8-/16-bit low-level + parallel interface implementation. + menuconfig KCONFIG_USEMODULE_LCD bool "Configure LCD driver" depends on USEMODULE_LCD diff --git a/drivers/lcd/Makefile.dep b/drivers/lcd/Makefile.dep index 0a69ce5216..fbccd59ae7 100644 --- a/drivers/lcd/Makefile.dep +++ b/drivers/lcd/Makefile.dep @@ -1,6 +1,6 @@ FEATURES_REQUIRED += periph_gpio -ifneq (,$(filter lcd_parallel_16bit,$(USEMODULE))) +ifneq (,$(filter lcd_parallel_%,$(USEMODULE))) USEMODULE += lcd_parallel endif diff --git a/drivers/lcd/Makefile.include b/drivers/lcd/Makefile.include index 92015f1c5f..689c7d6969 100644 --- a/drivers/lcd/Makefile.include +++ b/drivers/lcd/Makefile.include @@ -3,6 +3,7 @@ PSEUDOMODULES += lcd_multi_cntrl PSEUDOMODULES += lcd_spi PSEUDOMODULES += lcd_parallel PSEUDOMODULES += lcd_parallel_16bit +PSEUDOMODULES += lcd_parallel_ll_mcu USEMODULE_INCLUDES_lcd := $(LAST_MAKEFILEDIR)/include USEMODULE_INCLUDES += $(USEMODULE_INCLUDES_lcd) diff --git a/drivers/lcd/lcd.c b/drivers/lcd/lcd.c index aa7858e4f4..b6a89333f2 100644 --- a/drivers/lcd/lcd.c +++ b/drivers/lcd/lcd.c @@ -26,6 +26,7 @@ #include #include "byteorder.h" #include "kernel_defines.h" +#include "log.h" #include "ztimer.h" #if IS_USED(MODULE_LCD_SPI) @@ -43,24 +44,24 @@ static void lcd_ll_par_write_byte(lcd_t *dev, bool cont, uint8_t out) { - lcd_ll_par_gpio_write_byte(dev, cont, out); + lcd_ll_par_driver.write_byte(dev, cont, out); } static uint8_t lcd_ll_par_read_byte(lcd_t *dev, bool cont) { - return lcd_ll_par_gpio_read_byte(dev, cont); + return lcd_ll_par_driver.read_byte(dev, cont); } #if IS_USED(MODULE_LCD_PARALLEL_16BIT) static void lcd_ll_par_write_word(lcd_t *dev, bool cont, uint16_t out) { - lcd_ll_par_gpio_write_word(dev, cont, out); + lcd_ll_par_driver.write_word(dev, cont, out); } static uint16_t lcd_ll_par_read_word(lcd_t *dev, bool cont) { - return lcd_ll_par_gpio_read_word(dev, cont); + return lcd_ll_par_driver.read_word(dev, cont); } #endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ @@ -83,7 +84,7 @@ static void lcd_ll_par_write_bytes(lcd_t *dev, bool cont, } return; } -#endif +#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ for (size_t i = 0; i < len; i++) { lcd_ll_par_write_byte(dev, i == (len - 1) ? cont : true, data_out[i]); @@ -108,7 +109,7 @@ static void lcd_ll_par_read_bytes(lcd_t *dev, bool cont, } return; } -#endif +#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ for (size_t i = 0; i < len; i++) { data_in[i] = lcd_ll_par_read_byte(dev, i == (len - 1) ? cont : true); @@ -129,8 +130,6 @@ static inline void lcd_ll_write_byte(lcd_t *dev, bool cont, uint8_t data) #if IS_USED(MODULE_LCD_PARALLEL) /* MCU 8080 8-/16-bit parallel interface is used */ lcd_ll_par_write_byte(dev, cont, data); -#else - assert(false); #endif #if IS_USED(MODULE_LCD_SPI) } @@ -151,8 +150,6 @@ static inline void lcd_ll_write_bytes(lcd_t *dev, bool cont, #if IS_USED(MODULE_LCD_PARALLEL) /* MCU 8080 8-/16-bit parallel interface is used */ lcd_ll_par_write_bytes(dev, cont, data, len); -#else - assert(false); #endif #if IS_USED(MODULE_LCD_SPI) } @@ -172,21 +169,19 @@ static inline void lcd_ll_read_bytes(lcd_t *dev, bool cont, dev->params->cs_pin, cont, NULL, data, len); } else { -#endif +#endif /* IS_USED(MODULE_LCD_SPI) */ #if IS_USED(MODULE_LCD_PARALLEL) /* MCU 8080 8-/16-bit parallel interface is used */ /* switch GPIO mode to input */ - lcd_ll_par_gpio_set_data_dir(dev, false); + lcd_ll_par_driver.set_data_dir(dev, false); /* Dummy read */ lcd_ll_par_read_byte(dev, true); lcd_ll_par_read_bytes(dev, cont, data, len); /* switch GPIO mode back to output */ - lcd_ll_par_gpio_set_data_dir(dev, true); -#else - assert(false); + lcd_ll_par_driver.set_data_dir(dev, true); #endif #if IS_USED(MODULE_LCD_SPI) } @@ -197,7 +192,7 @@ static void lcd_ll_cmd_start(lcd_t *dev, uint8_t cmd, bool cont) { #if IS_USED(MODULE_LCD_PARALLEL) if (dev->params->mode != LCD_IF_SPI) { - lcd_ll_par_gpio_cmd_start(dev, cmd, cont); + lcd_ll_par_driver.cmd_start(dev, cmd, cont); } else { #endif @@ -265,8 +260,6 @@ void lcd_ll_acquire(lcd_t *dev) #if IS_USED(MODULE_LCD_PARALLEL) /* MCU 8080 8-/16-bit parallel interface is used */ mutex_lock(&dev->lock); -#else - assert(false); #endif #if IS_USED(MODULE_LCD_SPI) } @@ -290,8 +283,6 @@ void lcd_ll_release(lcd_t *dev) #if IS_USED(MODULE_LCD_PARALLEL) /* MCU 8080 8-/16-bit parallel interface is used */ mutex_unlock(&dev->lock); -#else - assert(false); #endif #if IS_USED(MODULE_LCD_SPI) } @@ -336,11 +327,11 @@ int lcd_init(lcd_t *dev, const lcd_params_t *params) { dev->params = params; - assert(gpio_is_valid(dev->params->dcx_pin)); - gpio_init(dev->params->dcx_pin, GPIO_OUT); - #if IS_USED(MODULE_LCD_SPI) if (dev->params->spi != SPI_UNDEF) { + assert(gpio_is_valid(dev->params->dcx_pin)); + gpio_init(dev->params->dcx_pin, GPIO_OUT); + /* SPI serial interface is used */ int res = spi_init_cs(dev->params->spi, dev->params->cs_pin); @@ -350,12 +341,25 @@ int lcd_init(lcd_t *dev, const lcd_params_t *params) } } else { -#endif +#endif /* IS_USED(MODULE_LCD_SPI) */ + #if IS_USED(MODULE_LCD_PARALLEL) - lcd_ll_par_gpio_init(dev); -#else + mutex_init(&dev->lock); +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + dev->word_access = false; +#endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ + + /* Low-level parallel interface initialization */ + lcd_ll_par_driver.init(dev); + /* set output data direction */ + lcd_ll_par_driver.set_data_dir(dev, true); + +#else /* IS_USED(MODULE_LCD_PARALLEL) */ + + LOG_ERROR("[lcd] either lcd_parallel or lcd_spi has to be enabled"); assert(false); #endif + #if IS_USED(MODULE_LCD_SPI) } #endif @@ -368,10 +372,6 @@ int lcd_init(lcd_t *dev, const lcd_params_t *params) } ztimer_sleep(ZTIMER_MSEC, 120); -#if IS_USED(MODULE_LCD_PARALLEL) - mutex_init(&dev->lock); -#endif - /* controller-specific init function has to be defined */ assert(dev->driver->init); return dev->driver->init(dev, params); diff --git a/drivers/lcd/lcd_ll_par_gpio.c b/drivers/lcd/lcd_ll_par_gpio.c index 3a246322bc..7db624f355 100644 --- a/drivers/lcd/lcd_ll_par_gpio.c +++ b/drivers/lcd/lcd_ll_par_gpio.c @@ -243,4 +243,20 @@ uint16_t lcd_ll_par_gpio_read_word(lcd_t *dev, bool cont) } #endif /* IS_USED(MODULE_LCD_PARALLEL_16BIT) */ +#if !IS_USED(MODULE_LCD_PARALLEL_LL_MCU) +/* If MCU-driven low-level implementation is not used, the GPIO-driven + * implementation is used as driver. */ +const lcd_ll_par_driver_t lcd_ll_par_driver = { + .init = lcd_ll_par_gpio_init, + .set_data_dir = lcd_ll_par_gpio_set_data_dir, + .cmd_start = lcd_ll_par_gpio_cmd_start, + .write_byte = lcd_ll_par_gpio_write_byte, + .read_byte = lcd_ll_par_gpio_read_byte, +#if IS_USED(MODULE_LCD_PARALLEL_16BIT) + .write_word = lcd_ll_par_gpio_write_word, + .read_word = lcd_ll_par_gpio_read_word, +#endif +}; +#endif + #endif /* IS_USED(MODULE_LCD_PARALLEL) */