PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : STM32F1xx FSMC und DMA zur LCD-Ansteuerung bereitet Probleme



erik_wolfram
19.10.2016, 09:16
Hallo,

ich habe mir nun das STM32F103 Board mit LCD von Pollin beschafft. Das Board verwendet einen ili9325 Display Controller der über FSMC angebunden ist.
Damit bekomme ich eine schicke Oberfläche hin. Leider ist das manuelle "Beschreiben" des FSMC-Registers sehr träge. Das "LCD-Clear" benötigt bei 72 MHz ca 32 ms (for-Schleife für 320x240 Pixel).

Nun dachte ich mir ich "schmeiße" mal den DMA an und lasse diesen fleißig schreiben - es muss ja nur eine Farbwert 320x240 mal geschrieben werden. Da der DMA nur 2^16 65535 BufferSize schafft muss er ja zweimal "angeschmissen" werden.
Aber: setze ich das um sind die ersten 10-20 Pixel Müll. Dann folgen die restlichen in einer gleichen, aber falschen Farbe (rot statt gelb).

Ich vermute hier einen Fehler im Timing. Auf der Suche nach einer Lösung bin ich leider nicht fündig geworden. Es verwundert mich sehr, dass noch niemand auf die Idee gekommen ist, dass mit DMA umzusetzen.

Der DMA wurde so initialisiert:


uint16_t ColorDMA = Color;

RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

DMA_InitTypeDef DMA_InitStructure;

DMA_StructInit( &DMA_InitStructure );


DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &(ColorDMA);
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) 0x60020000;//&(LCD_DAT16);
DMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

DMA_InitStructure.DMA_BufferSize = 65535; // max 65535

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;

DMA_DeInit( DMA1_Channel1 );
DMA_Init( DMA1_Channel1, &DMA_InitStructure );

DMA_ITConfig( DMA1_Channel1, DMA_IT_TC, ENABLE );

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/*
NVIC_InitTypeDef NVIC_InitStructure;
//Enable DMA1 channel IRQ Channel
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriori ty = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NVIC_InitStructure );
*/

DMA_Cmd( DMA1_Channel1, ENABLE );

while(!(DMA_GetITStatus(DMA1_IT_TC1)));



Eine Möglichkeit den DMA mit dem FSMC zu triggern habe ich nicht gefunden - in der ReferenceManuall 0008 gibt es hierzu nur eine Abbildung welche auf eine Kombination von DMA und FSMC verweist.
Als weitere Möglichkeit fällt mir noch das FSMC_Timing ein - dort wüsste ich aber nicht, wo ich ansetzen sollte...

Ich wäre sehr dankbar, wenn mir jemand hierzu etwas Unterstützung oder einen Ratschlag geben könnte!

Gruß Erik

erik_wolfram
21.10.2016, 06:35
Hallo,

ich habe es nun zum Laufen bekommen. Für die Nachwelt möchte ich die Lösung hier festhalten:

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 hießt 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