1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/stm32: add FMC support for LCD with parallel interface

This commit is contained in:
Gunar Schorcht 2023-07-30 09:43:10 +02:00
parent dd62f419d7
commit 94fdead641
8 changed files with 272 additions and 0 deletions

View File

@ -78,6 +78,7 @@ rsource "periph/Kconfig.fmc"
if TEST_KCONFIG
rsource "lcd_fmc/Kconfig"
rsource "periph/Kconfig"
rsource "stmclk/Kconfig"
rsource "vectors/Kconfig"

View File

@ -6,4 +6,8 @@ ifneq (,$(filter bootloader_stm32,$(USEMODULE)))
DIRS += bootloader
endif
ifneq (,$(filter lcd_fmc,$(USEMODULE)))
DIRS += lcd_fmc
endif
include $(RIOTBASE)/Makefile.base

View File

@ -54,6 +54,10 @@ ifneq (,$(filter stm32_eth,$(USEMODULE)))
endif
endif
ifneq (,$(filter lcd_parallel_ll_mcu,$(USEMODULE)))
USEMODULE += lcd_fmc
endif
ifneq (,$(filter periph_can,$(FEATURES_USED)))
FEATURES_REQUIRED += periph_gpio
FEATURES_REQUIRED += periph_gpio_irq

View File

@ -0,0 +1,60 @@
/*
* 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.
*/
/**
* @defgroup cpu_stm32_lcd_fmc STM32 FMC/FSMC LCD low-level parallel interface driver
* @ingroup cpu_stm32
*
* @{
*/
#ifndef LCD_FMC_H
#define LCD_FMC_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Number of LCDs using FMC banks
*
* It represents the number of elements in LCD FMC bank descriptor array of
* type @ref lcd_fmc_desc_t. Because it is used by the preprocessor, it has
* to be defined as a number. It is not possible to use the @ref ARRAY_SIZE
* macro here.
*
* @note `LCD_FMC_NUMOF` has to be equal to the number of elements in the
* LCD FMC bank descriptor array of type @ref lcd_fmc_desc_t.
*/
#if DOXYGEN
#define LCD_FMC_NUMOF 1
#endif
/**
* @brief Descriptor of the FMC bank used for a LCD
*
* The board definition has to specify the array @ref lcd_fmc_desc of type
* @ref lcd_fmc_desc_t which defines the FMC banks and the address offsets used
* for the LCD displays that are connected to FMC banks.
*
* @note In the case that multiple LCDs are connected to FMC banks, the FMC
* bank descriptors for LCDs of type @ref lcd_fmc_desc_t
* must be defined in same order as the LCD devices.
*/
typedef struct {
const fmc_bank_conf_t *bank; /**< FMC bank config used for the LCD */
uint32_t cmd_offset; /**< offset to the bank address used for commands */
uint32_t data_offset; /**< offset to the bank address used for data */
} lcd_fmc_desc_t;
#ifdef __cplusplus
}
#endif
#endif /* LCD_FMC_H */
/** @} */

View File

