Forum: Mikrocontroller und Digitale Elektronik STM32F103 ADC und DMA Verständnisfrage


von alex (Gast)


Lesenswert?

Hallo,

ich werde aus den ganzen Datenblätter nicht schlau:

habe ein grundsätzliches Verständnisproblem beim STM32F103 wenn ich den 
ADC per DMA im circular mode auslesen möchte.

Es geht um das "End Of Conversion Interrupt Enable Bit" im CR1 und das 
"DMA" bit im CR2 (Control register 2)

wenn ich mit DMA arbeiten muss, warum dieses Interrupt Enable Bit ?!?

ich dachte, dass der DMA eigenständig läuft ? klar muss man diesen 
konfigurieren, aber warum löst der ADC einen interrupt aus um den DMA zu 
rufen dass eine Conversion fertig ist und er diese bitte abholen 
kann/soll ?

Es gibt beim ADC doch auch das EOC flag bit was ja auch sagt, dass eine 
Konversion fertig ist. Warum kann dieses EOC Flag nicht den DMA rufen ?

Ein DMA request ist gleich einem Interrupt ?


Und welche Bedeutung hat das DMA Bit im Control Register 2 des ADC 
Wandlers ?

von Carl D. (jcw2)


Lesenswert?

"Circular Mode" bedeutet erst mal nur, daß die DMA am Ende angekommen, 
einfach wieder von vorne beginnt. Zusätzlich feuert die DMA auch noch 
einen "transmition complete"-Int.
Getakted wird die DMA vom ADC, das wird über das "DMA-Bit" freigegeben. 
ADC-Ints braucht es dazu nicht, denn dann bräuchte man ja kein DMA.

Kennst du AN2558?

http://read.pudn.com/downloads106/sourcecode/embed/437624/stm32/STM32F%20Documents/Application%20Note/STM32F10xxx%20ADC%20application%20examples.pdf

: Bearbeitet durch User
von alex (Gast)


Lesenswert?

ok jetzt muss ich doch konkreter werden:

ich habe 3 Channels, welche jeweils in ein bestimmtes Array-Field 
geschrieben werden sollen:

Ch1 in Field 1
Ch2 in Field 2
Ch3 in Field 3

und wieder von vorne. Es dürfen keine Umwandlungen verloren gehen sonst 
kommt die Reihenfolge durcheinander. Heisst: jede einzelne Conversion 
des ADC muss per DMA in das richtige Array Field geschrieben werden.

"Getakted wird die DMA vom ADC, das wird über das "DMA-Bit" 
freigegeben."

der ADC läuft doch auf einem anderen Takt als der AHB Bus über den der 
DMA läuft ?! ADC hat max. 14 MHZ der AHB Bus 72 MHz oder habe ich Dich 
da jetzt falsch verstanden ?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Der ADC teilt dem DMA Controller mit, wann er fertig ist und DMA holt 
das Datum ab und speichert es im Feld ab. 'Taktung' ist nicht das 
passende Wort - Request wäre treffender.

Interrupts sind dafür nicht nötig. Die DMA Buffersize muss aber passen. 
Wenn du 3 Kanäle abfragst, sollte Buffersize also auch 3 sein, sonst 
gibt Kuddelmuddel.

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

alex schrieb:
> ok jetzt muss ich doch konkreter werden:
>
> ich habe 3 Channels, welche jeweils in ein bestimmtes Array-Field
> geschrieben werden sollen:
>
> Ch1 in Field 1
> Ch2 in Field 2
> Ch3 in Field 3
>
> und wieder von vorne. Es dürfen keine Umwandlungen verloren gehen sonst
> kommt die Reihenfolge durcheinander. Heisst: jede einzelne Conversion
> des ADC muss per DMA in das richtige Array Field geschrieben werden.
>
> "Getakted wird die DMA vom ADC, das wird über das "DMA-Bit"
> freigegeben."

Ja, etwas lässig beschrieben.
Jedes Ende einer Wandlung stößt einen DMA-Transfer an. Verloren gehen 
wird da nichts, da sind ja keine eventuell gesperrten Interrupts im 
Spiel.
Und das Ende eines Durchlaufs wird von der DMA signalisiert. Welche 
Kanäle in welcher Reihenfolge gewandelt werden, muß im ADC eingestellt 
werden.

Mit letzterem hab ich bisher keine Erfahrung, aber per DMA hab ich über 
Timer/PWM 2x4 Kanäle on-the-Fly erzeugten Sound ausgegeben. Da hat sich 
die DMA nie vertan.

