Hallo,
ich baue gerade für meinen Neffen ein Kettenfahrzeug mit zwei über
ATmega32 + Doppel-H-Brücke angesteuerten Getriebemotoren.
Die H-Brücke:
https://de.aliexpress.com/item/Hohe-Qualit-t-Neueste-H-Br-cke-Dual-Motor-Treiber-Laufwerk-Modul-Board-DC-MOSFET-IRF3205/32896741176.html
erfordert zwei DIR und zwei PWM-Signale für links und rechts.
Eigentlich kein Problem für mich.
Ich nehme also den Timer1 vom ATmega32, 16Mhz, P&F Correct PWM, Output
Compare über OC1A und OC1B, ICR1 als TOP (=1000 > 8khz PWM Frequenz) für
PWM und zwei normale IOs für DIR.
Variiert werden OCR1A und OCR1B in der while(1)-Schleife in der main.c.
cli() wird davor aufgerufen und sei() danach, denn die Werte für OCR1A
und OCR1B stammen vom ADC (Free Running Mode, 125khz Sampling Rate,
Interrupt) an dem an zwei Kanälen je ein Poti 5k hängt.
Folgende Funktion kümmert sich um die Berechnung von OCR1A und B statt:
(Parameter "steering_poti_value" ist der 10-Bit-Wert von den ADCs, für
links und rechts wird die Funktion zweimal aufgerufen)
Nun zum Problem. Wie im Video
(https://1drv.ms/v/s!ApNuZK_DFrUOgfM83u_v-ly2vpnh0g) stelle ich bis zur
Zeitmarke 0:20 nur den einen PWM Kanal (CH2 auf dem Oszi) mit dem Poti
ein. Ich fahre einmal von der Mittelstellung auf den unteren Anschlag
vom Poti(0 Ohm) und dann über die Mittelstellung zum oberen Anschlag
(5kOhm) und wieder zurück zur Mitte. Dann stelle ich ab 0:20 den zweiten
PWM Kanal (CH1) auf ein von der Mittelstellung abweichenden Wert und
wiederhole die Prozedur mit dem ersten Kanal (CH2 auf Oszi). Ab der
Zeitmarke 0:37 ist zu sehen ist, dass ich den Kanal nicht mehr richtig
getriggert bekomme. Ich vermute, dass es an einem Übersprechen des
anderen Kanals liegt. Ich habe extra alles von den PWM-Pins abgelötet,
um durch die Leiterbahnen/Jumperkabel keine kapazitive Kopplung zwischen
die beiden Kanäle zu bekommen - bringt aber nichts.
Liegt dies dann überhaupt noch an einem elektrischen Problem oder soll
ich in der Software suchen?
Kann gerne den ganzen Sourcecode posten, aber der ist halt für einen
Beitrag recht umfangreich.
Timo N. schrieb:> Liegt dies dann überhaupt noch an einem elektrischen Problem oder soll> ich in der Software suchen?
Das werden dir dein Debugger und Kontrollausgaben verraten.
Guck dir die Werteverteilung an, die von deinem ADC geliefert wird und
was calc_speed_cmd() daraus für Steuerwerte generiert.
Zum Prüfen des Algorithmus muss die ganze Sache vielleicht nicht mit
125khz laufen.
Wie schnell willst du eigenlich am Poti reißen, damit eine Abtastrate
von 125kSa/s irgendwelchen Informationsgewinn bringt. Ist sicher, dass
dein ADC schnell genug getaktet wird, um das zu schaffen? Lässt du nach
einer Umschaltung des Multiplexers genug Zeit, damit sich am Eingang des
ADC die Spannung an den neuen Wert ausreichend genau anpassen kann?
Ich habe keinen Debugger. Ausgabe über UART könnte ich machen.
125khz ist die geringste Frequenz, die ich mit 16Mhz bekommen kann (128
Prescaler). Hab die Werte nun auch noch durch eine gleitende
Mittelwertberechnung gejagt (Mittelwert aus 16).
Ich habe festgestellt, dass ich nur bei Werten unterhalb des ADC Werts
von 511 Probleme mit dem "Übersprechen" habe. Das würde für einen
Softwarefehler sprechen (Die Spannung des Spannungsteiler am Poti ist
stabil).
Was meinst du mit genügend Zeit?
ADC läuft im Free Running Modus:
1
//ADC für kontinuierliche Messung konfigurieren
2
ADMUX=(1<<REFS0);// external voltage reference on AVCC pin with external capacitor at AREF pin
Hallo,
vielleicht verwechselst Du den ADC-Takt mit der Samplingrate.
"By default, the successive approximation circuitry requires an input
clock frequency between 50 kHz and 200 kHz to get maximum resolution. If
a lower resolution than 10 bits is needed, the input clock frequency to
the ADC can be higher than 200 kHz to get a higher sample rate.
The ADC module contains a prescaler, which generates an acceptable ADC
clock frequency from any CPU frequency above 100 kHz. The prescaling is
set by the ADPS bits in ADCSRA. The prescaler starts counting from the
moment the ADC is switched on by setting the ADEN bit in ADCSRA. The
prescaler keeps running for as long as the ADEN bit is set, and is
continuously reset when ADEN is low."
MfG
Ja ok. Die Sampling Rate ist dann 125khz / 13 (weil im Free Running Mode
13 ADC Clock Cycles für eine Conversion benötigt werden).
Das habe ich in meinem ersten Post falsch geschrieben. Hast du Recht.
Den Free Running Mode nehme ich nur, wenn nur ein Eingang gemessen
werden soll.
Mußt Du aber den MUX umstellen, dann kann das im Free Running Mode in
die Hose gehen, wenn der Interrupt nicht rechtzeitig aufgerufen wird.
Daher starte ich dann erst die Messung, nachdem der MUX umgeschaltet
wurde.
Dachte so funktioniert es?
ADC_UPDATE wird extern auf 0 gesetzt (ist ein Flag um zu sehen, dass
neue Daten existieren
AddToFloagAvg fügt den neuen wert in den Ringbuffer für die gleitende
Mittelwertberechnung ein.
ADMUX|=(1<<MUX0);//setzt MUX0 Bit: ADC5 Eingang als nächstes lesen
13
}
14
}
Ich habe den gleitenden Mittelwert jetzt von 16 Werten auf 256 Werte
erhöht. Nun habe ich keine Störungen mehr.
Ich würde es aber gerne Wissen, warum ich so ausreißer bei den
ADC-Werten habe (daran lag es ja anscheinend).
Timo N. schrieb:> AddToFloatAvg(&STEERING_POTI_L_AVG, ADCL + (ADCH << 8));
Damit ist die Reihenfolge undefiniert, aber sie ist wichtig: "ADCL must
be read first, then ADCH".
Laß es daher den Compiler machen:
Aus dem Datenblatt geht hervor:
If both ADATE and ADEN is written to one, an interrupt event can occur
at any time. If the
ADMUX Register is changed in this period, the user cannot tell if the
next conversion is based
on the old or the new settings. ADMUX can be safely updated in the
following ways:
1. When ADATE or ADEN is cleared.
2. During conversion, minimum one ADC clock cycle after the trigger
event.
3. After a conversion, before the Interrupt Flag used as trigger source
is cleared.
When updating ADMUX in one of these conditions, the new settings will
affect the next ADC
conversion.
D.h. es kann sein, dass zum Zeitpunkt der Umschaltung des Multiplexers
schon die nächste Messung gestartet wurde und ggf. verfälscht wird.
Evtl. hilft es hier nach dem Umschalten der Multiplexers erstmal eine
"Opfer"-Messung des ADC durchzuführen und zu verwerfen.
Tja, ich weiß nicht wie ich es sonst machen soll.
Ich warte ja auf den Interrupt und schaue darauf wie die ADMUX Bits
gesetzt sind. Dann lese ich entsprechend den Wert und schalte dann
gleich auch um.
Du meinst dann wohl, dass ich mir hier ein Flag setzten soll
Timo N. schrieb:> AddToFloatAvg(&STEERING_POTI_L_AVG, ADCL + (ADCH << 8));
Die grosse Frage ist, wie lange diese Routine dauert. Ansonsten würde
ich den ADC nur einmal triggern beim Initialisieren und ihn dann immer
am Ende der ISR wieder starten. So ist gewährleistet, das der nächste
ADC-IRQ erst kommt, wenn der hier fertig bearbeitet ist.
Naja, viel macht die nicht
(https://rn-wissen.de/wiki/index.php?title=Gleitender_Mittelwert_in_C).
Ich frage mich eben, warum überhaupt FreeRunning-Mode, wenn es nicht
genutzt werden kann. Das kann ich mir nicht vorstellen.
Soll der Modus nur ohne Multiplexing (also nur für einen Kanal) gedacht
sein? Fraglich.
Ich habe ja jetzt mit dem Gleitenden Mittelwert über 255 Werte ein
akzeptables Ergebnis. Eventuell versuche ich es noch mit der Lösung, die
Reiner_Gast vorgeschlagen hat.
Check doch mal bitte die Hardware am ADC Eingang. Das sollte wenig
Quellimpedanz haben (5k Poti wäre aber ok) und vorzugsweise noch einen
kleinen externen C (z.B. 1nF - 3,3nF) direkt an jedem ADC Eingang. Dann
wird das Übersprechen recht klein.
So als Vorschlag in preudocode
<xxx> = optional
global mux
global machwas
global adc_ergebnis[2]
<global muell_messung>
ISR_Timer_alle_25ms (so wird jeder mux z.b. 20 mal je sek. aktualisiert)
Start ADC
end
ISR_ADC_fertig
<if (!muell_messung)>
Read and Save Ergebnis [mux]
if mux==0
mux==1
Set_Mux(2)
<muell_messung=1>
<Start ADC>
else
mux==0
Set_Mux(1)
<muell_messung=1>
<Start ADC>
endif
machwas = 1
<else>
<muell_messung=0>
end
main
init ADC
init Timer
adc_ergebnis[2]=0
mux=0
machwas=1 (je nachdem ob gleich was zu tun ist oder erst nach der
ersten messung)
muell_messung=1
start ADC
start timer
do
if (machwas)
tu_irgendwas()
machwas = 0
endif
schlafe_bis_zum_nächsten_event()
while_endlos
end
Damit laufen die ADC messungen und das umschalten der Mux schön synchron
und nach dem jeweiligen umschalten ist auch etwas Zeit für die S&H sich
auf den neuen Wert einzustellen. Zur Not je nach Prozessor nach dem
umschalten der Mux erst noch eine "Opfermessung" zum wegwerfen.
Und viel mehr Hektik ist beim abfragen eines Potis meistens auch
überhaupt nicht notwendig - schon garnicht im kHz Bereich.
Timo N. schrieb:> Ich frage mich eben, warum überhaupt FreeRunning-Mode, wenn es nicht> genutzt werden kann.
Natürlich kann der genutzt werden.
Schließlich ist er der Modus mit der höchsten Sampelrate.
Aber nur eben auf einem Kanal, ohne Verluste.
Merke --> Du brauchst den free running Mode nicht.
Denn ebenso gut, kannst du in der ISR den Kanal umschalten und dann die
Messung wieder starten.
Die paar Samples, welche du dadurch verlierst, sind doch bei einem Poti
egal...
Timo N. schrieb:> Ich habe ja jetzt mit dem Gleitenden Mittelwert über 255 Werte ein> akzeptables Ergebnis.
Du arbeitest mit korrupten Daten.
Das muss doch nicht, oder?
Timo N. schrieb:> Eventuell versuche ich es noch mit der Lösung, die> Reiner_Gast vorgeschlagen hat.
Es gibt einige bessere Lösungen, als deine Variante.
Hallo Fanboy,
klar sind die paar Samples egal. Ging nur um Grundsätzliches. Kann ja
gut sein, dass ich wann anders wirklich brauche.
Wieso sind gefilterte Daten = korrupte Daten? Die paar Samples die
"falsch" gesamplet wurden, fallen bei 255 Werten nicht ins Gewicht.
Deswegen ja Filtern.
Es gibt immer bessere Varianten. Schon klar.
Timo N. schrieb:> Wieso sind gefilterte Daten = korrupte Daten?
Du filterst korrupte Daten.
Schrott sammeln, und dann wieder gerade rechnen.
Kostet Zeit und Speicher.
Was du nicht machen müsstest, wenn du "geschickter" sampeln würdest.
Timo N. schrieb:> klar sind die paar Samples egal.
Ja dann....
Steht doch einer anderen Strategie nichts mehr im Wege....
Irgendwer schrieb:> Zur Not je nach Prozessor nach dem> umschalten der Mux erst noch eine "Opfermessung" zum wegwerfen.
Der AVR braucht keine Opfermessung.
Wichtig ist aber, die Reihenfolge beim Lesen von ADC einzuhalten!
ADCH wird beim Lesen von ADCL gelatcht.
Und wenn man die Messung nicht freerunning, sondern erst nach MUX
umschalten startet, dann kann sich auch nichts in die Quere kommen.
Der Mittelwert über 256 Messungen verschleiert nur, daß da was nicht
stimmt.
Hier die ADC Kernroutine meines neusten Projektes - eine Herdsteuerung
mit 4 ADC Kanälen (allerdings nur 8 Bit, weil ich es besser hier nicht
brauche):
1
/* ADC complete interrupt
2
* this version scans 4 channels (0 to 3) and
3
* places the results in globals channel1 to channel4 */
4
ISR(ADC_vect)
5
{
6
switch(ADMUX)
7
{
8
caseADMUX_CH1:
9
channel1=(channel1+ADCH)>>1;// do some averaging with the last value obtained