Forum: Mikrocontroller und Digitale Elektronik STM32F4 Transfer Error


von Peter M. (lctromnml)


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:

1
void ADC_Setup(void)
2
{
3
  ADC_InitTypeDef       ADC_InitStructure;
4
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
5
  DMA_InitTypeDef       DMA_InitStructure;
6
  GPIO_InitTypeDef      GPIO_initStruct;
7
 
8
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
9
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
10
    RCC_AHB1PeriphClockCmd((RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB |
11
            RCC_AHB1Periph_GPIOC), ENABLE);
12
 
13
  // DMA2 Stream0 channel0 configuration
14
  DMA_Cmd(DMA2_Stream0, DISABLE);
15
  while (DMA2_Stream0->CR & DMA_SxCR_EN);
16
 
17
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;                                    //DMA channel 0..7
18
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;                   //ADC1 Address
19
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADC_Buffer;                     //uint16_t ADC_Buffer
20
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;                           // Data from Peripheral to memory
21
  DMA_InitStructure.DMA_BufferSize = NBR_OF_CHANNELS;                               //Number of Data
22
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                  //increment peripheral pointer
23
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                           //increment address pointer
24
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;       //size of data .. 16bit
25
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;               //size of data
26
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                   //start from beginning when end is reached
27
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;                               //high - only adc exists
28
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;                            //FIFO or direct
29
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;                 //FIFO specific
30
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;                       //specifies the amount of data to be transferred in a single non interruptable transaction
31
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;               //specifies the amount of data to be transferred in a single non interruptable transaction
32
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
33
 
34
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
35
 
36
  NVIC_InitTypeDef NVIC_InitStructure;
37
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
38
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
39
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
40
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
41
  NVIC_Init(&NVIC_InitStructure);
42
 
43
  DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_FEIF2|DMA_FLAG_DMEIF2|DMA_FLAG_TEIF2|DMA_FLAG_HTIF2|DMA_FLAG_TCIF2);
44
  DMA_Cmd(DMA2_Stream0, ENABLE);
45
 
46
 
47
  // Configure ADC1 Pins
48
  GPIO_initStruct.GPIO_Mode = GPIO_Mode_AN;
49
  GPIO_initStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
50
 
51
  GPIO_initStruct.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_6 | GPIO_Pin_7;
52
  GPIO_Init(GPIOA, &GPIO_initStruct);
53
 
54
  GPIO_initStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
55
  GPIO_Init(GPIOC, &GPIO_initStruct);
56
 
57
  // ADC1 Init
58
  ADC_DeInit();
59
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;                          //single, dual, triple
60
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div6;                       //max 14MHz aus APB2? (Diller)
61
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;           //
62
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;     //Delay between to ADC-cycles
63
  ADC_CommonInit(&ADC_CommonInitStructure);
64
 
65
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
66
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;                                      //Enable: Multiplexing
67
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;                                //Start next Conv. immediately
68
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
69
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;       //this case: Software trigger
70
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
71
  ADC_InitStructure.ADC_NbrOfConversion = NBR_OF_CHANNELS;                      //number of channels in regular group
72
  ADC_Init(ADC1, &ADC_InitStructure);
73
 
74
  // ADC1 regular channel config
75
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_480Cycles);
76
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_480Cycles);
77
    ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_480Cycles);
78
    ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_480Cycles);
79
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 5, ADC_SampleTime_480Cycles);
80
 
81
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 6, ADC_SampleTime_480Cycles);
82
 
83
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 7, ADC_SampleTime_480Cycles);
84
    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 8, ADC_SampleTime_480Cycles);
85
    ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 9, ADC_SampleTime_480Cycles);
86
    ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 10, ADC_SampleTime_480Cycles);
87
    ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 11, ADC_SampleTime_480Cycles);
88
    ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 12, ADC_SampleTime_480Cycles);
89
 
90
 // Enable DMA request after last transfer (Single-ADC mode)
91
  ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
92
 
93
  // Enable ADC1 DMA
94
  ADC_DMACmd(ADC1, ENABLE);
95
 
96
  // Enable ADC1
97
  ADC_Cmd(ADC1, ENABLE);
98
 
99
  ADC_SoftwareStartConv(ADC1);
100
}

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


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)


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


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
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.