> der ADC läuft doch auf einem anderen Takt als der AHB Bus über den der
> DMA läuft ?! ADC hat max. 14 MHZ der AHB Bus 72 MHz oder habe ich Dich
> da jetzt falsch verstanden ?

Mit "getaktet" war das Auslösen des DMA-Transfers gemeint, nicht der 
Bus-Takt.


PS: ich habe meine HW-Konfigurationsfragen via Debugger gelöst. 
Eclipse/GnuMcu/OpenODC/ST/Link-Clone.
Dazu svd-Datei des Chips, dann kann man online in der Peripherie 
stochern.

von alex (Gast)


Lesenswert?

Matthias S. schrieb:
> Der ADC teilt dem DMA Controller mit, wann er fertig ist und DMA holt
> das Datum ab und speichert es im Feld ab. 'Taktung' ist nicht das
> passende Wort - Request wäre treffender.

ok nur wie teilt der ADC dem DMA das mit ? auf welcher "Leitung" oder 
mit welchem Signal ?

reicht es das DMA Bit im CR2 des ADC auf 1 zu setzen ? das EOC flag im 
Statusregister des ADCs brauche ich dann so nicht

von alex (Gast)


Lesenswert?

Carl D. schrieb:
> Jedes Ende einer Wandlung stößt einen DMA-Transfer an. Verloren gehen
> wird da nichts, da sind ja keine eventuell gesperrten Interrupts im
> Spiel.

dieses Anstoßen eines DMA Transfers macht der ADC wenn ich also in 
dessen CR2 das DMA Bit auf 1 stelle, richtig ?

das EOCIE (End of conversion interrupt enable) Bit brauche ich dann 
nicht.

Vielen Dank auch Dir Carl für Deine Hilfe.

von NichtWichtig (Gast)


Lesenswert?

CubeMX löst das alles wunderbar ohne in den Tiefen der Register zu 
buddeln.

von W.S. (Gast)


Lesenswert?

alex schrieb:
> ich habe 3 Channels, welche jeweils in ein bestimmtes Array-Field
> geschrieben werden sollen:
>
> Ch1 in Field 1
> Ch2 in Field 2
> Ch3 in Field 3
>
> und wieder von vorne.

Ähemm..

Und das einfach so? Ohne daß irgend ein Programmstück mit diesen Werten 
irgendwas anfängt?

W.S.

von alex (Gast)


Lesenswert?

NichtWichtig schrieb:
> CubeMX löst das alles wunderbar ohne in den Tiefen der Register zu
> buddeln.

ich möchte aber in den Tiefen der Register "buddeln" :) weil ich es 
richtig lernen und verstehen möchte. Zudem möchte ich nicht mit HAL 
arbeiten.

von alex (Gast)


Lesenswert?

W.S. schrieb:
> Und das einfach so? Ohne daß irgend ein Programmstück mit diesen Werten
> irgendwas anfängt?

doch, sicherlich fange ich mit den Werten was an, aber darum geht es mir 
hier nicht. Die Fragestellung war ja wie der ADC dem DMA das mitteilt, 
dass er fertige Daten "zur Abholung" für den DMA hat.

Reicht das also wenn im ADC Control Register das DMA Bit auf "1" gesetzt 
wird so dass der ADC den DMA jedesmal nach einer Conversion einen 
Request schickt?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

alex schrieb:
> Reicht das also wenn im ADC Control Register das DMA Bit auf "1" gesetzt
> wird so dass der ADC den DMA jedesmal nach einer Conversion einen
> Request schickt?

Du musst auch den ADC einmal starten. Ich kann dir gerne mal ein SPL 
Beispiel für 4 Kanäle posten.
NbrOfChannels des ADC und Buffersize des DMA sollten gleich gross sein. 
DMA auf HalfWords einstellen und die MemoryAdresse auf deine struct mit 
den ADC Ergebnissen und Increment stellen. PeripheralAdress auf &ADC->DR 
zeigen lassen - ohne Increment.

von alex (Gast)


Lesenswert?

Matthias S. schrieb:
> Du musst auch den ADC einmal starten.

Hallo Matthias, wie meinst Du das "den ADC einmal starten" ? Ich 
konfiguriere den ADC und den DMA und dann starte ich die Conversion 
indem ich den ADC starte (nach Kalibrierung). Dann sollte alles 
automatisch gehen. Die Adressen sollten (hoffe ich jetzt einmal) kein 
Problem darstellen, soweit sollte es mir klar sein. Mir geht es um die 
Sache des DMA Request durch den ADC also wie dieser Request stattfindet. 
Wahrscheinlich über das DMA Bit im CR2 des ADC weiss dieser also, dass 
er mit dem DMA gemeinsame Sache machen soll

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

