/* * This file is subject to the terms of the GFX License. If a copy of * the license was not distributed with this file, you can obtain one at: * * http://ugfx.org/license.html */ #ifndef _GDISP_LLD_BOARD_H #define _GDISP_LLD_BOARD_H /* Avoid naming collisions with CubeHAL. */ #undef Red #undef Green #undef Blue /* HAL drivers needed for configuration. */ #include "stm32f4xx_hal.h" #include "stm32f4xx_hal_rcc.h" #include "stm32f4xx_hal_gpio.h" #include "stm32f4xx_hal_dsi.h" /* sdram driver provided by ST. */ #include "stm32f469i_discovery_sdram.h" /* OTM8009A driver provided by ST. */ #include "otm8009a.h" /** Manually set the LTDC timing. */ #ifndef GFX_LTDC_TIMING_SET #define GFX_LTDC_TIMING_SET #endif /** Most boards will be revision A. */ #ifndef USE_STM32469I_DISCO_REVA #define USE_STM32469I_DISCO_REVA #endif /** @brief Panel parameters * * This panel is a KoD KM-040TMP-02-0621 DSI LCD Display. */ static const ltdcConfig driverCfg = { 800, 480, // Width, Height (pixels) 1, 2, // Horizontal, Vertical sync (pixels) 15, 34, // Horizontal, Vertical back porch (pixels) 16, 34, // Horizontal, Vertical front porch (pixels) 0, // Sync flags 0x000000, // Clear color (RGB888) { // Background layer config (LLDCOLOR_TYPE *)SDRAM_DEVICE_ADDR, // Frame buffer address 800, 480, // Width, Height (pixels) 800 * LTDC_PIXELBYTES, // Line pitch (bytes) LTDC_PIXELFORMAT, // Pixel format 0, 0, // Start pixel position (x, y) 800, 480, // Size of virtual layer (cx, cy) LTDC_COLOR_FUCHSIA, // Default color (ARGB8888) 0x980088, // Color key (RGB888) LTDC_BLEND_FIX1_FIX2, // Blending factors 0, // Palette (RGB888, can be NULL) 0, // Palette length 0xFF, // Constant alpha factor LTDC_LEF_ENABLE // Layer configuration flags }, LTDC_UNUSED_LAYER_CONFIG // Foreground layer config }; /** Display timing setting */ #define KoD_FREQUENCY_DIVIDER 7 /** Global DSI handle to hold DSI parameters. */ DSI_HandleTypeDef dsiHandle; static GFXINLINE void reset_lcd(GDisplay* g); /** * @brief Function to intialize the STM32F46i-DISCO board. * * @param g: Structure holding display parameters. */ static GFXINLINE void init_board(GDisplay *g) { // As we are not using multiple displays we set g->board to NULL as we don't use it g->board = 0; #ifdef GFX_LTDC_TIMING_SET // KoD LCD clock configuration // PLLSAI_VCO Input = HSE_VALUE/PLL_M = 1 Mhz // PLLSAI_VCO Output = PLLSAI_VCO Input * PLLSAIN = 384 Mhz // PLLLCDCLK = PLLSAI_VCO Output/PLLSAIR = 384/7 = 54.857 Mhz // LTDC clock frequency = PLLLCDCLK / LTDC_PLLSAI_DIVR_2 = 54.857/2 = 27.429Mhz #define STM32_SAISRC_NOCLOCK (0 << 23) /**< No clock. */ #define STM32_SAISRC_PLL (1 << 23) /**< SAI_CKIN is PLL. */ #define STM32_SAIR_DIV2 (0 << 16) /**< R divided by 2. */ #define STM32_SAIR_DIV4 (1 << 16) /**< R divided by 4. */ #define STM32_SAIR_DIV8 (2 << 16) /**< R divided by 8. */ #define STM32_SAIR_DIV16 (3 << 16) /**< R divided by 16. */ #define STM32_PLLSAIN_VALUE 384 #define STM32_PLLSAIQ_VALUE 4 #define STM32_PLLSAIR_VALUE KoD_FREQUENCY_DIVIDER #define STM32_PLLSAIR_POST STM32_SAIR_DIV2 RCC->PLLSAICFGR = (STM32_PLLSAIN_VALUE << 6) | (STM32_PLLSAIR_VALUE << 28) | (STM32_PLLSAIQ_VALUE << 24); RCC->DCKCFGR = (RCC->DCKCFGR & ~RCC_DCKCFGR_PLLSAIDIVR) | STM32_PLLSAIR_POST; RCC->CR |= RCC_CR_PLLSAION; while(!(RCC->CR & RCC_CR_PLLSAIRDY)); // wait for PLLSAI to lock #endif __HAL_RCC_DSI_CLK_ENABLE(); DSI_PLLInitTypeDef dsiPllInit; DSI_CmdCfgTypeDef dsiCmdMode; DSI_LPCmdTypeDef dsiAPBCmd; /* Filling the DSI intialization struct. */ dsiHandle.Instance = DSI; // There is only one DSI interface dsiHandle.Init.AutomaticClockLaneControl = DSI_AUTO_CLK_LANE_CTRL_ENABLE; // Automatic clock lane control: powers down the clock lane when not in use /* Highest speed = 500MHz. */ uint16_t laneByteClk_kHz = 62500; /* 500 MHz / 8 = 62.5 MHz = 62500 kHz */ /* TXEscapeCkdiv = f(LaneByteClk)/15.62 = 4 -> 500MHz/4 = 25MHz datasheet says around 20MHz */ dsiHandle.Init.TXEscapeCkdiv = laneByteClk_kHz/15620; // Low power clock relative to the laneByteClock dsiHandle.Init.NumberOfLanes = DSI_TWO_DATA_LANES; // Two data lines for the fastest transfer speed /* Fill in the command mode struct. */ dsiCmdMode.VirtualChannelID = 0; // The first virtual channel /* Select the appropriate color coding. */ #ifdef GDISP_PIXELFORMAT_RGB565 dsiCmdMode.ColorCoding = DSI_RGB565; #else dsiCmdMode.ColorCoding = DSI_RGB888; #endif dsiCmdMode.CommandSize = driverCfg.width; // Amount of pixels sent at once -> one line at a time dsiCmdMode.TearingEffectSource = DSI_TE_EXTERNAL; // Use pin PJ2 dsiCmdMode.TearingEffectPolarity = DSI_TE_RISING_EDGE; dsiCmdMode.HSPolarity = DSI_HSYNC_ACTIVE_HIGH; dsiCmdMode.VSPolarity = DSI_VSYNC_ACTIVE_HIGH; dsiCmdMode.DEPolarity = DSI_DATA_ENABLE_ACTIVE_HIGH; dsiCmdMode.VSyncPol = DSI_VSYNC_FALLING; dsiCmdMode.AutomaticRefresh = DSI_AR_ENABLE; // Use the automatic refresh mode dsiCmdMode.TEAcknowledgeRequest = DSI_TE_ACKNOWLEDGE_DISABLE; // Not needed when using TE through GPIO /* GPIO configuration. */ GPIO_InitTypeDef gpioInit; /* PH7 LCD_RESET */ __HAL_RCC_GPIOH_CLK_ENABLE(); gpioInit.Pin = GPIO_PIN_7; gpioInit.Mode = GPIO_MODE_OUTPUT_OD; gpioInit.Pull = GPIO_NOPULL; gpioInit.Speed = GPIO_SPEED_HIGH; HAL_GPIO_Init(GPIOH, &gpioInit); /* PJ2 DSIHOST_TE */ __HAL_RCC_GPIOJ_CLK_ENABLE(); gpioInit.Pin = GPIO_PIN_2; gpioInit.Mode = GPIO_MODE_AF_PP; gpioInit.Alternate = GPIO_AF13_DSI; HAL_GPIO_Init(GPIOJ, &gpioInit); /* PA3 LCD_BL_CTRL This pin is not physically connected. */ __HAL_RCC_GPIOA_CLK_ENABLE(); gpioInit.Pin = GPIO_PIN_3; gpioInit.Mode = GPIO_MODE_OUTPUT_OD; HAL_GPIO_Init(GPIOA, &gpioInit); /* Fvco = f(CLKin/IDF) * 2 * NDIV; fPHI = Fvco/(2*ODF) */ #if !defined(USE_STM32469I_DISCO_REVA) /* fPHI = CLKin*62.5; Fvco = CLKin*125 */ dsiPllInit.PLLNDIV = 125; dsiPllInit.PLLIDF = DSI_PLL_IN_DIV2; #else /* fPHI = CLKin*20; Fvco = CLKin*40 */ dsiPllInit.PLLNDIV = 100; dsiPllInit.PLLIDF = DSI_PLL_IN_DIV5; #endif dsiPllInit.PLLODF = DSI_PLL_OUT_DIV1; /* Initialize the DSI peripheral. */ HAL_DSI_Init(&dsiHandle, &dsiPllInit); DSI_PHY_TimerTypeDef PhyTimings; /* Configure DSI PHY HS2LP and LP2HS timings. Datasheet says 95ns max */ PhyTimings.ClockLaneHS2LPTime = 35; PhyTimings.ClockLaneLP2HSTime = 35; PhyTimings.DataLaneHS2LPTime = 35; PhyTimings.DataLaneLP2HSTime = 35; PhyTimings.DataLaneMaxReadTime = 0; PhyTimings.StopWaitTime = 10; HAL_DSI_ConfigPhyTimer(&dsiHandle, &PhyTimings); /* Configure adapted command mode. */ HAL_DSI_ConfigAdaptedCommandMode(&dsiHandle, &dsiCmdMode); /* Hardware reset LCD */ reset_lcd(g); /* Initialize the SDRAM */ BSP_SDRAM_Init(); } static GFXINLINE void set_backlight(GDisplay* g, uint8_t percent) { (void)g; (void)percent; } /** * @brief Perform a hardware reset on the LCD. * * @param g: Display parameter structure. */ static GFXINLINE void reset_lcd(GDisplay* g) { (void)g; /* Hardware display reset. */ HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_RESET); gfxSleepMilliseconds(20); HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_SET); gfxSleepMilliseconds(10); /* Turn on backlight. */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET); } /** * @brief Extra initialization that is performed after the LTDC intialization. * * @param g: Display paramter structure. */ static GFXINLINE void post_init_board(GDisplay* g) { (void)g; DSI_LPCmdTypeDef dsiAPBCmd; /* Enable the DSI host and wrapper after the LTDC initialization To avoid any synchronization issue, the DSI shall be started after enabling the LTDC */ HAL_DSI_Start(&dsiHandle); /* The configuration commands for the LCD have to be send through the dsiAPBCmd * interface as the adapted command mode only supports DCS, WMS and WMC commands. */ dsiAPBCmd.LPGenShortWriteNoP = DSI_LP_GSW0P_ENABLE; // Put everything in low power mode dsiAPBCmd.LPGenShortWriteOneP = DSI_LP_GSW1P_ENABLE; dsiAPBCmd.LPGenShortWriteTwoP = DSI_LP_GSW2P_ENABLE; dsiAPBCmd.LPGenShortReadNoP = DSI_LP_GSR0P_ENABLE; dsiAPBCmd.LPGenShortReadOneP = DSI_LP_GSR1P_ENABLE; dsiAPBCmd.LPGenShortReadTwoP = DSI_LP_GSR2P_ENABLE; dsiAPBCmd.LPGenLongWrite = DSI_LP_GLW_ENABLE; dsiAPBCmd.LPDcsShortWriteNoP = DSI_LP_DSW0P_ENABLE; dsiAPBCmd.LPDcsShortWriteOneP = DSI_LP_DSW1P_ENABLE; dsiAPBCmd.LPDcsShortReadNoP = DSI_LP_DSR0P_ENABLE; dsiAPBCmd.LPDcsLongWrite = DSI_LP_DLW_ENABLE; HAL_DSI_ConfigCommand(&dsiHandle, &dsiAPBCmd); /* Configure the LCD. */ #ifdef GDISP_PIXELFORMAT_RGB565 OTM8009A_Init(OTM8009A_FORMAT_RBG565, 1); #else OTM8009A_Init(OTM8009A_FORMAT_RGB888, 1); #endif /* Enable the tearing effect line. */ HAL_DSI_ShortWrite(&dsiHandle, 0, DSI_DCS_SHORT_PKT_WRITE_P1, DSI_SET_TEAR_ON, 0); // Only V-Blanking info /* Disable the APB command mode again to go into adapted command mode. (going into high speed mode) */ dsiAPBCmd.LPGenShortWriteNoP = DSI_LP_GSW0P_DISABLE; dsiAPBCmd.LPGenShortWriteOneP = DSI_LP_GSW1P_DISABLE; dsiAPBCmd.LPGenShortWriteTwoP = DSI_LP_GSW2P_DISABLE; dsiAPBCmd.LPGenShortReadNoP = DSI_LP_GSR0P_DISABLE; dsiAPBCmd.LPGenShortReadOneP = DSI_LP_GSR1P_DISABLE; dsiAPBCmd.LPGenShortReadTwoP = DSI_LP_GSR2P_DISABLE; dsiAPBCmd.LPGenLongWrite = DSI_LP_GLW_DISABLE; dsiAPBCmd.LPDcsShortWriteNoP = DSI_LP_DSW0P_DISABLE; dsiAPBCmd.LPDcsShortWriteOneP = DSI_LP_DSW1P_DISABLE; dsiAPBCmd.LPDcsShortReadNoP = DSI_LP_DSR0P_DISABLE; dsiAPBCmd.LPDcsLongWrite = DSI_LP_DLW_DISABLE; HAL_DSI_ConfigCommand(&dsiHandle, &dsiAPBCmd); HAL_DSI_Refresh(&dsiHandle); } /** * @brief DCS or Generic short/long write command * @param NbrParams: Number of parameters. It indicates the write command mode: * If inferior to 2, a long write command is performed else short. * @param pParams: Pointer to parameter values table. * @retval HAL status */ void DSI_IO_WriteCmd(uint32_t NbrParams, uint8_t *pParams) { if(NbrParams <= 1) { HAL_DSI_ShortWrite(&dsiHandle, 0, DSI_DCS_SHORT_PKT_WRITE_P1, pParams[0], pParams[1]); } else { HAL_DSI_LongWrite(&dsiHandle, 0, DSI_DCS_LONG_PKT_WRITE, NbrParams, pParams[NbrParams], pParams); } } /** * @brief Delay function for the OTM8009A driver. * * @param Delay: The requested delay in ms. */ void OTM8009A_IO_Delay(uint32_t Delay) { gfxSleepMilliseconds(Delay); } #endif /* _GDISP_LLD_BOARD_H */