Forum: Mikrocontroller und Digitale Elektronik STM32F4: Triple ADC + Timer + DMA


von kuds (Gast)


Lesenswert?

Hi,
ich habe ein STM32F407 und möchte die drei onboard-ADCs mit einem Timer 
(TIM2) getriggert via DMA (DMA2, Stream 4, Channel 0) auslesen. (Ein 
ganz ähnliches Problem hatten wir hier schonmal 
Beitrag "stm32F4 + ADC + Timer + DMA" )
Mein Timer läuft mit den Einstellungen in unten stehendem Code mit genau 
100Hz. Das habe ich auch mit einem "normalen" Interrupt getestet und 
nachgemessen.
In meiner DMA-Konfiguration verwende ich den TripleADC-Modus.

Das Problem ist, dass der DMA-Interrupt-Handler viel häufiger aufgerufen 
wird als er sollte... Ich denke also irgendwas läuft mit der Triggerung 
falsch oder ich muss an irgendeiner Stelle was deaktivieren. Ich weiß 
nur nicht wo.

Meine Funktion zum Konfigurieren des Timers:
1
void timer_init()
2
{
3
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
4
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
5
  TIM_TimeBaseStructure.TIM_Period = 99;  //diese kombi aus Period und Prescaler gibt 100 HZ (ist getestet!)
6
  TIM_TimeBaseStructure.TIM_Prescaler = 2099;
7
  TIM_TimeBaseStructure.TIM_ClockDivision =0;
8
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
9
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
10
11
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
12
}

Meine Funktion zur Konfiguration des DMAs:
1
void PDHADC_DMA_init()
2
{
3
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); // Enable the DMA clock
4
  
5
  // Enable the DMA Stream IRQ Channel
6
  NVIC_InitTypeDef NVIC_InitStructure;
7
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream4_IRQn;
8
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
9
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
10
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
11
  NVIC_Init(&NVIC_InitStructure);
12
13
  DMA_InitTypeDef DMA_InitStructure;
14
15
  DMA_Cmd(DMA2_Stream4, DISABLE);
16
  DMA_DeInit(DMA2_Stream4);
17
18
  // Set the parameters to be configured
19
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;         //use channel 0
20
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC->CDR;    // Read from common mode register
21
  DMA_InitStructure.DMA_Memory0BaseAddr    = (uint32_t)&PDHADC_buffer[0]; // Send the data to the RAM buffer
22
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;     // periphery -- into --> memory
23
  DMA_InitStructure.DMA_BufferSize = 3;     //einfachster fall: DMA sollte nach einer Triple-ADC-Konversion auslösen.
24
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;   //the periphery address stays the same
25
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;     //but the memory address is incremented (buffer[0], buffer[1], ... buffer[BUFFER_SIZE])
26
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //16bit=Half Word (the actual data are only 12 bits)
27
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;  //buffer type is also 16bit.
28
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;        //if one reaches the end start from 0 again
29
  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
30
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;       
31
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;   
32
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;     
33
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;   
34
  DMA_Init(DMA2_Stream4, &DMA_InitStructure);         //write the configuration
35
  
36
  DMA_ITConfig(DMA2_Stream4, DMA_IT_TC, ENABLE);// Enable DMA Transfer Complete (TC) interrupt
37
}
und der zugehörige Interrupt-Handler:
1
extern "C" void DMA2_Stream4_IRQHandler(void)
2
{
3
  if (DMA_GetITStatus(DMA2_Stream4, DMA_IT_TCIF4))
4
  {
5
        //hier kommt spaeter noch was hin...
6
  DMA_ClearITPendingBit(DMA2_Stream4, DMA_IT_TCIF4);
7
   }
8
}

Meine Funktion zur Konfiguration des Triple-ADC:

