/* * Copyright (C) 2023 Daniele Lacamera * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /* Includes ------------------------------------------------------------------*/ #include #include "malloc.h" #include "ui.h" #include #include #include #include "stm32f7_ltdc.h" /* Private function prototypes -----------------------------------------------*/ static void ltdc_config(void); static void ltdc_pinmux(void); static void ltdc_clock(void); static int ltdc_config_layer(void); static uint32_t Screen_size = 0; static int ltdc_config_layer(void) { uint32_t format = 0; Screen_size = xres * yres * (bits_per_pixel/8); /* Screen configuration */ ltdc_setup_windowing(LTDC_LAYER_1, xres, xres); ltdc_set_pixel_format(LTDC_LAYER_1, pixel_format); ltdc_set_default_colors(LTDC_LAYER_1, 0, 0, 0, 0); ltdc_set_constant_alpha(LTDC_LAYER_1, 255); ltdc_set_blending_factors(LTDC_LAYER_1, LTDC_LxBFCR_BF1_CONST_ALPHA, LTDC_LxBFCR_BF2_CONST_ALPHA); ltdc_set_fbuffer_address(LTDC_LAYER_1, Screen_address); ltdc_set_fb_line_length(LTDC_LAYER_1, xres * (bits_per_pixel/8), xres * (bits_per_pixel/8)); ltdc_set_fb_line_count(LTDC_LAYER_1, yres); #if (OVERLAY) /* Overlay configuration */ ltdc_setup_windowing(LTDC_LAYER_2, xres, xres); ltdc_set_pixel_format(LTDC_LAYER_2, LTDC_LxPFCR_AL44); ltdc_set_default_colors(LTDC_LAYER_2, 255, 0, 0, 0); ltdc_set_constant_alpha(LTDC_LAYER_2, 0); // ltdc_set_blending_factors(LTDC_LAYER_2, LTDC_LxBFCR_BF1_CONST_ALPHA, LTDC_LxBFCR_BF2_CONST_ALPHA); ltdc_set_fbuffer_address(LTDC_LAYER_2, Overlay_address); ltdc_set_fb_line_length(LTDC_LAYER_2, xres * (bits_per_pixel/8), xres * (bits_per_pixel/8)); ltdc_set_fb_line_count(LTDC_LAYER_2, yres); /* Enable layer 2 */ ltdc_layer_ctrl_enable(LTDC_LAYER_2, LTDC_LxCR_LAYER_ENABLE); #endif /* Enable layer 1 */ ltdc_layer_ctrl_enable(LTDC_LAYER_1, LTDC_LxCR_LAYER_ENABLE); /* Sets the Reload type */ ltdc_reload(LTDC_SRCR_IMR); return 0; } /** * @brief LCD Configuration. * @note This function Configure tha LTDC peripheral : * 1) Configure the Pixel Clock for the LCD * 2) Configure the LTDC Timing and Polarity * 3) Configure the LTDC Layer 1 : * - The frame buffer is located at FLASH memory * - The Layer size configuration : 480x272 * @retval * None */ static void ltdc_config(void) { /* LTDC Initialization */ ltdc_ctrl_disable(LTDC_GCR_HSPOL_ACTIVE_HIGH); /* Active Low Horizontal Sync */ ltdc_ctrl_disable(LTDC_GCR_VSPOL_ACTIVE_HIGH); /* Active Low Vertical Sync */ ltdc_ctrl_disable(LTDC_GCR_DEPOL_ACTIVE_HIGH); /* Active Low Date Enable */ ltdc_ctrl_disable(LTDC_GCR_PCPOL_ACTIVE_HIGH); /* Active Low Pixel Clock */ /* Configure the LTDC */ ltdc_set_tft_sync_timings(RK043FN48H_HSYNC, RK043FN48H_VSYNC, RK043FN48H_HBP, RK043FN48H_VBP, RK043FN48H_WIDTH, RK043FN48H_HEIGHT, RK043FN48H_HFP, RK043FN48H_VFP); ltdc_set_background_color(0, 0, 0); ltdc_ctrl_enable(LTDC_GCR_LTDC_ENABLE); } static void ltdc_clock(void) { /* LCD clock configuration */ /* PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz */ /* PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 192 Mhz */ /* PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 192/5 = 38.4 Mhz */ /* LTDC clock frequency = PLLLCDCLK / LTDC_PLLSAI_DIVR_4 = 38.4/4 = 9.6Mhz */ /* Disable PLLSAI */ RCC_CR &= ~RCC_CR_PLLSAION; while((RCC_CR & (RCC_CR_PLLSAIRDY))) {}; /* N and R are needed, * P and Q are not needed for LTDC */ RCC_PLLSAICFGR &= ~RCC_PLLSAICFGR_PLLSAIN_MASK; RCC_PLLSAICFGR |= 192 << RCC_PLLSAICFGR_PLLSAIN_SHIFT; RCC_PLLSAICFGR &= ~RCC_PLLSAICFGR_PLLSAIR_MASK; RCC_PLLSAICFGR |= 5 << RCC_PLLSAICFGR_PLLSAIR_SHIFT; RCC_DCKCFGR1 &= ~RCC_DCKCFGR1_PLLSAIDIVR_MASK; RCC_DCKCFGR1 |= RCC_DCKCFGR1_PLLSAIDIVR_DIVR_4; /* Enable PLLSAI */ RCC_CR |= RCC_CR_PLLSAION; while(!(RCC_CR & (RCC_CR_PLLSAIRDY))) {}; } int ltdc_blank(void); static void ltdc_screen_on(void) { /* Assert display enable LCD_DISP pin */ gpio_set(GPIOI, GPIO12); /* Assert backlight LCD_BL_CTRL pin */ gpio_set(GPIOK, GPIO3); } void ltdc_enable_clut(void) { /* Disable LTDC color lookup table by setting CLUTEN bit */ ltdc_layer_ctrl_enable(LTDC_LAYER_1, LTDC_LxCR_CLUT_ENABLE); /* Sets the Reload type */ ltdc_reload(LTDC_SRCR_IMR); } /* Only L8 CLUTs supported for now */ void ltdc_config_clut(uint32_t *CLUT, uint32_t size) { uint32_t i = 0; for(i = 0; (i < size); i++) { /* Specifies the C-LUT address and RGB value */ LTDC_LxCLUTWR(LTDC_LAYER_1) = ((i << 24) | ((uint32_t)(*CLUT) & 0xFF) | ((uint32_t)(*CLUT) & 0xFF00) | ((uint32_t)(*CLUT) & 0xFF0000)); CLUT++; } } int ltdc_set_cmap(uint32_t *cmap) { ltdc_config_clut(cmap, 256); ltdc_enable_clut(); return 0; } static void lcd_pinmux(void) { int i; uint32_t base, pin, af; rcc_periph_clock_enable(RCC_GPIOE); rcc_periph_clock_enable(RCC_GPIOG); rcc_periph_clock_enable(RCC_GPIOI); rcc_periph_clock_enable(RCC_GPIOJ); rcc_periph_clock_enable(RCC_GPIOK); /* Enable the LTDC Clock */ rcc_periph_clock_enable(RCC_LTDC); /* PE4 */ base = GPIOE; pin = GPIO4; af = GPIO_AF14; gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); gpio_set_af(base, af, pin); /* PG12 */ base = GPIOG; pin = GPIO12; af = GPIO_AF9; gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); gpio_set_af(base, af, pin); /* LTDC PI8:PI10*/ for (i = 8; i < 11; i++) { base = GPIOI; pin = (1 << i); af = GPIO_AF14; gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); gpio_set_af(base, af, pin); } /* LTDC PI14:PI15 */ for (i = 14; i < 16; i++) { base = GPIOI; pin = (1 << i); af = GPIO_AF14; gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); gpio_set_af(base, af, pin); } /* LTDC PJ0:PJ15 (excl. PJ12) */ for (i = 0; i < 16; i++) { if (i != 12) { base = GPIOJ; pin = (1 << i); af = GPIO_AF14; gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); gpio_set_af(base, af, pin); } } /* LTDC PK0:PK7 (excl. PK3) */ for (i = 0; i < 8; i++) { if (i != 3) { base = GPIOK; pin = (1 << i); af = GPIO_AF14; gpio_mode_setup(base, GPIO_MODE_AF, GPIO_PUPD_NONE, pin); gpio_set_af(base, af, pin); } } /* LCD_DISP control PI12 (output) */ base = GPIOI; pin = GPIO12; gpio_mode_setup(base, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin); /* LCD_BL control PK3 (output) */ base = GPIOK; pin = GPIO3; gpio_mode_setup(base, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, pin); ltdc_screen_on(); } /* DRIVER INIT */ void ltdc_init(void) { extern const uint32_t xterm_cmap[256]; lcd_pinmux(); ltdc_clock(); ltdc_config(); /* Configure LCD : Only one layer is used */ ltdc_config_layer(); ltdc_set_cmap(xterm_cmap); } int ltdc_blank(void) { uint32_t pixels = (xres * yres * (bits_per_pixel/8)); int i; uint8_t *screen = (uint8_t *)Screen_address; for (i = 0; i < pixels; i++) { screen[i] = 0x00; } #if OVERLAY screen = (uint8_t *)Overlay_address; for (i = 0; i < pixels; i++) { screen[i] = 0xFF; } #endif #ifdef GTEST draw_h_segment(BRIGHT(RED), 20, 20, 30); draw_h_segment(BRIGHT(RED), 50, 20, 30); draw_v_segment(BRIGHT(PURPLE), 20, 20, 30); draw_v_segment(BRIGHT(BLUE), 50, 20, 30); text_at(BRIGHT(YELLOW), 50,50,"hello world"); fill_area(BRIGHT(CYAN), 200, 200, 270, 400); #endif return 0; }