display-dma2d  2.0.1
display-dma2d
drawing_dma2d.c
Go to the documentation of this file.
1 /*
2  * C
3  *
4  * Copyright 2019-2022 MicroEJ Corp. All rights reserved.
5  * Use of this source code is governed by a BSD-style license that can be found with this software.
6  */
7 
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 
21 /* Includes ------------------------------------------------------------------*/
22 
23 #include "drawing_dma2d.h"
24 #include "ui_drawing.h"
25 #include "ui_drawing_soft.h"
26 #include "bsp_util.h"
27 
28 /* Defines and Macros --------------------------------------------------------*/
29 
30 /*
31  * @brief Defines the DMA2D format according DRAWING_DMA2D_BPP.
32  */
33 #if DRAWING_DMA2D_BPP == 16
34 #define DRAWING_DMA2D_FORMAT DMA2D_RGB565
35 #elif DRAWING_DMA2D_BPP == 24
36 #define DRAWING_DMA2D_FORMAT DMA2D_RGB888
37 #elif DRAWING_DMA2D_BPP == 32
38 #define DRAWING_DMA2D_FORMAT DMA2D_ARGB8888
39 #else
40 #error "Define 'DRAWING_DMA2D_BPP' is required (16, 24 or 32)"
41 #endif
42 
43 /* Structs and Typedefs ------------------------------------------------------*/
44 
45 /*
46  * @brief Function used to notify the graphical engine about the end of DMA2D work.
47  * Available values are LLUI_DISPLAY_notifyAsynchronousDrawingEnd() and LLUI_DISPLAY_flushDone().
48  */
49 typedef void (*t_drawing_notification)(bool under_isr);
50 
51 /* Private fields ------------------------------------------------------------*/
52 
53 /*
54  * @brief STM32 HAL DMA2D declaration.
55  */
56 static DMA2D_HandleTypeDef g_hdma2d;
57 
58 /*
59  * @brief Next notification to call after DMA2D work.
60  */
61 static t_drawing_notification g_callback_notification;
62 
63 /*
64  * @brief This boolean is set to true before launching a DMA2D action and back to
65  * false during DMA2D interrupt. It allows to know if the DMA2D is running or not.
66  */
67 static bool g_dma2d_running;
68 
69 /*
70  * @brief Binary semaphore used to lock a DMA2D user when DMA2D is already running.
71  */
72 static void* g_dma2d_semaphore;
73 
74 /* Private functions ---------------------------------------------------------*/
75 
76 /*
77  * @brief Ensures DMA2D previous work is done before returning.
78  */
79 static inline void _drawing_dma2d_wait(void) {
80  while(g_dma2d_running) {
81  LLUI_DISPLAY_IMPL_binarySemaphoreTake(g_dma2d_semaphore);
82  }
83 }
84 
85 /*
86  * @brief Notify the DMA2D has finished previous job.
87  */
88 static inline void _drawing_dma2d_done(void) {
89  g_dma2d_running = false;
90  LLUI_DISPLAY_IMPL_binarySemaphoreGive(g_dma2d_semaphore, true);
91 }
92 
93 /*
94  * @brief Adjusts the given address to target the given point.
95  */
96 static inline uint8_t* _drawing_dma2d_adjust_address(uint8_t* address, uint32_t x, uint32_t y, uint32_t stride, uint32_t bpp) {
97  return address + ((((y * stride) + x) * bpp) / 8);
98 }
99 
100 /*
101  * @brief For A4 and A8 formats, alpha contains both the global alpha + wanted color.
102  */
103 static inline void _drawing_dma2d_configure_alpha_image_data(MICROUI_GraphicsContext* gc, jint* alphaAndColor) {
104  // for A4 and A8 formats, alphaAndColor is both the global alpha + wanted color
105  *(alphaAndColor) <<= 24;
106  *(alphaAndColor) |= (gc->foreground_color & 0xffffff);
107 }
108 
109 /*
110  * @brief Cleans the cache.
111  *
112  * Before each DMA_2D transfer, the data cache must be cleaned because the
113  * graphics memory is in the memory which is defined "cache enabled" in the MPU
114  * configuration.
115  *
116  * This feature is only required on STM32 CPUs that hold a cache.
117  */
118 static inline void _cleanDCache(void) {
119 #if defined (__DCACHE_PRESENT) && (__DCACHE_PRESENT == 1U)
120  SCB_CleanDCache();
121 #endif
122 }
123 
124 /* Interrupt functions -------------------------------------------------------*/
125 
126 void DRAWING_DMA2D_IRQHandler(void) {
127  // notify STM32 HAL
128  HAL_DMA2D_IRQHandler(&g_hdma2d);
129 
130  // notify DMA2D users
131  _drawing_dma2d_done();
132 
133  // notify graphical engine
134  g_callback_notification(true);
135 }
136 
137 /* Public functions ----------------------------------------------------------*/
138 
139 void DRAWING_DMA2D_initialize(void* binary_semaphore_handle) {
140  // configure globals
141  g_dma2d_running = false;
142  g_dma2d_semaphore = binary_semaphore_handle;
143 
144  // configure DMA2D IRQ handler
145  HAL_NVIC_SetPriority(DMA2D_IRQn, 5, 3);
146  HAL_NVIC_EnableIRQ(DMA2D_IRQn);
147 
148  // configure DMA2D
149  g_hdma2d.Init.ColorMode = DRAWING_DMA2D_FORMAT;
150  g_hdma2d.Instance = DMA2D;
151 }
152 
153 void DRAWING_DMA2D_configure_memcpy(uint8_t* srcAddr, uint8_t* destAddr, uint32_t xmin, uint32_t ymin, uint32_t xmax, uint32_t ymax, uint32_t stride, DRAWING_DMA2D_memcpy* memcpy_data) {
154  _drawing_dma2d_wait();
155 
156  uint32_t width = (xmax - xmin + (uint32_t)1);
157  uint32_t height = (ymax - ymin + (uint32_t)1);
158 
159  // de-init DMA2D
160  HAL_DMA2D_DeInit(&g_hdma2d);
161 
162  // configure foreground
163  g_hdma2d.LayerCfg[1].InputOffset = stride - width;
164  g_hdma2d.LayerCfg[1].InputColorMode = DRAWING_DMA2D_FORMAT;
165  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
166 
167  // configure DMA2D
168  g_hdma2d.Init.Mode = DMA2D_M2M;
169  g_hdma2d.Init.OutputOffset = stride - width;
170  HAL_DMA2D_Init(&g_hdma2d) ;
171 
172  // configure given structure (to give later to DRAWING_DMA2D_start_memcpy())
173  memcpy_data->src_address = _drawing_dma2d_adjust_address(srcAddr, xmin, ymin, stride, DRAWING_DMA2D_BPP);
174  memcpy_data->dest_address = _drawing_dma2d_adjust_address(destAddr, xmin, ymin, stride, DRAWING_DMA2D_BPP);
175  memcpy_data->width = width;
176  memcpy_data->height = height;
177 
178  // configure environment
179  g_callback_notification = &LLUI_DISPLAY_flushDone;
180  g_dma2d_running = true;
181 }
182 
183 void DRAWING_DMA2D_start_memcpy(DRAWING_DMA2D_memcpy* memcpy_data) {
184  _cleanDCache();
185  HAL_DMA2D_Start_IT(
186  &g_hdma2d,
187  (uint32_t)memcpy_data->src_address,
188  (uint32_t)memcpy_data->dest_address,
189  memcpy_data->width,
190  memcpy_data->height
191  );
192 }
193 
194 /* ui_drawing.h functions --------------------------------------------*/
195 
196 DRAWING_Status UI_DRAWING_fillRectangle(MICROUI_GraphicsContext* gc, jint x1, jint y1, jint x2, jint y2) {
197 
198  if (LLUI_DISPLAY_isClipEnabled(gc) && !LLUI_DISPLAY_clipRectangle(gc, &x1, &y1, &x2, &y2))
199  {
200  // drawing is out of clip: nothing to do
201  return DRAWING_DONE;
202  }
203  // else: clip disabled (means drawing is fully in clip) or drawing fits the clip
204 
205  _drawing_dma2d_wait();
206 
207  LLUI_DISPLAY_setDrawingLimits(x1, y1, x2, y2);
208 
209  uint32_t rectangle_width = x2 - x1 + 1;
210  uint32_t rectangle_height = y2 - y1 + 1;
211  uint32_t stride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
212 
213  // de-init DMA2D
214  HAL_DMA2D_DeInit(&g_hdma2d);
215 
216  // configure DMA2D
217  g_hdma2d.Init.Mode = DMA2D_R2M;
218  g_hdma2d.Init.OutputOffset = stride - rectangle_width;
219  HAL_DMA2D_Init(&g_hdma2d);
220 
221  // configure environment
222  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
223  g_dma2d_running = true;
224 
225  // start DMA2D
226  _cleanDCache();
227  uint8_t* destination_address = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x1, y1, stride, DRAWING_DMA2D_BPP);
228  HAL_DMA2D_Start_IT(&g_hdma2d, gc->foreground_color, (uint32_t)destination_address, rectangle_width, rectangle_height);
229 
230  return DRAWING_RUNNING;
231 }
232 
233 DRAWING_Status UI_DRAWING_drawImage(MICROUI_GraphicsContext* gc, MICROUI_Image* image, jint x_src, jint y_src, jint width, jint height, jint x_dest, jint y_dest, jint alpha) {
234 
235  DRAWING_Status ret;
236 
237  if (((uint32_t)&(gc->image) == (uint32_t)image) && (((y_dest > y_src) && (y_dest < (y_src + height))) || ((y_dest == y_src) && (x_dest > x_src) && (x_dest < (x_src + width))))) {
238  // image in image or display in display
239  // overlap bottom and/or right: DMA2D is not able to perform the drawing
240  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
241  ret = DRAWING_DONE;
242  }
243  else {
244 
245  ret = DRAWING_RUNNING;
246  _drawing_dma2d_wait();
247 
248  uint8_t format;
249  uint8_t bpp;
250 
251  switch(image->format) {
252  case MICROUI_IMAGE_FORMAT_RGB565:
253  format = CM_RGB565;
254  bpp = 16;
255  break;
256  case MICROUI_IMAGE_FORMAT_ARGB8888:
257  format = CM_ARGB8888;
258  bpp = 32;
259  break;
260  case MICROUI_IMAGE_FORMAT_RGB888:
261  format = CM_RGB888;
262  bpp = 24;
263  break;
264  case MICROUI_IMAGE_FORMAT_ARGB1555:
265  format = CM_ARGB1555;
266  bpp = 16;
267  break;
268  case MICROUI_IMAGE_FORMAT_ARGB4444:
269  format = CM_ARGB4444;
270  bpp = 16;
271  break;
272  case MICROUI_IMAGE_FORMAT_A4: {
273  // check DMA2D limitations: first and last vertical lines addresses must be aligned on 8 bits
274 
275  jint xAlign = x_src & 1;
276  jint wAlign = width & 1;
277 
278  if (xAlign == 0) {
279  if (wAlign != 0) {
280  // hard cannot draw last vertical line
281  --width;
282  UI_DRAWING_SOFT_drawImage(gc, image, x_src + width, y_src, 1, height, x_dest + width, y_dest, alpha);
283  }
284  // else: easy case: first and last vertical lines are aligned on 8 bits
285  }
286  else {
287  if (wAlign == 0) {
288  // worst case: hard cannot draw first and last vertical lines
289 
290  // first line
291  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, 1, height, x_dest, y_dest, alpha);
292 
293  // last line
294  --width;
295  UI_DRAWING_SOFT_drawImage(gc, image, x_src + width, y_src, 1, height, x_dest + width, y_dest, alpha);
296 
297  ++x_src;
298  ++x_dest;
299  --width;
300  }
301  else {
302  // cannot draw first vertical line
303  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, 1, height, x_dest, y_dest, alpha);
304  ++x_src;
305  ++x_dest;
306  --width;
307  }
308  }
309 
310  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
311  format = CM_A4;
312  bpp = 4;
313  break;
314  }
315  case MICROUI_IMAGE_FORMAT_A8:
316  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
317  format = CM_A8;
318  bpp = 8;
319  break;
320  default:
321  // unsupported image format
322  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
323  ret = DRAWING_DONE;
324  break;
325  }
326 
327  if (DRAWING_DONE != ret) {
328 
329  uint32_t srcStride = LLUI_DISPLAY_getStrideInPixels(image);
330  uint32_t destStride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
331  uint8_t* srcAddr = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(image), x_src, y_src, srcStride, bpp);
332  uint8_t* destAddr = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x_dest, y_dest, destStride, DRAWING_DMA2D_BPP);
333 
334  // de-init DMA2D
335  HAL_DMA2D_DeInit(&g_hdma2d);
336 
337  // configure background
338  g_hdma2d.LayerCfg[0].InputOffset = destStride - width;
339  g_hdma2d.LayerCfg[0].InputColorMode = DRAWING_DMA2D_FORMAT;
340  g_hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
341  g_hdma2d.LayerCfg[0].InputAlpha = 255;
342  HAL_DMA2D_ConfigLayer(&g_hdma2d, 0);
343 
344  // configure foreground
345  HAL_DMA2D_DisableCLUT(&g_hdma2d, 1);
346  g_hdma2d.LayerCfg[1].InputOffset = srcStride - width;
347  g_hdma2d.LayerCfg[1].InputColorMode = format;
348  g_hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA;
349  g_hdma2d.LayerCfg[1].InputAlpha = alpha;
350  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
351 
352  // configure DMA2D
353  g_hdma2d.Init.Mode = DMA2D_M2M_BLEND;
354  g_hdma2d.Init.OutputOffset = destStride - width;
355  HAL_DMA2D_Init(&g_hdma2d) ;
356 
357  // configure environment
358  g_dma2d_running = true;
359  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
360  LLUI_DISPLAY_setDrawingLimits(x_dest, y_dest, x_dest + width - 1, y_dest + height - 1);
361 
362  // start DMA2D
363  _cleanDCache();
364  HAL_DMA2D_BlendingStart_IT(&g_hdma2d, (uint32_t)srcAddr, (uint32_t)destAddr, (uint32_t)destAddr, width, height);
365  }
366  }
367  return ret;
368 }
369 
370 /* EOF -----------------------------------------------------------------------*/
371 
372 #ifdef __cplusplus
373 }
374 #endif
375 
376 
Use STM32 DMA2D (ChromART) for MicroEJ ui_drawing.h implementation.