mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/stm32: add support for LTDC periph
This commit is contained in:
parent
dc26dc14b9
commit
deccc720e3
96
cpu/stm32/include/periph/cpu_ltdc.h
Normal file
96
cpu/stm32/include/periph/cpu_ltdc.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief LTDC CPU specific definitions for the STM32 family
|
||||
*
|
||||
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||
*/
|
||||
|
||||
#ifndef PERIPH_CPU_LTDC_H
|
||||
#define PERIPH_CPU_LTDC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "periph/cpu_gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LTDC GPIO configuration
|
||||
*/
|
||||
typedef struct {
|
||||
gpio_t pin; /**< GPIO pin */
|
||||
gpio_af_t af; /**< Alternate function */
|
||||
} ltdc_gpio_t;
|
||||
|
||||
/**
|
||||
* @brief LTDC Peripheral configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t bus; /**< APB bus */
|
||||
uint32_t rcc_mask; /**< bit in clock enable register */
|
||||
ltdc_gpio_t clk_pin; /**< CLK pin */
|
||||
ltdc_gpio_t de_pin; /**< Data enable pin */
|
||||
ltdc_gpio_t hsync_pin; /**< Horizontal synchronization pin */
|
||||
ltdc_gpio_t vsync_pin; /**< Vertical synchronization pin */
|
||||
ltdc_gpio_t r_pin[8]; /**< Red color pins */
|
||||
ltdc_gpio_t g_pin[8]; /**< Green color pins */
|
||||
ltdc_gpio_t b_pin[8]; /**< Blue color pins */
|
||||
uint8_t hsync; /**< Horizontal synchronization */
|
||||
uint8_t vsync; /**< Vertical synchronization */
|
||||
uint8_t hbp; /**< Horizontal back porch */
|
||||
uint8_t hfp; /**< Horizontal front porch */
|
||||
uint8_t vbp; /**< Vertical back porch */
|
||||
uint8_t vfp; /**< Vertical front porch */
|
||||
} ltdc_conf_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the LTDC (LCD-TFT Display Controller) peripheral
|
||||
*/
|
||||
void ltdc_init(void);
|
||||
|
||||
/**
|
||||
* @brief Clear the LTDC display
|
||||
*/
|
||||
void ltdc_clear(void);
|
||||
|
||||
/**
|
||||
* @brief Map a buffer of RGB565 (16bit depth) colors to the display
|
||||
*
|
||||
* @param[in] x1 horizontal start position
|
||||
* @param[in] x2 horizontal end position (included)
|
||||
* @param[in] y1 vertical start position
|
||||
* @param[in] y2 vertical end position (included)
|
||||
* @param[in] color the color buffer
|
||||
*/
|
||||
void ltdc_map(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2, const uint16_t *color);
|
||||
|
||||
/**
|
||||
* @brief Fill a region of the display with the same color
|
||||
*
|
||||
* @param[in] x1 horizontal start position
|
||||
* @param[in] x2 horizontal end position (included)
|
||||
* @param[in] y1 vertical start position
|
||||
* @param[in] y2 vertical end position (included)
|
||||
* @param[in] color the color value in RGB565 format (16bit depth)
|
||||
*/
|
||||
void ltdc_fill(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2, const uint16_t color);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PERIPH_CPU_LTDC_H */
|
||||
/** @} */
|
@ -63,6 +63,7 @@
|
||||
#include "periph/cpu_eth.h"
|
||||
#include "periph/cpu_gpio.h"
|
||||
#include "periph/cpu_i2c.h"
|
||||
#include "periph/cpu_ltdc.h"
|
||||
#include "periph/cpu_pm.h"
|
||||
#include "periph/cpu_pwm.h"
|
||||
#include "periph/cpu_qdec.h"
|
||||
|
@ -34,3 +34,9 @@ config MODULE_PERIPH_ADC
|
||||
select MODULE_ZTIMER if HAS_CPU_STM32F3 || HAS_CPU_STM32L4 || HAS_CPU_STM32WL
|
||||
select MODULE_ZTIMER_MSEC if HAS_CPU_STM32F3 || HAS_CPU_STM32L4 || HAS_CPU_STM32WL
|
||||
select MODULE_PERIPH_COMMON
|
||||
|
||||
config MODULE_PERIPH_LTDC
|
||||
bool "LTDC peripheral driver"
|
||||
depends on HAS_PERIPH_LTDC
|
||||
help
|
||||
STM32 LCD-TFT Display controller
|
||||
|
208
cpu/stm32/periph/ltdc.c
Normal file
208
cpu/stm32/periph/ltdc.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Inria
|
||||
*
|
||||
* 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
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level LTDC (LCD-TFT Display controller) driver implementation
|
||||
*
|
||||
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdalign.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu.h"
|
||||
#include "board.h"
|
||||
#include "periph/gpio.h"
|
||||
#include "periph_conf.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#ifndef LCD_SCREEN_WIDTH
|
||||
#define LCD_SCREEN_WIDTH 480
|
||||
#endif
|
||||
|
||||
#ifndef LCD_SCREEN_HEIGHT
|
||||
#define LCD_SCREEN_HEIGHT 272
|
||||
#endif
|
||||
|
||||
#define LTDC_BLENDING_FACTOR1_CA 0x00000400U
|
||||
#define LTDC_BLENDING_FACTOR2_CA 0x00000005U
|
||||
|
||||
typedef struct {
|
||||
uint32_t hsync;
|
||||
uint32_t vsync;
|
||||
uint32_t acc_hbp;
|
||||
uint32_t acc_vbp;
|
||||
uint32_t acc_active_h;
|
||||
uint32_t acc_active_w;
|
||||
uint32_t height;
|
||||
uint32_t width;
|
||||
} ltdc_display_conf_t;
|
||||
|
||||
/* allocate array of 16bit pixels for the framebuffer (TODO: use external SRAM via FMC) */
|
||||
static uint16_t _ltdc_frame_buffer[LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT];
|
||||
|
||||
static void _init_gpio(void)
|
||||
{
|
||||
gpio_init(ltdc_config.clk_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.clk_pin.pin, ltdc_config.clk_pin.af);
|
||||
gpio_init(ltdc_config.de_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.de_pin.pin, ltdc_config.de_pin.af);
|
||||
gpio_init(ltdc_config.hsync_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.hsync_pin.pin, ltdc_config.hsync_pin.af);
|
||||
gpio_init(ltdc_config.vsync_pin.pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.vsync_pin.pin, ltdc_config.vsync_pin.af);
|
||||
|
||||
for (uint8_t pin = 0; pin < 8; ++pin) {
|
||||
if (!gpio_is_valid(ltdc_config.r_pin[pin].pin)) {
|
||||
continue;
|
||||
}
|
||||
gpio_init(ltdc_config.r_pin[pin].pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.r_pin[pin].pin, ltdc_config.r_pin[pin].af);
|
||||
gpio_init(ltdc_config.g_pin[pin].pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.g_pin[pin].pin, ltdc_config.g_pin[pin].af);
|
||||
gpio_init(ltdc_config.b_pin[pin].pin, GPIO_OUT);
|
||||
gpio_init_af(ltdc_config.b_pin[pin].pin, ltdc_config.b_pin[pin].af);
|
||||
}
|
||||
}
|
||||
|
||||
static void _configure_ltdc(void)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
const ltdc_display_conf_t ltdc_display_config = {
|
||||
.hsync = ltdc_config.hsync - 1,
|
||||
.vsync = ltdc_config.vsync - 1,
|
||||
.acc_hbp = ltdc_config.hsync + ltdc_config.hbp - 1,
|
||||
.acc_vbp = ltdc_config.vsync + ltdc_config.vbp - 1,
|
||||
.acc_active_h = LCD_SCREEN_HEIGHT + ltdc_config.vsync + ltdc_config.vbp - 1,
|
||||
.acc_active_w = LCD_SCREEN_WIDTH + ltdc_config.hsync + ltdc_config.hbp - 1,
|
||||
.height = LCD_SCREEN_HEIGHT + ltdc_config.vsync + ltdc_config.vbp + ltdc_config.vfp - 1,
|
||||
.width = LCD_SCREEN_WIDTH + ltdc_config.hsync + ltdc_config.hbp + ltdc_config.hfp - 1,
|
||||
};
|
||||
|
||||
/* Configure the HS, VS, DE and PC polarity: all active low*/
|
||||
LTDC->GCR &= ~(LTDC_GCR_HSPOL | LTDC_GCR_VSPOL | LTDC_GCR_DEPOL | LTDC_GCR_PCPOL);
|
||||
|
||||
/* Set Synchronization size */
|
||||
LTDC->SSCR &= ~(LTDC_SSCR_VSH | LTDC_SSCR_HSW);
|
||||
tmp = (ltdc_display_config.hsync << 16U);
|
||||
LTDC->SSCR |= (tmp | ltdc_display_config.vsync);
|
||||
|
||||
/* Set Accumulated Back porch */
|
||||
LTDC->BPCR &= ~(LTDC_BPCR_AVBP | LTDC_BPCR_AHBP);
|
||||
tmp = (ltdc_display_config.acc_hbp << 16U);
|
||||
LTDC->BPCR |= (tmp | ltdc_display_config.acc_vbp);
|
||||
|
||||
/* Set Accumulated Active Width */
|
||||
LTDC->AWCR &= ~(LTDC_AWCR_AAH | LTDC_AWCR_AAW);
|
||||
tmp = (ltdc_display_config.acc_active_w << 16U);
|
||||
LTDC->AWCR |= (tmp | ltdc_display_config.acc_active_h);
|
||||
|
||||
/* Set Total Width */
|
||||
LTDC->TWCR &= ~(LTDC_TWCR_TOTALH | LTDC_TWCR_TOTALW);
|
||||
tmp = (ltdc_display_config.width << 16U);
|
||||
LTDC->TWCR |= (tmp | ltdc_display_config.height);
|
||||
|
||||
/* Set the background color value: black */
|
||||
LTDC->BCCR &= ~(LTDC_BCCR_BCBLUE | LTDC_BCCR_BCGREEN | LTDC_BCCR_BCRED);
|
||||
|
||||
/* Enable LTDC */
|
||||
LTDC->GCR |= LTDC_GCR_LTDCEN;
|
||||
}
|
||||
|
||||
static void _configure_ltdc_layer(void)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
/* Configure the horizontal start and stop position */
|
||||
tmp = ((LCD_SCREEN_WIDTH + ((LTDC->BPCR & LTDC_BPCR_AHBP) >> 16U)) << 16U);
|
||||
LTDC_Layer1->WHPCR &= ~(LTDC_LxWHPCR_WHSTPOS | LTDC_LxWHPCR_WHSPPOS);
|
||||
LTDC_Layer1->WHPCR = ((0 + ((LTDC->BPCR & LTDC_BPCR_AHBP) >> 16U) + 1U) | tmp);
|
||||
|
||||
/* Configure the vertical start and stop position */
|
||||
tmp = ((LCD_SCREEN_HEIGHT + (LTDC->BPCR & LTDC_BPCR_AVBP)) << 16U);
|
||||
LTDC_Layer1->WVPCR &= ~(LTDC_LxWVPCR_WVSTPOS | LTDC_LxWVPCR_WVSPPOS);
|
||||
LTDC_Layer1->WVPCR = ((0 + (LTDC->BPCR & LTDC_BPCR_AVBP) + 1U) | tmp);
|
||||
|
||||
/* Set the pixel format: RGB565 (16bit) */
|
||||
LTDC_Layer1->PFCR &= ~(LTDC_LxPFCR_PF);
|
||||
LTDC_Layer1->PFCR = 2;
|
||||
|
||||
/* Configure the default color values: all black */
|
||||
LTDC_Layer1->DCCR &= ~(LTDC_LxDCCR_DCBLUE | LTDC_LxDCCR_DCGREEN | LTDC_LxDCCR_DCRED | LTDC_LxDCCR_DCALPHA);
|
||||
|
||||
/* Set the constant alpha value: fully opaque */
|
||||
LTDC_Layer1->CACR &= ~(LTDC_LxCACR_CONSTA);
|
||||
LTDC_Layer1->CACR = 255;
|
||||
|
||||
/* Set the blending factors */
|
||||
LTDC_Layer1->BFCR &= ~(LTDC_LxBFCR_BF2 | LTDC_LxBFCR_BF1);
|
||||
LTDC_Layer1->BFCR = (LTDC_BLENDING_FACTOR1_CA | LTDC_BLENDING_FACTOR2_CA);
|
||||
|
||||
/* Configure the color frame buffer start address */
|
||||
LTDC_Layer1->CFBAR &= ~(LTDC_LxCFBAR_CFBADD);
|
||||
LTDC_Layer1->CFBAR = (uint32_t)_ltdc_frame_buffer;
|
||||
|
||||
/* Configure the color frame buffer pitch in byte */
|
||||
LTDC_Layer1->CFBLR &= ~(LTDC_LxCFBLR_CFBLL | LTDC_LxCFBLR_CFBP);
|
||||
LTDC_Layer1->CFBLR = (((LCD_SCREEN_WIDTH * 2) << 16U) | ((LCD_SCREEN_WIDTH * 2) + 3U));
|
||||
/* Configure the frame buffer line number */
|
||||
LTDC_Layer1->CFBLNR &= ~(LTDC_LxCFBLNR_CFBLNBR);
|
||||
LTDC_Layer1->CFBLNR = LCD_SCREEN_HEIGHT;
|
||||
|
||||
/* Enable LTDC_Layer by setting LEN bit */
|
||||
LTDC_Layer1->CR |= (uint32_t)LTDC_LxCR_LEN;
|
||||
}
|
||||
|
||||
void ltdc_init(void)
|
||||
{
|
||||
DEBUG("[ltdc] init: initializing device\n");
|
||||
|
||||
periph_clk_en(ltdc_config.bus, ltdc_config.rcc_mask);
|
||||
|
||||
_init_gpio();
|
||||
_configure_ltdc();
|
||||
_configure_ltdc_layer();
|
||||
}
|
||||
|
||||
void ltdc_clear(void)
|
||||
{
|
||||
memset(_ltdc_frame_buffer, 0, LCD_SCREEN_WIDTH * LCD_SCREEN_HEIGHT * sizeof(uint16_t));
|
||||
LTDC->SRCR = LTDC_SRCR_IMR;
|
||||
}
|
||||
|
||||
void ltdc_map(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2, const uint16_t *color)
|
||||
{
|
||||
for (uint16_t y = y1; y <= y2; y++) {
|
||||
for (uint16_t x = x1; x <= x2; x++) {
|
||||
*(_ltdc_frame_buffer + (x + y * LCD_SCREEN_WIDTH)) = *color++;
|
||||
}
|
||||
}
|
||||
LTDC->SRCR = LTDC_SRCR_IMR;
|
||||
}
|
||||
|
||||
void ltdc_fill(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2, const uint16_t color)
|
||||
{
|
||||
for (uint16_t y = y1; y <= y2; y++) {
|
||||
for (uint16_t x = x1; x <= x2; x++) {
|
||||
*(_ltdc_frame_buffer + (x + y * LCD_SCREEN_WIDTH)) = color;
|
||||
}
|
||||
}
|
||||
|
||||
LTDC->SRCR = LTDC_SRCR_IMR;
|
||||
}
|
@ -92,6 +92,13 @@
|
||||
#error No suitable 48MHz found, USB will not work
|
||||
#endif
|
||||
|
||||
/* PLLSAI is enabled when LTDC is used */
|
||||
#if IS_USED(MODULE_PERIPH_LTDC)
|
||||
#define CLOCK_REQUIRE_PLLSAIR 1
|
||||
#else
|
||||
#define CLOCK_REQUIRE_PLLSAIR 0
|
||||
#endif
|
||||
|
||||
/* PLLI2S configuration: the following parameters configure a 48MHz I2S clock
|
||||
with HSE (8MHz) or HSI (16MHz) as PLL input clock */
|
||||
#ifndef CONFIG_CLOCK_PLLI2S_M
|
||||
@ -175,7 +182,7 @@
|
||||
#define CONFIG_CLOCK_PLLSAI_Q (8) /* SAI clock, 48MHz by default */
|
||||
#endif
|
||||
#ifndef CONFIG_CLOCK_PLLSAI_R
|
||||
#define CONFIG_CLOCK_PLLSAI_R (8) /* LCD clock, 48MHz by default */
|
||||
#define CONFIG_CLOCK_PLLSAI_R (4) /* LCD clock, 48MHz by default */
|
||||
#endif
|
||||
|
||||
#if defined(RCC_PLLSAICFGR_PLLSAIM_Pos)
|
||||
@ -455,7 +462,7 @@
|
||||
#endif
|
||||
|
||||
/* Check whether PLLSAI must be enabled */
|
||||
#if IS_ACTIVE(CLOCK_REQUIRE_PLLSAIP)
|
||||
#if IS_ACTIVE(CLOCK_REQUIRE_PLLSAIP) || IS_ACTIVE(CLOCK_REQUIRE_PLLSAIR)
|
||||
#define CLOCK_ENABLE_PLLSAI 1
|
||||
#else
|
||||
#define CLOCK_ENABLE_PLLSAI 0
|
||||
@ -556,6 +563,20 @@ void stmclk_init_sysclk(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RCC_DCKCFGR1_PLLSAIDIVR)
|
||||
if (IS_USED(MODULE_PERIPH_LTDC)) {
|
||||
RCC->DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR;
|
||||
RCC->DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_0; /* Divide by 4 */
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RCC_DCKCFGR_PLLSAIDIVR)
|
||||
if (IS_USED(MODULE_PERIPH_LTDC)) {
|
||||
RCC->DCKCFGR &= ~RCC_DCKCFGR_PLLSAIDIVR;
|
||||
RCC->DCKCFGR |= RCC_DCKCFGR_PLLSAIDIVR_0; /* Divide by 4 */
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(RCC_CR_PLLSAION)
|
||||
if (IS_ACTIVE(CLOCK_ENABLE_PLLSAI)) {
|
||||
RCC->PLLSAICFGR = (PLLSAI_M | PLLSAI_N | PLLSAI_P | PLLSAI_Q | PLLSAI_R);
|
||||
|
@ -206,6 +206,11 @@ config HAS_PERIPH_LPUART
|
||||
help
|
||||
Indicates that a low-power UART peripheral is present.
|
||||
|
||||
config HAS_PERIPH_LTDC
|
||||
bool
|
||||
help
|
||||
Indicates that a LTDC peripheral is present.
|
||||
|
||||
config HAS_PERIPH_MCG
|
||||
bool
|
||||
help
|
||||
|
Loading…
Reference in New Issue
Block a user