Forum: Mikrocontroller und Digitale Elektronik STM32F1xx DMA - FSMC (LCD)


von Erik Z. (erik_wolfram)


Lesenswert?

Hallo,

ich habe mir grade ein STM32F103VET Board mit TFT von Pollin zugelegt.
Das LCD besitzt einen ILI9325 Display-Controller und ist über 16 Bit 
FSMC angebunden. Hierzu habe ich mir entsprechende Bibliotheken 
zusammengeführt um die volle Funktionalität zu erzielen.

Soweit so gut:
Ich kann Texte schreiben, Graphen zeichnen und Bildchen anzeigen. (Alles 
was man sich so wünscht). Touch gibts auch!

Aber:
Funktionen wie "LCD_Clear" benötigen ca.32 ms für die 320x240 Pixel. 
Damit ist keine vernüftige Bildrate umzusetzen. Der Grund dafür ist 
recht einfach - in sämmtlichen Bibliotheken wird eine for-Schleife für 
die 320x240=76800 Pixel genutzt und jedes mit einer entsprechenden Farbe 
gefüllt. Mit einer Taktfrequenz von 72 MHz weiß man dann schnell wo die 
32 ms herkommen.


Da ich ein großer Fan von DMA bin dachte ich mir, dass ich diesen doch 
nutzen könnte um die Pixel im Hintergrund und wesentlich schnell zu 
füllen. Ebenso könnte man so schnell kleine Bildchen/Icons aus einem 
Puffer ausgeben.

Leider scheitere ich momentan an dieser Aufgabe: Der DMA scheint ein 
Timing-Problem mit dem FSMC zu haben - zwar kommen Daten an - diese sind 
jedoch fehlerhaft. Die ersten 10-20 Pixel sind müll, während die 
nachfolgenden Pixel eine kontinuierliche, falsche Farbe haben (rot statt 
gelb).
Der Code dafür sieht so aus:
1
  uint16_t ColorDMA = Color;
2
3
  LCD_SetCursor( 0, 0 );
4
  LCD_WriteRAM_Prepare(); /* Prepare to write GRAM */
5
6
  RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );
7
8
  DMA_InitTypeDef DMA_InitStructure;
9
10
  DMA_StructInit( &DMA_InitStructure );
11
12
13
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &(ColorDMA);
14
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
15
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
16
17
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) 0x60020000;
18
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord;
19
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
20
21
  DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
22
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//DMA_DIR_PeripheralSRC;
23
24
  DMA_InitStructure.DMA_BufferSize = 3000;      // max 65535
25
26
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
27
  DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
28
29
  DMA_DeInit( DMA1_Channel1 );
30
  DMA_Init( DMA1_Channel1, &DMA_InitStructure );
31
32
  DMA_ITConfig( DMA1_Channel1, DMA_IT_TC, ENABLE );
33
34
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
35
36
  DMA_Cmd( DMA1_Channel1, ENABLE );
37
38
  while(!(DMA_GetITStatus(DMA1_IT_TC1)));
39
  DMA_ClearFlag(DMA1_FLAG_TC1);

Der Code sollte eigentlich die ersten 3000 Pixel in der vorgebenen Farbe 
füllen. Stattdessen sind die ersten 10-20 Pixel schwarz und die 
restlichen rot statt gelb.


Was mir auch vollständig unerklärlich ist: warum nutzt keine der 
Bibliotheken den DMA für solche Funktionen? Eigentlich liegt das doch 
auf der Hand... Laut STM32 Datenblättern sollte der DMA auf FSMC 
zugreifen können...

Ich hoffe hier kann mir jemand helfen!

Gruß Erik

von Philipp K. (philipp_k59)


Lesenswert?

Erik Z. schrieb:
> Was mir auch vollständig unerklärlich ist: warum nutzt keine der
> Bibliotheken den DMA für solche Funktionen? Eigentlich liegt das doch
> auf der Hand...

das liegt nicht auf der Hand wenn man mit 5Leitungen SPI und DMA optisch 
vergleichbar schnell wie FSMC mit mindestens 16 Steuerleitungen ist.

Gerade so kleine Displays sind sowieso nicht allzuschnell im Refresh das 
man unbedingt FSMC nutzen muss.. Als Übung ist das bestimmt hilfreich!

von Erik Z. (erik_wolfram)


Lesenswert?

Hallo,

ich habe es nun zum Laufen bekommen. Mit der Suche nach AN2790 bekommt 
mann neben einer PDF eine Bibliothek welche entsprechende Funktionen 
bereit stellt.
Außerdem ist dort das Timing für das LCD genau beschrieben (bzw. genaue 
Werte für mein Display).
Lediglich bei den Adressen der FSMC-Schnittstelle ist in dieser 
Bibliothek eine Einschränkung da der Ausgang A0 verwendet wird. Wird der 
Ausgang A16 verwendet muss für die Bank1 0x60000000 das 16-Bit (A16) 
eingeschoben werden:
0x60000000 | (0x00000001<16) -> 0x60020000
Die Bänke sehen dann so aus:
Bank1 (NE1) 0x60000000
Bank2 (NE2) 0x64000000
Bank3 (NE3) 0x68000000
Bank4 (NE4) 0x6C000000

Das hierß für mich, dass ich auf die Struktur LCD verzichte und 
LCD_REG16 und LCD_DATA16 manuell setze:
#define LCD_REG16     (*((volatile uint16_t*)0x60000000))
#define LCD_DAT16     (*((volatile uint16_t*)0x60020000))
Die RAM-Addresse für den DMA geht ohne Pointer:
#define LCD_RAM_ADDR     ((uint32_t)(0x60020000))


Jetzt konnte ich das ganze so machen wie es angedacht war. Per DMA 
benötigt ein "LCD_Clear" knapp 15 ms @ 72 MHz OHNE Prozessorlast. Das 
entspricht ca. 66,7 FPS für das Füllen des kompletten Displays. (Vorher 
31,25 FPS mit 100% Prozessorlast)
Für meine Anwendung sollte das auf jeden Fall reichen, da ich das 
Display nur partiell neu füllen möchte. (Graph für einen Datenlogger)

Gruß Erik

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.