Forum: Mikrocontroller und Digitale Elektronik STM32: Suche Hilfe zu ADC


von Walter T. (nicolas)


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.

: Bearbeitet durch User
von m m (Gast)


Lesenswert?


von Walter T. (nicolas)


Lesenswert?

m m schrieb:
> http://www.diller-technologies.de/stm32.html

Naja, also ein Geheimtipp ist das aber nicht gerade. Freilaufend 
funktioniert der ADC schon lange. Ich suche eine Übersicht zu der Arbeit 
mit den injected channels.

von Walter T. (nicolas)


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.

von Walter T. (nicolas)


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.

von Walter T. (nicolas)


Lesenswert?

In fast allen ADC-Beispielen mit DMA steht das folgende Define:
1
#define ADC1_DR_Address    ((uint32_t)0x4001244C)
Was bedeutet diese Adresse? Man kann sich in der stm32f10x.h 
entlanghangeln zu:

PERIPH_BASE ->
APB2PERIPH_BASE ->
ADC1_BASE + 0x4C

Aber was ist der Offset von 4C?

von Davis (Gast)


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.

von Walter T. (nicolas)


Angehängte Dateien:

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.

von Nils P. (ert)


Lesenswert?

Ist doch auch möglich

11.12.14 ADC regular data register (ADC_DR)
Address offset: 0x4C

RM0008 Seite 247...

ja ist ein wenig verwirrend, aber es geht schon.

Viel Erfolg
Gruß Ert

von Walter T. (nicolas)


Angehängte Dateien:

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.

von Walter T. (nicolas)


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?

: Bearbeitet durch User
von Walter T. (nicolas)


Angehängte Dateien:

Lesenswert?

OK, den DMA1-Clock hatte ich vergessen. Und in keinem Tutorial finde ich 
noch eine extra-Aktivierung der Interrupts. Dummerweise funktioniert der 
IRQ immer noch nicht.

: Bearbeitet durch User
von Walter T. (nicolas)


Angehängte Dateien:

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.

von Walter T. (nicolas)


Angehängte Dateien:

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.

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.