Forum: Mikrocontroller und Digitale Elektronik ADC Interrupt beim stm32f4


von Philipp B. (phill4563)


Lesenswert?

Hallo liebe Community,
ich habe eine Frage bezüglich meines Programms.
Es geht zunächst darum Werte vom ADC über einen Interrupt in eine 
Variable zu schreiben.

Der Eingangskanal PA1 wird von mir extern über einen Potti beschaltet.
In der interrupt routine sollte eigentlich der Wert an "x" übergeben 
werden,  er springt aber nicht in diese Funktion hinein.
Später sollen einmal 2 channels jeweils in 2 variabeln gespeichert 
werden, dass ist momentan aber noch nicht relevant.

Hier mal mein bsp Programm
1
#include "stm32f4xx.h"
2
#include "stm32f4xx_conf.h"
3
 
4
GPIO_InitTypeDef  GPIO_InitStructure;
5
ADC_InitTypeDef ADC_InitStructure;
6
ADC_CommonInitTypeDef ADC_CommonInitStructure;
7
NVIC_InitTypeDef    NVIC_adc;
8
9
uint16_t x;
10
uint16_t y;
11
12
void Delay(__IO uint32_t nCount) {
13
  while(nCount--) {
14
  }
15
}
16
 
17
18
void ADC_IRQHandler()  
19
 {
20
   x= ADC_GetConversionValue(ADC1);
21
   GPIO_ToggleBits (GPIOD, GPIO_Pin_14|GPIO_Pin_13|GPIO_Pin_12);
22
   ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
23
 }
24
 
25
 
26
int main(void) {
27
  /* GPIOD Periph clock enable */
28
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
29
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
30
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
31
 
32
  /* Configure PD12, 13, 14 and PD15 in output pushpull mode */
33
    /* Nur zur Darstellung*/
34
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
35
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
36
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
37
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
38
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
39
  GPIO_Init(GPIOD, &GPIO_InitStructure);
40
  
41
  //  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
42
  //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
43
  //  GPIO_Init(GPIOA,&GPIO_InitStructure);
44
  
45
  
46
 
47
48
 
49
GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_1|GPIO_Pin_2;
50
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
51
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
52
GPIO_Init(GPIOA, &GPIO_InitStructure);
53
54
55
  ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
56
  ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
57
  ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
58
  ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
59
  ADC_CommonInit(&ADC_CommonInitStructure);
60
61
62
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
63
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
64
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
65
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
66
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1  ;
67
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
68
  ADC_InitStructure.ADC_NbrOfConversion = 1;
69
  ADC_Init(ADC1, &ADC_InitStructure);
70
  ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
71
72
  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_144Cycles);
73
 //ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);
74
  ADC_Cmd(ADC1, ENABLE);
75
  ADC_SoftwareStartConv(ADC1);
76
  
77
  
78
  NVIC_adc.NVIC_IRQChannel = ADC_IRQn;
79
  NVIC_adc.NVIC_IRQChannelCmd = ENABLE;
80
  NVIC_adc.NVIC_IRQChannelPreemptionPriority = 0x0F;
81
  NVIC_adc.NVIC_IRQChannelSubPriority = 0x01;
82
  NVIC_Init(&NVIC_adc);
83
  
84
 
85
  while (1) 
86
    {
87
88
    }
89
}
Wobei ich mir absolut nicht sicher bin ist der Befehl:
1
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1  ;
da ich ja eigentlich ein internes Ereignis triggere.
Ich hoffe ihr könnt mich auf meine Fehler hinweisen und bedanke mich 
schon einmal im voraus.
Viele Grüße
Phil

von Nils P. (ert)


Lesenswert?

Ich nehme mal an die von ST meinten damit außerhalb des ADCs...

Ich hatte damals auch Problem mit diesem Interrupt. Habe ein paar Sachen 
ausprobiert. Da ich aber noch einen Timer über hatte habe ich in dessen 
Interrupt die ADC-Umwandlung gestartet.

Nutze den DMA der ist super, dann kannste du dir den ADC-Int komplett 
sparen und hast Zeit für schöne Sachen (zb im delay amzuhängen ;) )

Gruß Ert

von Philipp B. (phill4563)


Lesenswert?

Das mit dem Timer habe ich mir auch schon überlegt, jedoch kann ich so 
ja nur einen channel des adc's auslesen.
Da ich aber später auf mehrere Channel zuggreifen möchte, wird es so 
nicht funktionieren denke ich.
Viele Grüße
Phil

von Nils P. (ert)


