1616#define LCD_SCREEN_WIDTH 800 // Actual screen width in landscape mode
1717#define LCD_SCREEN_HEIGHT 480 // Actual screen height in landscape mode
1818#define FRAMEBUFFER_SIZE (uint32_t)(LCD_SCREEN_HEIGHT * LCD_SCREEN_WIDTH)
19- #define DMA_XFERS_NEEDED \
20- FRAMEBUFFER_SIZE / 2 // We need half as many transfers because the buffer is an array of
21- // 16 bits but the transfers are 32 bits.
19+
20+ #define DRAW_BUF_LINES 48
21+ #define DRAW_BUF_SIZE (uint32_t)(LCD_SCREEN_WIDTH * DRAW_BUF_LINES)
2222
2323/*
2424 * Handles to peripherals
@@ -31,23 +31,19 @@ extern DMA2D_HandleTypeDef hdma2d;
3131 * Global variables
3232 */
3333lv_disp_drv_t lv_display_driver ;
34- __attribute__((section (".framebuffer" ))) lv_color_t screen_buffer [FRAMEBUFFER_SIZE ];
35- __attribute__((section (".framebuffer" ))) lv_color_t framebuffer_1 [FRAMEBUFFER_SIZE ];
34+ __attribute__((section (".framebuffer" ))) lv_color_t screen_buffer [FRAMEBUFFER_SIZE ]; // LTDC scans this
35+ __attribute__((section (".framebuffer" ))) lv_color_t draw_buf_1 [DRAW_BUF_SIZE ]; // LVGL render buffer A
36+ __attribute__((section (".framebuffer" ))) lv_color_t draw_buf_2 [DRAW_BUF_SIZE ]; // LVGL render buffer B
3637
3738#define FRAMEBUFFER_ADDR ((uint32_t)0xC0000000)
3839
40+ // The flush in progress: saved so the DMA2D transfer-complete IRQ can release LVGL.
3941static lv_disp_drv_t * flush_disp_drv ;
40- static lv_disp_t * flush_disp ;
41- static uint32_t flush_src ;
42- static uint32_t flush_dst ;
43- static uint16_t flush_area_idx ;
4442
4543/*
4644 * Private functions prototypes
4745 */
4846void stm32_flush_cb (lv_disp_drv_t * disp_drv , const lv_area_t * area , lv_color_t * color_p );
49- static void dma2d_start_area_it (lv_area_t area , uint32_t src_buffer , uint32_t dst_buffer );
50- static void dma2d_flush_next_area (void );
5147static void dma2d_xfer_cplt (DMA2D_HandleTypeDef * h );
5248static void dma2d_xfer_error (DMA2D_HandleTypeDef * h );
5349
@@ -97,7 +93,7 @@ void screen_driver_init(void)
9793
9894 /* ---- LVGL init ---- */
9995 static lv_disp_draw_buf_t draw_buf ;
100- lv_disp_draw_buf_init (& draw_buf , framebuffer_1 , screen_buffer , FRAMEBUFFER_SIZE );
96+ lv_disp_draw_buf_init (& draw_buf , draw_buf_1 , draw_buf_2 , DRAW_BUF_SIZE );
10197
10298 lv_disp_drv_init (& lv_display_driver );
10399 lv_display_driver .hor_res = LCD_SCREEN_WIDTH ;
@@ -113,76 +109,33 @@ void screen_driver_init(void)
113109 */
114110void stm32_flush_cb (lv_disp_drv_t * disp_drv , const lv_area_t * area , lv_color_t * color_p )
115111{
116- lv_disp_t * disp = _lv_refr_get_disp_refreshing ();
117- if (!lv_disp_flush_is_last (disp_drv ))
118- {
119- lv_disp_flush_ready (disp_drv );
120- return ;
121- }
122-
123- // Swap the buffer for the one to display and reload the screen at the next vertical blanking
124- HAL_LTDC_SetAddress_NoReload (& hltdc , (uint32_t )color_p , 0 );
125- HAL_LTDC_Reload (& hltdc , LTDC_RELOAD_VERTICAL_BLANKING ); // VSYNC
126-
127- // The just-rendered buffer (color_p) is now on screen; sync the dirty areas
128- // back into the other buffer so it is coherent for the next frame. The copies
129- // run on DMA2D in the background, chained one per transfer-complete IRQ; the
130- // final one calls lv_disp_flush_ready() to release LVGL.
131- flush_disp_drv = disp_drv ;
132- flush_disp = disp ;
133- flush_src = (uint32_t )color_p ;
134- flush_dst = (color_p == screen_buffer ) ? (uint32_t )framebuffer_1 : (uint32_t )screen_buffer ;
135- flush_area_idx = 0 ;
136- dma2d_flush_next_area ();
137- }
138-
139- // Start an interrupt-driven DMA2D copy of the next non-joined dirty area. When
140- // no areas remain, release LVGL. Runs in task context for the first area and in
141- // DMA2D IRQ context for the rest.
142- static void dma2d_flush_next_area (void )
143- {
144- while (flush_area_idx < flush_disp -> inv_p )
145- {
146- uint16_t i = flush_area_idx ++ ;
147- // Skip areas that were joined into another (and thus already copied)
148- if (!flush_disp -> inv_area_joined [i ])
149- {
150- dma2d_start_area_it (flush_disp -> inv_areas [i ], flush_src , flush_dst );
151- return ; // wait for the transfer-complete IRQ to advance
152- }
153- }
154-
155- // All dirty areas synced into the back buffer
156- lv_disp_flush_ready (flush_disp_drv );
157- }
112+ size_t area_width = 1 + area -> x2 - area -> x1 ;
113+ size_t area_height = 1 + area -> y2 - area -> y1 ;
114+ size_t dst_offset =
115+ (LCD_SCREEN_WIDTH * area -> y1 + area -> x1 ) * 2 ; // byte offset of the area's top-left in screen_buffer
158116
159- static void dma2d_start_area_it (lv_area_t area , uint32_t src_buffer , uint32_t dst_buffer )
160- {
161- size_t start_offset =
162- (LCD_SCREEN_WIDTH * (area .y1 ) + (area .x1 )) * 2 ; // address offset (not pixel offset so it is multiplied by 2)
163- size_t area_width = 1 + area .x2 - area .x1 ;
164- size_t area_height = 1 + area .y2 - area .y1 ;
165- size_t in_out_offset = LCD_SCREEN_WIDTH - area_width ;
117+ flush_disp_drv = disp_drv ;
166118
167- // Set up DMA2D to transfer parts of picture to part of picture
168119 hdma2d .Init .Mode = DMA2D_M2M ; // plain memory to memory
169120 hdma2d .Init .ColorMode = DMA2D_OUTPUT_RGB565 ;
170- hdma2d .Init .OutputOffset = in_out_offset ; // nb pixels in buffer between end of area line and start of next area line
121+ hdma2d .Init .OutputOffset = LCD_SCREEN_WIDTH - area_width ; // skip to the start of the next screen row
171122 hdma2d .LayerCfg [DMA2D_FOREGROUND_LAYER ].InputColorMode = DMA2D_INPUT_RGB565 ;
172- hdma2d .LayerCfg [DMA2D_FOREGROUND_LAYER ].InputOffset = in_out_offset ;
123+ hdma2d .LayerCfg [DMA2D_FOREGROUND_LAYER ].InputOffset = 0 ; // compact source: rows are contiguous
173124 hdma2d .LayerCfg [DMA2D_FOREGROUND_LAYER ].AlphaMode = DMA2D_NO_MODIF_ALPHA ;
174125 hdma2d .LayerCfg [DMA2D_FOREGROUND_LAYER ].InputAlpha = 0 ;
175126
176127 HAL_DMA2D_Init (& hdma2d );
177128 HAL_DMA2D_ConfigLayer (& hdma2d , DMA2D_FOREGROUND_LAYER );
178- HAL_DMA2D_Start_IT (& hdma2d , src_buffer + start_offset , dst_buffer + start_offset , area_width , area_height );
129+ // Copy runs on DMA2D in the background; dma2d_xfer_cplt() releases LVGL when done,
130+ // so LVGL can render the next chunk into the other draw buffer meanwhile.
131+ HAL_DMA2D_Start_IT (& hdma2d , (uint32_t )color_p , (uint32_t )screen_buffer + dst_offset , area_width , area_height );
179132}
180133
181- // DMA2D transfer-complete IRQ: kick off the next dirty area (or finish) .
134+ // DMA2D transfer-complete IRQ: the chunk has been blitted, release the draw buffer .
182135static void dma2d_xfer_cplt (DMA2D_HandleTypeDef * h )
183136{
184137 (void )h ;
185- dma2d_flush_next_area ( );
138+ lv_disp_flush_ready ( flush_disp_drv );
186139}
187140
188141// DMA2D error IRQ: give up on the remaining areas and release LVGL so the
0 commit comments