Description
I’ve built my own 8 bit parallel (8080) display driver that supports ILI9488/9486/9481 and R61529 on a Teensy MicroMod.
I have a very simple test sketch running lvgl v8 (the issue happens on v7.11 as well).
When I flush the display on a polling method, everything works fine and runs infinitely
But when I flush the display with DMA I get a hard fault on the following line of code:
DACCVIOL) Data Access Violation
(MMARVALID) Accessed Address: 0xC (nullptr)
The code is set up so that LVGL calls a pushPixle funciton in the lcd library that loads lv_color_t into DMA and transfers it to the GPIO.
Then, when the DMA transfer completes it triggers a callback function that will call lv_disp_flush_ready instead of getting stuck in some while loop while it waits for the DMA transfer to complete.
When using the standard blocking method (polling), I call lv_disp_flush_ready after it finishes the transfer and all works well.
What MCU/Processor/Board and compiler are you using?
Teensy Micromod (IMXRT1062)
What do you want to achieve?
Flush LVGL with DMA
What have you tried so far?
Code to reproduce
#include "ILI948x_t4_mm.h"
#include "lvgl.h"
ILI948x_t4_mm lcd = ILI948x_t4_mm(17, 16, 5); //(dc, cs, rst)
const int screenWidth = 480;
const int screenHeight = 320;
#define LVGL_TICK_PERIOD 5
/*A static or global variable to store the buffers*/
static lv_disp_draw_buf_t disp_buf;
/*Static or global buffer(s). The second buffer is optional*/
static lv_color_t buf_1[screenWidth * screenHeight /4];
IntervalTimer tick;
static void lv_tick_handler(void)
{
lv_tick_inc(LVGL_TICK_PERIOD);
}
static lv_obj_t * meter;
static void set_value(void * indic, int32_t v)
{
lv_meter_set_indicator_value(meter, indic, v);
}
/**
* A simple meter
*/
void lv_example_meter_1(void)
{
meter = lv_meter_create(lv_scr_act());
lv_obj_center(meter);
lv_obj_set_size(meter, 200, 200);
/*Add a scale first*/
lv_meter_scale_t * scale = lv_meter_add_scale(meter);
lv_meter_set_scale_ticks(meter, scale, 41, 2, 10, lv_palette_main(LV_PALETTE_GREY));
lv_meter_set_scale_major_ticks(meter, scale, 8, 4, 15, lv_color_black(), 10);
lv_meter_indicator_t * indic;
/*Add a blue arc to the start*/
indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);
/*Make the tick lines blue at the start of the scale*/
indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_BLUE), false, 0);
lv_meter_set_indicator_start_value(meter, indic, 0);
lv_meter_set_indicator_end_value(meter, indic, 20);
/*Add a red arc to the end*/
indic = lv_meter_add_arc(meter, scale, 3, lv_palette_main(LV_PALETTE_RED), 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);
/*Make the tick lines red at the end of the scale*/
indic = lv_meter_add_scale_lines(meter, scale, lv_palette_main(LV_PALETTE_RED), lv_palette_main(LV_PALETTE_RED), false, 0);
lv_meter_set_indicator_start_value(meter, indic, 80);
lv_meter_set_indicator_end_value(meter, indic, 100);
/*Add a needle line indicator*/
indic = lv_meter_add_needle_line(meter, scale, 4, lv_palette_main(LV_PALETTE_GREY), -10);
/*Create an animation to set the value*/
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_value);
lv_anim_set_var(&a, indic);
lv_anim_set_values(&a, 0, 100);
lv_anim_set_time(&a, 2000);
lv_anim_set_repeat_delay(&a, 100);
lv_anim_set_playback_time(&a, 500);
lv_anim_set_playback_delay(&a, 100);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&a);
}
FASTRUN void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
*`put_px` is just an example, it needs to implemented by you.*/
lcd.pushPixels16bitDMA((uint16_t*)(color_p),area->x1, area->y1, area->x2, area->y2);
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
//lv_disp_flush_ready(disp_drv);
}
FASTRUN void flushCB()
{
static lv_disp_drv_t * disp_drv;
lv_disp_flush_ready(disp_drv);
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.print(CrashReport);
pinMode(12, OUTPUT);
pinMode(14, OUTPUT);
digitalWrite(12, HIGH);
digitalWrite(14, HIGH);
lcd.begin(12);
lcd.setRotation(3);
lcd.onCompleteCB(&flushCB);
lv_init();
lv_disp_draw_buf_init(&disp_buf, buf_1, NULL, screenWidth * screenHeight /4);
static lv_disp_drv_t disp_drv; /*A variable to hold the drivers. Must be static or global.*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.draw_buf = &disp_buf; /*Set an initialized buffer*/
disp_drv.flush_cb = my_flush_cb; /*Set a flush callback to draw to the display*/
disp_drv.hor_res = screenWidth; /*Set the horizontal resolution in pixels*/
disp_drv.ver_res = screenHeight; /*Set the vertical resolution in pixels*/
lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/
Serial.println("tick.begin");
tick.begin(lv_tick_handler, LVGL_TICK_PERIOD * 1000); // Start ticker
lv_example_meter_1();
}
const int loopDelay1 = 5; // Make a request every 500ms
unsigned long timeNow1 = 0;
void loop() {
if (millis() > timeNow1 + loopDelay1)
{
timeNow1 = millis();
lv_timer_handler(); /* let the GUI do its work */
}
}
Screenshot and/or video
It loads the first 1/4 of the image and hard-faults when transferring with DMA.
When transferring polling method:
FASTRUN void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one
*`put_px` is just an example, it needs to implemented by you.*/
lcd.pushPixels16bit((uint16_t*)(color_p),area->x1, area->y1, area->x2, area->y2);
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}