Forum: Mikrocontroller und Digitale Elektronik STM32 ADC getriggert über Timer - was fehlt (StdperiphLib)


von Michael D. (Gast)


Lesenswert?

Hallo zusammen,

ich arbeite gerade mit einem STM32F4 und hänge an der Stelle wo ADC1 mit 
Timer 3 getriggert (Conversion-start) werden soll.

Was funktioniert bereits? (Testweise)

TIM3_Interrupt wird im gewünschten Intervall aufgerufen, in welchem 
testweise ADC_SoftwareStartConv() gestartet und der ADC-Wert im ADC_IRQ 
korrekt ausgegeben wurde.

Was ich nicht zustande bringe ist es den ADC direkt bei Timerüberlauf 
(Update) zu starten (anstatt im TIM3_IRQ)

Dies wollte ich mit
"ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO" 
machen. (Ich nehme an TRGO steht für Trigger Output).
Hierfür habe ich beim Timer
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update);
den Updateevent gesetzt.

Leider wird der ADC nie gestartet.
Evtl. hat ja jemand einen Hinweis für mich.

Vielen Dank im voraus,
Michael

Hier mein Code:
1
// NVIC Configuration
2
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
4
NVIC_InitStructure.NVIC_IRQChannelSubPriority =  0;
5
NVIC_InitStructure.NVIC_IRQChannelCmd =  ENABLE;
6
NVIC_Init(&NVIC_InitStructure);
7
8
// TIM3 Configuration
9
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
10
11
// Time Base configuration
12
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
13
TIM_TimeBaseStructure.TIM_Prescaler = 168;
14
TIM_TimeBaseStructure.TIM_CounterMode =   TIM_CounterMode_Up;
15
TIM_TimeBaseStructure.TIM_Period = 1000;
16
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
17
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
18
19
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
20
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update);
21
22
TIM_Cmd(TIM3, ENABLE);
23
24
// ADC
25
// NVIC Configuration
26
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
27
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
28
NVIC_InitStructure.NVIC_IRQChannelSubPriority =  0;
29
NVIC_InitStructure.NVIC_IRQChannelCmd =  ENABLE;
30
NVIC_Init(&NVIC_InitStructure);
31
32
// Enable GPIOC and ADC1 clock
33
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
34
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
35
36
// GPIO Configuration PC.00 (ADC Channel10) as analog input
37
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
38
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
39
GPIO_Init(GPIOC, &GPIO_InitStructure);
40
41
// ADC Common Init
42
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
43
ADC_CommonInitStructure.ADC_Prescaler =  ADC_Prescaler_Div2;
44
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
45
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
46
ADC_CommonInit(&ADC_CommonInitStructure);
47
48
// ADC1 Configuration
49
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
50
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
51
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
52
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
53
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
54
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
55
ADC_InitStructure.ADC_NbrOfConversion =  1;
56
ADC_Init(ADC1, &ADC_InitStructure);
57
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
58
59
// ADC3 regular channel7 configuration
60
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_3Cycles);
61
ADC_Cmd(ADC1, ENABLE);

von Christoph A. (shadowrunner93)


Lesenswert?

Hallo Michael.

Hier ist mein Code der bei mir funktioniert. Ich verwende den Timer aber 
im PWM-Mode, der ADC wird durch ein Capture-Compare Event getriggert und 
die Werte mittels DMA ausgelesen, ich hab dir nur die wichtigen 
Funktionen rausgesucht ->
1
 /* TIM2 configuration ------------------------------------------------------*/ 
2
  /* Time Base configuration */
3
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
4
  TIM_TimeBaseStructure.TIM_Period = sample_gap;          
5
  TIM_TimeBaseStructure.TIM_Prescaler = TIM1_PRESCALER - 1;       
6
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;    
7
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
8
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
9
  
10
  /* TIM2 channel2 configuration in PWM mode */
11
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
12
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                
13
  TIM_OCInitStructure.TIM_Pulse = 1; 
14
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;         
15
  TIM_OC2Init(TIM2, &TIM_OCInitStructure);
16
17
  /* TIM2 enable counter */
18
  TIM_Cmd(TIM2, ENABLE);
19
20
   /* ADC1 configuration ------------------------------------------------------*/
21
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
22
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
23
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
24
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;
25
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
26
  ADC_InitStructure.ADC_NbrOfChannel = 1;
27
  ADC_Init(ADC1, &ADC_InitStructure);
28
29
  /* ADC1 regular channel14 configuration */ 
30
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, sample_time);
31
32
  /* Enable ADC1 DMA */
33
  ADC_DMACmd(ADC1, ENABLE);
34
  
35
  /* Enable ADC1 external trigger */ 
36
  ADC_ExternalTrigConvCmd(ADC1, ENABLE);
37
38
  /* Enable ADC1 */
39
  ADC_Cmd(ADC1, ENABLE);
40
  
41
    /* Enable ADC1 reset calibration register */   
42
  ADC_ResetCalibration(ADC1);