alex schrieb:
> Hallo Matthias, wie meinst Du das "den ADC einmal starten" ? Ich
> konfiguriere den ADC und den DMA und dann starte ich die Conversion
> indem ich den ADC starte (nach Kalibrierung).

Ja, genau so.

alex schrieb:
> Dann sollte alles
> automatisch gehen.

Tut es.

alex schrieb:
> Mir geht es um die
> Sache des DMA Request durch den ADC also wie dieser Request stattfindet.

Muss dich gar nicht interessieren, da ADC und DMA das unter sich 
aushandeln, wenns richtig konfiguriert ist - und das macht das DMA Bit 
im ADC->CR2 Register.

W.S. schrieb:
> Und das einfach so? Ohne daß irgend ein Programmstück mit diesen Werten
> irgendwas anfängt?

Das kann uns wurscht sein. Wichtig ist nur, das die Felder für die ADC 
Ergebnisse volatile deklariert sind, denn die werden ja nur vom DMA 
geändert.

: Bearbeitet durch User
von Christian K. (the_kirsch)


Lesenswert?

Das Datenblatt Kapitel zum ADC ist da nicht ganz klar formuliert. Ich 
hatte damals auch ein bisschen Probleme bis es funktionierte.

Will man mehrere Wandlungen Hintereinander machen (ADC_SQR1_L > 1) geht 
das nur mithilfe das DMA.

Will man das der ADC am Ende wieder mit dem ersten Kanal automatisch von 
vorne startet, muss das ADC_CR1_SCAN Bit gesetzt sein.

Hier ein paar Funktion Schnipsel aus meinen Projekten (Nicht 
vollständig, soll nur als Denkansatz dienen, Headers, Typdefs und Enums 
fehlen)

Es werden ADC0, ADC1 und ADC2 Zyklisch vom ADC gewandelt und mittels DMA 
in das Array geschrieben.

Alles Interrupts.

DMA:
1
#define MEM2MEM(value) ((value << DMA_CCR_MEM2MEM_Pos) & DMA_CCR_MEM2MEM_Msk)
2
#define PL(value)      ((value << DMA_CCR_PL_Pos     ) & DMA_CCR_PL_Msk     )
3
#define MSIZE(value)   ((value << DMA_CCR_MSIZE_Pos  ) & DMA_CCR_MSIZE_Msk  )
4
#define PSIZE(value)   ((value << DMA_CCR_PSIZE_Pos  ) & DMA_CCR_PSIZE_Msk  )
5
#define MINC(value)    ((value << DMA_CCR_MINC_Pos   ) & DMA_CCR_MINC_Msk   )
6
#define PINC(value)    ((value << DMA_CCR_PINC_Pos   ) & DMA_CCR_PINC_Msk   )
7
#define CIRC(value)    ((value << DMA_CCR_CIRC_Pos   ) & DMA_CCR_CIRC_Msk   )
8
#define DIR(value)     ((value << DMA_CCR_DIR_Pos    ) & DMA_CCR_DIR_Msk    )
9
#define HTIE(value)    ((value << DMA_CCR_HTIE_Pos   ) & DMA_CCR_HTIE_Msk   )
10
#define TEIE(value)    ((value << DMA_CCR_TEIE_Pos   ) & DMA_CCR_TEIE_Msk   )
11
#define TCIE(value)    ((value << DMA_CCR_TCIE_Pos   ) & DMA_CCR_TCIE_Msk   )
12
#define EN(value)      ((value << DMA_CCR_EN_Pos     ) & DMA_CCR_EN_Msk     )
13
14
void STM32F1_DMA_init(void) {
15
  SET_BIT(RCC->AHBENR, RCC_AHBENR_DMA1EN);
16
}
17
18
void STM32F1_DMA1_configChannel1ForADC1(uint8_fast count, uint16 *memory) {
19
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_EN);
20
21
  DMA1_Channel1->CPAR = (uint32) &(ADC1->DR);
22
23
  DMA1_Channel1->CMAR = (uint32) memory;
24
25
  DMA1_Channel1->CNDTR = count;
26
27
  DMA1_Channel1->CCR = MEM2MEM(0) | PL(DMA_CCR_PL_LOW) | MSIZE(DMA_CCR_SIZE_16) | PSIZE(DMA_CCR_SIZE_16)| MINC(1) | MINC(0) | CIRC(1) | DIR(0) | TEIE(0) | HTIE(0) | TCIE(0) | EN(0);
28
}
29
30
void STM32F1_DMA1_startChannel1(void) {
31
  CLEAR_BIT(DMA1_Channel1->CCR, DMA_CCR_EN);
32
  SET_BIT(DMA1_Channel1->CCR, DMA_CCR_EN);
33
}

ADC:
1
#define REGULAR_GROUP_COUNT 3
2
static volatile uint16 regularGroup1[REGULAR_GROUP_COUNT] = { 0 };
3
4
void STM32F1_ADC_init(void) {
5
  // Enable clock for ADC
6
  SET_BIT(RCC->APB2ENR, RCC_APB2ENR_ADC1EN);
7
8
  // Enable clock for GPIO A
9
  SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);
10
11
  STM32F1_GPIO_configInput(GPIOA, 0, STM32F1_ANALOG);
12
  STM32F1_GPIO_configInput(GPIOA, 1, STM32F1_ANALOG);
13
  STM32F1_GPIO_configInput(GPIOA, 2, STM32F1_ANALOG);
14
15
  // Divide APB2 clock frequency by 6: 72 MHz / 6 = 12 MHz
16
  MODIFY_REG(RCC->CFGR, RCC_CFGR_ADCPRE_Msk, RCC_CFGR_ADCPRE_6 << RCC_CFGR_ADCPRE_Pos);
17
18
  // Switch the ADC on
19
  SET_BIT(ADC1->CR2, ADC_CR2_ADON);
20
21
  // Wait 2 ADC-Clock cycles before starting calibration: (72MHz / 12MHz) * 2 = 12 => 12 Nops
22
  __NOP();
23
  __NOP();
24
  __NOP();
25
  __NOP();
26
  __NOP();
27
  __NOP();
28
  __NOP();
29
  __NOP();
30
  __NOP();
31
  __NOP();
32
  __NOP();
33
  __NOP();
34
35
  // Start calibration
36
  SET_BIT(ADC1->CR2, ADC_CR2_CAL);
37
38
  // Wait until the calibration is finished
39
  while (READ_BIT(ADC1->CR2, ADC_CR2_CAL))
40
    ;
41
42
  setSQR(ADC1, REGULAR_GROUP_COUNT - 1, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
43
  setSMPR(ADC1, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5,
44
      ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5,
45
      ADC_SMPR_SMP_239_5, ADC_SMPR_SMP_239_5);
46
47
  SET_BIT(ADC1->CR1, ADC_CR1_SCAN);
48
49
  SET_BIT(ADC1->CR2, ADC_CR2_CONT);
50
51
  SET_BIT(ADC1->CR2, ADC_CR2_DMA);
52
53
  STM32F1_DMA1_configChannel1ForADC1(REGULAR_GROUP_COUNT, (uint16*) regularGroup1);
54
  STM32F1_DMA1_startChannel1();
55
56
  // Start conversion
57
  SET_BIT(ADC1->CR2, ADC_CR2_ADON);
58
}
59
60
uint16 STM32F1_ADC_getRegularGroupe1(uint8_fast channel) {
61
  if (channel >= REGULAR_GROUP_COUNT) {
62
    return 0xFFFF;
63
  }
64
  return regularGroup1[channel];
65
}

von alex (Gast)


Lesenswert?

Matthias S. schrieb:
> Muss dich gar nicht interessieren, da ADC und DMA das unter sich
> aushandeln, wenns richtig konfiguriert ist - und das macht das DMA Bit
> im ADC->CR2 Register.

Matthias !! Vielen lieben Dank, Du hast mir echt viel geholfen !! Ich 
werde das am Wochenende mal umsetzen.

von alex (Gast)


Lesenswert?

Christian K. schrieb:
> Will man das der ADC am Ende wieder mit dem ersten Kanal automatisch von
> vorne startet, muss das ADC_CR1_SCAN Bit gesetzt sein.

ich hatte das bisher immer so verstanden, dass das SCAN Bit alle im SQR 
Register (nach Sequenzreihenfolge) eingetragenen Channels von Channel x 
bis Channel y "durchscannt", mehr nicht

will man den Scan-Vorgang im Loop muss man das Cont-Bit im ADC CR setzen 
(Continuous)

von Christian K. (the_kirsch)


Lesenswert?

Du hast natürlich recht.

ADC_CR2_CONT Bit ist dafür da, das automatisch wieder von vorne 
gestartet wird.

ADC_CR1_SCAN mit der Kombination ADC_SQR1_L größer null geht nur mit 
DMA.

von alex (Gast)


Lesenswert?

Christian K. schrieb:
> Du hast natürlich recht.
>
> ADC_CR2_CONT Bit ist dafür da, das automatisch wieder von vorne
> gestartet wird.
>
> ADC_CR1_SCAN mit der Kombination ADC_SQR1_L größer null geht nur mit
> DMA.

ok dann bin ich beruhigt ! Achja: Vielen Dank für Deinen Code!

von alex (Gast)


Lesenswert?

jetzt habe ich aber ein anderes Problem:
ich arbeite mit CooCox und einem Nucleo 64 Board mit STM32F103RBT6.

Ich möchte 3 Kanäle per ADC scannen.

ADC_IN0
ADC_IN1
ADC_IN4

Die Sequenz Reihenfolge gebe ich im SQR Register 3 mit :

Erstens: Ch0
Zweitens: Ch1
Drittens: Ch4

und wieder von vorne (Circular continuous). Da ich im SCAN Mode arbeite,
verwende ich auch den DMA Controller.

Jeder der drei Channel Ch0, Ch1 und Ch4 sollen einen festen Platz in 
einem Array RGB[3] bekommen. Ch0 also RGB[0], Ch1 in RGB[1] und Ch4 in 
RGB[2].

Das sage ich dem DMA Controller:

independent mode enable
memory increment enable
circular mode enable
direction bit > lesen von Peripherie
adresse vom ADC Datenregister (uint32_t)&ADC1->DR;
adresse vom Array: (uint32_t)RGB;
Menge an Daten: 3 (dann wieder von vorne)

da ich keine andere Möglichkeit habe zu sehen was denn nun wirklich 
passiert lasse ich immer den Debugger laufen und beoachte dort 
schrittweise per Mausklick das RGB Array und habe dort ständig 
springende Werte drin. Drehe ich eines der Potis an den ADC Eingängen, 
wandert dieser Wert fleissig durch die Array Fields.

Was mache ich falsch ? Ist das Debugging hier vllt völlig nutzlos wenn 
man mit ADC und DMA arbeitet ?

Beitrag #6180212 wurde vom Autor gelöscht.
von Stefan F. (Gast)


Lesenswert?

alex schrieb:
> Was mache ich falsch ?

Es klingt so, als ob dein DMA Puffer eine andere Größe hat, als die 
Anzahl der eingestellten ADC Kanäle. Eventuell stimmt die Größe der 
Werte (16/32bit) nicht.

von Christian K. (the_kirsch)


Lesenswert?

Hast du beachtet, die Bits in ADC_SQR1_L sind Kanalanzahl minus 1?

von alex (Gast)


Lesenswert?

Christian K. schrieb:
> Hast du beachtet, die Bits in ADC_SQR1_L sind Kanalanzahl minus 1?

Ich glaub ich spinne.....
Du bist der Beste !!! Ja ich hab zwar die 2 da reingeschrieben weil ich 
3 Conversions machen möchte, aber gerade beim Nachsehen folgendes 
feststellen müssen:

ADC1->SQR1 |= ~(2<<20);


Dir/Euch fällt jetzt bestimmt was auf ?

Das "~" ist noch von einem bits resetten vergessen worden weil ich immer 
wieder am Code rumgelöscht und umgeschrieben habe...

Ich werde das morgen ausprobieren und Dir / Euch auf jeden Fall Bescheid 
geben. Für heute ist mein Kopf nicht mehr aufnahmefähig :D

von alex (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> alex schrieb:
>> Was mache ich falsch ?
>
> Es klingt so, als ob dein DMA Puffer eine andere Größe hat, als die
> Anzahl der eingestellten ADC Kanäle. Eventuell stimmt die Größe der
> Werte (16/32bit) nicht.

auch Du hattest Recht !!! Vielen Dank ich bin gerade sowas von 
erleichtert (siehe meine vorletzte Antwort)

Es ist auch echt heftig wie schnell ihr euch da reindenken konntet und 
mich auf die richtige Fährte gebracht habt... vor allem es war von mir 
nur ein Leichtsinnsfehler (Copy paste machen aber das "~" nicht 
weglöschen) nur weil zu faul zum schreiben war.

Ich liebe dieses Forum!

ich geb Euch morgen Bescheid

von alex (Gast)


Lesenswert?

Es funktioniert !!! Die Werte in den Array fields wandern nun nicht 
mehr.
Es war nur ein copy+paste Fehler der mir da unterlaufen ist.

Vielen Dank für Eure Hilfe.

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.