STM32: Suche Hilfe zu ADC

OP #3555733
Lesenswert?

Guten Morgen zusammen,
ich bin gerade auf der Suche nach einer etwas ausführlicheren 
Beschreibung der ADCs des STM32F103, als es das Reference Manual RM0008 
bieten kann. Mein Anwendungsfall ist folgender:

Ich habe eine PWM mit einer Pulslänge von 2-80µs und einem Duty Cycle 
von maximal 30%, d.h. nach jedem Puls ist mindestens 4µs Zeit.

Ich will einen ADC-Kanal während des Pulses so oft wie möglich abtasten. 
In der Pause danach ist ja genügend Zeit, das zu verarbeiten. Weiter 
will ich die Pause nutzen, um noch mehrere andere ADC-Kanäle abzutasten, 
bei denen mir die Abtastrate fast egal ist (OK, 10Hz sollten es schon 
mindestens sein).

Vermutlich brauche ich also zwei Interrupt-Routinen in der PWM, die den 
ADC zwischen einem interleaved-Messen beider ADCs mit DMA für die 
schnellen Kanäle und dem langsamen einzelnen Samplen der langsamen 
Kanäle umschalten.

Leider habe ich noch keine schöne Beschreibung der Nutzung dieser Modi 
gefunden. RM0008 zeigt zwar schön, was geht, aber ich verstehe noch 
nicht, wie man es benutzt.

Viele Grüße
W.T.
OP #3556971
Lesenswert?

Gibt es keine übersichtlichere Dokumentation der ADC-Funktionen als die 
"stm32f10x_stdperiph_lib_um.chm" ? Da muß man nach jedem Parameternamen 
den Quelltext mit dem RM0008 vergleichen oder Googlen, wenn man nicht 
sicher ist, ob er nur das bedeutet, was der Name suggeriert.
OP #3557641
Lesenswert?

OK, wenn hier keine sinnvollen Antworten kommen, dokumentiere ich das, 
was ich finde mal selbst - für den Fall, daß jemand per Google 
hierhingestoßen wird, weil er dasselbe sucht.

Eine gut lesebare Übersicht über die ADC-Modi liefert die Application 
Note AN3116 "STM32's ADC modes and their applications". Damit kennt man 
die Namen, die ST benutzt.

Damit wiederum ist dann in der "stm32f10x_stdperiph_lib_um.chm"-Doku das 
Beispiel "STM32F10x_StdPeriph_Examples/ADC/3ADCs_DMA/main.c" ganz gut 
lesbar.
Gast #3558909
Lesenswert?

> Aber was ist der Offset von 4C?

Abschnitt 11.12.13 im REF-2011

ADC injected data register x (ADC_JDRx) (x= 1..4)

Address offset: 0x3C - 0x48

Bits 31:16 Reserved, must be kept at reset value.
Bits 15:0 JDATA[15:0]: Injected data

These bits are read only. They contain the conversion result from 
injected channel x. The data is left or right-aligned as shown in Figure 
27 and Figure 28.
OP #3558915
Lesenswert?

Danke für die Antwort. Allerdings bin ich jetzt etwas aufgeschmissen. 
Ich war bis jetzt davon ausgegangen, den Regular Mode mit DMA zu nutzen.

Momentan sieht mein Plan (mit noch nicht ganz der Zielfunktionalität) so 
aus, alle Kanäle per DMA in ein Array zu bringen und nach der Wandlung 
des letzten Wertes per Interrupt zur Weiterverarbeitung zu kopieren. So 
habe ich zwar noch nicht die ganze maximale ADC-Rate, aber immerhin 
schon ein Viertel. Sozusagen zum kennenlernen.
Angehängte Dateien:
OP #3559059
Lesenswert?

Nils P. schrieb:
> 0x4C

Stimmt, das hatte ich überlesen. Aus irgendeinem Grund funktioniert das 
Ganze allerdings noch immer nicht; aus dem struct gls_adc werden alle 
Werte als 0 ausgelesen.

Und beim zweiten Aufruf von edm_adc_init() bleibt sie in einer 
Endlosschleife hängen.
Angehängte Dateien:
OP #3559112
Lesenswert?

Ich habe die Interrupt-Routine mal auf einen Pin gelegt: Sie wird nie 
aufgerufen:
1
void ADC1_IRQHandler (void) {
2
  GPIO_SetBits(SPARE1_GPIO,SPARE1_Pin);
3

4
  if(ADC1->SR & ADC_SR_EOC) {
5
   gls_adc.sensep =  (ADC1ConvertedValues[0]
6
                    + ADC1ConvertedValues[2] \
7
                    + ADC1ConvertedValues[4] \
8
                    + ADC1ConvertedValues[6] \
9
                    + ADC1ConvertedValues[8] \
10
                    + 3*gls_adc.sensep)/8;
11

12
   gls_adc.sensem =  (ADC1ConvertedValues[1] + 3*gls_adc.sensep)/4;
13

14
   gls_adc.updwn = (uint8_t) (ADC1ConvertedValues[3] >> 4);
15
   gls_adc.iSet = (uint8_t)  (ADC1ConvertedValues[5] >> 4);
16
   gls_adc.tOff = (uint8_t)  (ADC1ConvertedValues[7] >> 4);
17
   gls_adc.tOn = (uint8_t)   (ADC1ConvertedValues[9] >> 4);
18

19

20
   }
21
  GPIO_ResetBits(SPARE1_GPIO,SPARE1_Pin);
22
 }
Müssen Interrupts vielleicht noch irgendwo global eingeschaltet werden?
Und fängt die Liste der ADC-RegularChannel wirklich mit 1 an?
OP #3561511
Lesenswert?