43
  /* Check the end of ADC1 reset calibration register */
44
  while(ADC_GetResetCalibrationStatus(ADC1));
45
  
46
  /* Start ADC1 calibration */
47
  ADC_StartCalibration(ADC1);
48
  /* Check the end of ADC1 calibration */
49
  while(ADC_GetCalibrationStatus(ADC1));

von Michael D. (Gast)


Lesenswert?

Hallo Christoph,

vielen Dank für das posten deines Codes, und sorry für die verspätete 
Antwort.

Ich verwende die Lib für den F4/F4, welche leicht von der F1 Lib 
abweicht.

Schlussendlich habe ich das ganze dann doch noch in meiner Version zum 
laufen bekommen.

Dachte zuerst dass es an ADC_ExternalTrigConvEdge lag, weil dieses auf 
"none" gesetzt war, das war es dann aber doch nicht.

Ich glaube dass irgendetwas bei der Initialisierungsreihenfolge nicht 
gestimmt hat, zumindest habe ich jetzt bei der funktionierenden Variante 
keine Parameter/Funktionen geändert, hinzugefügt.

Ein ähnliches Problem hatte ich bei der UART auch schon mal.

Nochmals vielen Dank,
Mike

von Michael L. (michaelmichael)


Lesenswert?

Hallo,
Obwohl schon lange her, möchte ich das Thema nochmal aufgreifen. Ich 
stehe vor dem gleichen Problem. Ich habe einen STM32F4 Discovery. Ich 
möchte 1000 Werte des ADC über DMA ins RAM speichern. Und das mit einer 
Abtastrate von 1MHz. Dazu triggere ich den ADC auf das Timer event 
„TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);“. Jedoch werden 
die Daten nicht mit der gewünschten Frequenz in den DMA geschaufelt. Und 
zwar mit etwa 1.6MHz. Dies ist auch unabhängig ob ich 1MHz oder 10kHz 
einstelle.

Die oberen Beiträge, Beiträge in diesem Forum und Google hat mir leider 
nicht weitergeholen. Und in der Reference Manual habe ich bis jetzt auch 
nicht der richtige Hinweis gefunden.
Hier mein Versuch:
1
TIM2_Config()
2
{
3
TIM_TimeBaseInitTypeDef    TIM_TimeBaseStructure;  
4
NVIC_InitTypeDef NVIC_InitStructure;  
5
  NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
6
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
7
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
8
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
9
  NVIC_Init(&NVIC_InitStructure);
10
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
11
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
12
  TIM_TimeBaseStructure.TIM_Period = 4-1;
13
  TIM_TimeBaseStructure.TIM_Prescaler = 21-1;
14
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
15
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
16
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
17
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);
18
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
19
}
1
adc_dma_init()
2
{
3
ADC_InitTypeDef       ADC_InitStructure;
4
ADC_CommonInitTypeDef ADC_CommonInitStructure;
5
  DMA_InitTypeDef       DMA_InitStructure;
6
  GPIO_InitTypeDef      GPIO_InitStructure;
7
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA, ENABLE);
8
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
9
  DMA_InitStructure.DMA_Channel = DMA_Channel_2;
10
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS;
11
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADC3ConvertedValue;
12
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
13
  DMA_InitStructure.DMA_BufferSize = 1000;
14
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
15
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
16
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
17
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
18
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
19
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
20
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
21
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
22
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
23
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
24
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
25
  DMA_Cmd(DMA2_Stream0, ENABLE);
26
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
27
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
28
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
29
  GPIO_Init(GPIOA, &GPIO_InitStructure);
30
31
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
32
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
33
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
34
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
35
  ADC_CommonInit(&ADC_CommonInitStructure);
36
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
37
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
38
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
39
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
40
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
41
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
42
  ADC_InitStructure.ADC_NbrOfConversion = 1; 
43
  ADC_Init(ADC3, &ADC_InitStructure);  
44
ADC_RegularChannelConfig(ADC3, ADC_Channel_1, 1, ADC_SampleTime_15Cycles); 
45
  ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
46
  ADC_DMACmd(ADC3, ENABLE);
47
  ADC_Cmd(ADC3, ENABLE);
48
}
1
main()
2
{
3
TIM2_Config();
4
adc_dma_init();
5
TIM_Cmd(TIM2, ENABLE);
6
while(!DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0));
7
DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0);
8
}

Kann mir jemand sagen was ich hier falsch mache und wie ich den ADC 
triggern kann?

Danke gruß Michael

von Michael D. (Gast)


Lesenswert?

Hab jetzt nicht alles durchgesehen, aber einen verdacht..
Du hast den Timer Interrupt aktiviert. Deaktiviere diesen.
Bzw. stelle sicher dass du im Interrupt das Flag löscht.

void ADC_IRQHandler(void) {
  if(ADC_GetITStatus(ADC3,ADC_IT_EOC) != RESET) {
    ADC_ClearITPendingBit(ADC3, ADC_IT_EOC);
  }
}

