display-dma2d  1.0.8
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  _drawing_dma2d_wait();
198 
199  LLUI_DISPLAY_setDrawingLimits(gc, x1, y1, x2, y2);
200 
201  uint32_t rectangle_width = x2 - x1 + 1;
202  uint32_t rectangle_height = y2 - y1 + 1;
203  uint32_t stride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
204 
205  // de-init DMA2D
206  HAL_DMA2D_DeInit(&g_hdma2d);
207 
208  // configure DMA2D
209  g_hdma2d.Init.Mode = DMA2D_R2M;
210  g_hdma2d.Init.OutputOffset = stride - rectangle_width;
211  HAL_DMA2D_Init(&g_hdma2d);
212 
213  // configure environment
214  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
215  g_dma2d_running = true;
216 
217  // start DMA2D
218  _cleanDCache();
219  uint8_t* destination_address = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x1, y1, stride, DRAWING_DMA2D_BPP);
220  HAL_DMA2D_Start_IT(&g_hdma2d, gc->foreground_color, (uint32_t)destination_address, rectangle_width, rectangle_height);
221 
222  return DRAWING_RUNNING;
223 }
224 
225 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) {
226 
227  DRAWING_Status ret;
228 
229  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))))) {
230  // image in image or display in display
231  // overlap bottom and/or right: DMA2D is not able to perform the drawing
232  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
233  ret = DRAWING_DONE;
234  }
235  else {
236 
237  ret = DRAWING_RUNNING;
238  _drawing_dma2d_wait();
239 
240  uint8_t format;
241  uint8_t bpp;
242 
243  switch(image->format) {
244  case MICROUI_IMAGE_FORMAT_RGB565:
245  format = CM_RGB565;
246  bpp = 16;
247  break;
248  case MICROUI_IMAGE_FORMAT_ARGB8888:
249  format = CM_ARGB8888;
250  bpp = 32;
251  break;
252  case MICROUI_IMAGE_FORMAT_RGB888:
253  format = CM_RGB888;
254  bpp = 24;
255  break;
256  case MICROUI_IMAGE_FORMAT_ARGB1555:
257  format = CM_ARGB1555;
258  bpp = 16;
259  break;
260  case MICROUI_IMAGE_FORMAT_ARGB4444:
261  format = CM_ARGB4444;
262  bpp = 16;
263  break;
264  case MICROUI_IMAGE_FORMAT_A4: {
265  // check DMA2D limitations: first and last vertical lines addresses must be aligned on 8 bits
266 
267  jint xAlign = x_src & 1;
268  jint wAlign = width & 1;
269 
270  if (xAlign == 0) {
271  if (wAlign != 0) {
272  // hard cannot draw last vertical line
273  --width;
274  UI_DRAWING_SOFT_drawImage(gc, image, x_src + width, y_src, 1, height, x_dest + width, y_dest, alpha);
275  }
276  // else: easy case: first and last vertical lines are aligned on 8 bits
277  }
278  else {
279  if (wAlign == 0) {
280  // worst case: hard cannot draw first and last vertical lines
281 
282  // first line
283  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, 1, height, x_dest, y_dest, alpha);
284 
285  // last line
286  --width;
287  UI_DRAWING_SOFT_drawImage(gc, image, x_src + width, y_src, 1, height, x_dest + width, y_dest, alpha);
288 
289  ++x_src;
290  ++x_dest;
291  --width;
292  }
293  else {
294  // cannot draw first vertical line
295  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, 1, height, x_dest, y_dest, alpha);
296  ++x_src;
297  ++x_dest;
298  --width;
299  }
300  }
301 
302  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
303  format = CM_A4;
304  bpp = 4;
305  break;
306  }
307  case MICROUI_IMAGE_FORMAT_A8:
308  _drawing_dma2d_configure_alpha_image_data(gc, &alpha);
309  format = CM_A8;
310  bpp = 8;
311  break;
312  default:
313  // unsupported image format
314  UI_DRAWING_SOFT_drawImage(gc, image, x_src, y_src, width, height, x_dest, y_dest, alpha);
315  ret = DRAWING_DONE;
316  break;
317  }
318 
319  if (DRAWING_DONE != ret) {
320 
321  uint32_t srcStride = LLUI_DISPLAY_getStrideInPixels(image);
322  uint32_t destStride = LLUI_DISPLAY_getStrideInPixels(&gc->image);
323  uint8_t* srcAddr = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(image), x_src, y_src, srcStride, bpp);
324  uint8_t* destAddr = _drawing_dma2d_adjust_address(LLUI_DISPLAY_getBufferAddress(&gc->image), x_dest, y_dest, destStride, DRAWING_DMA2D_BPP);
325 
326  // de-init DMA2D
327  HAL_DMA2D_DeInit(&g_hdma2d);
328 
329  // configure background
330  g_hdma2d.LayerCfg[0].InputOffset = destStride - width;
331  g_hdma2d.LayerCfg[0].InputColorMode = DRAWING_DMA2D_FORMAT;
332  g_hdma2d.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
333  g_hdma2d.LayerCfg[0].InputAlpha = 255;
334  HAL_DMA2D_ConfigLayer(&g_hdma2d, 0);
335 
336  // configure foreground
337  HAL_DMA2D_DisableCLUT(&g_hdma2d, 1);
338  g_hdma2d.LayerCfg[1].InputOffset = srcStride - width;
339  g_hdma2d.LayerCfg[1].InputColorMode = format;
340  g_hdma2d.LayerCfg[1].AlphaMode = DMA2D_COMBINE_ALPHA;
341  g_hdma2d.LayerCfg[1].InputAlpha = alpha;
342  HAL_DMA2D_ConfigLayer(&g_hdma2d, 1);
343 
344  // configure DMA2D
345  g_hdma2d.Init.Mode = DMA2D_M2M_BLEND;
346  g_hdma2d.Init.OutputOffset = destStride - width;
347  HAL_DMA2D_Init(&g_hdma2d) ;
348 
349  // configure environment
350  g_dma2d_running = true;
351  g_callback_notification = &LLUI_DISPLAY_notifyAsynchronousDrawingEnd;
352  LLUI_DISPLAY_setDrawingLimits(gc, x_dest, y_dest, x_dest + width - 1, y_dest + height - 1);
353 
354  // start DMA2D
355  _cleanDCache();
356  HAL_DMA2D_BlendingStart_IT(&g_hdma2d, (uint32_t)srcAddr, (uint32_t)destAddr, (uint32_t)destAddr, width, height);
357  }
358  }
359  return ret;
360 }
361 
362 /* EOF -----------------------------------------------------------------------*/
363 
364 #ifdef __cplusplus
365 }
366 #endif
367 
368 
Use STM32 DMA2D (ChromART) for MicroEJ ui_drawing.h implementation.