/** * @file tft.c * */ /********************* * INCLUDES *********************/ #include "lv_conf.h" #include "lvgl/lvgl.h" #include #include #include "tft.h" #include "stm32f7xx.h" #include "stm32746g_discovery.h" #include "stm32746g_discovery_sdram.h" #include "stm32746g_discovery_ts.h" #include "../Components/rk043fn48h/rk043fn48h.h" /********************* * DEFINES *********************/ /* LCD Timings */ /** ET070006DM6 HSYNC Timing **/ #define TFT_HSYNC ((uint16_t)40) /* Horizontal synchronization */ #define TFT_HBP ((uint16_t)46) /* Horizontal back porch */ #define TFT_HFP ((uint16_t)21) /* Horizontal front porch */ /** ET070006DM6 VSYNC Timing **/ #define TFT_VSYNC ((uint16_t)10) /* Vertical synchronization */ #define TFT_VBP ((uint16_t)23) /* Vertical back porch */ #define TFT_VFP ((uint16_t)22) /* Vertical front porch */ #if LV_COLOR_DEPTH != 16 && LV_COLOR_DEPTH != 24 && LV_COLOR_DEPTH != 32 #error LV_COLOR_DEPTH must be 16, 24, or 32 #endif /** * @brief LCD status structure definition */ #define LCD_OK ((uint8_t)0x00) #define LCD_ERROR ((uint8_t)0x01) #define LCD_TIMEOUT ((uint8_t)0x02) /** * @brief LCD special pins */ /* Display enable pin */ #define LCD_DISP_PIN GPIO_PIN_12 #define LCD_DISP_GPIO_PORT GPIOI #define LCD_DISP_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE() #define LCD_DISP_GPIO_CLK_DISABLE() __HAL_RCC_GPIOI_CLK_DISABLE() /* Backlight control pin */ #define LCD_BL_CTRL_PIN GPIO_PIN_3 #define LCD_BL_CTRL_GPIO_PORT GPIOK #define LCD_BL_CTRL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOK_CLK_ENABLE() #define LCD_BL_CTRL_GPIO_CLK_DISABLE() __HAL_RCC_GPIOK_CLK_DISABLE() #define CPY_BUF_DMA_STREAM DMA2_Stream2 #define CPY_BUF_DMA_CHANNEL DMA_CHANNEL_0 #define CPY_BUF_DMA_STREAM_IRQ DMA2_Stream2_IRQn #define CPY_BUF_DMA_STREAM_IRQHANDLER DMA2_Stream2_IRQHandler /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ /*These 3 functions are needed by LittlevGL*/ static void ex_disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t * color_p); static void ex_disp_clean_dcache(lv_disp_drv_t *drv); static uint8_t LCD_Init(void); static void LCD_LayerRgb565Init(uint32_t FB_Address); static void LCD_DisplayOn(void); static void DMA_Config(void); static void DMA_TransferComplete(DMA_HandleTypeDef *han); static void DMA_TransferError(DMA_HandleTypeDef *han); /********************** * STATIC VARIABLES **********************/ LTDC_HandleTypeDef hLtdcHandler; TIM_OC_InitTypeDef sConfig; static lv_disp_drv_t disp_drv; int currentBuffer=0; #if LV_COLOR_DEPTH == 16 typedef uint16_t uintpixel_t; #elif LV_COLOR_DEPTH == 24 || LV_COLOR_DEPTH == 32 typedef uint32_t uintpixel_t; #endif /* You can try to change buffer to internal ram by uncommenting line below and commenting * SDRAM one. */ //static uintpixel_t my_fb[TFT_HOR_RES * TFT_VER_RES]; //#define SDRAM_DEVICE_ADDR ((uint32_t)0xC0000000) //static lv_color_t sdram[1000000] __attribute__ ((section (".sdram"))); //static __IO uintpixel_t * my_fb = (__IO uintpixel_t*) (0x60000000); //__attribute__ ((section(".extram"))) uintpixel_t my_fb[800*480]; __attribute__ ((section(".extram"))) uintpixel_t draw_buf[2][TFT_HOR_RES * TFT_VER_RES]; /*A buffer for 10 rows*/ static DMA_HandleTypeDef DmaHandle,DmaHandleCopy; static int32_t x1_flush; static int32_t y1_flush; static int32_t x2_flush; static int32_t y2_fill; static int32_t y_fill_act; static const lv_color_t * buf_to_flush; static lv_disp_t *our_disp = NULL; TIM_HandleTypeDef TimHandleLight; /* TIM12 init function */ void InitBackLightPWM (void) { GPIO_InitTypeDef GPIO_InitStruct; /* Counter Prescaler value */ uint32_t uhPrescalerValue = 0; /* Private typedef * -----------------------------------------------------------*/ #define PERIOD_VALUE (uint32_t) (833 - 1) /* Period Value */ __HAL_RCC_TIM12_CLK_ENABLE (); __HAL_RCC_GPIOH_CLK_ENABLE (); GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_MEDIUM; GPIO_InitStruct.Alternate = GPIO_AF9_TIM12; HAL_GPIO_Init (GPIOH, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_11; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init (GPIOH, &GPIO_InitStruct); // zal/wyl przetwornicy /* Compute the prescaler value to have TIM2 counter clock equal to 20000000 * Hz */ uhPrescalerValue =(uint32_t) ((SystemCoreClock) / 80000000) - 1; // czestotliwosc 60 kHz TimHandleLight.Instance = TIM12; TimHandleLight.Init.Prescaler = uhPrescalerValue; TimHandleLight.Init.Period = PERIOD_VALUE; TimHandleLight.Init.ClockDivision = 0; TimHandleLight.Init.CounterMode = TIM_COUNTERMODE_UP; TimHandleLight.Init.RepetitionCounter = 0; HAL_TIM_Base_Init (&TimHandleLight); if (HAL_TIM_PWM_Init (&TimHandleLight) != HAL_OK) { /* Initialization Error */ } /*##-2- Configure the PWM channels * #########################################*/ /* Common configuration for all channels */ sConfig.OCMode = TIM_OCMODE_PWM1; sConfig.OCPolarity = TIM_OCPOLARITY_HIGH; sConfig.OCFastMode = TIM_OCFAST_DISABLE; sConfig.OCNPolarity = TIM_OCNPOLARITY_HIGH; sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET; sConfig.OCIdleState = TIM_OCIDLESTATE_RESET; /* Set the pulse value for channel 1 */ sConfig.Pulse = 0; // 650 o warto�5% // jasno��Â��‚ if (HAL_TIM_PWM_ConfigChannel (&TimHandleLight, &sConfig, TIM_CHANNEL_1) !=HAL_OK) { /* Configuration Error */ // HAL_TIM_PWM_Stop(&TimHandle, &sConfig); } // TIM_ForcedOC1Config(TIM11, TIM_ForcedAction_InActive); // Be sure output // is low if (HAL_TIM_PWM_Start (&TimHandleLight, TIM_CHANNEL_1) != HAL_OK) { /* PWM Generation Error */ }; } // załączenie przetwornicy do podswietlania ekranu void BackLight (bool on) { GPIO_PinState state= (GPIO_PinState)on; HAL_GPIO_WritePin (GPIOH, GPIO_PIN_11, state); } // jasnosc pwm 0-100% 0 czarny 100% full void SetPWMLight (int level) { int value = 700 - 7 * level; /* Set the pulse value for channel 1 */ sConfig.Pulse = value; // 650 o warto��Â��‚ if (HAL_TIM_PWM_ConfigChannel (&TimHandleLight, &sConfig, TIM_CHANNEL_1) != HAL_OK) { /* Configuration Error */ // HAL_TIM_PWM_Stop(&TimHandle, &sConfig); } if (HAL_TIM_PWM_Start (&TimHandleLight, TIM_CHANNEL_1) != HAL_OK) { /* PWM Generation Error */ }; } /********************** * MACROS **********************/ /** * Initialize your display here */ void tft_init(void) { /* There is only one display on STM32 */ if(our_disp != NULL) abort(); /* LCD Initialization */ LCD_Init(); /* LCD Initialization */ LCD_LayerRgb565Init((uint32_t)&draw_buf[0][0]); /* Enable the LCD */ LCD_DisplayOn(); DMA_Config(); /*----------------------------- * Create a buffer for drawing *----------------------------*/ /* LittlevGL requires a buffer where it draws the objects. The buffer's has to be greater than 1 display row*/ static lv_disp_draw_buf_t disp_buf_1; //static __attribute__ ((section(".extram"))) lv_color_t buf1_1[TFT_HOR_RES * 480*2]; //static __attribute__ ((section(".extram"))) lv_color_t buf2_1[TFT_HOR_RES * 480]; //static lv_color_t buf1_2[TFT_HOR_RES * 48]; //draw_buf lv_disp_draw_buf_init(&disp_buf_1, &draw_buf[0][0], &draw_buf[1][0], TFT_HOR_RES * TFT_VER_RES); /*Initialize the display buffer*/ /*----------------------------------- * Register the display in LittlevGL *----------------------------------*/ InitBackLightPWM (); BackLight (1); SetPWMLight(100); lv_disp_drv_init(&disp_drv); /*Basic initialization*/ /*Set up the functions to access to your display*/ /*Set the resolution of the display*/ disp_drv.hor_res = TFT_HOR_RES; disp_drv.ver_res = TFT_VER_RES; //disp_drv.full_refresh=1; disp_drv.direct_mode=1; /*Used to copy the buffer's content to the display*/ disp_drv.flush_cb = ex_disp_flush; disp_drv.clean_dcache_cb = ex_disp_clean_dcache; /*Set a display buffer*/ disp_drv.draw_buf = &disp_buf_1; /*Finally register the driver*/ our_disp = lv_disp_drv_register(&disp_drv); } static void update_dual_buf( lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *colour_p ) { lv_disp_t* disp = _lv_refr_get_disp_refreshing(); lv_coord_t y, hres = lv_disp_get_hor_res(disp); uint16_t a; lv_color_t *buf_cpy; int err; if( colour_p == disp_drv->draw_buf->buf1) buf_cpy = disp_drv->draw_buf->buf2; else buf_cpy = disp_drv->draw_buf->buf1; for(a = 0; a < disp->inv_p; a++) { if(disp->inv_area_joined[a]) continue; lv_coord_t w = lv_area_get_width(&disp->inv_areas[a]); for(y = disp->inv_areas[a].y1; y <= disp->inv_areas[a].y2 && y < disp_drv->ver_res; y++) { memcpy(buf_cpy+(y * hres + disp->inv_areas[a].x1), colour_p+(y * hres + disp->inv_areas[a].x1), w * sizeof(lv_color_t)); } } } /********************** * STATIC FUNCTIONS **********************/ /* Flush the content of the internal buffer the specific area on the display * You can use DMA or any hardware acceleration to do this operation in the background but * 'lv_flush_ready()' has to be called when finished * This function is required only when LV_VDB_SIZE != 0 in lv_conf.h*/ static void ex_disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t * color_p) { int i; int ret; if( lv_disp_flush_is_last( drv ) ) { SwapBuffers(); update_dual_buf(drv, area, color_p); } lv_disp_flush_ready( drv ); // SCB_CleanInvalidateDCache(); // SCB_InvalidateICache(); return; // SCB_CleanInvalidateDCache(); // SCB_InvalidateICache(); // if( lv_disp_flush_is_last( drv ) ) { // UpdateDualBuffer(drv, area, color_p); //s_framePending = true; // }; // lv_disp_flush_ready(drv ); // return; int32_t x1 = area->x1; int32_t x2 = area->x2; int32_t y1 = area->y1; int32_t y2 = area->y2; /*Return if the area is out the screen*/ if(x2 < 0) return; if(y2 < 0) return; if(x1 > TFT_HOR_RES - 1) return; if(y1 > TFT_VER_RES - 1) return; /*Truncate the area to the screen*/ int32_t act_x1 = x1 < 0 ? 0 : x1; int32_t act_y1 = y1 < 0 ? 0 : y1; int32_t act_x2 = x2 > TFT_HOR_RES - 1 ? TFT_HOR_RES - 1 : x2; int32_t act_y2 = y2 > TFT_VER_RES - 1 ? TFT_VER_RES - 1 : y2; x1_flush = act_x1; y1_flush = act_y1; x2_flush = act_x2; y2_fill = act_y2; y_fill_act = act_y1; buf_to_flush = color_p; SCB_CleanInvalidateDCache(); SCB_InvalidateICache(); /*##-7- Start the DMA transfer using the interrupt mode #*/ /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */ /* Enable All the DMA interrupts */ HAL_StatusTypeDef err; uint32_t length = (x2_flush - x1_flush + 1); #if LV_COLOR_DEPTH == 24 || LV_COLOR_DEPTH == 32 length *= 2; /* STM32 DMA uses 16-bit chunks so multiply by 2 for 32-bit color */ #endif //printf("x1:%i y1:%i x2:%i x3:%i s:%i\r\n",x1,y1,x2,y2,length); err = HAL_DMA_Start_IT(&DmaHandle,(uint32_t)buf_to_flush, (uint32_t)&draw_buf[currentBuffer][y_fill_act * TFT_HOR_RES + x1_flush], length); if(err != HAL_OK) { while(1); /*Halt on error*/ } } static void ex_disp_clean_dcache(lv_disp_drv_t *drv) { SCB_CleanInvalidateDCache(); } /** * @brief Configure LCD pins, and peripheral clocks. */ static void LCD_MspInit(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOI_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_5 |GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_14|GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOH, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF9_LTDC; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_8|GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_11|GPIO_PIN_12; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOG, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_6; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate = GPIO_AF14_LTDC; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); /* Set LTDC Interrupt to the lowest priority */ // HAL_NVIC_SetPriority(LTDC_IRQn, 0xE, 0); /* Enable LTDC Interrupt */ // HAL_NVIC_EnableIRQ(LTDC_IRQn); /* LCD_DISP GPIO configuration */ GPIO_InitStruct.Pin = GPIO_PIN_11; /* LCD_DISP pin has to be manually controlled */ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11, GPIO_PIN_RESET); HAL_Delay(10); HAL_GPIO_WritePin(GPIOI, GPIO_PIN_11, GPIO_PIN_SET); } /** * @brief Configure LTDC PLL. */ static void LCD_ClockConfig(void) { static RCC_PeriphCLKInitTypeDef periph_clk_init_struct; /* Enable the LTDC clocks */ __HAL_RCC_LTDC_CLK_ENABLE(); /* RK043FN48H 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 */ periph_clk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_LTDC; periph_clk_init_struct.PLLSAI.PLLSAIN = 192; periph_clk_init_struct.PLLSAI.PLLSAIR = 2; periph_clk_init_struct.PLLSAIDivR = RCC_PLLSAIDIVR_2; HAL_RCCEx_PeriphCLKConfig(&periph_clk_init_struct); } /** * @brief Initializes the LCD. * @retval LCD state */ static uint8_t LCD_Init(void) { hLtdcHandler.Init.HorizontalSync = (TFT_HSYNC - 1); hLtdcHandler.Init.VerticalSync = (TFT_VSYNC - 1); hLtdcHandler.Init.AccumulatedHBP = (TFT_HSYNC + TFT_HBP - 1); hLtdcHandler.Init.AccumulatedVBP = (TFT_VSYNC + TFT_VBP - 1); hLtdcHandler.Init.AccumulatedActiveH = (480 + TFT_VSYNC + TFT_VBP - 1); hLtdcHandler.Init.AccumulatedActiveW = (800 + TFT_HSYNC + TFT_HBP - 1); hLtdcHandler.Init.TotalHeigh = (480 + TFT_VSYNC + TFT_VBP + TFT_VFP - 1); hLtdcHandler.Init.TotalWidth = (800 + TFT_HSYNC + TFT_HBP + TFT_HFP - 1); /* LCD clock configuration */ LCD_ClockConfig(); /* Initialize the LCD pixel width and pixel height */ hLtdcHandler.LayerCfg->ImageWidth = 800; hLtdcHandler.LayerCfg->ImageHeight = 480; /* Background value */ hLtdcHandler.Init.Backcolor.Blue = 0; hLtdcHandler.Init.Backcolor.Green = 0; hLtdcHandler.Init.Backcolor.Red = 0; /* Polarity */ hLtdcHandler.Init.HSPolarity = LTDC_HSPOLARITY_AL; hLtdcHandler.Init.VSPolarity = LTDC_VSPOLARITY_AL; hLtdcHandler.Init.DEPolarity = LTDC_DEPOLARITY_AL; hLtdcHandler.Init.PCPolarity = LTDC_PCPOLARITY_IPC; hLtdcHandler.Instance = LTDC; if(HAL_LTDC_GetState(&hLtdcHandler) == HAL_LTDC_STATE_RESET) { /* Initialize the LCD Msp: this __weak function can be rewritten by the application */ LCD_MspInit(); } HAL_LTDC_Init(&hLtdcHandler); BSP_SDRAM_Init(); HAL_EnableFMCMemorySwapping(); uint32_t i; for(i = 0; i < (TFT_HOR_RES * TFT_VER_RES) ; i++) { draw_buf[0][i] = 0xffff; draw_buf[1][i] = 0xff00; } HAL_Delay(200); /* Set LTDC Interrupt to the lowest priority */ // HAL_NVIC_SetPriority(LTDC_IRQn, 0xE, 0); /* Enable LTDC Interrupt */ // HAL_NVIC_EnableIRQ(LTDC_IRQn); return LCD_OK; } static void LCD_LayerRgb565Init(uint32_t FB_Address) { LTDC_LayerCfgTypeDef layer_cfg; /* Layer Init */ layer_cfg.WindowX0 = 0; layer_cfg.WindowX1 = TFT_HOR_RES; layer_cfg.WindowY0 = 0; layer_cfg.WindowY1 = TFT_VER_RES; #if LV_COLOR_DEPTH == 16 layer_cfg.PixelFormat = LTDC_PIXEL_FORMAT_RGB565; #elif LV_COLOR_DEPTH == 24 || LV_COLOR_DEPTH == 32 layer_cfg.PixelFormat = LTDC_PIXEL_FORMAT_ARGB8888; #else #error Unsupported color depth (see tft.c) #endif layer_cfg.FBStartAdress = FB_Address; layer_cfg.Alpha = 255; layer_cfg.Alpha0 = 0; layer_cfg.Backcolor.Blue = 0; layer_cfg.Backcolor.Green = 0; layer_cfg.Backcolor.Red = 0; layer_cfg.BlendingFactor1 = LTDC_BLENDING_FACTOR1_PAxCA; layer_cfg.BlendingFactor2 = LTDC_BLENDING_FACTOR2_PAxCA; layer_cfg.ImageWidth = TFT_HOR_RES; layer_cfg.ImageHeight = TFT_VER_RES; HAL_LTDC_ConfigLayer(&hLtdcHandler, &layer_cfg, 0); } static void LCD_DisplayOn(void) { /* Display On */ // __HAL_LTDC_ENABLE(&hLtdcHandler); //HAL_GPIO_WritePin(LCD_DISP_GPIO_PORT, LCD_DISP_PIN, GPIO_PIN_SET); /* Assert LCD_DISP pin */ // HAL_GPIO_WritePin(LCD_BL_CTRL_GPIO_PORT, LCD_BL_CTRL_PIN, GPIO_PIN_SET); /* Assert LCD_BL_CTRL pin */ } static void DMA_Config(void) { /*## -1- Enable DMA2 clock #################################################*/ __HAL_RCC_DMA2_CLK_ENABLE(); /*##-2- Select the DMA functional Parameters ###############################*/ DmaHandle.Init.Channel = CPY_BUF_DMA_CHANNEL; /* DMA_CHANNEL_0 */ DmaHandle.Init.Direction = DMA_MEMORY_TO_MEMORY; /* M2M transfer mode */ DmaHandle.Init.PeriphInc = DMA_PINC_ENABLE; /* Peripheral increment mode Enable */ DmaHandle.Init.MemInc = DMA_MINC_ENABLE; /* Memory increment mode Enable */ DmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; /* Peripheral data alignment : 16bit */ DmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; /* memory data alignment : 16bit */ DmaHandle.Init.Mode = DMA_NORMAL; /* Normal DMA mode */ DmaHandle.Init.Priority = DMA_PRIORITY_HIGH; /* priority level : high */ DmaHandle.Init.FIFOMode = DMA_FIFOMODE_ENABLE; /* FIFO mode enabled */ DmaHandle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL; /* FIFO threshold: 1/4 full */ DmaHandle.Init.MemBurst = DMA_MBURST_SINGLE; /* Memory burst */ DmaHandle.Init.PeriphBurst = DMA_PBURST_SINGLE; /* Peripheral burst */ /*##-3- Select the DMA instance to be used for the transfer : DMA2_Stream0 #*/ DmaHandle.Instance = CPY_BUF_DMA_STREAM; /*##-4- Initialize the DMA stream ##########################################*/ if(HAL_DMA_Init(&DmaHandle) != HAL_OK) { while(1) { } } /*##-5- Select Callbacks functions called after Transfer complete and Transfer error */ HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_CPLT_CB_ID, DMA_TransferComplete); HAL_DMA_RegisterCallback(&DmaHandle, HAL_DMA_XFER_ERROR_CB_ID, DMA_TransferError); /*##-6- Configure NVIC for DMA transfer complete/error interrupts ##########*/ HAL_NVIC_SetPriority(CPY_BUF_DMA_STREAM_IRQ, 0, 0); HAL_NVIC_EnableIRQ(CPY_BUF_DMA_STREAM_IRQ); } /** * @brief DMA conversion complete callback * @note This function is executed when the transfer complete interrupt * is generated * @retval None */ void SwapBuffers(void) { while ((LTDC->CDSR & LTDC_CDSR_VSYNCS) == 0) { } // LTDC_Layer1->CFBAR = (uint32_t)&draw_buf[currentBuffer][0]; HAL_LTDC_SetAddress(&hLtdcHandler, (uint32_t)&draw_buf[currentBuffer][0], 0); currentBuffer=!currentBuffer; } static void DMA_TransferComplete(DMA_HandleTypeDef *han) { y_fill_act ++; if(y_fill_act > y2_fill) { // if(lv_disp_flush_is_last(&disp_drv)) { // SwapBuffers(han); // }; SCB_CleanInvalidateDCache(); SCB_InvalidateICache(); lv_disp_flush_ready(&disp_drv); // while(1); // HAL_Delay(500); } else { uint32_t length = (x2_flush - x1_flush + 1); buf_to_flush += x2_flush - x1_flush + 1; /*##-7- Start the DMA transfer using the interrupt mode ####################*/ /* Configure the source, destination and buffer size DMA fields and Start DMA Stream transfer */ /* Enable All the DMA interrupts */ #if LV_COLOR_DEPTH == 24 || LV_COLOR_DEPTH == 32 length *= 2; /* STM32 DMA uses 16-bit chunks so multiply by 2 for 32-bit color */ #endif if(HAL_DMA_Start_IT(han,(uint32_t)buf_to_flush, (uint32_t)&draw_buf[currentBuffer][y_fill_act * TFT_HOR_RES + x1_flush], length) != HAL_OK) { while(1); /*Halt on error*/ } } } /** * @brief DMA conversion error callback * @note This function is executed when the transfer error interrupt * is generated during DMA transfer * @retval None */ static void DMA_TransferError(DMA_HandleTypeDef *han) { while(1); /*Halt on error*/ } /** * @brief This function handles DMA Stream interrupt request. * @param None * @retval None */ void CPY_BUF_DMA_STREAM_IRQHANDLER(void) { /* Check the interrupt and clear flag */ HAL_DMA_IRQHandler(&DmaHandle); }