Hallo zusammen,
ich würde gerne ein Servosignal mit einem Atmel auswerten.
Kann mir bitte jemand sagen, wie ich das Problem angehen kann oder wo
ich Beispiele dafür bekomme ?
Es geht konkret darum, das ich von einem ferngesteuerten Auto (27 oder
40 MHz sollten egal sein!?) die Signale des Empfängers (also am
Servoanschluss) auswerten will. Ich will quasi auswerten, ob das Auto
rechts oder links abbiegt.
Würde mich über jeden Tip freuen.
Vielen Dank
Grüße
Vici
Auch wenn ich hier einen uralten Thread wieder aus der Versenkung hole,
so fehlt mir nach Studium aller Links und der Links in den Links immer
noch der Ansatz zur Lösung meines Problems.
Ich habe eine E-Heli, die Fernsteuerung hat 9 Kanäle, von denen 6 für
die Steuerung des Heli Vewendung finden und 3 "zum Spielen" frei sind.
Ich habe nun gelesen, das man 3 Timer braucht um das Servosignal eines
Kanals auszuwerten. Stimmt das soweit ?
Ich will eigentlich nur mit einem 3-Stellungsschalter an der Funke in
Stellung 1 die Blink- und Blitzbeleuchtung des Heli einschalten (1 ms
Impuls alle 20 ms z.B. an Kanal 8) in Stellung Mitte soll ein
Scheinwerfer eingeschaltet werden (1,5 ms Impuls alle 20 ms) und in
Stellung 2 (2 ms Impuls alle 20 ms) soll dann eine Kamera anfangen zu
filmen.
Soweit verstehe ich die Theorie und das Blinken, Scheinwerfer und Impuls
für die Kamera sollten so schwer nicht sein.
Ich komme bloß nicht dahinter wie ich aus dem Servosignal am INT0 meines
Mega8 rausbekommen soll, wann ich denn anfangen muß auf einen Impuls zu
warten und wie ich die Länge von dem Impuls dann rausbekomme.
Wenn mir jemand, der C Programmiert und ein Beispiel hat, mal seines zur
Verfühgung stellen könnte. Es muß kein komplettes Programm sein, aber
ich will nachvollziehen und lernen, wie man Impulslängen misst und diese
von Störungen unterscheidet.
Danke.
Ein Modellbauer
> Ich habe nun gelesen, das man 3 Timer braucht um das Servosignal> eines Kanals auszuwerten. Stimmt das soweit ?
Einer reicht völlig.
Den hängst du am Empfänger auf einen ganz normalen Servo-Ausgang.
Irgendwann kommt auf diesem Eingang ein Impuls daher. Wenn der
Impuls beginnt (steigende Flanke) startest du den Timer und
wenn der Impuls endet (fallende Flanke) stoppst du den Timer wieder.
Der Zählerstand des Timers ist direkt proportional zur Länge des
Impulses. Und in der Länge des Impulses ist ja die Schalterstellung
kodiert.
Hinweis: Den INT0 Eingang kann man so konfigurieren, dass
er bei einer steigenden Flanke einen Interrupt auslöst. Man
kann ihn auch so programmieren, dass er bei einer fallenden
Flanke einen Interrupt auslöst. Und man kann ihn zwischendurch
auch immer von dem einen Modus in den anderen umschalten.
Hinweis 2:
Du musst das nicht über einen INTx Eingang machen. Der Timer 1
hat eine sog. Input Capture Unit. Je nachdem auf welche
Flanke die programmiert ist (steigend oder fallend) speichert
die den momentanen Zählerstand des Timers in einem Register und
löst einen Interrupt aus. Wiederrum: Man kann programmieren
ob die auslösende Flanke steigend oder fallend (oder beides)
sein soll und der gemessene Zählerstand ist proportional zur Länge
des Impulses.
Die 20 ms sind nett zu wissen und für eine Fehlerauswertung sicher
nicht schlecht zu wissen. Aber fürs erste brauchst du die nicht.
Dein Timing sieht so aus: Die Impulsleitung ist auf low.
Irgendwann wechselt die von low nach high. Das ist der Beginn
des Impulses. Von dort misst du die Zeit weg. Iregendwann
fällt die Leitung wieder zurück nach low. Jetzt ist der Impuls
wieder zuende und du beginnst damit, aus den gemessenen Timerwerten
am Impulsanfang und am Impulsende die Länge des Impulses und damit
die zu setzende Aktion zu bestimmen.
ich würde es am einfachsten so machen:
while (1)
{
uint16_t zaehler=0;
while (!(servo_high) {}; //warten bis low
while (servo_high) zaehler++; //Zeit des High impulses zählen
if (zaehler<1000) blink_on; //wenn die Zeit weniger als.. dann
if ((zaehler>999)&(zaehler<1500)) blitz_on;
if (zaehler>1499) kamera_on;
}
diese Variante benötigt keine Interrups oder ähnliches. Wenn der µC
sonst noch für was gebraucht wird, dann muss man doch über interrups
gehen. Die Zahlenwerte sind natürlich von der Taktfrequenz abhängig. Du
musst aber noch die Geräte wieder ausschalten. Evtl sollte man die If
Anweisungen in eine Switch-Case abändern. macht keinen Unterschied,
sieht aber eleganter aus.
Vielen Dank Karl Heinz Buchegger und Matthias,
Ich bin gerade dabei die Datenblätter des Mega8 nach den beschriebenen
Timerfunktionen abzugrasen.
Was ich nun im Moment noch nicht kapiere:
Wenn ich den Timer 1 auf die steigende Flanke programmiere, dann startet
der Timer und beginnt zu laufen ... bleibt der dann auch stehen, wenn
der Signalpegel wieder auf 0 fällt und löst den Interrupt aus den ich
mit signal gemeldet bekomme oder muß ich, nachdem der Pegelwechsel
stattgefunden hat per Programm die Stopbedingung mit fallender Flanke
setzen ?
Das Datenblatt beschreibt es sicher, aber mit meinem Schulenglisch und
dem absoluten Neuland, das diese Aufgabe für mich ist, wird das ein ganz
schöner Denksalat im Kopf.
Ich bleibe aber dran, denn wenn ich in den Foren so sehen, was die Leute
so an Videos von OnBoard machen und wie die Ihre Modelle so beleuchten,
dann will ich das auch unbedingt können. Aber nicht kaufen, sondern
selbermachen.
Danke schon einmal bis hierher, ich werde über die Tage dann etwas zu
basteln haben.
Gruß und Frohe Weihnachten.
Der Modellbauer
So,
nachdem ich nun ein paar Bücher gewälzt habe und nicht die Hälfte davon
verstanden habe, hier ein Ansatz, den ich mir aus einem Lehrbuch auf
Basis des CVAR zusammengesucht habe.
Im main Teil soll dann bei Blinken, Blitzen und Kamera die Ausgaenge
geschaltet werden. Den Code dazu muß ich aber nochmal neuschreiben, weil
der erste Anlauf nicht klappte.
Kann mal bitte jemand über den Code drüberschauen und mir sagen, ob ich
total auf dem Holzweg bin, oder ob da schon ein Ansatz drin ist, der die
Auswertung möglich macht.
Danke schonmal an alle die, die sich die Mühe machen mir zu helfen.
Der Modellbauer
> oder ob da schon ein Ansatz drin ist, der die> Auswertung möglich macht.
Der Ansatz ist schon ok.
Nur misst du natürlich von einer steigenden Flanke zur
nächsten steigenden Flanke. Da wird immer so um die 20
ms rauskommen. Die Zeit die dich interessiert ist von
der steigenden Flanke bis zur fallenden Flanke. D.h.
du musst das Capturing in der Interrupt Funktion
umprogrammieren.
1
//Blinken bei 1 ms (1000 Impulse bei 8 MHz und Teiler durch 8)
2
if(laenge==1000)
3
{
4
Blinken=ein;
5
}
6
7
//Blitzen bei 1,5 ms (1500 Impulse bei 8 MHz und Teiler durch 8)
8
if(laenge==1500)
9
{
10
Blitzen=ein;
11
}
12
13
//Kamera ein bei 2 ms (2000 Impulse bei 8 MHz und Teiler durch 8)
14
if(laenge=2000)
Rechne lieber nicht damit, dass du exakt einen Messwert von
1000, 1500 oder 2000 kriegst. Immer in Bereichen arbeiten:
if( laenge < 1250 )
Blinken = ein;
else if( laenge < 1750 )
Blitzen = ein;
else if( laenge < 2250 )
Kamera = ein;
O. K. danke,
muss ich bei TCCR1B = 0x82 eintragen ? (Also den NOISE-Canceler
einschalten, Trigger auf fallende Flanke)
Dann verstehe ich auch, was Du mit den Bereichen meinst, weil ich ja 4
Taktimpulse bei gleichem Impulspegel brauche, damit der Interrupt
getriggert wird.
Ich versuche nun auch gerade das Programm zu übersetzen, damit ich das
mit dem Programmers Notepad 2 eingeben kann. Der CVAR ist mir zu teuer.
Liege ich mit meiner Vermutung, was die Ändeung angeht dann richtig ?
Ich will es ja lernen und die einfachen Sachen verstehe ich. Nur das
Beschreiben der Register und das handeln der Interrupts macht mir noch
kopfzerbrechen.
Der Modellbauer
So,
im Anhang noch ein Versuch von mir.
Ich kriege noch den Fön, wenn ich mich mit dem Kram auseinandersetze.
Die ganzen Beispiele von Hannes sind echt super. Ich finde das auch
wirklich nett, das man mir die Beispiele zur Verfügung stellt, aber ich
will das begreifen und in C Programmieren lernen.
Leider muß ich das immer noch Alles alleine machen, nur mit Internet und
Büchern. Aber die ersetzen mir nicht die Rückmeldung aus einer Gemeinde
von Softwareerfahrenen Leuten, die mir entsprechend meiner unbeholfenen
auf den Weg helfen.
Danke für eure Geduld mit mir.
Der Modellbauer
Bei den 'neueren' AVRs kannst du den INT0 und INT1 auch auf beide
Flanken reagieren lassen. In der INT-Funktion dann noch abfragen ob der
INT-Pin high oder low ist und dementsprechend den Timer auf 0 setzen
oder auslesen. Ist das einfachste der Welt.
Ein Widerspruch:
impulslaenge = (zaehlerstand - alterstand) / 1000 ; //Weil Systemtakt 1
MHz
alterstand = zaehlerstand;
if (ueberlauf !=1)
{
if (impulslaenge <= 1250) // das wären schon Sekunden...
Entweder impulslaenge = zaehlerstand - alterstand;
oder mir kleinen floats rechnen. Ersteres sollte besser funktionieren...
Erst einmal danke für den Hinweis unbeschreiblicher Rahul. Werde ich
ändern.
Was ich aber nun nicht ganz verstehe:
Wenn ich INT0 und INT1 zusammenschalte, wie kriege ich dann den Timer
gestartet und gestoppt.
Timer ein und aus wird doch über das TCCR0 geseteuert und ich müsste das
Register bei INT0 auf steigende Flanke schreiben, das TIMER0 läuft und
mit INT1 auf fallender Flanke das Register wieder so schreiben, das der
Timer wieder steht.
Ist das so, oder verstehe ich da Etwas falsch ??
Der Modellbauer
Teiler so dimensionieren dass der Timer innerhalb 2ms nicht überläuft.
Steigende Flanke: TCNTx = 0;
Fallende Flanke: Variable = TCNTx;
Angenommene Werte: Variable = 100 sind 1ms, Variable = 250 sind 2ms.
Wo ist das Problem? 16 bit-Timer ist wegen der Auflösung natürlich
besser.
Bei 8 bit-Timer: 'Variable' als integer deklarieren und pro Überlauf des
Timers 256 dazuzählen.
> Leider muß ich das immer noch Alles alleine machen, nur mit Internet und> Büchern.
Das geht mir nicht anders.
Ich möchte behaupten, dass das Jedem so geht, der nicht das Glück hatte,
eine entsprechende berufliche Ausbildung zu machen.
> Aber die ersetzen mir nicht die Rückmeldung aus einer Gemeinde> von Softwareerfahrenen Leuten, die mir entsprechend meiner unbeholfenen> auf den Weg helfen.
Es wird wohl kaum Jemand die Zeit finden, Jedem, der sich neu mit der
Programmierung beschäftigt, mittels persönlichen Frage-Antwort-Spiels
die Grundlagen beizubringen. Da muss man schon selbst etwas tun und
seine eigenen Ergebnisse mit Veröffentlichungen Anderer im Netz
vergleichen.
Wenn Du die Hardware des AVRs verstehen lernen willst, dann solltest Du
entweder C bereits souverän beherrschen oder Dich in ASM einarbeiten,
ansonsten eierst Du nur rum und tappst von einer Stolperfalle in die
andere. Zeitkritische Dinge wie Servoimpulse jitterfrei
erzeugen/auswerten lassen sich in ASM nunmal bedeutend übersichtlicher
realisieren, denn in ASM ist man direkt an der Hardware und auch direkt
am ausführbaren Befehlssatz des Controllers. Man kann also Takte zählen
und jeden Schritt exakt nachvollziehen. Und die Programmierer, die das
in C souverän können, verstehen in der Regel auch ASM.
Auf Deine Fragen kann man Dir zwar (wenn überhaupt) zeigen, wie man
Ähnliches realisiert habe und wo Du das nachlesen kannst. Man kann Dir
auch diese oder jene Frage zum Verständnis meiner Veröffentlichung
beantworten, aber man kann Dich nicht an die Hand nehmen und Dir jeden
Schritt der Programmierung Deines Projektes ausführlich erklären. Dazu
fehlt einfach mal die Zeit. Außerdem grenzt das schon an eine
Diensleistung, von der viele Dozenten und Lehrer ganz gut leben können.
...
Ähm,
das Problem ist, das ich normalerweise "nur" Modellhelikopter / Autos
bauen und fliegen / fahren kann.
Ich habe Das mit dem AVR nur angefangen, weil mir fertige Lösungen zu
teuer waren und ich aus diesem Forum hier und mehreren Besuchen in der
Stadtbücherei gesehen habe, das man als "Normalsterblicher" mit kleinem
Aufwand auch lernen kann, µC zu programmieren.
Dann habe ich die Blinkschaltungen und das ein oder andere Beispiel aus
dem AVR GCC Tutorial gemacht.
Nun beginne ich mit Interrupts und Timern und habe irgendwie da noch
nicht so den Durchblick.
Was da eben im Anhang war, hat mich den 25. bis 27. Dezember gekostet,
weil ich mir alles anlesen musste. Das ist mein Problem.
Deshalb bitte ich zu entschuldigen, wenn die Fragen etwas dumm und
amateurhaft klingen.
Der Modellbauer
>Deshalb bitte ich zu entschuldigen, wenn die Fragen etwas dumm und>amateurhaft klingen.
Es gibt keine dummen Fragen, nur dumme Antworten.
Es gibt höchstens Fragen, die sich (in regelmässigen Abständen)
wiederholen...
Dein Programm sieht für einen Anfänger richtig gut aus.
Die Kommentare finde ich total klasse ("wollen wir erst einmal"); klingt
so wie "da wollen wir mal den Mund ganz weit aufmachen und 'Aaah'
sagen".
Aber wenigstens erklären sie, was passiert/was passieren soll...
>DDRC |= (1<<DDC0) | (1<<DDC1) | (1<<DDC2);
Wenn du irgendwas initialisierst, also erstmalig beschreibst, solltest
du nur
"DDRC = ...;" schreiben.
>DDRB &= ~(1<<DDB0);
Die Ports sind im Einschaltmoment immer Eingänge... ("gesunde
Redundanz").
>PORTB |= (1<<PB0);
Initialisierung; lass also das | weg.
>While (1)
Wird nicht funktionieren, da c case-sensitive ist (while(1);)
Die Überlauf-Behandlung kannst du dir sparen:
Du rechnest in einem besgrenzten Zahlenraum. Deswegen kommt es trotzdem
zum richtigen Ergebnis, wenn man eine grössere Zahl von einer kleineren
subtrahiert. Das gilt natürlich nur für "unsigned"-Variablen.
> niederbyte = TCNT1L;> hochbyte = TCNT1H;> zaehlerstand = (256 * hochbyte) + niederbyte;
Geht einfacher (und besser):
zaehlerstand = ICR1;
impulslaenge = (zaehlerstand - alterstand); //Weil Systemtakt 1 MHz
alterstand = zaehlerstand;
Wenn dein Programm sich nur um die Impulsauwertung kümmern soll, ist es
in Ordnung, alles in der ISR zu machen.
Sobald mehr dazu kommt (USART zwecks Impulslängenausgabe an den PC, ADC
zwecks Batteriespannungsmnessung ...), solltest du in der ISR nur das
nötigste machen (zaehlerstand = ICR1), ein Flag setzen und dieses Flag
dann in der Hauptschleife abfragen und die Sachen entsprechend
auswerten.
Nun, wenn ich mich in Etwas hineinsteiger, dann kann das richtig schlimm
werden.
Ich habe hier 6 Bücher liegen. Das Buch von dem Schmitt
"Microcontrollertechnik mit den Controllern der Atmel AVR-RISC Familie",
das AVR Buch von Rowalt, dann noch "Embedded C Programming and the Atmel
AVR". Ausserdem habe ich mir die Datenblätter von dem Mega8 ausgedruckt
und ein Buch über C-Prgrammierung ausgeliehen.
Aber das nur mal am Rande.
Ich werde nun mal wieder ein wenig Weiterlesen und den Weg von Sonic mal
ausprobieren. Vielleicht hilft das ja.
Oder soll Deine, doch recht positiv konstruktive Kritik, bedeuten, das
das Programm bis auf die angesprochenen Syntaxfehler eigentlich
funktioniert ???
Ich bin noch nicht zuhause und habe deshalb noch nicht testen können, ob
das Programm läuft.
Michael
>Oder soll Deine, doch recht positiv konstruktive Kritik, bedeuten, das>das Programm bis auf die angesprochenen Syntaxfehler eigentlich>funktioniert ???
Ja... so auf den ersten Blick.
Modellbauer wrote:
> Ähm,>> das Problem ist, das ich normalerweise "nur" Modellhelikopter / Autos> bauen und fliegen / fahren kann.
Respekt, das kann ich nicht...
>> Ich habe Das mit dem AVR nur angefangen, weil mir fertige Lösungen zu> teuer waren und ich aus diesem Forum hier und mehreren Besuchen in der> Stadtbücherei gesehen habe, das man als "Normalsterblicher" mit kleinem> Aufwand auch lernen kann, µC zu programmieren.
Das ging mir ähnlich, ich hatte mich mit AVRs beschäftigt, weil das
Problem, das ich lösen wollte, ohne Mikrocontroller zu aufwändig
geworden wäre. Fertige Lösungen gab es aber dafür nicht.
>> Dann habe ich die Blinkschaltungen und das ein oder andere Beispiel aus> dem AVR GCC Tutorial gemacht.
Nunja, ich hatte das ASM-Tutorial als erste Vorlage.
>> Nun beginne ich mit Interrupts und Timern und habe irgendwie da noch> nicht so den Durchblick.
Mein erstes eigenes AVR-Programm (AT90S1200 mit dreistelliger
7-Segment-Anzeige, 12 Tastern, Impulsausgang) kann ich heute nur noch
belächeln, es nutzte aber bereits den Timer-Interrupt. In Assembler ist
das recht einfach zu verstehen. Mit meinem Vorwissen in BASIC wäre das
vermutlich unübersichtlicher geworden. In C kann ich nicht mitreden, man
kann nicht überall kompetent sein. Und ich möchte Niemandem zumuten,
sich die Arbeit zu machen, mich in AVR-C einzuarbeiten.
>> Was da eben im Anhang war, hat mich den 25. bis 27. Dezember gekostet,> weil ich mir alles anlesen musste. Das ist mein Problem.
Das geht anderen Leuten auch nicht anders. Ich brauche auch meine Zeit,
bis meine Programme fertig sind.
>> Deshalb bitte ich zu entschuldigen, wenn die Fragen etwas dumm und> amateurhaft klingen.
Ist doch ok.
Nur ist es ein Unterschied, wenn man sagt, "dass mit Timern und
Interrupts verstehe ich nicht" oder wenn man eine konkrete Frage zu
einem I/O-Register stellt, weil man das Datenblatt an dieser Stelle
nicht ganz versteht.
>> Der Modellbauer
Du schreibst weiter oben von Int0 und Int1 für fallende und steigende
Flanke. Das brauchst Du nicht. Den externen Interrupt kann man
einstellen, worauf er reagieren soll. Da steht Low-Level, steigende
Flanke, fallende Flanke und bei einigen AVRs auch Pegelwechsel zur
Auswahl.
Du kannst also mit nur einem externen Interrupt (der fest an einen
bestimmten Portpin gekoppelt ist) beide Flanken exakt erkennen und auch
noch voneinander unterscheiden (Portpin prüfen oder Flanke umschalten).
Wenn Du nun einen Timer frei durchlaufen lässt, kannst Du im Int0 bei
steigender Flanke (Impulsbeginn) den Zählerstand des Timers auslesen und
merken, bei fallender Flanke (Impulsende) den Zählerstand einlesen und
den gemerkten Wert subtrahieren. Und schon hast Du die neue Impulsbreite
in Deiner eigenen Maßeinheit, die vom Takt des AVRs und vom Vorteiler
des Timers abhängig ist. Das Ganze geht noch etwas eleganter und
exakter, wenn man statt des Int0 den Input-Capture-Interrupt (und den
entsprechenden Portpin) nutzt.
In meinen Projekten mit Tiny15 habe ich den Timer mit 100kHz Zähltakt
laufen lassen (1,6MHz, Vorteiler 16), das ergibt Zählerstände von 100
(1,0ms), 150 (1,5ms) und 200(2,0ms), und bis zum Überlauf von 256 ist
noch etwas Reserve, es können also Impulse bis 2,55ms gemessen werden.
Ich nutzte damals noch eine uneffizientere Messmethode, aber das ist
auch schon etwas länger her.
All diese Überlegungen (Takt, Vorteiler, Auflösung, usw.) haben nichts
mit der verwendeten Programmiersprache zu tun, in ASM ist es aber am
leichtesten nachzuvollziehen, da der Controller (mit seiner ALU, seinen
Registern, RAM und I/O-Bereich) in Maschinensprache arbeitet, die 1 zu 1
Assembler entspricht.
Denn mal viel Erfolg, das Jahr ist noch lang...
...