Lg,
Mike

von Michael L. (michaelmichael)


Lesenswert?

Hallo,
danke für die schnelle Antwort.
Ich habs gelöst bekommen. Am Interrupt liegts wohl nicht, wede ich 
jedoch bei gelegenheit auch nochmal untersuchen.

In der ADC_init musste ich
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
DISABLEN.

Ich hoffe, dass nicht nochmal sowas auftaucht. Vielen Dank nochmal für 
die schnelle Antwort.

Gruß Michael

von Andreas T. (skycurve)


Lesenswert?

Hallo,
ich habe eine Verständnisfrage.

Es wird ein STM32F4 Board verwendet.

Ich will während einer center-aligned PWM genau in der Mitte des 
Stromimpulses gleichzeitig zwei Spannungen messen. Damit die Messungen 
gleichzeitig geschehen, verwende ich 2 verschiede ADCs. Die zwei ADCs 
sollen mit dem Timer4 ausgelöst werden.

Hier mein Code:
1
  //ADC structure configuration
2
  ADC_InitTypeDef ADC_init_structure;
3
  ADC_DeInit();
4
  ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Right; 
5
  ADC_init_structure.ADC_Resolution = ADC_Resolution_12b;
6
  ADC_init_structure.ADC_ContinuousConvMode = DISABLE; 
7
  ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;
8
  ADC_init_structure.ADC_ExternalTrigConvEdge =  ADC_ExternalTrigConvEdge_None;
9
  ADC_init_structure.ADC_NbrOfConversion = 1;
10
  ADC_init_structure.ADC_ScanConvMode = DISABLE; 
11
  
12
  ADC_Init(ADC1, &ADC_init_structure);
13
  ADC_Init(ADC2, &ADC_init_structure); 
14
15
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_144Cycles);
16
  ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 1, ADC_SampleTime_144Cycles);
17
  
18
  //Enable ADC conversion
19
  ADC_Cmd(ADC1, ENABLE);
20
  ADC_Cmd(ADC2, ENABLE);

Kann ich nun sicher sein, dass die Spannungen wirklich gleichzeitig 
gemessen werden?

Ich habe mir auch den Dual regular simultaneous mode angeschaut, bei dem 
ADC1 den ADC2 auslöst, jedoch ich finde es schöner wenn die 2 ADCs 
direkt von dem Timer(4) getriggert werden.

MfG
 Andreas

: Bearbeitet durch User
von Alex E. (tecnologic) Benutzerseite


Lesenswert?

Andreas True schrieb:
> ADC_init_structure.ADC_ExternalTrigConvEdge =
> ADC_ExternalTrigConvEdge_None;

Moin,

Ändere die Flanke auf Rising.

Gruß

Tec

von Andreas T. (skycurve)


Lesenswert?

Guten Morgen,

vielen Dank für den Tipp. Gibt man mit ADC_ExternalTrigConvEdge an, ob 
der ADC bei steigender oder bei fallender Flanke ausgelöst wird?

Wenn ich den ADC in der Mitte des Duty Cycles einer center aligned PWM 
auslösen möchte, müsste ich die Flanke nicht Falling setzen?

MfG
  Andreas

von Alex E. (tecnologic) Benutzerseite


Lesenswert?

Moin,

Du triggerst ja auf CC4 des Tim4. Wenn du jetzt genau oben Triggern 
willst setzt du CCR4 des Tim 4 auf ARR-1 also kurz vor den Overflow, 
laut RM kannst du sogar auf CCR4 = ARR gehen dann sitzt der Trigger 
genau oben, ich würde dem aber nicht trauen. Wenn du Sauber in der Mitte 
der PWM triggern willst musst du sowieso die Samplezeit des ADCs mit 
beachten, also muss das Samplen eh etwas vorher getriggert werden damit 
er mit der Samplezeit genau mittig über dem Overflow des Timers liegt. 
Dann kannst du die PWMs auch maximal auf fahren und hast n Max Duty von 
ARR - ADCSampletime/2 - Sicherheit.

Die Sicherheit würde ich jenach nach Schaltflanken immer etwas 
großzügiger wählen. Musst ausprobieren.

Zu der Flanke: du musst auf rising triggern wenn du im Overflow triggern 
willst. Willst du bei aktiver low side triggern dann muss CCR4 sehr 
klein sein und du triggerst auf Falling. Weil hier ja das OCRef Signal 
fast die ganze Zeit high ist und nur fällt wenn der Counter unter CCR4 
geht. und das ist kurz vor dem Underflow.

Gruß

Tec

von Andreas T. (skycurve)


Lesenswert?

Also muss ich davon ausgehen, dass man als Ergebnis der Messung den Wert 
der Spannung bekommt, die am Ende der Sample-Zeit anliegt, richtig?

Ich denke (oder hoffe :D), ich muss die Sample Zeit nicht 
berücksichtigen, die PWM wird voraussichtlich nicht über 20kHz 
hinausgehen (Motorsteuerung).