1
void ADC_init()
2
{
3
  //create init structures
4
  GPIO_InitTypeDef GPIO_InitStructure;
5
  ADC_InitTypeDef ADC_InitStructure;
6
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
7
8
  //configure ADC_Common
9
  ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_RegSimult;   //ADC1 as master, ADC2+3 as slaves
10
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;    //default
11
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; //ADC1[0], ADC2[0], ADC3[0], ADC1[1], ADC2[1]...
12
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
13
  ADC_CommonInit(&ADC_CommonInitStructure);
14
15
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //enable clocks for ADC pins (A0, A1, A2) 
16
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 |RCC_APB2Periph_ADC3 |RCC_APB2Periph_ADC3, ENABLE); //enable clock for ADCs itself
17
18
  //configure ADC pins is analog input.
19
  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
20
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
21
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
22
  GPIO_Init(GPIOA, &GPIO_InitStructure);
23
24
  ADC_InitStructure.ADC_Resolution=ADC_Resolution_12b;  //12bit
25
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;     //only 1 channel per ADC --> no scan
26
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; //trigger on rising edge of trigger event
27
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  //only one conversion per trigger event?
28
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO; //triggered by TIM2 
29
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 0b0000(Bit12)(Bit11)....(Bit0) with Bit12 being MSB
30
  ADC_InitStructure.ADC_NbrOfConversion = 1;     //only one channel per ADC
31
32
  ADC_Init(ADC1, &ADC_InitStructure); //master
33
  //hier bin ich unsicher... kann ich mit dem selben struct auch ADC2 und ADC3 (die slaves) initialisieren?
34
  ADC_Init(ADC2, &ADC_InitStructure); 
35
  ADC_Init(ADC3, &ADC_InitStructure);
36
37
  ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
38
  ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 1, ADC_SampleTime_15Cycles);
39
  ADC_RegularChannelConfig(ADC3, ADC_Channel_2, 1, ADC_SampleTime_15Cycles);
40
41
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
42
}

Ich starte mein Programm folgendermaßen:
1
...
2
timer_init();
3
dma_init();
4
adc_init();
5
6
...
7
TIM_Cmd(TIM2, ENABLE);
8
DMA_Cmd(DMA2_Stream4, ENABLE);
9
ADC_DMACmd(ADC1, ENABLE);
10
//ADC_DMACmd(ADC2, ENABLE); //die slaves hier brauch ich nicht, oder?
11
//ADC_DMACmd(ADC3, ENABLE);
12
13
ADC_Cmd(ADC1,ENABLE);
14
ADC_Cmd(ADC2,ENABLE);
15
ADC_Cmd(ADC3,ENABLE);
16
ADC_SoftwareStartConv(ADC1);
17
...

Wie gesagt: Das Problem ist, dass der DMA Interrupt-Handler viel zu 
häufig aufgerufen wird; also viel häufiger als 100Hz, die der TIM2 
vorgeben sollte. Die Kopplung von Timer und ADC/DMA scheint also nicht 
zu funktionieren. Habt ihr eine Idee?

Vielen Dank für eure Hilfe!

von kuds (Gast)


Lesenswert?

Lösung selbst gefunden. Für alle, die ein ähnliches Problem haben:
Es kommt auf die Initialisierungsreihenfolge an!
Folgende Reihenfolge funktioniert bei mir:
1. RCC
2. NVIC
3. DMA
4. GPIO
5. TIM
6. ADC

von kuds (Gast)


Lesenswert?

und das
ADC_DMACmd(ADC1, ENABLE)
braucht man NICHT!

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


Lesenswert?

kuds schrieb:
> Lösung selbst gefunden. Für alle, die ein ähnliches Problem haben:
> Es kommt auf die Initialisierungsreihenfolge an!
> Folgende Reihenfolge funktioniert bei mir:
> 1. RCC
> 2. NVIC
> 3. DMA
> 4. GPIO
> 5. TIM
> 6. ADC

Mmmh, spontan hätte ich die Initialisierung in dieser Reihenfolge 
durchgeführt:

1. RCC    <- Power!
4. GPIO   <- Portpins aktivieren
6. ADC    <- ADC parametrieren
2. NVIC   <- Interrupt-System parametrieren
3. DMA    <- DMA Kanäle parametrieren
5. TIM    <- Timer loslaufen lassen

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