Hallo zusammen,
ich arbeite gerade mit einem STM32F4 und hänge an der Stelle wo ADC1 mit
Timer 3 getriggert (Conversion-start) werden soll.
Was funktioniert bereits? (Testweise)
TIM3_Interrupt wird im gewünschten Intervall aufgerufen, in welchem
testweise ADC_SoftwareStartConv() gestartet und der ADC-Wert im ADC_IRQ
korrekt ausgegeben wurde.
Was ich nicht zustande bringe ist es den ADC direkt bei Timerüberlauf
(Update) zu starten (anstatt im TIM3_IRQ)
Dies wollte ich mit
"ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO"
machen. (Ich nehme an TRGO steht für Trigger Output).
Hierfür habe ich beim Timer
TIM_SelectOutputTrigger(TIM3,TIM_TRGOSource_Update);
den Updateevent gesetzt.
Leider wird der ADC nie gestartet.
Evtl. hat ja jemand einen Hinweis für mich.
Vielen Dank im voraus,
Michael
Hier mein Code:
Hallo Michael.
Hier ist mein Code der bei mir funktioniert. Ich verwende den Timer aber
im PWM-Mode, der ADC wird durch ein Capture-Compare Event getriggert und
die Werte mittels DMA ausgelesen, ich hab dir nur die wichtigen
Funktionen rausgesucht ->
Hallo Christoph,
vielen Dank für das posten deines Codes, und sorry für die verspätete
Antwort.
Ich verwende die Lib für den F4/F4, welche leicht von der F1 Lib
abweicht.
Schlussendlich habe ich das ganze dann doch noch in meiner Version zum
laufen bekommen.
Dachte zuerst dass es an ADC_ExternalTrigConvEdge lag, weil dieses auf
"none" gesetzt war, das war es dann aber doch nicht.
Ich glaube dass irgendetwas bei der Initialisierungsreihenfolge nicht
gestimmt hat, zumindest habe ich jetzt bei der funktionierenden Variante
keine Parameter/Funktionen geändert, hinzugefügt.
Ein ähnliches Problem hatte ich bei der UART auch schon mal.
Nochmals vielen Dank,
Mike
Hallo,
Obwohl schon lange her, möchte ich das Thema nochmal aufgreifen. Ich
stehe vor dem gleichen Problem. Ich habe einen STM32F4 Discovery. Ich
möchte 1000 Werte des ADC über DMA ins RAM speichern. Und das mit einer
Abtastrate von 1MHz. Dazu triggere ich den ADC auf das Timer event
„TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);“. Jedoch werden
die Daten nicht mit der gewünschten Frequenz in den DMA geschaufelt. Und
zwar mit etwa 1.6MHz. Dies ist auch unabhängig ob ich 1MHz oder 10kHz
einstelle.
Die oberen Beiträge, Beiträge in diesem Forum und Google hat mir leider
nicht weitergeholen. Und in der Reference Manual habe ich bis jetzt auch
nicht der richtige Hinweis gefunden.
Hier mein Versuch:
Hab jetzt nicht alles durchgesehen, aber einen verdacht..
Du hast den Timer Interrupt aktiviert. Deaktiviere diesen.
Bzw. stelle sicher dass du im Interrupt das Flag löscht.
void ADC_IRQHandler(void) {
if(ADC_GetITStatus(ADC3,ADC_IT_EOC) != RESET) {
ADC_ClearITPendingBit(ADC3, ADC_IT_EOC);
}
}
Lg,
Mike
Hallo,
danke für die schnelle Antwort.
Ich habs gelöst bekommen. Am Interrupt liegts wohl nicht, wede ich
jedoch bei gelegenheit auch nochmal untersuchen.
In der ADC_init musste ich
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
DISABLEN.
Ich hoffe, dass nicht nochmal sowas auftaucht. Vielen Dank nochmal für
die schnelle Antwort.
Gruß Michael
Hallo,
ich habe eine Verständnisfrage.
Es wird ein STM32F4 Board verwendet.
Ich will während einer center-aligned PWM genau in der Mitte des
Stromimpulses gleichzeitig zwei Spannungen messen. Damit die Messungen
gleichzeitig geschehen, verwende ich 2 verschiede ADCs. Die zwei ADCs
sollen mit dem Timer4 ausgelöst werden.
Hier mein Code:
Kann ich nun sicher sein, dass die Spannungen wirklich gleichzeitig
gemessen werden?
Ich habe mir auch den Dual regular simultaneous mode angeschaut, bei dem
ADC1 den ADC2 auslöst, jedoch ich finde es schöner wenn die 2 ADCs
direkt von dem Timer(4) getriggert werden.
MfG
Andreas
Guten Morgen,
vielen Dank für den Tipp. Gibt man mit ADC_ExternalTrigConvEdge an, ob
der ADC bei steigender oder bei fallender Flanke ausgelöst wird?
Wenn ich den ADC in der Mitte des Duty Cycles einer center aligned PWM
auslösen möchte, müsste ich die Flanke nicht Falling setzen?
MfG
Andreas
Moin,
Du triggerst ja auf CC4 des Tim4. Wenn du jetzt genau oben Triggern
willst setzt du CCR4 des Tim 4 auf ARR-1 also kurz vor den Overflow,
laut RM kannst du sogar auf CCR4 = ARR gehen dann sitzt der Trigger
genau oben, ich würde dem aber nicht trauen. Wenn du Sauber in der Mitte
der PWM triggern willst musst du sowieso die Samplezeit des ADCs mit
beachten, also muss das Samplen eh etwas vorher getriggert werden damit
er mit der Samplezeit genau mittig über dem Overflow des Timers liegt.
Dann kannst du die PWMs auch maximal auf fahren und hast n Max Duty von
ARR - ADCSampletime/2 - Sicherheit.
Die Sicherheit würde ich jenach nach Schaltflanken immer etwas
großzügiger wählen. Musst ausprobieren.
Zu der Flanke: du musst auf rising triggern wenn du im Overflow triggern
willst. Willst du bei aktiver low side triggern dann muss CCR4 sehr
klein sein und du triggerst auf Falling. Weil hier ja das OCRef Signal
fast die ganze Zeit high ist und nur fällt wenn der Counter unter CCR4
geht. und das ist kurz vor dem Underflow.
Gruß
Tec
Also muss ich davon ausgehen, dass man als Ergebnis der Messung den Wert
der Spannung bekommt, die am Ende der Sample-Zeit anliegt, richtig?
Ich denke (oder hoffe :D), ich muss die Sample Zeit nicht
berücksichtigen, die PWM wird voraussichtlich nicht über 20kHz
hinausgehen (Motorsteuerung).
Du hast richtig erkannt, dass ich später bei aktiver LOW-Side triggern
will, aber das kommt erst später. Erstmal würde gerne das System
komplett verstehen.
Mit TIM4 CCR1..CCR3 steuere ich 3 PWM Ausgänge.
Weil TIM4 die ADCs nur mit ADC_ExternalTrigConv_T4_CC4 triggern kann
muss CCR4 auch konfiguriert werden, oder?
Hier wäre mein Code:
TimerConfig:
Die OC Konfigurationen betreffen die Pins, sind also beide nicht nötig.
Du musst aber den Duty vom Kanal 4 setzen, das ist klar oder? Dass
bestimmt dir ja den Trigger-Zeitpunkt.
Als kleine Anmerkung ADC_SampleTime_144Cycles ist sehr lange. Wenn dein
Spannungsteiler für die Spannungsmessung nicht allzu hochohmig ist
kannst du auch viel weiter runter gehen.
Die Zeit besagt nur wie lange der ADC-Pin auf auf den Samplekondensator
geschaltet wird. Der Kondensator ist n paar Picofarad, der braucht nicht
lange zum laden. Irgend was bei 20 Takten reicht locker aus. Was ist das
für ein STM? Bei einem STM32F4 nehme ich bei 168MHz und ADC Clock von
21MHz, 3 Cycles zum samplen sind ca. 150ns oder so, bei 100ns wirds bei
dem laut Datenblatt kritisch. Nur damit du n Eindruck bekommst wo deine
Zeit liegt.
Gruß
Tec
Wie hochohmig mein Spannungsteiler wird, weiß ich leider noch nicht,
muss mich noch mit Intrumentenverstärkern auseinandersetzen.
Aber danke für den Hinweis, ich wusste nicht was die Sample-Zeit genau
bedeutet.
Legt man nicht mit
1
TIM4->CCR4=TIM4->ARR-1;
den Duty vom Kanal 4 fest?
Hier wäre die Duty fast bei 100%.
Mir ist noch nicht klar warum man den CCR4 für die Triggerung des ADC
hochsetzten muss.
Bei center aligned PWM sieht ein Cycle doch zB so aus:
Zähler zählt von 1 bis 255 hoch, registriert einen Overflow, und zählt
dann wieder von 255 bis 1 runter, umabhängig vom Duty.
Wenn ich nun ein Duty von 10 festlege, geht der Ausgang auf HIGH wenn
der Zähler hochzählend bei 250 angelangt. Der Ausgang geht wieder auf
LOW wenn der Zähler runterzählend bei 250 ist.
Der ADC soll bei Zähler == 255 getriggert werden. Also beim Overflow.
Warum hängt die Triggerung mit der Duty Einstellung zusammen?
Verwende ein STM32F4 Board mit 168MHz
MfG
Andreas
Angenommen:
TIM4->ARR = 255;
TIM4->CCR4 = TIM4->ARR -1;
Bedeutet das: Timer zählt von 0 bis 253 => OCRef4 (Ausgang4 des Timers
auf Low, Wenn du keine OC Konfiguration machst hängt da zwar kein Pin
dran, aber das Signal gibts ja trozdem noch)
bei 254 kommt das Compare event. OCRef4 geht auf high. => Rising edge am
ADC_ExternalTrigConv_T4_CC4 und die hast du ja als Trigger für den ADC
eingestellt. Der ADC sampled also immer beim hochzählen bei 254, was
quasi genau am overflow ist.
bei 255 zählt der Zähler wieder runter und wenn er wieder bei 253 is ist
OCRef4 wieder low. und der Spass beginnt von vorn.
die meiste Zeit (253/255) auf LOW?
Wenn ich am Pin messe, habe ich aber fast 100% Duty, also knapp 3 Volt.
Oder hängt es mit dieser Einstellung zusammen:
Vielen Dank dass du das alles mit mir durchgekaut hast, ich hatte noch
einige Lücken.
Nur noch zur Sicherheit:
Rising / falling edges Trigger beziehen sich auf OCRef(4).
Wird OCRef4 auf HIGH gestellt, werden im gleichen Moment alle ADCs mit
... = ADC_ExternalTrigConv_T4_CC4;
... = ADC_ExternalTrigConvEdge_Rising;
getriggert.
Wird OCRef4 auf LOW gestellt, werden im selben Moment alle ADCs mit
... = ADC_ExternalTrigConv_T4_CC4;
... = ADC_ExternalTrigConvEdge_Falling;
getriggert.
OC4 hängt von OCRef4 und von TIM_OCPolarity Einstellung ab.
Folgende Reihenfolge der Steuerung für PWM:
TIM4Counter -> OCRef4 -> OC4 -> GPIO_AF_TIM4 -> GPIO_PinSource ->
Physikalischer Pin am Board.
Folgende Reihenfolge für ADC Triggerung:
TIM4Counter -> OCRef4 -> ADC_ExternalTrigConv_T4_CC4 ->
ADC_ExternalTrigConvEdge_Rising|Falling -> ADC auslösen.
Gibt es einen Trigger der von den ADCs ausgelöst wird, sobald das
Sampling abgeschlossen ist?
Ich muss gleich nachdem die beiden Spannungen gemessen wurden jede Menge
berechnungen anstellen.
Edit: gerade was gefunden.
RM0008 s.235 Table 71. ADC interrupts:
---------------------------------------
End of conversion regular group | EOC |
---------------------------------------
Müsste der richtige Interrupt sein.
Mit freundlichen Grüßen
Andreas
ist alles Korrekt was du da schreibst.
Genau, häng dich in den "End of Sequence", bzw. bei nur einer Conversion
pro ADC, in den "End of Conversion" Interrupt. Das wäre der Standardweg.
Guten Morgen.
Ich habe noch Fragen zu dem EOC Interrupt.
Wie in den Posts davor schon geschrieben brauche ich einen Interrupt
Handler direkt nachdem ADC1 und ADC2 die anliegende Spannung
fertiggemessen haben.
Mit folgendem Code scheint es zu funktionieren, sprich der
IRQHandler(void){..} wird aufgerufen. Ich bin mir aber nicht sicher, ob
der EOC Interrupt wirklich im richtigen Moment aufgerufen wird und weiß
auch nicht wie ich das nachprüfen könnte :(
Relevante Stellen aus dem Code:
//while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); <- braucht man das(Frage1)?
4
// wurde doch sowieso schon hier eingestellt:
5
// ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);
6
// es kann sich also nur um einen EOC Interrupt handeln?!
7
8
// Tests für debug:
9
inta=1;
10
a=2;
11
lastADC1Value1=ADCDualConvertedValues[0];
12
a=3;
13
lastADC1Value2=ADCDualConvertedValues[0];
14
a=4;
15
16
//if(ADC_GetITStatus(ADC1, ADC_IT_EOC) != RESET) ADC_ClearITPendingBit(ADC1, ADC_IT_EOC); <- braucht man das(Frage2)?
17
18
19
// Frage3:
20
// wenn der ADC nicht kontrolliert vom PWM Timer getriggert sein würde,
21
// sondern im ContinuousConvMode = ENABLE laufen würde, müsste man hier
22
// irgendetwas setzten damit der Interrupt nicht wieder aufgerufen wird
23
// bevor der jetzige Aufruf fertig verarbeitet ist?
24
25
26
}
Ich habe meine Fragen direkt in void ADC_IRQHandler(void)
geschrieben, damit man weiß welche Stelle gemeint ist.
Was noch eventuell wichtig ist: Messergebnisse vom ADC1 und ADC2 werden
zusätzlich ‚im Hintergrund‘ vom DMA in ein array[2] übertragen. Das hat
aber keinen Einfluss auf den EOC ADC_IRQHandler(void) nicht wahr?
Habe ich sonst alles richtig gemacht (zusammenkopiert :D )?
- der ADC kann nur von ADC_ExternalTrigConv_T4_CC4 getriggert werden
- Nur dann wird Spannung gemessen
- Nur nachdem die Spannung gemessen wurde, wird ADC_IRQHandler
aufgerufen, sonst nie
Mit freundlichen Grüßen
Andreas
Flag clear muss immer sein, (Frage 2) sonst bleibt der proze immer im
int.
Zum messen toggle ne led das kannste dann aufm scope mit der pwm
vergleichen.
Gruß
Tec
Ps: das reference manual ist immer n blick wert. Braucht etwas erfahrung
die infos zu bewerten aber das brauchts für zusammen kopierten code
auch. Sonst wären deine fragen selbst erklärend. Und hast du dir mal das
stm32 tutorial vom moritz diller angesehen?
Nochmal vielen Dank für deine Antworten.
Solange ich nicht
1
ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
aufrufe, kann der ADC_IRQHandler(void){..} nicht nochmals aufgerufen
werden, weil
1
ADC_ITConfig(ADC1,ADC_IT_EOC,ENABLE);
festlegt, dass der IRQHandler nur bei ADC_IT_EOC aufgerufen wird.
(welcher ja erst gecleared werden muss)
Das ist also auch die Absicherung gegen erneutes Aufrufen, wenn der
aktuelle Durchlauf noch nicht abgeschlossen ist. Was ich mit der dritten
Frage meinte.
Also sollte man
1
ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);
erst am Ende des Rumpfes, nachdem man seine eigenen Berechnungen
durchgeführt hat, aufrufen.
Das Tutorial diller-technologies habe ich schonmal angesehen, muss aber
zugeben, dass ich jetzt bei der Interrupt Frage vergessen habe
reinzuschauen :(
Gruß
Andreas
Das clearen des flags macht man generell am Beginn des ints. Sollte der
int während der Laufzeit der interrupt Routine noch mal getriggert
werden bekommt man das ja nicht mit. Und für deinen fall bedeutet das du
hast ein laufzeit problem. Dein int handler braucht zu lange. Also
Interrupts kurz halten nur n flag/semaphore setzen und raus da. Das
handling in einer task/main loop machen. Und immer mit leds synchron zur
pwm mit dem scope das zeit verhalten nach vollziehen.
Gruß
Tec
Ps: schreibe vom handy also nicht auf satzzeichen oder so achten
Bei meiner Anwendung muss der Interrupt Handler unbedingt fertig werden,
denn dort werden aus den Messungen die 3 PWM Dutys für den nächsten PWM
Schritt berechnet .
Und aus diesem nächsten PWM Schritt heraus wird schon wieder der ADC
Interrupt aufgerufen -> PWM Werte für den nächsten Schritt... usw.
(3Phasen Motor mit FOC).
Aber normalerweise sollte der STm32 da absolut problemlos fertigwerden.
Aber aus Interesse: wenn der Handler nochmal aufgerufen wird, bevor der
aktuelle fertig wird, stappeln sich die Aufrufe irgendwo oder werden die
'überholenden' Aufrufe einfach ignoriert?
Gruß
Andreas
PS: Habe leider noch keinen Scope, deshalb heißts für mich immer: PWM
Frequenz ganz weiter runterfahren (~10Hz) und es mit einem Arduino und
kleinen PC Programm oszilloskopieren :D
N Scope solltest dir aber leisten. Sonst wird das echt haarig. Ich
programmiere beruflich Wechselrichter. Von der Sw Architektur her ist es
ziemlicher bullshit 20us mit 50us periode im int zu sein. Dann musst du
die ints nesten lassen sonst geht z.b n uart nicht gleichzeitig. Weil
der int vom uart ja nicht dran kommt. Das musst du dir genau überlegen.
Hallo,
ich betreibe ADC1 und ADC2 im Dual Mode mit DMA. Über TIM2 möchte ich
die Umwandlung auslösen. TIM2 ist ja eigentlich unabhängig vom ADC und
soll immer zur gleichen Zeit auslösen, sodass immer nach einer
bestimmten Zeit die ADCs umwandeln.
Allerdings tritt jetzt bei mir der Effekt auf, dass je größer mein ADC
buffer ist, desto länger braucht das Programm um wieder im
DMA2_Stream0_IRQHandler zu landen, was ich daran erkenne, dass dort
immer eine LED getoggelt wird. Liegt das daran, dass bei größerem Buffer
die TC_Flag erst später gesetzt wird, weil es länger dauert den DMA zu
beschreiben und wird aber die Umwandlung trotzdem immer nach der
gleichen Zeit ausgelöst? Der Effekt ist nämlich sehr stark, also wenn
ich den Buffer von 8 auf 64 erhöhe, dauert es bestimmt 4-5 sekunden
länger bis die LED wieder getoggelt wird.
Vielleicht muss ich den Timer auch manuell wieder neu starten? Ich stehe
momentan etwas auf dem Schlauch und würde mich über konstruktive
Anregungen freuen!
Hier die wichtigsten Codeausschnitte:
Was soll das hier werden? Warum startet du die DMA manuell, obwohl du
einen Timer erstellt hast?!
mfg
Aljo E. schrieb:> Vielleicht muss ich den Timer auch manuell wieder neu starten? Ich stehe> momentan etwas auf dem Schlauch und würde mich über konstruktive> Anregungen freuen!
Am besten solltest du: ADC_SoftwareStartConv(ADC1); in die ISR stecken.
Denn nur dann startet die DMA_ADC erneut zyklisch mit den Timer
Interrupt und deine ADC Werte können aus den Buffer ausgewertet werden.
Es ist egal, ob 64 oder 128 ADC Werte im Buffer gefüllt werden sollten.
Beim stm32 geht das Ratz Fatz.
Hallo,
also DMA_TC_Flg wird am Ende von copyData() auf 0 gesetzt, sorry, hatte
ich vergessen zu erwähnen (ist auch als volatile deklariert).
Das ADC_SoftwareStartConv(ADC1) in die ISR zu stecken macht Sinn, sehe
ich ein. Beim debuggen ist mir allerdings aufegallren, dass während ich
die Daten in der Funktion copyData() aus dem DMA auslese, ändern sich
die Werte im ADCDualConvVal[] Buffer, der ADC sampled also fröhlich
weiter und schreibt das ins Register. Muss ich während ich die Daten
auslese nicht also am besten den ADC oder den DMA stoppen?