Du hast richtig erkannt, dass ich später bei aktiver LOW-Side triggern 
will, aber das kommt erst später. Erstmal würde gerne das System 
komplett verstehen.

Mit TIM4 CCR1..CCR3 steuere ich 3 PWM Ausgänge.
Weil TIM4 die ADCs nur mit ADC_ExternalTrigConv_T4_CC4 triggern kann 
muss CCR4 auch konfiguriert werden, oder?

Hier wäre mein Code:

TimerConfig:
1
    // TIM4 clock enable
2
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 , ENABLE);
3
4
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
5
6
    TIM_TimeBaseStructInit (& TIM_TimeBaseStructure);
7
    TIM_TimeBaseStructure.TIM_Prescaler = Prescaler;
8
    TIM_TimeBaseStructure.TIM_Period   = TIM_Period;
9
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1;
10
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
11
    TIM_TimeBaseInit(TIM4 , &TIM_TimeBaseStructure);
12
13
  //  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
14
15
    TIM_OCInitTypeDef  TIM_OCInitStructure;
16
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
17
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
18
    TIM_OCInitStructure.TIM_Pulse = 0;
19
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
20
    TIM_OC1Init(TIM4, &TIM_OCInitStructure); // erster PWM-Pin
21
    TIM_OC2Init(TIM4, &TIM_OCInitStructure); // zweiter PWM-Pin
22
    TIM_OC3Init(TIM4, &TIM_OCInitStructure); // dritter PWM-Pin
23
    TIM_OC4Init(TIM4, &TIM_OCInitStructure); // für ADC Triggerung    <- ist das nötig(1)?
24
25
26
27
    TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); // erster PWM-Pin
28
    TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable); // zweiter PWM-Pin
29
    TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); // dritter PWM-Pin
30
    TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); // für ADC Triggerung    <- ist das nötig(2)?
31
32
33
    TIM_ARRPreloadConfig(TIM4, ENABLE);
34
    TIM_Cmd(TIM4, ENABLE);
35
36
    TIM4->CCR1 = 0; // erster PWM-Pin
37
    TIM4->CCR2 = 0; // zweiter PWM-Pin
38
    TIM4->CCR3 = 0; // dritter PWM-Pin
39
    TIM4->CCR4 = TIM4->ARR-1; // für ADC Triggerung

ADC_Config:
1
  //Clock configuration
2
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
3
  RCC_AHB1PeriphClockCmd(RCC_AHB1ENR_GPIOBEN, ENABLE);
4
  
5
  ADC_InitTypeDef ADC_init_structure;
6
  ADC_DeInit();
7
  ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Right;
8
  ADC_init_structure.ADC_Resolution = ADC_Resolution_12b; 
9
  ADC_init_structure.ADC_ContinuousConvMode = DISABLE; // nur externe Trigger
10
  ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4; // conversion triggered by TIMER4
11
12
  ADC_init_structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising; // im Overflow triggern
13
  ADC_init_structure.ADC_NbrOfConversion = 1;
14
  ADC_init_structure.ADC_ScanConvMode = DISABLE; //  was bedeutet das?
15
16
17
  ADC_Init(ADC1, &ADC_init_structure); 
18
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_144Cycles);
19
20
21
  ADC_Cmd(ADC1, ENABLE);

MfG
  Andreas

von Alex E. (tecnologic) Benutzerseite


Lesenswert?

Die OC Konfigurationen betreffen die Pins, sind also beide nicht nötig. 
Du musst aber den Duty vom Kanal 4 setzen, das ist klar oder? Dass 
bestimmt dir ja den Trigger-Zeitpunkt.

Als kleine Anmerkung ADC_SampleTime_144Cycles ist sehr lange. Wenn dein 
Spannungsteiler für die Spannungsmessung nicht allzu hochohmig ist 
kannst du auch viel weiter runter gehen.

Die Zeit besagt nur wie lange der ADC-Pin auf auf den Samplekondensator 
geschaltet wird. Der Kondensator ist n paar Picofarad, der braucht nicht 
lange zum laden. Irgend was bei 20 Takten reicht locker aus. Was ist das 
für ein STM? Bei einem STM32F4 nehme ich bei 168MHz und ADC Clock von 
21MHz, 3 Cycles zum samplen sind ca. 150ns oder so, bei 100ns wirds bei 
dem laut Datenblatt kritisch. Nur damit du n Eindruck bekommst wo deine 
Zeit liegt.

Gruß

Tec

von Andreas T. (skycurve)


Lesenswert?

Wie hochohmig mein Spannungsteiler wird, weiß ich leider noch nicht, 
muss mich noch mit Intrumentenverstärkern auseinandersetzen.
Aber danke für den Hinweis, ich wusste nicht was die Sample-Zeit genau 
bedeutet.

Legt man nicht mit
1
TIM4->CCR4 = TIM4->ARR-1;
den Duty vom Kanal 4 fest?
Hier wäre die Duty fast bei 100%.


