How can I make my RGB565 parallel display work with DMA?
What MCU/Processor/Board and compiler are you using?
I am using the STM32H743IIT6 on a custom board and using STM32CubeIDE as the compiler.
What LVGL version are you using?
I am using LVGL version 8.3.
What do you want to achieve?
I want to port LVGL on my STM32H743 using DMA.
What have you tried so far?
I have successfully used the polling mode (which involves using two nested “for” loops in the disp_flush
function) to port LVGL on my STM32H743, and it works fine.
i have read this example lv_port_stm32f746_disco and get help from it.
here is my code (i put all related codes in main.c)
#define MY_DISP_HOR_RES 800
#define MY_DISP_VER_RES 480
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_drv_t disp_drv; /Descriptor of a display driver/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
static void ex_disp_clean_dcache(lv_disp_drv_t *drv);
static void DMA_TransferComplete(DMA_HandleTypeDef *han);
static void DMA_TransferError(DMA_HandleTypeDef *han);
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
LTDC_HandleTypeDef hltdc;
DMA_HandleTypeDef hdma_memtomem_dma2_stream0;
SDRAM_HandleTypeDef hsdram1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_FMC_Init(void);
static void MX_LTDC_Init(void);
/ USER CODE BEGIN PFP */
uint16_t * lcd_fb = (uint16_t*) (0xC0000000);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------/
/ USER CODE BEGIN 0 */
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
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 > MY_DISP_HOR_RES - 1) return;
if(y1 > MY_DISP_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 > MY_DISP_HOR_RES - 1 ? MY_DISP_HOR_RES - 1 : x2;
int32_t act_y2 = y2 > MY_DISP_VER_RES - 1 ? MY_DISP_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);
err = HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream0,
(uint32_t)buf_to_flush,
(uint32_t) &lcd_fb[y_fill_act * MY_DISP_HOR_RES + x1_flush],
length);
if(err != HAL_OK)
{
while(1); /*Halt on error*/
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
static void DMA_TransferComplete(DMA_HandleTypeDef *han)
{
y_fill_act ++;
if(y_fill_act > y2_fill)
{
SCB_CleanInvalidateDCache();
SCB_InvalidateICache();
lv_disp_flush_ready(&disp_drv);
}
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(HAL_DMA_Start_IT( han,
(uint32_t)buf_to_flush,
(uint32_t)&lcd_fb[y_fill_act * MY_DISP_HOR_RES + x1_flush],
length) != HAL_OK)
{
while(1); /*Halt on error*/
}
}
}
static void DMA_TransferError(DMA_HandleTypeDef *han){}
static void ex_disp_clean_dcache(lv_disp_drv_t *drv)
{
SCB_CleanInvalidateDCache();
}
void lv_port_disp_init(void)
{
MX_DMA_Init();
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf1_1[MY_DISP_HOR_RES * 10];
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf1_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize
the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
lv_disp_drv_init(&disp_drv);
/*Set the resolution of the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;/*800*/
disp_drv.ver_res = MY_DISP_VER_RES;/*480*/
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
disp_drv.clean_dcache_cb = ex_disp_clean_dcache;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**
- Enable DMA controller clock
- Configure DMA for memory to memory transfers
- hdma_memtomem_dma2_stream0
*/
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* Configure DMA request hdma_memtomem_dma2_stream0 on DMA2_Stream0 */
hdma_memtomem_dma2_stream0.Instance = DMA2_Stream0;
hdma_memtomem_dma2_stream0.Init.Request = DMA_REQUEST_MEM2MEM;
hdma_memtomem_dma2_stream0.Init.Direction = DMA_MEMORY_TO_MEMORY;
hdma_memtomem_dma2_stream0.Init.PeriphInc = DMA_PINC_ENABLE;
hdma_memtomem_dma2_stream0.Init.MemInc = DMA_MINC_ENABLE;
hdma_memtomem_dma2_stream0.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_memtomem_dma2_stream0.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_memtomem_dma2_stream0.Init.Mode = DMA_NORMAL;
hdma_memtomem_dma2_stream0.Init.Priority = DMA_PRIORITY_HIGH;
hdma_memtomem_dma2_stream0.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
hdma_memtomem_dma2_stream0.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;
hdma_memtomem_dma2_stream0.Init.MemBurst = DMA_MBURST_SINGLE;
hdma_memtomem_dma2_stream0.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_memtomem_dma2_stream0) != HAL_OK)
{
Error_Handler( );
}
HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,HAL_DMA_XFER_CPLT_CB_ID, DMA_TransferComplete);
HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream0,HAL_DMA_XFER_ERROR_CB_ID, DMA_TransferError);
/* DMA interrupt init /
/ DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals /
MX_GPIO_Init();
MX_FMC_Init();
MX_LTDC_Init();
/ USER CODE BEGIN 2 */
lv_init();
lv_port_disp_init();
lv_example_get_started_1();
/* USER CODE END 2 */
/* Infinite loop /
/ USER CODE BEGIN WHILE /
while (1)
{
/ USER CODE END WHILE */
/* USER CODE BEGIN 3 */
lv_timer_handler();
HAL_Delay(10);
}
/* USER CODE END 3 */
}
and it is my result
When I debug the code, I realize that my code doesn’t reach the function disp_flush
, and it gets stuck at lv_timer_handler()
. Subsequently, I encounter a HardFault error at the end.
/**
- @brief This function handles Hard fault interrupt.
/
void HardFault_Handler(void)
{
/ USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 /
while (1)
{
/ USER CODE BEGIN W1_HardFault_IRQn 0 /
/ USER CODE END W1_HardFault_IRQn 0 */
}
}
my code for download
stm32h7xx_it.c (6.0 KB)
main.c (16.0 KB)
Appreciate your help in advance.