Forum: Mikrocontroller und Digitale Elektronik STM32H735 Disco HAL DMA2D geht nicht


von Thorsten S. (thorstens)


Lesenswert?

Ich bin seit ein paar Tagen stolzer Besitzer eines STM32H735 Disco 
Boards. Ich bekomme aber HAL DMA2D nicht ans Laufen. Folgender Code soll 
eine Pixelzeile aus dem RAM in den Framebuffer kopieren:

DMA2D_HandleTypeDef hdma2d;

static void ConvertLineToRGB(uint32_t *pSrc, uint32_t *pDst, uint32_t 
xSize)
{
  hdma2d.Instance = DMA2D;

  /* Configure the DMA2D Mode, Color Mode and output offset */
  hdma2d.Init.Mode         = DMA2D_M2M;
  hdma2d.Init.ColorMode    = DMA2D_OUTPUT_ARGB8888;
  hdma2d.Init.OutputOffset = 0;

  /* Foreground Configuration */
  hdma2d.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
  //hdma2d.LayerCfg[1].InputAlpha = 0xFF;
  hdma2d.LayerCfg[1].InputColorMode = DMA2D_INPUT_ARGB8888;
  hdma2d.LayerCfg[1].InputOffset = 0;

  /* DMA2D Initialization */
  if(HAL_DMA2D_Init(&hdma2d) == HAL_OK)
  {
    if(HAL_DMA2D_ConfigLayer(&hdma2d, 1) == HAL_OK)
    {
      HAL_DMA2D_Start(&hdma2d, (uint32_t)pSrc, (uint32_t)pDst, xSize, 
1);
      HAL_DMA2D_PollForTransfer(&hdma2d, 25);
    }
  }
  //memcpy(pDst, pSrc, xSize * 4);
}

Im Debugger sehe ich, dass HAL_DMA2D_Start() den Zielspeicher mit Nullen 
vollschreibt, statt aus pSrc zu kopieren. Wenn ich den memcpy() darunter 
aktiviere, werden die Daten korrekt kopiert und das Display stellt die 
Grafik richtig dar. Ich habe inzwischen den Eindruck, der HAL hat einen 
Bug.

Tatsächlich gibt es eine ähnliche Funktion LL_ConvertLineToRGB() im BSP 
(Board Support Package). Allerdings ist auch im BSP der DMA2D Code per 
#define deaktiviert und die Pixel werden "zu Fuss" kopiert. DMA2D 
scheint bei ST auch nicht funktioniert zu haben. Hat jemand eine Idee?

von Johannes S. (Gast)


Lesenswert?

ein möglicherweise ähnliches Problem hatte ich ja gerade hier 
geschildert:
Beitrag "komplexes Problem mit Grafik und Dateisystem auf Disco-F746NG"

Allerdings mit dem F746 und F769. Da funktionierte DMA2/DMA2D 
prinzipiell und z.B. beim F746 war der auch für ConvertLine aktiv:
1
static void LL_ConvertLineToARGB8888(void *pSrc, void *pDst, uint32_t xSize, uint32_t ColorMode)
2
{    
3
  /* Configure the DMA2D Mode, Color Mode and output offset */
4
  hDma2dHandler.Init.Mode         = DMA2D_M2M_PFC;
5
  hDma2dHandler.Init.ColorMode    = DMA2D_ARGB8888;
6
  hDma2dHandler.Init.OutputOffset = 0;     
7
  
8
  /* Foreground Configuration */
9
  hDma2dHandler.LayerCfg[1].AlphaMode = DMA2D_NO_MODIF_ALPHA;
10
  hDma2dHandler.LayerCfg[1].InputAlpha = 0xFF;
11
  hDma2dHandler.LayerCfg[1].InputColorMode = ColorMode;
12
  hDma2dHandler.LayerCfg[1].InputOffset = 0;
13
  
14
  hDma2dHandler.Instance = DMA2D; 
15
  
16
  /* DMA2D Initialization */
17
  if(HAL_DMA2D_Init(&hDma2dHandler) == HAL_OK) 
18
  {
19
    if(HAL_DMA2D_ConfigLayer(&hDma2dHandler, 1) == HAL_OK) 
20
    {
21
      if (HAL_DMA2D_Start(&hDma2dHandler, (uint32_t)pSrc, (uint32_t)pDst, xSize, 1) == HAL_OK)
22
      {
23
        /* Polling For DMA transfer */  
24
        HAL_DMA2D_PollForTransfer(&hDma2dHandler, 10);
25
      }
26
    }
27
  } 
28
}

um einen rechteckigen Bereich zu übertragen habe ich eine weitere 
Funktion hinzugefügt:
1
/**
2
  * @brief  Converts an area to an ARGB8888 pixel format.
3
  * @param  pSrc: Pointer to source buffer
4
  * @param  pDst: Output color
5
  * @param  xSize: Buffer width
6
  * @param  ySize: Buffer width
7
  * @param  ColorMode: Input color mode   
8
  * @retval None
9
  */