Mir ist noch nicht klar warum man den CCR4 für die Triggerung des ADC 
hochsetzten muss.
Bei center aligned PWM sieht ein Cycle doch zB so aus:

Zähler zählt von 1 bis 255 hoch, registriert einen Overflow, und zählt 
dann wieder von 255 bis 1 runter, umabhängig vom Duty.

Wenn ich nun ein Duty von 10 festlege, geht der Ausgang auf HIGH wenn 
der Zähler hochzählend bei 250 angelangt. Der Ausgang geht wieder auf 
LOW wenn der Zähler runterzählend bei 250 ist.

Der ADC soll bei Zähler == 255 getriggert werden. Also beim Overflow.
Warum hängt die Triggerung mit der Duty Einstellung zusammen?

Verwende ein STM32F4 Board mit 168MHz

MfG
  Andreas

: Bearbeitet durch User
von Alex E. (tecnologic) Benutzerseite


Lesenswert?

Angenommen:
TIM4->ARR = 255;
TIM4->CCR4 = TIM4->ARR -1;

Bedeutet das: Timer zählt von 0 bis 253 => OCRef4 (Ausgang4 des Timers 
auf Low, Wenn du keine OC Konfiguration machst hängt da zwar kein Pin 
dran, aber das Signal gibts ja trozdem noch)
bei 254 kommt das Compare event. OCRef4 geht auf high. => Rising edge am
ADC_ExternalTrigConv_T4_CC4 und die hast du ja als Trigger für den ADC 
eingestellt. Der ADC sampled also immer beim hochzählen bei 254, was 
quasi genau am overflow ist.

bei 255 zählt der Zähler wieder runter und wenn er wieder bei 253 is ist 
OCRef4 wieder low. und der Spass beginnt von vorn.

von Andreas T. (skycurve)


Lesenswert?

Also liegt der Ausgang bei
1
TIM4->CCR4 = 254;
die meiste Zeit (253/255) auf LOW?

Wenn ich am Pin messe, habe ich aber fast 100% Duty, also knapp 3 Volt.
Oder hängt es mit dieser Einstellung zusammen:
1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
sprich wenn ich
1
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_LOW;
stelle, würde am Pin bei
1
CCR4 = 254;
sogut wie keine Spannung anliegen?


MfG
 Andreas

von Alex E. (tecnologic) Benutzerseite


Lesenswert?

Jop richtig erkannt.

von Andreas T. (skycurve)


Lesenswert?

Vielen Dank dass du das alles mit mir durchgekaut hast, ich hatte noch 
einige Lücken.

Nur noch zur Sicherheit:

Rising / falling edges Trigger beziehen sich auf OCRef(4).

Wird OCRef4 auf HIGH gestellt, werden im gleichen Moment alle ADCs mit
... = ADC_ExternalTrigConv_T4_CC4;
... = ADC_ExternalTrigConvEdge_Rising;
getriggert.

Wird OCRef4 auf LOW gestellt, werden im selben Moment alle ADCs mit
... = ADC_ExternalTrigConv_T4_CC4;
... = ADC_ExternalTrigConvEdge_Falling;
getriggert.

OC4 hängt von OCRef4 und von TIM_OCPolarity Einstellung ab.

Folgende Reihenfolge der Steuerung für PWM:
TIM4Counter -> OCRef4 -> OC4 -> GPIO_AF_TIM4 -> GPIO_PinSource -> 
Physikalischer Pin am Board.

Folgende Reihenfolge für ADC Triggerung:
TIM4Counter -> OCRef4 -> ADC_ExternalTrigConv_T4_CC4 -> 
ADC_ExternalTrigConvEdge_Rising|Falling -> ADC auslösen.

Gibt es einen Trigger der von den ADCs ausgelöst wird, sobald das 
Sampling abgeschlossen ist?
Ich muss gleich nachdem die beiden Spannungen gemessen wurden jede Menge 
berechnungen anstellen.

Edit: gerade was gefunden.
RM0008 s.235 Table 71. ADC interrupts:
---------------------------------------
End of conversion regular group | EOC |
---------------------------------------
Müsste der richtige Interrupt sein.

Mit freundlichen Grüßen
  Andreas

: Bearbeitet durch User
von Alex E. (tecnologic) Benutzerseite


Lesenswert?

ist alles Korrekt was du da schreibst.

Genau, häng dich in den "End of Sequence", bzw. bei nur einer Conversion 
pro ADC, in den "End of Conversion" Interrupt. Das wäre der Standardweg.

von Andreas T. (skycurve)


Lesenswert?

Guten Morgen.

Ich habe  noch Fragen zu dem EOC Interrupt.
Wie in den Posts davor schon geschrieben brauche ich einen Interrupt 
Handler direkt nachdem ADC1 und ADC2 die anliegende Spannung 
fertiggemessen haben.

