mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik STM32F4 Transfer Error


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Peter M. (lctromnml)


Bewertung
0 lesenswert
nicht lesenswert
Ich habe gerade ein seltsames Problem mit dem DMA vom STM32F4:

Ich nutze DMA2 Stream0 um ADC1 Werte einzulesen. Der Code (siehe unten) 
funktioniert auf zwei Platinen und auf der Dritten nicht. Die Platinen 
sind funktionell unterschiedlich haben jedoch die gleichen STM32-Chips, 
Quarze, Beschaltung, Referenz-Spannungsregler usw.

Ich habe mal alle Interrupts des DMA aktiviert, und damit festgestellt, 
dass es anstatt dem Transmit Complete zu einem DMA Transfer Error (TEIF) 
IRQ kommt.

Auf den beiden funktionierenden Boards kann ich das Verhalten 
nachstellen, in dem ich das ADC_Setup() an anderen Stellen im Code 
platziere (Es scheint also wirklich daran zu liegen, welche Funktionen 
davor und danach aufgerufen werden, sehr merkwürdig).

Ich habe mal versucht die Tipps die ich hier 
http://blog.frankvh.com/2011/08/18/stm32f2xx-dma-controllers/
gefunden habe umzusetzen (Das EN bit clearen bevor man den DMA 
konfiguriert, Alle Flags clearen bevor der IRQ aktiviert wird) aber das 
Verhalten ist unverändert.

Als IDE und Compiler benutze ich das Tasking-VX Toolset. 
Code-Optimierung habe ich auch schon an- und abgestellt.

Kennt jemand ein ähnliches Verhalten?

Anbei mein, auf manchen Platinen funktionierendes, ADC-DMA-Setup:

void ADC_Setup(void)
{
  ADC_InitTypeDef       ADC_InitStructure;
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
  DMA_InitTypeDef       DMA_InitStructure;
  GPIO_InitTypeDef      GPIO_initStruct;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
    RCC_AHB1PeriphClockCmd((RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
            RCC_AHB1Periph_GPIOC), ENABLE);
 
  // DMA2 Stream0 channel0 configuration
  DMA_Cmd(DMA2_Stream0, DISABLE);
  while (DMA2_Stream0->CR & DMA_SxCR_EN);
 
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;                                    //DMA channel 0..7
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;                   //ADC1 Address
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADC_Buffer;                     //uint16_t ADC_Buffer
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                           // Data from Peripheral to memory
  DMA_InitStructure.DMA_BufferSize = NBR_OF_CHANNELS;                               //Number of Data
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                  //increment peripheral pointer
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                           //increment address pointer
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;       //size of data .. 16bit
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;               //size of data
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                   //start from beginning when end is reached
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;                               //high - only adc exists
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                            //FIFO or direct
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;                 //FIFO specific
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                       //specifies the amount of data to be transferred in a single non interruptable transaction
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;               //specifies the amount of data to be transferred in a single non interruptable transaction
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
 
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
 
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
  NVIC_Init(&NVIC_InitStructure);
 
  DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_FEIF2|DMA_FLAG_DMEIF2|DMA_FLAG_TEIF2|DMA_FLAG_HTIF2|DMA_FLAG_TCIF2);
  DMA_Cmd(DMA2_Stream0, ENABLE);
 
 
  // Configure ADC1 Pins
  GPIO_initStruct.GPIO_Mode = GPIO_Mode_AN;
  GPIO_initStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
 
  GPIO_initStruct.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_Init(GPIOA, &GPIO_initStruct);
 
  GPIO_initStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
  GPIO_Init(GPIOC, &GPIO_initStruct);
 
  // ADC1 Init
  ADC_DeInit();
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                          //single, dual, triple
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div6;                       //max 14MHz aus APB2? (Diller)
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;           //
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;     //Delay between to ADC-cycles
  ADC_CommonInit(&ADC_CommonInitStructure);
 
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                      //Enable: Multiplexing
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                //Start next Conv. immediately
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;       //this case: Software trigger
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = NBR_OF_CHANNELS;                      //number of channels in regular group
  ADC_Init(ADC1, &ADC_InitStructure);
 
  // ADC1 regular channel config
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 5, ADC_SampleTime_480Cycles);
 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 6, ADC_SampleTime_480Cycles);
 
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_480Cycles);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_480Cycles);
 
 // Enable DMA request after last transfer (Single-ADC mode)
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
 
  // Enable ADC1 DMA
  ADC_DMACmd(ADC1, ENABLE);
 
  // Enable ADC1
  ADC_Cmd(ADC1, ENABLE);
 
  ADC_SoftwareStartConv(ADC1);
}

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
- Was ist der Unterschied zwischen den Boards?
- schau mal in der RM00090 nach, was einen TEIF auslösen könnte
- schieb die Enables im Code nach unten. Sprich: erst alle Setups (GPIO, 
ADC, DMA, NVIC, ...). Dann ggf. nochmal Flags löschen und dann starten.