10
static void LL_ConvertAreaToARGB8888(void *pSrc, void *pDst, uint32_t xSize, uint32_t ySize, uint32_t ColorMode)
11
{    
12
  /* Configure the DMA2D Mode, Color Mode and output offset */
13
  hDma2dHandler.Init.Mode         = DMA2D_M2M_PFC;
14
  hDma2dHandler.Init.ColorMode    = ColorMode;
15
  hDma2dHandler.Init.OutputOffset = BSP_LCD_GetXSize() - xSize; 
16
  
17
  /* Foreground Configuration */
18
  hDma2dHandler.LayerCfg[1].AlphaMode = DMA2D_REPLACE_ALPHA;
19
  hDma2dHandler.LayerCfg[1].InputAlpha = 0xff;
20
  hDma2dHandler.LayerCfg[1].InputColorMode = ColorMode;
21
  hDma2dHandler.LayerCfg[1].InputOffset = 0;
22
  
23
  // Background
24
  hDma2dHandler.LayerCfg[0].AlphaMode = DMA2D_NO_MODIF_ALPHA;
25
  hDma2dHandler.LayerCfg[0].InputColorMode = DMA2D_INPUT_ARGB8888;
26
  hDma2dHandler.LayerCfg[0].InputOffset = BSP_LCD_GetXSize() - xSize;  
27
28
  hDma2dHandler.Instance = DMA2D; 
29
  
30
  /* DMA2D Initialization */
31
  if(HAL_DMA2D_Init(&hDma2dHandler) == HAL_OK) 
32
  {
33
    if(HAL_DMA2D_ConfigLayer(&hDma2dHandler, 1) == HAL_OK) 
34
    {
35
#ifdef _USE_DMA2D_POLLING_      
36
      if (HAL_DMA2D_Start(&hDma2dHandler, (uint32_t)pSrc, (uint32_t)pDst, xSize, ySize) == HAL_OK)
37
      {
38
        /* Polling For DMA transfer */  
39
        HAL_DMA2D_PollForTransfer(&hDma2dHandler, 100);
40
      }
41
#else
42
      if (HAL_DMA2D_Start_IT(&hDma2dHandler, (uint32_t)pSrc, (uint32_t)pDst, xSize, ySize) == HAL_OK)
43
      {
44
        // DMA Complete Interrupt should be prepared
45
      }
46
#endif
47
    }
48
  } 
49
}
50
51
void BSP_LCD_SetDMACpltCallback( void (*fn)(DMA2D_HandleTypeDef *hdma2d))
52
{
53
  hDma2dHandler.XferCpltCallback = fn;
54
}
55
56
/**
57
* @brief override weak DMA2D ISR 
58
  call HAL handler, which calls callbacks
59
*/
60
61
void DMA2D_IRQHandler(void)
62
{
63
    HAL_DMA2D_IRQHandler(&hDma2dHandler);
64
}

Die DMA Funktion muss aber vorher auch initialisiert werden, wird das im 
H735 BSP ausgeführt?
Sinnvoll wird DMA erst richtig mit Interrupt und double buffering, 
warten und Zeit verbraten ist wenig effektiv. Da muss man sich aber mit 
den fürchterlichen weak Deklarationen rumschlagen.
Die Probleme die ich hatte liessen sich am einfachsten durch Abschalten 
des D-Cache lösen, letztlich ging es aber durch das ClearInvalidate vor 
der DMA Operation.

Und bitte keine HAL bashing, die bügelt eigentlich eher die 
Hardwarefehler aus. Von den H7 gibt es teilweise schon mehrere 
Revisionen und die HAL hat Workarounds eingebaut.

Das H735 Board liegt aber auch schon vor meiner Nase und ist der nächste 
Kandidat für die Portierung :)

von Thorsten S. (thorstens)


Lesenswert?

> Die DMA Funktion muss aber vorher auch initialisiert werden, wird das im
H735 BSP ausgeführt?

Ich habe das BSP Projekt kopiert und dort meine Änderungen eingefügt. In 
main() wird BSP_LCD_Init() aufgerufen, dass - soweit ich das sehe - 
DMA2D initialisiert?

von Johannes S. (Gast)


Lesenswert?

um welches Beispiel geht es denn konkret? Im CubeH7 sind ja mehrere 
drin.
https://github.com/STMicroelectronics/STM32CubeH7

von Thorsten S. (thorstens)


Lesenswert?


von Johannes S. (Gast)


Lesenswert?

Habe das Beispiel in die CubeIDE importiert, aber der Compiler meckert. 
Es fehlen includes oder Includepfade stimmen nicht. Andere Beispiele 
ließen sich kompilieren. Habe mich schon lange nicht mehr mit Eclipse 
gequält.

von Johannes S. (Gast)


Lesenswert?

habe das Beispiel kompiliert bekommen, man musste noch einen Teil 
lizensierten Code von ST runterladen der nicht im Repo enthalten ist. 
Sowas dürfte ruhig im readme stehen, aber nun läufts.
Ich sehe nur Beispiele mit aktivem DMA2D und die Beispiele funktionieren 
auch.
Im readme findet man aber den Hinweis auf die RAM/Cache Problematik die 
mir auch zu schaffen macht. Wenn aus Speicher im DTCM kopiert wird, dann 
stört der Cache nicht weil dieser RAM nicht gecashed wird. Bei RAM an 
anderen Bussen wird es komplizierter. Entweder Cache vor dem DMA 
durchschreiben (mit SCB_CleanDCache()), oder die MPU konfigurieren das 
write through gemacht wird. Letzteres ist mir aber nicht gelungen, und 
ich habe zig Einstellungen probiert.

Edit:
jetzt habe ich die Stellen gefunden die du vermutlich meinst, da kann 
eingestellt werden ob DMA oder Pixelweises füllen verwendet werden soll. 
Und wenn DMA, dann noch ob Cache Pflege nötig ist, was ja von dem 
Speicher abhängt.

von Thorsten S. (thorstens)


Lesenswert?

Johannes S. schrieb:
> musste noch einen Teil
> lizensierten Code von ST runterladen

Das kann man sich ersparen, wenn man mit der CubeIDE die Firmware-Pakete 
für das Board installiert.

Es war in der Tat der Cache schuld. Nachdem ich I und D Caches beide 
deaktiviert hatte, funktionierte es. Bin bisher nur Mikrocontroller ohne 
Caches gewohnt.

> write through

MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

sollte write-through aktivieren.

> jetzt habe ich die Stellen gefunden die du vermutlich meinst, da kann
> eingestellt werden ob DMA oder Pixelweises füllen verwendet werden soll.
> Und wenn DMA, dann noch ob Cache Pflege nötig ist, was ja von dem
> Speicher abhängt.

Ich sehe da nichts für Cache-Einstellungen. In der Datei 
stm32h735g_discovery_conf.h steht die Zeile:

#define USE_DMA2D_TO_FILL_RGB_RECT          0U

Also ist DMA für die FillRGBRect() Funktion deaktiviert. Die hatten wohl 
auch das Problem mit den Caches. :-)

In der Datei stm32h735g_discovery_lcd.c ist übrigens noch ein Bug:

BSP_LCD_Ctx_t       LcdCtx[LCD_INSTANCES_NBR];

Es muss Lcd_Ctx heißen.

von Thorsten S. (thorstens)


Lesenswert?

Johannes S. schrieb:
> lange nicht mehr mit Eclipse
> gequält.

Welche IDE benutzt du denn? Ich finde die CubeIDE auch nicht besonders 
gut. Aber sie ist kostenlos.

von Johannes S. (Gast)


Lesenswert?

Thorsten S. schrieb:
> Ich sehe da nichts für Cache-Einstellungen. In der Datei
> stm32h735g_discovery_conf.h steht die Zeile:
>
> #define USE_DMA2D_TO_FILL_RGB_RECT          0U
>
> Also ist DMA für die FillRGBRect() Funktion deaktiviert. Die hatten wohl
> auch das Problem mit den Caches. :-)

aber das define kann man ja ändern. Nur muss man dann wie im readme.txt 
zu den Projekten sich auch um den Cache kümmern.
entweder
- Sourcebuffer (komplett) in DTCM legen, das hat 0 Waitstates und den 
Cache nicht aktiv
oder
- Sourcebuffer in SRAM und vor dem DMA Cache Clean ausführen (kostet 
aber Zeit, je nach Größe des Buffers)
oder
- Sourcebuffer in SRAM und Write through konfigurieren. Da war noch das 
Alignment wichtig, siehe meinen anderen Thread.

> Es war in der Tat der Cache schuld. Nachdem ich I und D Caches beide
> deaktiviert hatte, funktionierte es. Bin bisher nur Mikrocontroller ohne
> Caches gewohnt.

In diesem Fall sollte es reichen den D-Cache deaktiviert zu lassen.
Ja, es gibt ja nicht so viele µC mit Cache und ich kannte die 
Fallstricke auch noch nicht. Bei F7/H7 ist definitv Schluss mit 
einfachem µC.
Da stellt sich sowieso die Frage ob für Grafiklastige Anwendungen so ein 
µC noch die richtige Wahl ist oder schon ein embedded Linux auf Cortex-A 
passender wäre. Aber ST scheint da ja einen Markt für zu haben.


> Welche IDE benutzt du denn? Ich finde die CubeIDE auch nicht besonders
> gut. Aber sie ist kostenlos.
Wenn man mit CubeMX arbeitet, dann ist die IDE schon ok. Das Eclipse ist 
nur übermächtig und ein allgemeines Framework, daher sind einige 
Einstellungen sehr versteckt. Ich benutze lieber VSCode und C++ 
Komponenten mit Mbed-OS, den Codegenerator von CubeMX dann nur für Teile 
die das OS nicht unterstützt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.