Mit folgendem Code scheint es zu funktionieren, sprich der 
IRQHandler(void){..} wird aufgerufen. Ich bin mir aber nicht sicher, ob 
der EOC Interrupt wirklich im richtigen Moment aufgerufen wird und weiß 
auch nicht wie ich das nachprüfen könnte :(

Relevante Stellen aus dem Code:
1
void ADC_Configuration(void)
2
{
3
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
4
  ADC_InitTypeDef ADC_InitStructure;
5
6
  /* ADC Common Init */
7
  ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
8
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
9
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; // 2 half-words one by one, 1 then 2
10
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
11
  ADC_CommonInit(&ADC_CommonInitStructure);
12
13
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
14
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 Channel
15
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions Triggered
16
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
17
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;
18
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
19
  ADC_InitStructure.ADC_NbrOfConversion = 1;
20
  ADC_Init(ADC1, &ADC_InitStructure);
21
  ADC_Init(ADC2, &ADC_InitStructure); // Mirror on ADC2
22
23
  /* ADC1 regular channel 11 configuration */
24
  ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 1, ADC_SampleTime_15Cycles); // PB0
25
26
  /* ADC2 regular channel 12 configuration */
27
  ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 1, ADC_SampleTime_15Cycles); // PB1
28
29
  /* Enable DMA request after last transfer (Multi-ADC mode)  */
30
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
31
32
  /* Enable ADC1 */
33
  ADC_Cmd(ADC1, ENABLE);
34
35
  /* Enable ADC2 */
36
  ADC_Cmd(ADC2, ENABLE);
37
38
  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); // für den IRQHandler Interrupt der nach der Spannungsmessung ausgelöst werden soll
39
}
1
void NVIC_Configuration(void)
2
{
3
  NVIC_InitTypeDef  NVIC_InitStructure;
4
  NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQn;
5
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
6
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
7
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
8
  NVIC_Init(&NVIC_InitStructure);
9
}
1
void ADC_IRQHandler(void)
2
{
3
  //while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));    <- braucht man das(Frage1)?
4
  //  wurde doch sowieso schon hier eingestellt:
5
  //  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE); 
6
  //  es kann sich also nur um einen EOC Interrupt handeln?!
7
8
  // Tests für debug:
9
  int a = 1;
10
  a = 2;
11
  lastADC1Value1 = ADCDualConvertedValues[0];
12
a = 3;
13
  lastADC1Value2 = ADCDualConvertedValues[0];
14
  a = 4;
15
16
  //if(ADC_GetITStatus(ADC1, ADC_IT_EOC) != RESET) ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);    <- braucht man das(Frage2)? 
17
18
19
  // Frage3:
20
  // wenn der ADC nicht kontrolliert vom PWM Timer getriggert sein würde,
21
  // sondern im ContinuousConvMode = ENABLE laufen würde, müsste man hier
22
  // irgendetwas setzten damit der Interrupt nicht wieder aufgerufen wird
23
  // bevor der jetzige Aufruf fertig verarbeitet ist?
24
  
25
26
}

Ich habe meine Fragen direkt in void ADC_IRQHandler(void)
geschrieben, damit man weiß welche Stelle gemeint ist.

Was noch eventuell wichtig ist: Messergebnisse vom ADC1 und ADC2 werden 
zusätzlich ‚im Hintergrund‘ vom DMA in ein array[2] übertragen. Das hat 
aber keinen Einfluss auf den EOC ADC_IRQHandler(void) nicht wahr?
1
static void DMA_Configuration(void)
2
{
3
  DMA_InitTypeDef DMA_InitStructure;
4
5
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
6
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCDualConvertedValues[0];
7
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)0x40012308; // CDR_ADDRESS; Packed ADC1, ADC2
8
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
9
  DMA_InitStructure.DMA_BufferSize = 2;//BUFFERSIZE; // Count of 16-bit words
10
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
11
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
12
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
13
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
14
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
15
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
16
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
17
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
18
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
19
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
20
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
21
22
  /* Enable DMA Stream Half / Transfer Complete interrupt */
23
//  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
24
25
  /* DMA2_Stream0 enable */
26
  DMA_Cmd(DMA2_Stream0, ENABLE);
27
}

Habe ich sonst alles richtig gemacht (zusammenkopiert :D )?
- der ADC kann nur von ADC_ExternalTrigConv_T4_CC4 getriggert werden
- Nur dann wird Spannung gemessen
- Nur nachdem die Spannung gemessen wurde, wird ADC_IRQHandler 
aufgerufen, sonst nie


Mit freundlichen Grüßen
  Andreas

von Tecnologic (Gast)


Lesenswert?

Flag clear muss immer sein, (Frage 2) sonst bleibt der proze immer im 
int.

Zum messen toggle ne led das kannste dann aufm scope mit der pwm 
vergleichen.

Gruß

Tec

Ps: das reference manual ist immer n blick wert. Braucht etwas erfahrung 
die infos zu bewerten aber das brauchts für zusammen kopierten code 
auch. Sonst wären deine fragen selbst erklärend. Und hast du dir mal das 
stm32 tutorial vom moritz diller angesehen?

von Andreas T. (skycurve)