von Peter M. (lctromnml)


Bewertung
0 lesenswert
nicht lesenswert
Marcus H. schrieb:
> - Was ist der Unterschied zwischen den Boards?

Spannungsversorgung, Quarze und ADC Schaltung sind exakt gleich. 
Ansonsten unterscheiden sich die Boards erheblich was weitere Peripherie 
angeht, aber das sollte wohl nicht den DMA beeinflussen können

> - schau mal in der RM00090 nach, was einen TEIF auslösen könnte

Du meinst die Reference Manual (RM0090, ein RM00090 habe ich nicht 
gefunden)? Dort habe ich bereits geschaut:

"the transfer error interrupt flag (TEIFx) is set when:
– A bus error occurs during a DMA read or a write access
– A write access is requested by software on a memory address register 
in Double
buffer mode whereas the stream is enabled and the current target memory 
is the
one impacted by the write into the memory address register (refer to
Section 10.3.9: Double buffer mode)"

Double buffer mode benutze ich nicht, und mit "A bus error occurs during 
a DMA read or a write access" kann ich wenig anfangen...



> - schieb die Enables im Code nach unten. Sprich: erst alle Setups (GPIO,
> ADC, DMA, NVIC, ...). Dann ggf. nochmal Flags löschen und dann starten.

Ich habe so eben auch den letzten Controller zum Laufen gebracht. 
Tatsächlich wieder durch blindes herumschieben der ADC_Setup()-Funktion. 
Nach dem GPIO-Setup und vor den anderen Setups (CAN, SPI, I2C) 
funktioniert es jetzt einwandfrei. Schiebe ich es woanders hin, geht es 
nicht.

Das kann doch nicht sein?? Ich habe die Frage auch im ST-Forum gestellt, 
allerdings keine Antwort erhalten. Mit dem DMA gibt es beim STM32 auf 
jeden Fall diverse seltsame Verhaltensweisen...

von Marcus H. (Firma: www.harerod.de) (lungfish) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Hi Peter,
danke für die Rückmeldung.

> - schieb die Enables im Code nach unten. Sprich: erst alle Setups (GPIO,
> ADC, DMA, NVIC, ...). Dann ggf. nochmal Flags löschen und dann starten.
- Das kann doch nicht sein?? Ich habe die Frage auch im ST-Forum 
gestellt,
- allerdings keine Antwort erhalten. Mit dem DMA gibt es beim STM32 auf
- jeden Fall diverse seltsame Verhaltensweisen...

Ganz allgemein:

Der STM32 ist eine Ansammlung von Hardwaremodulen, zwei davon sind 
zufällig Cortex M und NVIC.
Richtig leistungsfähig wird die Kiste erst durch das DMA-System, welches 
die einzelnen Peripherien direkt untereinander verbindet.
Damit das erfolgreich funktioniert, müssen zunächst alle 
Zustandsautomaten der Peripherien initialisiert werden - bevor die 
Hardware freigegeben wird.
Deswegen Initialisierung nach Checkliste. Der DMA-Auslöser wird als 
letztes freigegeben.

Richtig interessant wird das Ganze, wenn Du mehrere Peripherien zu 
verschiedenen Funktionsblöcken kaskadierst und dann mehrere Interrupts 
und DMAs gleichzeitig freigegeben sind.
Und ja, dann kommen langsam die Funktionen die tatsächlich nicht so 
wirklich dokumentiert sind zum Einsatz.
Aber bisher war ich zum Glück noch nie der erste mit einem neuen 
Problem. Und gerade der F1 ist ja "bewährte Technik". Ausführliches 
Suchen hat immer passende Antworten von "clive" gefunden.

Grüße,
 marcus

: Bearbeitet durch User

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.