Lesenswert?

Dann hast du den ADC falsch verstanden ;)

Einfach hier:
ADC_InitStructure.ADC_NbrOfConversion = x;

und hier:
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, y, 
ADC_SampleTime_144Cycles);

erweitern.

Dann wandelt der ADC alle angegebenen Kanäle mit
ADC_SoftwareStartConv(ADC1);

Oder wenn du nichts regelt willst, du nur dauerhaft Werte benötigst und 
der Samplezeitpunkt nicht so wichtig ist, stell den ADC gleich in 
Continuous Mode ein

Gruß Ert

von Philipp B. (phill4563)


Lesenswert?

hi ;)
also ich habe das bisher so verstanden, dass ich bei
1
ADC_InitStructure.ADC_NbrOfConversion = x;
die Anzahl der Wandlungen pro Zyklus eintragen muss. Also bei 2 Channel 
des ADC1 muss dort eine 2 stehen.

Un mit der Funktion:
1
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, y, 
2
ADC_SampleTime_144Cycles);

stelle ich konkret die Kanäle ein, welche ich benutzen möchte wobei der 
parameter y sagt in welcher reihenfolge diese gewandelt werden.

Deswegen wollte ich die Ergebnisse ja auch über einen interrupt den 
beiden variablen zuweisen, weil ich ja nicht weiss welchen Channel der 
ADC1 gerade gewandelt hat. (über das EOC flag)

würde mich freuen falls mein Denkfehler hier berichtigt werden würde ;)
grüße
phil

von Nils P. (ert)


Lesenswert?

Beide Angaben sind so korrekt.

Da ich immer mit dem DMA arbeite und da bei der richtigen Wahl der Größe 
(n*Channelanzahl) keine Missverständnisse auftreten hatte ich dein 
Problem nicht... Die Injeceted-Funktion ist auch ganz nett, da steht der 
ADC-Wert auch immer im gleichen Register.

Du müsstes eine Zählvariable nutzen welche du beim Timer Int null setzt. 
und beim ADC Int inkrementierst.

Greez Ert

von Pete K. (pete77)


Lesenswert?

Fehlt da nicht irgendwo ein SystemInit(); ?

von Philipp B. (phill4563)


Lesenswert?

Ich habe jetzt noch in einer Schleife folgende Abfrage hinzugefügt:
1
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
2
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
3
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
4
ADC_InitStructure.ADC_NbrOfConversion = 2;
5
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
6
ADC_Init(ADC1, &ADC_InitStructure);
7
8
9
ADC_CommonInitStructure.ADC_Mode =ADC_Mode_Independent;
10
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8  ;
11
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles ;
12
ADC_CommonInit(&ADC_CommonInitStructure);
13
14
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_56Cycles);
15
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_56Cycles);
16
ADC_Cmd(ADC1, ENABLE);
17
ADC_EOCOnEachRegularChannelCmd  ( ADC1,ENABLE ) ;
18
ADC_SoftwareStartConv(ADC1);
19
  
20
 
21
  while (1) {
22
23
    if (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) && k==1)
24
    {
25
  
26
      k=2;
27
      x=ADC_GetConversionValue(ADC1);
28
    }  
29
      if (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC) && k==2)
30
      {
31
      y=ADC_GetConversionValue(ADC1);
32
        
33
      k=1;
34
      
35
      }

normalerweise sollte er doch das EOC Flag nach jeder Wandlung eines 
Channels setzen, so habe ich das zumindest aus den ref manus. 
verstanden.

Weiterhin sollte doch jetzt jeder variable (x,y) der jeweilige Wert 
übergeben werden, dass ist aber nicht der fall. (in beiden steht der 
gleiche wert)

Deine Idee mit den Injeceted Channels habe ich mir auch mal angeschaut. 
Mit denen sollte es eigentlich auch gehen, da in der STD-Lib eine 
Funktion

"uint16_t ADC_GetInjectedConversionValue  ( ADC_TypeDef *  ADCx,
  uint8_t  ADC_InjectedChannel
 )
"
vorhanden ist. Diese gibt es allerdings nicht für die Regular Channel, 
was ich nicht verstehe...
Am besten werde ich es mit 2 Wandlern machen, oder doch die DMA benutzen

Grüße Phil

: Bearbeitet durch User
von Philipp B. (phill4563)


Lesenswert?

Als kurzen Nachtrag:
bei dem obigen Beispiel habe den Code nicht im Interrupt sondern in der 
"while1" ausgeführt

Grüße
Phil

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.