Lesenswert?

Nochmal vielen Dank für deine Antworten.

Solange ich nicht
1
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
aufrufe, kann der ADC_IRQHandler(void){..} nicht nochmals aufgerufen 
werden, weil
1
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
festlegt, dass der IRQHandler nur bei ADC_IT_EOC aufgerufen wird. 
(welcher ja erst gecleared werden muss)

Das ist also auch die Absicherung gegen erneutes Aufrufen, wenn der 
aktuelle Durchlauf noch nicht abgeschlossen ist. Was ich mit der dritten 
Frage meinte.

Also sollte man
1
ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
erst am Ende des Rumpfes, nachdem man seine eigenen Berechnungen 
durchgeführt hat, aufrufen.

Das Tutorial diller-technologies habe ich schonmal angesehen, muss aber 
zugeben, dass ich jetzt bei der Interrupt Frage vergessen habe 
reinzuschauen :(

Gruß
Andreas

von Tecnologic (Gast)


Lesenswert?

Das clearen des flags macht man generell am Beginn des ints. Sollte der 
int während der Laufzeit der interrupt Routine noch mal getriggert 
werden bekommt man das ja nicht mit. Und für deinen fall bedeutet das du 
hast ein laufzeit problem. Dein int handler braucht zu lange. Also 
Interrupts kurz halten nur n flag/semaphore setzen und raus da. Das 
handling in einer task/main loop machen. Und immer mit leds synchron zur 
pwm mit dem scope das zeit verhalten nach vollziehen.

Gruß

Tec

Ps: schreibe vom handy also nicht auf satzzeichen oder so achten

von Andreas T. (skycurve)


Lesenswert?

Bei meiner Anwendung muss der Interrupt Handler unbedingt fertig werden, 
denn dort werden aus den Messungen die 3 PWM Dutys für den nächsten PWM 
Schritt berechnet .
Und aus diesem nächsten PWM Schritt heraus wird schon wieder der ADC 
Interrupt aufgerufen -> PWM Werte für den nächsten Schritt... usw.
(3Phasen Motor mit FOC).

Aber normalerweise sollte der STm32 da absolut problemlos fertigwerden.

Aber aus Interesse: wenn der Handler nochmal aufgerufen wird, bevor der 
aktuelle fertig wird, stappeln sich die Aufrufe irgendwo oder werden die 
'überholenden' Aufrufe einfach ignoriert?

Gruß
Andreas

PS: Habe leider noch keinen Scope, deshalb heißts für mich immer: PWM 
Frequenz ganz weiter runterfahren (~10Hz) und es mit einem Arduino und 
kleinen PC Programm oszilloskopieren :D

: Bearbeitet durch User
von Alex E. (tecnologic) Benutzerseite


Lesenswert?

N Scope solltest dir aber leisten. Sonst wird das echt haarig. Ich 
programmiere beruflich Wechselrichter. Von der Sw Architektur her ist es 
ziemlicher bullshit 20us mit 50us periode im int zu sein. Dann musst du 
die ints nesten lassen sonst geht z.b n uart nicht gleichzeitig. Weil 
der int vom uart ja nicht dran kommt. Das musst du dir genau überlegen.

von Aljo E. (alrue)


Lesenswert?

Hallo,
ich betreibe ADC1 und ADC2 im Dual Mode mit DMA. Über TIM2 möchte ich 
die Umwandlung auslösen. TIM2 ist ja eigentlich unabhängig vom ADC und 
soll immer zur gleichen Zeit auslösen, sodass immer nach einer 
bestimmten Zeit die ADCs umwandeln.

Allerdings tritt jetzt bei mir der Effekt auf, dass je größer mein ADC 
buffer ist, desto länger braucht das Programm um wieder im 
DMA2_Stream0_IRQHandler zu landen, was ich daran erkenne, dass dort 
immer eine LED getoggelt wird. Liegt das daran, dass bei größerem Buffer 
die TC_Flag erst später gesetzt wird, weil es länger dauert den DMA zu 
beschreiben und wird aber die Umwandlung trotzdem immer nach der 
gleichen Zeit ausgelöst? Der Effekt ist nämlich sehr stark, also wenn 
ich den Buffer von 8 auf 64 erhöhe, dauert es bestimmt 4-5 sekunden 
länger bis die LED wieder getoggelt wird.

Vielleicht muss ich den Timer auch manuell wieder neu starten? Ich stehe 
momentan etwas auf dem Schlauch und würde mich über konstruktive 
Anregungen freuen!

Hier die wichtigsten Codeausschnitte:
1
void ADC_Configuration(void)
2
{
3
  ADC_CommonInitTypeDef ADC_CommonInitStructure;
4
  ADC_InitTypeDef ADC_InitStructure;
5
6
  ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
7
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
8
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; // 2 half-words one by one, 1 then 2
9
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
10
  ADC_CommonInit(&ADC_CommonInitStructure);
11
12
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
13
  ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 1 Channel
14
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; // Conversions Triggered
15
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
16
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
17
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
18
  ADC_InitStructure.ADC_NbrOfConversion = 1;
19
  ADC_Init(ADC1, &ADC_InitStructure);
20
  ADC_Init(ADC2, &ADC_InitStructure);
21
22
  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_3Cycles);
23
  ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 1, ADC_SampleTime_3Cycles);