Die ADC-Wandlung läuft und der IRQ für die fertige Wandlung wird mit der 
richtigen Taktfrequenz aufgerufen.

Vorher hatte ich einige Fehler gemacht: Es wird nicht der ADC-Interrupt 
sondern der DMA-Interrupt gebraucht.

Durch die Display-Darstellung sieht man, daß das Array 
"ADC1ConvertedValues[10]" mit plausiblen Werten gefüllt wird. Allerdings 
ergeben sich beim Kopieren in das struct "gls_adc" im 
DMA1_Channel1_IRQHandler total inplausible Werte - so als hätte das 
Array zum Zeitpunkt des IRQs komplett andere Werte, als wenn es in der 
Hauptschleife aufgerufen wird. Wird im IRQHandler der Wert hart gesetzt 
(z.B. gls_adc.tOn = 3;) stimmt alles.

"gls_adc" ist wie folgt deklariert und definiert:
1
  typedef struct {
2
    uint16_t sensep;
3
    uint16_t sensem;
4
    uint8_t iSet;
5
    uint8_t tOn;
6
    uint8_t tOff;
7
    uint8_t updwn;
8
  } gls_adc_t;
9

10
  volatile gls_adc_t gls_adc;
11

12
  ...
13
// In *.c-Datei:
14
  volatile gls_adc_t gls_adc = {0xAAAA,0xAAAA,0xAA,0xAA,0xAA,0xAA};

Gibt es die Möglichkeit, in einen IRQ zu debuggen?

Viele Grüße
W.T.
Angehängte Dateien:
OP #3561645
Lesenswert?

Es ist wie verhext. Der IRQ-Handler scheint sehr empfindlich gegenüber 
Veränderungen zu sein: Werden zwei zusätzliche Variablen "gl_a" und 
"gl_b" einkommentiert, hängt sich das Programm auf. Der Inhalt des 
globalen Structs "gls_adc" scheint nichts mit dem Inhalt von 
ADC1ConvertedValues zu tun zu haben (siehe obiges Bild);
1
extern volatile uint16_t gl_a;
2
extern volatile uint16_t gl_b;
3
void DMA1_Channel1_IRQHandler(void) {
4
  gls_adc.sensep =  (ADC1ConvertedValues[0]
5
                     + ADC1ConvertedValues[2] \
6
                     + ADC1ConvertedValues[4] \
7
                     + ADC1ConvertedValues[6] \
8
                     + ADC1ConvertedValues[8] \
9
                     + 3*gls_adc.sensep)/8;
10

11
  gls_adc.sensem =  (ADC1ConvertedValues[1] + 3*gls_adc.sensem)/4;
12

13
  gls_adc.updwn =  (ADC1ConvertedValues[3] >> 4 );
14
  gls_adc.iSet  = (uint8_t) (ADC1ConvertedValues[5] >> 4);
15
  gls_adc.tOff  = (uint8_t) (ADC1ConvertedValues[7] >> 4);
16
  gls_adc.tOn   = (uint8_t) (ADC1ConvertedValues[9] >> 4);
17

18
  //gl_a = ADC1ConvertedValues[3];
19
  //gl_b = ADC1ConvertedValues[5];
20

21
  DMA_ClearFlag(DMA1_FLAG_TC1);
22
}
Definiert und deklariert sind die globalen Variablen hier:
1
volatile uint16_t gl_a;
2
volatile uint16_t gl_b;
3
/* Test der ADC-Routinen (Multiplex und Interrupt-Aufruf)                     */
4
void edm_test_adc(void) {
5
  printf_P(PSTR("\fTest EDM-ADC \n"
6
  "Beenden durch Knopfdruck\a"));
7
  
8
  edm_adc_init();
9
  printf_P(PSTR("Init OK\n\a"));
10
  
11
  edm_adc_start();
12
  printf_P(PSTR("Start OK\n\a"));
13

14
  while(!key_getstroke()) {
15
    //togglespare2();
16

17
    printf_P(PSTR("\fTest EDM-ADC \n"\
18
    "Beenden mit Taster\n"\
19
      "Sen+=%5u\t"\
20
      "Sen-=%5u\t"\
21
      "iSet=%5u\t"\
22
      "tOn =%5u\t"\
23
      "tOff=%5u\t"\
24
      "updo=%5u\n"),\
25
          gls_adc.sensep,gls_adc.sensem,gls_adc.iSet,gls_adc.tOn,gls_adc.tOff,\
26
    gls_adc.updwn);
27
    printf_P(PSTR("Roh:"\
28
        "%4u,%4u,%4u,%4u,%4u,%4u,%4u,%4u,%4u,%4u"),\
29
          ADC1ConvertedValues[0],ADC1ConvertedValues[1],
30
          ADC1ConvertedValues[2],ADC1ConvertedValues[3],
31
          ADC1ConvertedValues[4],ADC1ConvertedValues[5],
32
          ADC1ConvertedValues[6],ADC1ConvertedValues[7],
33
          ADC1ConvertedValues[8],ADC1ConvertedValues[9]);
34

35
    printf_P(PSTR("|%4u,%u|"),gl_a,gl_b);
36
    printf_P(PSTR("\n\a"));
37
  }
38
}
Warum steht in "gl_a" 19? Nirgendwo wird hineingeschrieben, die Variable 
kommt in keiner anderen Datei vor und globale Variablen werden implizit 
mit 0 initialisiert. Es sieht aus, als wären in den IRQ-Handlern Reste 
vorheriger Builds - oder als ob Zeiger marodierten.
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren