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 ?
"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
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 ?
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
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.
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
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.
CubeMX löst das alles wunderbar ohne in den Tiefen der Register zu buddeln.
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.
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.
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?
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.
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
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
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 | }
|
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.
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)
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.
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!
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.
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.
Hast du beachtet, die Bits in ADC_SQR1_L sind Kanalanzahl minus 1?
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.