24
25
  // Enable DMA request after last transfer (Multi-ADC mode)
26
  ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
27
28
  ADC_Cmd(ADC1, ENABLE);
29
  ADC_Cmd(ADC2, ENABLE);
30
}
31
32
static void DMA_Configuration(void)
33
{
34
  DMA_InitTypeDef DMA_InitStructure;
35
36
  DMA_InitStructure.DMA_Channel = DMA_Channel_0;
37
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCDualConvVal;
38
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) 0x40012308; // CDR_ADDRESS; Packed ADC1, ADC2
39
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
40
  DMA_InitStructure.DMA_BufferSize = BUFFERSIZE;
41
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
42
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
43
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
44
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
45
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
46
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
47
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
48
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
49
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
50
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
51
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
52
53
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
54
55
  DMA_Cmd(DMA2_Stream0, ENABLE);
56
}
57
58
59
void TIM2_Configuration(void)
60
{
61
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
62
63
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
64
  TIM_TimeBaseStructure.TIM_Period = 5000;
65
  TIM_TimeBaseStructure.TIM_Prescaler = 5000;
66
  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV2;
67
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
68
  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
69
  TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
70
71
  TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update); // ADC_ExternalTrigConv_T2_TRGO
72
73
  TIM_Cmd(TIM2, ENABLE);
74
}
75
76
void NVIC_Configuration(void)
77
{
78
  NVIC_InitTypeDef NVIC_InitStructure;
79
80
  // Enable the DMA Stream IRQ Channel
81
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
82
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
83
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
84
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
85
  NVIC_Init(&NVIC_InitStructure);
86
}
87
88
89
void DMA2_Stream0_IRQHandler(void)
90
{
91
  if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))
92
  {
93
    DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
94
95
    GPIO_ToggleBits(GPIOA, GPIO_Pin_5);
96
97
    DMA_TC_Flg = 1;
98
  }
99
}
100
101
int main(void)
102
{
103
  RCC_Configuration();
104
  GPIO_Configuration();
105
  NVIC_Configuration();
106
  TIM2_Configuration();
107
  DMA_Configuration();
108
  ADC_Configuration();
109
110
  ADC_SoftwareStartConv(ADC1);
111
112
  while(1)
113
  {
114
    if(DMA_TC_Flg == 1)
115
    {
116
      DMA_Cmd(DMA2_Stream0, DISABLE);
117
      copyData();
118
      DMA_Cmd(DMA2_Stream0, ENABLE);
119
      sendData();
120
    }
121
  }
122
}

von aSma>> (Gast)


Lesenswert?

Aljo E. schrieb:
> DMA_TC_Flg = 1;

Wann wird der Flag null? Als volatile deklariert?

Aljo E. schrieb:
1
> ADC_SoftwareStartConv(ADC1);
2
> 
3
>   while(1)
4
>   {
5
>     if(DMA_TC_Flg == 1)
6
>     {
7
>       DMA_Cmd(DMA2_Stream0, DISABLE);
8
>       copyData();
9
>       DMA_Cmd(DMA2_Stream0, ENABLE);
10
>       sendData();
11
>     }
12
>   }
13
> }

Was soll das hier werden? Warum startet du die DMA manuell, obwohl du 
einen Timer erstellt hast?!

mfg

Aljo E. schrieb:
> Vielleicht muss ich den Timer auch manuell wieder neu starten? Ich stehe
> momentan etwas auf dem Schlauch und würde mich über konstruktive
> Anregungen freuen!

Am besten solltest du: ADC_SoftwareStartConv(ADC1); in die ISR stecken. 
Denn nur dann startet die DMA_ADC erneut zyklisch mit den Timer 
Interrupt und deine ADC Werte können aus den Buffer ausgewertet werden. 
Es ist egal, ob 64 oder 128 ADC Werte im Buffer gefüllt werden sollten. 
Beim stm32 geht das Ratz Fatz.

von Aljo E. (alrue)


Lesenswert?

Hallo,
also DMA_TC_Flg wird am Ende von copyData() auf 0 gesetzt, sorry, hatte 
ich vergessen zu erwähnen (ist auch als volatile deklariert).

Das ADC_SoftwareStartConv(ADC1) in die ISR zu stecken macht Sinn, sehe 
ich ein. Beim debuggen ist mir allerdings aufegallren, dass  während ich 
die Daten in der Funktion copyData() aus dem DMA auslese, ändern sich 
die Werte im ADCDualConvVal[] Buffer, der ADC sampled also fröhlich 
weiter und schreibt das ins Register. Muss ich während ich die Daten 
auslese nicht also am besten den ADC oder den DMA stoppen?

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.