@ -64,6 +64,17 @@
extern "C" {
#endif
/**
* @brief Gives the configuration of n-th bank
*
* This macro gives a pointer to the n-th entry of type @ref fmc_bank_conf_t of
* the banks configured by the board in the @ref fmc_bank_config array. n is in
* the range 0 ... @ref FMC_BANK_NUMOF - 1.
*/
#ifndef FMC_BANK_CONFIG
#define FMC_BANK_CONFIG(n) (&fmc_bank_config[n])
#endif
/**
* @brief Number of data pins used
*

13
cpu/stm32/lcd_fmc/Kconfig Normal file
View File

@ -0,0 +1,13 @@
# 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.
#
config MODULE_LCD_FMC
bool
depends on MODULE_LCD
select MODULE_PERIPH_FMC
select MODULE_PERIPH_FMC_NOR_SRAM
default y if HAVE_LCD_PARALLEL_LL_MCU

View File

@ -0,0 +1,3 @@
MODULE = lcd_fmc
include $(RIOTBASE)/Makefile.base

176
cpu/stm32/lcd_fmc/lcd_fmc.c Normal file
View File

@ -0,0 +1,176 @@
/*
* 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_stm32
* @ingroup drivers_periph_fmc
* @{
*
* @file
* @brief FMC peripheral driver implementation
*
* @author Gunar Schorcht <gunar@schorcht.net>
* @}
*/
#include <assert.h>
#include "periph/gpio.h"
#include "lcd.h"
#include "lcd_internal.h"
#include "ztimer.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifndef LCD_FMC_NUMOF
#define LCD_FMC_NUMOF 1
#endif
#define FMC_LCD_CMD(d) (*((__IO uint16_t *)(d->bank->address + d->cmd_offset)))
#define FMC_LCD_DATA(d) (*((__IO uint16_t *)(d->bank->address + d->data_offset)))
/* sanity check */
static_assert(LCD_FMC_NUMOF == ARRAY_SIZE(lcd_fmc_desc),
"LCD_FMC_NUMOF is not equal to the number of elements in lcd_fmc_desc");
#if LCD_FMC_NUMOF > 1
/* In the case that multiple LCDs are connected to FMC banks, an array
* for mapping the LCD device pointer to the FMC bank descriptor is used.
* This requires that the FMC bank descriptors for LCDs in `lcd_fmc_desc`
* are defined in same order as the LCD devices. */
static lcd_t *_lcd_fmc_desc_map[LCD_FMC_NUMOF];
static uint8_t _lcd_index = 0;
static inline uint8_t _dev_to_lcd_fmc_desc(lcd_t *dev)
{
for (uint8_t i = 0; i < LCD_FMC_NUMOF; i++) {
if (_lcd_fmc_desc_map[i] == dev) {
return i;
}
}
assert(false);
}
#endif
static void lcd_ll_mcu_init(lcd_t *dev)
{
#if LCD_FMC_NUMOF > 1
/* The FMC bank descriptors for LCDs in `lcd_fmc_desc` must be defined
* in same order as the LCD display devices. We suppose that the
* LCDs are initialized in that order. */
assert(_lcd_index < LCD_FMC_NUMOF);
_lcd_fmc_desc_map[_lcd_index++] = dev;
#else
(void)dev;
#endif
}
static void lcd_ll_mcu_set_data_dir(lcd_t *dev, bool output)
{
/* no action needed */
(void)dev;
(void)output;
}
static void lcd_ll_mcu_cmd_start(lcd_t *dev, uint8_t cmd, bool cont)
{
DEBUG("[lcd_ll_mcu] write cmd: %02x\n", cmd);
(void)cont;
#if LCD_FMC_NUMOF > 1
const lcd_fmc_desc_t *desc = &lcd_fmc_desc[_dev_to_lcd_fmc_desc(dev)];
#else
(void)dev;
const lcd_fmc_desc_t *desc = lcd_fmc_desc;
#endif
FMC_LCD_CMD(desc) = cmd;
/* data synchronization barrier seems to be necessary on some STM32 MCUs. */
__DSB();
}
static void lcd_ll_mcu_write_byte(lcd_t *dev, bool cont, uint8_t out)
{
DEBUG("[lcd_ll_mcu] write byte: %02x\n", out);
(void)cont;
#if LCD_FMC_NUMOF > 1
const lcd_fmc_desc_t *desc = &lcd_fmc_desc[_dev_to_lcd_fmc_desc(dev)];
#else
(void)dev;
const lcd_fmc_desc_t *desc = lcd_fmc_desc;
#endif
FMC_LCD_DATA(desc) = out;
/* data synchronization barrier seems to be necessary on some STM32 MCUs. */
__DSB();
}
static uint8_t lcd_ll_mcu_read_byte(lcd_t *dev, bool cont)
{
(void)cont;
#if LCD_FMC_NUMOF > 1
const lcd_fmc_desc_t *desc = &lcd_fmc_desc[_dev_to_lcd_fmc_desc(dev)];
#else
(void)dev;
const lcd_fmc_desc_t *desc = lcd_fmc_desc;
#endif
uint8_t in = FMC_LCD_DATA(desc);
DEBUG("[lcd_ll_mcu] read byte: %02x\n", in);
return in;
}
#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);
(void)cont;
#if LCD_FMC_NUMOF > 1
const lcd_fmc_desc_t *desc = &lcd_fmc_desc[_dev_to_lcd_fmc_desc(dev)];
#else
(void)dev;
const lcd_fmc_desc_t *desc = lcd_fmc_desc;
#endif
FMC_LCD_DATA(desc) = out;
/* data synchronization barrier seems to be necessary on some STM32 MCUs. */
__DSB();
}
static uint16_t lcd_ll_mcu_read_word(lcd_t *dev, bool cont)
{
(void)cont;
#if LCD_FMC_NUMOF > 1
const lcd_fmc_desc_t *desc = &lcd_fmc_desc[_dev_to_lcd_fmc_desc(dev)];
#else
(void)dev;
const lcd_fmc_desc_t *desc = lcd_fmc_desc;
#endif
uint16_t in = FMC_LCD_DATA(desc);
DEBUG("[lcd_ll_mcu] read word: %04x\n", in);
return in;
}
#endif /* 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
};