/********************* * INCLUDES *********************/ #include "lv_conf.h" #include "lvgl/src/lv_hal/lv_hal.h" #include #include "lvgl/src/lv_core/lv_group.h" #include "lvgl/src/lv_core/lv_disp.h" #include "lvgl_ex_functions.h" #include "stm32f4xx.h" #include "main.h" /********************* * DEFINES *********************/ /* DMA Stream parameters definitions. You can modify these parameters to select a different DMA Stream and/or channel. But note that only DMA2 Streams are capable of Memory to Memory transfers. */ #define DMA_STREAM DMA2_Stream0 #define DMA_CHANNEL DMA_CHANNEL_0 #define DMA_STREAM_IRQ DMA2_Stream0_IRQn #define DMA_STREAM_IRQHANDLER DMA2_Stream0_IRQHandler /******************** * Extern VARIABLES ********************/ /********************** * 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 bool ex_tp_read(lv_indev_t * indev, lv_indev_data_t * data); static bool ex_kp_read(lv_indev_t * indev, lv_indev_data_t * data); static bool ex_enc_read(lv_indev_t * indev, lv_indev_data_t*data); #if LV_USE_GPU != 0 static void ex_mem_blend(lv_disp_drv_t *drv, lv_color_t *dest, const lv_color_t *src, uint32_t length, lv_opa_t opa); static void ex_mem_fill(lv_disp_drv_t *disp_drv, lv_color_t *dest_buf, lv_coord_t dest_width, const lv_area_t *fill_area, lv_color_t color); #endif /*LCD*/ static void LCD_Config(void); void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc); void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc); #if LV_USE_GPU != 0 static void DMA2D_Config(void); #endif /*DMA to flush to frame buffer*/ static void DMA_Config(void); static void DMA_TransferComplete(DMA_HandleTypeDef *han); static void DMA_TransferError(DMA_HandleTypeDef *han); /********************** * STATIC VARIABLES **********************/ static LTDC_HandleTypeDef LtdcHandle; #if LV_USE_GPU != 0 static DMA2D_HandleTypeDef Dma2dHandle; #endif #if TFT_EXT_FB != 0 static __IO uint16_t * my_fb = (__IO uint16_t*) (SDRAM_BANK_ADDR); #else static uint16_t my_fb[LV_HOR_RES * LV_VER_RES]; #endif DMA_HandleTypeDef DmaHandle; static lv_disp_drv_t disp_drv; 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_indev_t *kp_indev; static lv_indev_t *enc_indev; /********************** * GLOBAL VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ /** * Initialize lvgl and define its needed functions */ void lvgl_init(void) { lv_init(); DMA_Config(); static lv_disp_buf_t disp_buf; static lv_color_t buf[LV_HOR_RES_MAX * 120U]; /*Declare a buffer for 100 lines*/ lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 120); /*Initialize the display buffer*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/ disp_drv.hor_res = 704; /*Set the horizontal resolution*/ disp_drv.ver_res = 704; /*Set the vertical resolution*/ disp_drv.flush_cb = ex_disp_flush; /*Set your driver function*/ disp_drv.buffer = &disp_buf; /*Assign the buffer to the display*/ #if LV_USE_GPU /*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/ disp_drv.gpu_blend_cb = ex_mem_blend; /*Blend two color array using opacity*/ disp_drv.gpu_fill_cb = ex_mem_fill; /*Fill a memory array with a color*/ DMA2D_Config(); #endif lv_disp_drv_register(&disp_drv);/*Finally register the driver*/ /************************* * Input device interface *************************/ /*Add a touchpad*/ /*touchpad_init();*/ /*Initialize your touchpad, It was inited before*/ lv_indev_drv_t indev_drv_tp; /*Descriptor of an input device driver*/ lv_indev_drv_init(&indev_drv_tp); /*Basic initialization*/ indev_drv_tp.type = LV_INDEV_TYPE_POINTER; /*The touchpad is pointer type device*/ indev_drv_tp.read_cb = ex_tp_read; /*Library ready your touchpad via this function*/ lv_indev_drv_register(&indev_drv_tp); /*Finally register the driver*/ #if 0 /*Add keypad*/ lv_indev_drv_t indev_drv_kb; lv_indev_drv_init(&indev_drv_kb); /*Basic initialization*/ indev_drv_kb.type = LV_INDEV_TYPE_KEYPAD; indev_drv_kb.read_cb = ex_kp_read; kp_indev = lv_indev_drv_register(&indev_drv_kb); /*Finally register the driver*/ #endif lv_indev_drv_t indev_drv_enc; lv_indev_drv_init(&indev_drv_enc); indev_drv_enc.type = LV_INDEV_TYPE_ENCODER; indev_drv_enc.read_cb = ex_enc_read; enc_indev = lv_indev_drv_register(&indev_drv_enc); } /*** * this is uses for connecting the new group to keypad * @return */ lv_indev_t *get_kp_indev(void) { return kp_indev; } /*** * this is uses for connecting the new group to encoder * @return */ lv_indev_t *get_enc_indev(void) { return enc_indev; } /********************** * STATIC FUNCTIONS **********************/ static void ex_disp_flush(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) { /*Return if the area is out the screen*/ if(area->x2 < 0) return; if(area->y2 < 0) return; if(area->x1 > LV_HOR_RES - 1) return; if(area->y1 > LV_VER_RES - 1) return; /*Truncate the area to the screen*/ int32_t act_x1 = area->x1 < 0 ? 0 : area->x1; int32_t act_y1 = area->y1 < 0 ? 0 : area->y1; int32_t act_x2 = area->x2 > LV_HOR_RES - 1 ? LV_HOR_RES - 1 : area->x2; int32_t act_y2 = area->y2 > LV_VER_RES - 1 ? LV_VER_RES - 1 : area->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; #if 0 if(act_x2 - act_x1 > 400) printf("%d, %d, %d, %d - ", act_x1, act_x2, act_y1, act_y2); #endif /*Start the DMA transfer using the interrupt mode */ HAL_StatusTypeDef err; err = HAL_DMA_Start_IT(&DmaHandle, (uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * LV_HOR_RES + x1_flush], (x2_flush - x1_flush + 1)); if(err != HAL_OK) { while(1); /*Halt on error*/ } } #if LV_USE_GPU != 0 /** * Copy pixels to destination memory using opacity * @param dest a memory address. Copy 'src' here. * @param src pointer to pixel map. Copy it to 'dest'. * @param length number of pixels in 'src' * @param opa opacity (0, OPA_TRANSP: transparent ... 255, OPA_COVER, fully cover) */ static void ex_mem_blend(lv_disp_drv_t * drv, lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa) { /*Wait for the previous operation*/ HAL_DMA2D_PollForTransfer(&Dma2dHandle, 100); Dma2dHandle.Init.Mode = DMA2D_M2M_BLEND; /* DMA2D Initialization */ if(HAL_DMA2D_Init(&Dma2dHandle) != HAL_OK) { /* Initialization Error */ while(1); } Dma2dHandle.LayerCfg[1].InputAlpha = opa; HAL_DMA2D_ConfigLayer(&Dma2dHandle, 1); HAL_DMA2D_BlendingStart(&Dma2dHandle, (uint32_t) src, (uint32_t) dest, (uint32_t)dest, length, 1); } /** * Copy pixels to destination memory using opacity * @param dest a memory address. Copy 'src' here. * @param src pointer to pixel map. Copy it to 'dest'. * @param length number of pixels in 'src' * @param opa opacity (0, OPA_TRANSP: transparent ... 255, OPA_COVER, fully cover) */ static void ex_mem_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, const lv_area_t * fill_area, lv_color_t color) { /*Wait for the previous operation*/ HAL_DMA2D_PollForTransfer(&Dma2dHandle, 100); Dma2dHandle.Init.Mode = DMA2D_R2M; /* DMA2D Initialization */ if(HAL_DMA2D_Init(&Dma2dHandle) != HAL_OK) { /* Initialization Error */ while(1); } Dma2dHandle.LayerCfg[1].InputAlpha = 0xff; HAL_DMA2D_ConfigLayer(&Dma2dHandle, 1); lv_color_t * dest_buf_ofs = dest_buf; dest_buf_ofs += dest_width * fill_area->y1; dest_buf_ofs += fill_area->x1; lv_coord_t area_w = lv_area_get_width(fill_area); uint32_t i; for(i = fill_area->y1; i <= fill_area->y2; i++) { /*Wait for the previous operation*/ HAL_DMA2D_PollForTransfer(&Dma2dHandle, 100); HAL_DMA2D_BlendingStart(&Dma2dHandle, (uint32_t) lv_color_to32(color), (uint32_t) dest_buf_ofs, (uint32_t)dest_buf_ofs, area_w, 1); dest_buf_ofs += dest_width; } } #endif #if LV_USE_GPU != 0 /** * @brief DMA2D Transfer completed callback * @param hdma2d: DMA2D handle. * @note This example shows a simple way to report end of DMA2D transfer, and * you can add your own implementation. * @retval None */ static void DMA2D_TransferComplete(DMA2D_HandleTypeDef *hdma2d) { } /** * @brief DMA2D error callbacks * @param hdma2d: DMA2D handle * @note This example shows a simple way to report DMA2D transfer error, and you can * add your own implementation. * @retval None */ static void DMA2D_TransferError(DMA2D_HandleTypeDef *hdma2d) { } /** * @brief DMA2D configuration. * @note This function Configure the DMA2D peripheral : * 1) Configure the Transfer mode as memory to memory with blending. * 2) Configure the output color mode as RGB565 pixel format. * 3) Configure the foreground * - first image loaded from FLASH memory * - constant alpha value (decreased to see the background) * - color mode as RGB565 pixel format * 4) Configure the background * - second image loaded from FLASH memory * - color mode as RGB565 pixel format * @retval None */ static void DMA2D_Config(void) { /* Configure the DMA2D Mode, Color Mode and output offset */ Dma2dHandle.Init.Mode = DMA2D_M2M_BLEND; Dma2dHandle.Init.ColorMode = DMA2D_RGB565; Dma2dHandle.Init.OutputOffset = 0x0; /* DMA2D Callbacks Configuration */ Dma2dHandle.XferCpltCallback = DMA2D_TransferComplete; Dma2dHandle.XferErrorCallback = DMA2D_TransferError; /* Foreground Configuration */ Dma2dHandle.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA; Dma2dHandle.LayerCfg[1].InputAlpha = 0xFF; Dma2dHandle.LayerCfg[1].InputColorMode = DMA2D_INPUT_RGB565; Dma2dHandle.LayerCfg[1].InputOffset = 0x0; /* Background Configuration */ Dma2dHandle.LayerCfg[0].AlphaMode = DMA2D_REPLACE_ALPHA; Dma2dHandle.LayerCfg[0].InputAlpha = 0xFF; Dma2dHandle.LayerCfg[0].InputColorMode = DMA2D_INPUT_RGB565; Dma2dHandle.LayerCfg[0].InputOffset = 0x0; Dma2dHandle.Instance = DMA2D; /* DMA2D Initialization */ if(HAL_DMA2D_Init(&Dma2dHandle) != HAL_OK) { /* Initialization Error */ Error_Handler(); } HAL_DMA2D_ConfigLayer(&Dma2dHandle, 0); HAL_DMA2D_ConfigLayer(&Dma2dHandle, 1); } #endif /** * @brief Configure the DMA controller according to the Stream parameters * defined in main.h file * @note This function is used to : * -1- Enable DMA2 clock * -2- Select the DMA functional Parameters * -3- Select the DMA instance to be used for the transfer * -4- Select Callbacks functions called after Transfer complete and Transfer error interrupt detection * -5- Initialize the DMA stream * -6- Configure NVIC for DMA transfer complete/error interrupts * @param None * @retval None */ static void DMA_Config(void) { /*## -1- Enable DMA2 clock #################################################*/ __HAL_RCC_DMA2_CLK_ENABLE(); /*##-2- Select the DMA functional Parameters ###############################*/ DmaHandle.Init.Channel = 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 = DMA_STREAM; /*##-4- Initialize the DMA stream ##########################################*/ if(HAL_DMA_Init(&DmaHandle) != HAL_OK) { assert_param(false); /*Halt on error*/ } /*##-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(DMA_STREAM_IRQ, 0, 0); HAL_NVIC_EnableIRQ(DMA_STREAM_IRQ); } /** * @brief DMA conversion complete callback * @note This function is executed when the transfer complete interrupt * is generated * @retval None */ static void DMA_TransferComplete(DMA_HandleTypeDef *han) { y_fill_act ++; if(y_fill_act > y2_fill) { lv_disp_flush_ready(&disp_drv); } else { 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(HAL_DMA_Start_IT(han,(uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * LV_HOR_RES + x1_flush], (x2_flush - x1_flush + 1)) != HAL_OK) { assert_param(false); /*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) { } /** * @brief This function handles DMA Stream interrupt request. * @param None * @retval None */ void DMA_STREAM_IRQHANDLER(void) { /* Check the interrupt and clear flag */ HAL_DMA_IRQHandler(&DmaHandle); }