Hallo, kann mir jemand einen Tip geben wie ich ein Signal mit 80µs High_Pegel, 20µs Low_Pegel und dann 100µs High_Pegel erzeugen kann....??? Gruß
Was für ein Controller (oder NE555:-?)? Bei AVR: Timer im CTC-Modus betreiben, Compare-Register auf 80µs einstellen, dann bei Compare Pin umschalten, Compare-Reg. umschalten auf 20 µs usw....
Für so etwas Spezielles glaub ich weniger... Aber mit den Infos oben solltest Du das eigentlich hinbekommen. Kleiner Hinweis noch: Wenn Dein µC mit 8 MHz läuft, stellst Du den Prescaler des betreffenden Timers auf Taktteilung durch 8, dann zählt der Timer µs. Brauchst dann im CTC-Modus nur noch in das entsprechende OCR z.B. ne 80 für 80µs reinzuschreiben. In der Compare-Interrupt-Routine schaltest Du dann den Pin um und schreibst den neuen Wert ins OCR (also z.B. beim ersten Interrupt die 20, dann die 100).
Kapier ich nicht ganz...! Hab bis jetzt dieses Programm: #include <avr/io.h> #include <avr/interrupt.h> ISR(TIMER0_OVF_vect) { PORTB =~ PORTB; // PORTB inventieren } int main() { DDRB = 0xFF; // PORTB als Ausgang schalten PORTB = 0x00; // Alle Ausgaenge auf 0 schalten TCCR0B |= (1 << CS01); //Prescaler auf 1024 stellen TIMSK0 |= (1 << TOIE0); // Timer 0 Overflow Interrupt enable sei(); // Interrupts einschalten for(;;); //ever } Das gibt mir ein Signal welches abwechselnd alle 160µs einen High- und Low-Pegel hat. Nur wie kann ich ich jetzt eine bestimmte Länge der Pegel einstellen? Versteh das mit dem OCR und dem Pin umschalten nicht ganz....
Mit Timer 0 geht das was ich vorgeschlagen habe (und wie ich es machen würde) auch nicht, weil der keine Compare-Unit hat. Entweder Timer 1 oder Timer 2 nehmen. Den dann im CTC-Modus betreiben (das heißt, der Timer (TCNTx) wird bei erreichen des Compare-Wertes zurückgesetzt). Je nachdem, mit welcher Taktfrequenz Du arbeitest, musst Du den Prescaler entsprechend einstellen. Dann nicht den Overflow-Interrupt, sondern den Compare-Interrupt benutzen und jeweils den gewünschten Zeitwert ins Compare-Register laden. Wenns unbedingt mit Timer 0 sein soll, dann in der Overflow-ISR jeweils den Pin umschalten und das TCNT0 mit dem entsprechenden Startwert laden. Geht auch, ist aber mit ein bisschen mehr Denkarbeit verbunden... Wenn Du das Umschalten der Portpins so machst, wie Du es oben schreibst, dann kriegst Du das Signal aber bei jedem Mal invertiert (also in jedem Zyklus High- und Low-Pegel vertauscht).
Ich krieg es nicht hin.... Kannst du mir mal nen kleines Beispiel für den Compare Interrupt geben, wie du ihn schreiben würdest?
Ähh, wäre vielleicht besser, wenn Du, bevor Du so 'komplizierte' Signale erzeugst, Dich erst mal mit den Grundlagen befasst... Wie ein Timer eigentlich arbeitet und so. Wenn das Verständnis nicht da ist, dann bringt es eh nix. Lass den Compare erst mal weg (wie gesagt, so würde ich es machen, aber mit dem Overflow gehts auch) und lass Dein Programm erst mal so wie es oben ist mit Timer 0 (außer, dass Du vielleicht darauf achten solltest, dass Dein Kommentar auch mit dem Programmcode übereinstimmt, was zumindest bei der Prescaler-Initialisierung nicht der Fall ist). Und dann überlegst Du mal, wie Du die Zeit, die der Timer vom Start bis zum Überlauf braucht, verändern kannst. Ohne das Verständnis könnte ich Dir höchstens ein komplettes Programm schreiben, wofür ich jetzt aber erstens keine Zeit habe und zweitens würde es Dir nix bringen. BTW: Mit welcher Taktfrequenz betreibst Du den µC eigentlich jetzt??? Wenn das oben tatsächlich 160µs ergeben sollen, dann müssten das 12,8 MHz sein...
Noch ein Hinweis von mir: Der Timer zählt ständig von 0 bis 255. Wenn 255 erreicht sind, kommt der Overflow Interrupt. Das dauert eine bestimmte Zeit. Wenn du den Zähler aber nicht bei 0 sondern bei x lauslaufen lässt, dann passiert was? (Da kommst du sicher selbst drauf. Ist völlig logisch).
Also, der Mikrocontroller läuft mit 8MHz. Das mit dem Prescaler Kommentar oben ist mir auch schon aufgefallen und ist falsch, der Teiler beträgt 8. Sowie ich das jetzt verstehe zählt das TCNT-Register von 0 bis 255 und fängt danach wieder bei 0 an. Wenn es jetzt nicht bei null anfängt sondern bei x, wird der Interrupt schneller ausgelöst.
Yep. D.h. du musst jetzt 'nur noch' die Werte rausfinden, bei denen du den Zähler loslaufen lässt, damit x Zeit vergeht, bis der nächste Overflow Interrupt kommt. Bei jedem Overflow Interrupt lädst du dann den Timer mit einem dieser Werte, schaltest den Port um und wartest bis der nächste Overflow kommt.
Genau. Aber bei 8 MHz bekommst Du mit den obigen Einstellungen keine 160 µs sondern 256 µs... Jetzt musst Du nur noch für die Zeiten, die Du brauchst, jeweils das TCNT0 mit dem richtigen Wert versehen.
Gut, soweit hab ich das kapiert! Die Dauer der High- und Low-Pegel kann ich schon ändern, das klappt. Nur verschiedene Längen bekomm ich noch nicht hin! Versteh das nicht ganz mit dem Port umschalten? Und wie kann ich denn angeben, mache beim ersten Overflow das, beim zweiten das usw.? So ganz check ich das noch nicht....
Das wäre der Vorteil der Sache mit dem Compare. Da bräuchtest Du bei jedem Interrupt nur nachsehen, was grad im Compare-Register (OCRxy) drinsteht und entsprechend den nächsten Wert reinschreiben, also z.B.: 1.-es tritt ein Compare-Interrupt auf 2.-Du schaltest den Portpin um 3.-Du schaust nach, was im OCR steht --steht eine 80 drin, dann schreib ne 20 rein --steht ne 20 drin, dann ne 100 rein --steht ne 100 drin, dann ist Ende der Sequenz 4.-Ende der Interrupt-Bearbeitung Das zu dem Beispiel ganz oben (Reihenfolge 80µs H - 20µs L - 100µs H). Bei der Lösung mit dem Overflow ist es komplizierter. Da müsstest Du mit einer globalen Hilfsvariable (Achtung! volatile nicht vergessen!) arbeiten, in der Du jeweils den aktuellen Reload-Wert für TCNT0 speicherst und den dann abfragst, also z.B.: 1.-Es tritt ein Overflow-Interrupt auf 2.-Du schaltest den Portpin um 3.-Du schaust, was in der Variable 'reload' steht --steht ne 176 drin, dann schreib ne 236 rein --steht ne 236 drin, dann schreib ne 156 rein --steht ne 156 drin, dann Ende der Sequenz 4.-Du schreibst reload in TCNT0 5.-Ende der Interrupt-Bearbeitung
Also ungefähr so: #include <avr/io.h> #include <avr/interrupt.h> volatile int reload; ISR(TIMER0_OVF_vect) { PORTB =~ PORTB; // PORTB inventieren reload = TCNT0; if (reload = 0) reload = 150; TCNT0 = reload; } int main() { DDRB = 0xFF; // PORTB als Ausgang schalten PORTB = 0x00; // Alle Ausgaenge auf 0 schalten TCCR0B |= (1 << CS01); //Teiler 8 TIMSK0 |= (1 << TOIE0); // Timer 0 Overflow Interrupt enable sei(); // Interrupts einschalten while(1); //ever } Doch wo gebe ich nun an, das er beim zweiten Overflow nicht mehr den Wert 150 sondern 30 ins TCNT0 schreibt. Wenn ich das auch in der ISR schreibe übernimmt er das doch sofort....
> Versteh das nicht ganz mit dem Port umschalten? Das versteh jetzt ich wieder nicht. Hast du doch schon gemacht: PORTB =~ PORTB; // PORTB inventieren > Und wie kann ich denn angeben, mache beim ersten Overflow das, > beim zweiten das usw.? Na ja. passeiern muss beim Overflow Interrupt immer das selbe: Der Timer wird mit einem Wert vorgeladen. Nur der Wert ändert sich von einem Overflow Interrupt zum nächsten. Du wirst also mitzählen müssen, der wievielte Interrupt das war. Mit diesem Wert gehst du zb. in eine Tabelle und holst dir von der Tabelle den nächsten vorzuladenden Wert: (Ich nehm jetzt mal irgendwelche Zahlenwerte) unsigned char Preloads[] = { 20, 180, 40, 230 }; unsigned char NrOverflow = 0; ISR(TIMER0_OVF_vect) { PORTB =~ PORTB; // PORTB inventieren // welcher Vorgabewert muss als // nächstes geladen werden? NrOverflow++; if( NrOverflow == 4 ) NrOverflow = 0; TCNT0 = Preloads[ NrOverflow ]; // Den Timer damit laden }
So wie Karl Heinz es unten vorgeschlagen hat, ist es elegant gelöst. Was bei Dir vor allem noch falsch ist, ist, dass Du TCNT0 in reload einliest! Was soll das? In TCNT0 steht am Anfang der Overflow-ISR immer der selbe Wert (wahrscheinlich 0, 1 oder 2, je nachdem, wie lange der Aufruf der ISR nach dem Auftreten des Interrupts dauert, und das sind u.U. schon einige Takte). Außerdem kann reload nur Werte von 0..255 annehmen, wozu also der int? Da reicht auch ein char.
Oops, da seh ich noch ein kleines Detail in deinem Code (den ich per Copy&Paste übernommen habe :-) PORTB =~ PORTB; // PORTB inventieren Das ist, nun ja, missverständlich formuliert (es geht nur um die Schreibweise). =~ bilden hier optisch eine Einheit. Da es in C ja diese Kurzschreibweisen, wie += *= &= etc gibt, könnte ein geneigter Leser beim drüberlesen, dass als eine dieser Kurzschreibweise ansehen. Ich muss gestehen, ich hab auch erst beim 3-ten Blick gesehen, dass es keine Kurzschreibweise ist! Und ich seh viel, was du noch nicht siehst! PORTB = ~PORTB; So ist es viel klarer, was hier wirklich passiert. Der Inhalt von PORTB wird genommen, wird invertiert und wieder PORTB zugewiesen. Das hat nichts mit irgendeiner dieser Kurzschreibweisen zu tun. Nur jetzt sieht mans auch auf einen Blick. Du glaubst gar nicht, wieviele läppische Fehler durch Verwendung einer vernünftigen Formatierung des Programmtextes vermeidbar sind.
Hey, vielen Dank! Jetzt hab ich es kapiert! Nur eine Frage noch. Wie bekomm ich es jetzt hin, dass er mit nach den 4 Interrupts einen dauerhaften High Pegel liefert?
Indem Du in der ISR den Port auf HIGH schaltest und den Timer anhältst...
Und noch eine kleine Frage. Wie kann ich das mit dem Port invertieren anders schreiben. Gibt es nicht ne Möglichkeit ihn einfach auf HIGH oder LOW zu setzen...??
> Gibt es nicht ne Möglichkeit ihn einfach auf HIGH oder LOW zu > setzen...?? Kann es sein, dass dir mächtig Grundlagen fehlen?
> Kann es sein, dass dir mächtig Grundlagen fehlen?
Vermutlich ist Björn ja deshalb hier und stellt diese Fragen, um sich
die fehlenden Grundlagen anzueignen.
Duck & wech...
...
Na, ja Es ist aber schon ein Unterschied ob jemand Schwierigkeiten mit dem Erzeugen eines Timings hat oder ob er Schwierigkeiten mit dem 'Einschalten einer LED' hat. In letzterem Fall ist die einzig sinnvolle Antwort: 'Schau ins Tutorial, dort ist alles erklärt. Wenn du das durchhast, dann haben wir mal eine Basis auf der man aufbauen kann' Ich erklär ja auch niemandem die Feinheiten einer Extremwertaufgabe, wenn er beim Lösen von einfachen Gleichungen in einer Variablen nur mit der Schulter zuckt.
Karl Heinz, du kennst meine Einstellung zum Thema Grundlagen aus anderen Threads und weißt daher vermutlich auch, wie du meinen Spruch zu interpretieren hast. Björn, wenn es unbedingt C sein muss, weil dir ASM zu primitiv ist, dann schau dir das GCC-Tutorial an und kauf dir ein C-Buch und versuche Beides zu verstehen. Ich mach' Sowas in ASM, für C bin ich zu doof. ...
> daher vermutlich auch, wie du meinen Spruch zu > interpretieren hast. Klar doch. Ich arbeite nur zur Zeit grade an einem Setup. Im Moment ist der Status so, dass zwar (meiner Ansicht nach) alles korrekt installiert wird, trotzdem schmiert das installierte Pgm sofort ab. Da hab ich wohl noch irgendeine DLL vergessen. Mal schön suchen :-) > für C bin ich zu doof. Das glaub ich nicht :-)
Hey, da Tuorial hab ich soweit schon durch. Das mit dem einschalten der LED's, einrichten PORT'S und DDR's habe ich verstanden. Ich denke meine Frage wurde oben falsch verstanden. Ich beschäftige mich erst seid ein paar Wochen mit der Programmierung von µC. Erst habe ich ein paar LED's ein und ausgeschaltet, danach dann Daten per UART gesendet und empfangen. Und nun wollte ich mich mal mit den Timern beschäftigen. Mein Ziel ist es nun, je nach ankommendem Byte daraus bestimmte Signale zu erzeugen und damit einen Servo zu steuern.... Zu dem Programm das Dank Karl Heinz und Johnny nun soweit läuft, bin ich schon ein kleines Stück weiter. Nun hab ich noch ne Frage. Hab mir das Signal gerade mal auf dem Osz. angeschaut. Wenn ich zb. 4mal den Wert 250 nehme oder 4mal den Wert 0, bekomme ich ein sauberes Signal. Bei verschiedenen Werten spiegelt sich das Signal leicht versetzt wieder. Woran kann das liegen....?? Schon einmal vielen Dank für die bisherige Hilfe!
> Mein Ziel ist > es nun, je nach ankommendem Byte daraus bestimmte Signale zu > erzeugen und damit einen Servo zu steuern.... Ein handelsübliches Modellbauservo? Das hat ein völlig anderes Timing. > Nun hab ich noch ne Frage. Hab mir das Signal gerade mal auf dem > Osz. angeschaut. Wenn ich zb. 4mal den Wert 250 nehme oder 4mal den > Wert 0, bekomme ich ein sauberes Signal. Bei verschiedenen Werten > spiegelt sich das Signal leicht versetzt wieder. Woran kann das > liegen....?? Bin mir jetzt nicht sicher ob ich deine Fehlerbeschreibung richtig verstehe. Was meinst du mit 'leicht versetzt'?
Was heißt 'versetzt'? Wenn Du die Verzögerungen meinst, die Du durch das verzögerte Umschalten der Portpins und das ebenfalls verzögerte Neuschreiben des TCNT0 bekommst, das liegt eben an der Zeit, die der µC benötigt um die ISR aufzurufen (dabei werden einige Register gesichert...), was schon einige Takte (so 1-2µs werden es wohl sein) in Anspruch nimmt. Der Port wird ja erst in der ISR, also einige Takte nach dem Auftritt des eigentlichen Interrupt-Ereignisses, umgeschaltet. Hinzu kommt noch die Tatsache, dass Du das Timer-Register TCNT0 ebenfalls erst verzögert neu schreibst. Dadurch gibts natürlich einen Versatz, den Du nicht hättest, wenn Du mit der Compare-Einheit arbeiten würdest. Da kannst Du nämlich einen bestimmten Portpin hardwaremäßig (also verzögerungsfrei) beim Compare-Ereignis umschalten und brauchst außerdem nicht das TCNT zu ändern, wodurch kein Versatz entsteht.
> Ein handelsübliches Modellbauservo? > Das hat ein völlig anderes Timing. Nein, keinen handelsüblichen Modellbauervo! Der Servo wird über eine bestimmte Impulsfolge gesteuert, wenn ein Byte empfangen wird. Wie das Signal zum steuern des Servos aussehen muss weiß ich. > Bin mir jetzt nicht sicher ob ich deine Fehlerbeschreibung > richtig verstehe. Was meinst du mit 'leicht versetzt'? Wenn ich mir ein Signal mit dem obigen Beispiel von dir erzeuge und setze 4mal die Werte für TCNT0 = 250 , bekomme ich ein sauberes Signal mit einem wechselden Pegel von 12µs. Sobald ich aber nur einen der Werte auf z.B. 247 ändere, bekomm ich zwar das gewünschte Signal, aber zusätzlich ein identisches Signal ein paar µs nach rechts verschoben und das Signal auch noch mal gespiegelt. Also es sieht so aus als wären es 3 Signale.
>> Ein handelsübliches Modellbauservo? >> Das hat ein völlig anderes Timing. > > Nein, keinen handelsüblichen Modellbauervo! Der Servo wird über > eine bestimmte Impulsfolge gesteuert, wenn ein Byte empfangen wird. > Wie das Signal zum steuern des Servos aussehen muss weiß ich. Alles klar. In diesem Forum ist mit einem 'Servo' normalerweise ein Modellbauservo gemeint. Daher dir Frage. > Wenn ich mir ein Signal mit dem obigen Beispiel von dir erzeuge > und setze 4mal die Werte für TCNT0 = 250 , bekomme ich ein > sauberes Signal mit einem wechselden Pegel von 12µs. Sobald > ich aber nur einen der Werte auf z.B. 247 ändere, bekomm ich > zwar das gewünschte Signal, aber zusätzlich ein identisches > Signal ein paar µs nach rechts verschoben und das Signal auch > noch mal gespiegelt. Also es sieht so aus als wären es 3 Signale. Ich kann mir immer noch nichts drunter vorstellen. Im Moment hört sich das für mich so an, als ob dein Oszi mal auf die eine Flanke und mal auf die andere Flanke triggert, daher siehst du am Oszi eine Überlagerung desselben Signals mit sich selbst. Der eine Puls ist 12 µs lang, der andere etwas kürzer. Wenn du die jetzt übereinander malst, könnte das der Beschreibung entsprechen. Kannst du vom Oszi einen Screenshot machen. Digicam - Photo machen, was auch immer.
Vermute ich mittlerweile auch. Kannst Du am Oszi die Holdoff-Zeit verstellen? Wenn ja, dann tu das mal und schau mal, was sich ergibt. Wenn die Holdoff-Zeit nicht 'synchron' mit der Zyklusdauer Deines Signals ist, dann 'wackelt' es halt auf dem Bildschirm...
Also am Osz. hab ich schon alles versucht, krieg das Signal nicht sauber hin. Hab mal ein Bild davon gemacht, leider nur mit dem Handy!
Kein Problem. Sieht tatsächlich so aus, als ob Dein Oszi abwechselnd mal auf den kurzen Puls und dann auf den langen Puls triggert. Zeigst du mir noch mal schnell das Pgm, dass da jetzt läuft?
Also ich habe nur einen Uralt-Oszi aus DDR-Zeiten, der arge Sync-Probleme hat. Aber beim Anzeigen selbst (mit AVR) erzeugter Signale gibt es doch die Möglichkeit, an einem weiteren Pin ein Synchronisationssignal zu generieren und damit den Oszi zu triggern. ...
Das ist ne gute Idee. Einfach an einem 2. Pin ein Sync-Signal erzeugen, wenn der AVR mit der Zeitsequenz von vorne anfängt.
Das hilft Dir zwar wahrscheinlich nicht weiter, aber es gibt Controller, z.B. den Renesas R8C/13, welche einen speziellen Timer-Modus haben, der sich "Programmable Waveform Generator" nennt. Mit diesem kann man die genaue High- und Lowzeit für einen Pin getrennt einstellen und so beliebige Impulsfolgen erzeugen.
Mit dem CTC Modus des Timers im Zusammenhang mit dem Compare Interrupt ist das auch kein Problem. Ich denke aber, dass es Björn mal darum geht, den Timer mal gaaaanz langsam in Betrieb zu nehmen und zu sehen wie man ihn programmiert und was man damit machen kann. Hab ich auch so gemacht.
Der Mega8 schafft das. Leider rückt Björn die Informationen nur häppchenweise auf Nachfrage heraus, so dass derzeit nicht bekannt ist, was der Mega8 außer der Impulserzeugung noch so alles machen muss. Denn das entscheidet, wie man an die Sache herangehen kann. Das Erzeugen einer Impulsfolge mit festem oder variablen Impulszeiten und Pausenzeiten kann ein Timer-Interrupt erledigen. Dazu würde ich (in ASM) die Zeiten (Timer1-Compare-Intervall) in einer Liste im SRAM führen und in der ISR mit Increment-Load einlesen. Dabei den Pointer überwachen und bei Erreichen des Endwertes definierten Pegel ausgeben (sbi oder cbi) und den Pointer auf Startwert setzen (ldi). In den übrigen Fällen (also nicht beim Telegrammende) wird der Port (aber nur das gewünschte Bit!) einfach invertiert. Das geschieht durch Einlesen (in) des Ports, exor mit Hilfsregister, in dem das zu toggelnde Bit gesetzt ist und Zurückschreiben des Ports. Sollen die Impulsmuster in Abhängigkeit von Ereignissen verändert werden, so kann das Hauptprogramm in aller Ruhe die Ereignisse pollen, auswerten und die neuen Impulszeiten in einen anderen SRAM-Bereich ablegen. Die ISR kann dann bei Erreichen des Telegrammendes (dann, wenn der Pointer zurückgesetzt wird) die neuen Zeiten in den dafür vorgesehenen SRAM-Bereich kopieren, womit Änderungen während des Telegramms verhindert werden (Glitch vermeiden). Also alles in allem ist das auf dem Mega8 mit meinen bescheidenen Kenntnissen (in ASM) machbar. In C geht das mit Sicherheit auch wenn man die Hardware des AVRs detailliert kennt und in C so formulieren kann, dass effizienter, hardwarenaher Code entsteht, der keine überflüssigen zeitverschwendenden Registersicherungen usw. enthält. Dazu muss man aber ASM und C schon recht gut beherrschen. Da mir die C-Kenntnisse fehlen, könnte ich es nur in (primitivem) ASM realisieren. ...
Hallo, nachdem ich von meinem Wochenendausflug zurück bin, werde ich mich nun wieder dem Timer widmen... Karl Heinz hat mal wieder Recht! Erst mal will ich schauen wie dieser Timer funktioniert und was ich damit überhaupt für Möglichkeiten habe. Das Programm habe ich angehängt! Wahrscheinlich wird es zwar mit dem anderen Timer, dem CTC-Modus und dem Compare Interrupt einfacher gehen. Doch das es mit diesem auch funktionieren muss, ist für mich Anreiz genug es damit hinzubekommen. Wie es bis jetzt funktioniert ist zwar ok, aber irgendwie nicht akzeptabel. Da jetzt aufzugeben bring mir glaube ich gar nichts. Wenn es mit diesem vernünftig funktioniert, werd ich es auch mal mit dem anderen probieren und den dann wohl auch verwenden. Nun zu meinem Projekt. Ich habe vor ein Modellauto zu steuern. Es sollen Licht, Blinker, Lenkung und Antrieb programmiert werden. Das Licht und die Blinker funktionieren bereits. Nun bin ich gerade beim Servo für die Lenkung. Wie das Signal des Servos aussehen muss weiß ich. Das habe ich bei einem funktionierendem Modell mit dem Osz. aufgezeichnet. Deshalb versuche ich dieses Signal einfach mal hinzubekommen und den Servo mal anzusteuern. Weiß nicht ob dieses die Richtige vorgehensweise ist, aber so habe ich es erst einmal vor um zu verstehen wie das alles funktioniert. Für Tips und Ratschläge bin ich super dankbar.... Gruß Björn
> Ich habe vor ein Modellauto zu steuern. Es > sollen Licht, Blinker, Lenkung und Antrieb programmiert werden. Das > Licht und die Blinker funktionieren bereits. Nun bin ich gerade beim > Servo für die Lenkung. Also doch ein Modellbauservo. Das Timing für solche Dinger ist ziemlich einfach. Du schickst ihm einfach ständig einen Puls. Die Breite des Pulses ist ein Mass für den Ausschlag des Servos. Pulsbreiten bewegen sich im Bereich von 1ms bis 2ms. Die meisten Servos können auch mit 0.8ms bis 2.4ms arbeiten. Tja. und das wars dann auch schon. Die 20ms Pause ist normalerweise nicht notwendig (die hat eigentlich mehr mit der Funkübertragung vom Sender zum Empfänger zu tun als mit allem anderen). Und ja: So ein Timer kann diesen 1-2 ms Puls ganz leicht alleine erzeugen.
Noch eine Idee (ohne mir das Programm angeguckt zu haben): Einige Timer haben ja 2 OC-Register. Wenn man nun beiden Registern aufeinanderfolgende Werte übergibt, so dass zwei Interrupts in den vorgegeben Zeitabständen auftreten (einer fürs Einschalten, der andere fürs Ausschalten), dann bräuchte man in den ISR nur den OC-Wert für das folgende Ereignis dort eintragen. Sprich: Die erste OC-ISR scheibt den Timer-Vergleichswert für den folgenden Interrupt ins das zweite Register. Die ISR für das 2. OC-Ereignis schreibt dann den Wert für das andere Ereignis in das Register. In den ISR wird dann auch immer der Portpin ein- und ausgeschaltet. (Modellbau-)Servo-Signale erzeugt man am einfachsten mit dem CTC-Interrupt (den man auch noch mit ICP und/oder OC koppeln kann...)
> Also doch ein Modellbauservo.
Nein, ist kein typischer Modellbauservo. Dieser Servo hat 4 Anschlüsse
noch einen eigenen Controller. Er wird mit einem Byte angesteuert,
wobei das erste Bit (15µs Impuls) als Starbit verwendet wird. Je nach
Länge der anderen Impulse wird dann die Stellung des Servos verändert.
Was'n das für ein Teil? (persönliches Modellbauinteresse geweckt). Kannst mal ein Photo machen? (Auto + Details)
Was das für ein Teil ist...? Gute Frage! Hab ich mich auch schon gefragt. Keine Aufschrift, kein Datenblatt, keine Infos! Hab halt nur das Ding in nem funktionierendem Auto und dabei festgestellt wie die Anschlüsse belegt sind und das Signal aussieht. Hab da noch ne Hand voll von und würd die schon gern verwenden. Kann Morgen mal nen Foto machen, hab die Dinger nicht hier....
Schade, irgendwie klappt das mit dem Timer 0 nicht. Kann es vielleicht sein, das die Signale sich irgendwie überlagern. Bei 4 gleichen Werten bekomme ich ja ein sauberes Signal, nur bei unterschiedlichen Werten treten diese Probleme auf. Hab jetzt einfach mal versucht, den Timer 2 im CTC Modus zu programmieren. Ein Signal gibt er mir aus, aber es wird nur der Wert für OCR2A benutzt, der in der ISR gesetzt wird. Der vordefinierte hat gar keine Auswirkung. Sind da irgendwelche Fehler im Programm? Hab es mal angehängt....
Wie schon oben gesagt: Du hast Verzögerungen drin durch den Aufruf der ISRs und das Ändern von TCNT0. Dadurch bekommst Du bei gleichen Werten immer denselben Fehler, der u.U. gar nicht großartig auffällt. Bei den anderen Varianten sieht das dann anders aus. Deshalb macht man so etwas auch mit Compare-Einheiten, weil die 1. ohne Verzögerung den Portpin umschalten und 2. eine Änderung des Compare-Wertes in der ISR sich nicht sofort auf das Timing auswirkt.
..Ach ja, und in dem Code mit Timer 2 darfst Du natürlich nicht den Overflow-Interrupt nehmen, weil der nie auftritt, es sei denn, OCR2 = 255. Du musst den Compare-Interrupt nehmen!
Und was ist dann an dem Timer4.C Programm falsch, dass im vorigen Beitrag angehängt habe? Hab es da mit dem Compare versucht, haut aber auch nicht ganz hin.....
Zunächst mal solltest Du den Kommentar an den Code anpassen. Dann denke ich nämlich nicht, dass Du den falschen Interrupt aktiviert hast!
OCIE2B ist doch aber der Compare Inrerrupt, nur der Kommentar dahinter ist falsch.....
Falsch ist außerdem, dass der ATMega8 kein Register OCR2A hat. Das (einzige) Compare-Register von Timer2 heißt OCR2! Es gibt auch kein TCCR2A und TCCR2B sondern nur TCCR2. Ich weiß nicht, was Dein Compiler dazu gesagt hat, aber ich wundere mich, dass Du nichts von Fehlermeldungen beim compilieren erzählst...
Dein Programm habe ich mangels C-Kenntnissen nicht angesehen. Aber wenn du mit Timer-overflow arbeiten willst/musst, dann solltest du nicht einfach den Reload-Wert in den Timer schreiben, sondern ihn zum aktuellen Timerstand dazu addieren. Denn der Timer läuft ja weiter, auch wenn die ISR aus irgendwelchen Gründen "Verspätung" hat. Somit wird durch Addition der Relosds zum (eingelesenen) Stand die Verspätung kompensiert. Besser ist natürlich das Benutzen der Compare-Einheiten. ...
Du stellst den Wert für Compare-Match A ein, benutzt aber den Compare-Match-B-Interrupt.
Das liegt daran, dass ich im Moment nicht Zuhause bin und hier in der Uni den Atmega48 verwende. Hätte ich vielleicht angeben sollen, Sorry! Also das mit den Registern passt soweit alles und ein Signal wird auch erzeugt. Kann die Länge der Impulse auch durch setzen des OCR2A Wertes ändern. Anscheinend wird wohl aber nur der Wert verglichen, der in der ISR ins OCR2A geschrieben wird. Bei dem anderen, den ich in meiner main definiert habe, passiert gar nichts. Und das kapier ich nicht ganz...
Autsch! Das hättest Du tatsächlich mal früher sagen können. Frage mich dann aber immer noch (und da bin ich scheinbar nicht der einzige), was Du mit dem Compare Match B Interrupt machst. Das ergibt alles keinen Sinn. OCR1B wird nirgend initialisiert und dürfte deshalb '0' sein. Das kann so nicht wirklich funktionieren...
Wenn ich eine Wert für den Compare Match B einstelle, bekomme ich einen dauernden High-Pegel......
Dann lass doch zum Teufel noch mal die Finger von dem Compare Match B!!! Was soll das überhaupt? Für ein einzelnes Signal brauchst DU nur einen Compare. Du musst Dich aber entscheiden!
Jo, jetzt habe ich es kapiert. War mir nicht ganz klar was jetzt der Unterschied zwischen Compare Match A & B ist. Hab es jetzt noch mal umgeschrieben und angehängt. Bis zu 2 Werte klappen ohne Probeme. Nur bei mehr als 2 kommen wieder diese komischen Überlagerungen im Signal..... Wie könnte ich denn die Werte anders in das OCR1A schreiben....??
Nochmal: Wie misst Du die Signale? Du musst bei der Darstellung am Oszi drauf achten, dass immer auf die selbe Flanke des Signals getriggert wird. Entweder mit nem externen Trigger oder durch Veränderung der Holdoff-Zeit. Sonst kriegst Du nie ein stehendes Bild!
Ich hab ja ein stehendes Bild! Hab die Holdoff-Zeit schon geändert und alles mögliche probiert. Wenn ich in die Single Seq gehe, habe ich ein saubers Signal! Ansonsten nicht.....
> Ich hab ja ein stehendes Bild!
Nein, hast du nicht.
Na ja. Das Bild steht schon, nur siehst du alle
(was weiss ich) 10ms einen anderen, verschobenen
Ausschnitt aus dem Signal:
Einmal so (nur jetzt mal so zum Bleistift)
+----+ +--------+
| | | |
| | | |
-----+ +--------+ +------
und dann wieder so:
+--------+ +----+
| | | |
| | | |
-------+ +------+ +------
Wenn dein Oszi jetzt alle (sagen wir mal) 10 ms
auf die jeweils andere Flanke triggert, dann siehst
du (mit deinem trägen Auge) die Überlagerung der beiden:
+----+-----+ +---+----+
| | | | | | |
| | | | | | |
-----+-+ +-----+--+---+ +------
Erkennbar daran, dass du in einem Puls auch bei 0V
eine durchgehende Linie hast. Da sollte eigentlich
nichts sein. Der Schreibstrahl sollte nur eine einzige
durchgehende Linie von Links nach Rechts sein.
Du musst dafür sorgen, dass das Oszi immer
auf dieselbe Flanke triggert.
> Jo, jetzt habe ich es kapiert. War mir nicht ganz klar was jetzt > der Unterschied zwischen Compare Match A & B ist. Es sind einfach zwei getrennte Einheiten. Für den Timer gibt es zwei Vergleicher, die ständig den aktuellen Zählerstand mit dem Wert in einem Register vergleicht. Sobald sie gleich sind, gibt es ein Compare-Match-Event, das z.B. einen Interrupt auslösen kann. Die beiden Einheiten sind eben mit A und B benannt. Wenn du das Compare-Match-A-Register benutzt, bedeutet das, daß du auch die Compare-Match-A-Einheit verwendest. Diese kann aber nur den Compare-Match-A-Interrupt erzeugen.
Krieg ich nicht hin. Hab es so eingestellt, dass das Osz. nur auf die steigende Flanke triggert. Bei nur zwei Werten in meinem Programm klappt es ohne Probleme und es gibt ein sauberes Signal so wie es sein soll. Bei mehr als zwei kommen dann diese komischen Überlagerungen raus. Warum klappt das denn mit zwei aber nicht mit mehr Werten? Hab auch schon mehrere andere Signale mit dem Osz. gemessen, klappt alles wunderbar.....
> Hab es so eingestellt, dass das Osz. nur auf die steigende Flanke > triggert. Das bringt eigentlich auch nichts. Lass dir nochmal das Posting von Karl Heinz durch den Kopf gehen. Wenn du nacheinander zwei unterschiedlich lange Pulse hast, triggert dein Oszi auf die steigende Flanke beider Pulse. Es weiß ja nicht, welches der erste sein soll und welches der zweite. Es überlagert dir deshalb zwei gegeneinander verschobene Bilder desselben Signals.
Kann es nicht vielleicht doch irgenwie am Programm liegen? Hat nicht jemand die Möglichkeit, das Programm mal bei sich laufen zu lassen um zu schauen ob es da funktioniert?
Wie kann ich denn dafür sorgen, dass das Osz. immer nur auf die selbe Flanke triggert? Ihr habt was von externen triggern erzählt. Wäre das ne Möglichkeit? Was muss ich da machen...?
Externe Triggerung bedeutet, daß du ein zweites Signal hast, das nicht dargestellt, sondern nur zur Triggerung verwendet wird. Das wäre eine Möglichkeit. Du müßtest an einem zweiten Pin des Controllers ein Signal generieren, das nur eine einzige steigende Flanke pro Periode hat, z.B. indem es nur den ersten Impuls enthält.
Ich habs, was für eine schwere Geburt....!!! Wie ihr es alle schon gesagt habt, lag es an der Triggerung...!! Nun klappt es! Vielen Dank für die Hilfe......!!!!
Der Servo Signal soll nun so aussehen: Wenn ein Byte übertragen wird fungiert das erste Bit (15µs Lowpegel) als Startbit. Dann jeweils 100µs Pause (Highpegel) zwischen den anderen. Je nach gewünschter Stellung des Servos müssen die anderen Impule eine bestimmte Länge haben. Werd mich jetzt mal daran machen das hinzubekommen.... Haste nen Tip für mich wie ich da am besten vorgehen sollte....?? Gruß
> Haste nen Tip für mich wie ich da am besten vorgehen sollte....?? > Schade, nen paar Tips hätt ich echt gut gebrauchen können...!!! Sorry, ich war den ganzen Tag nicht online. Andererseits nützen dir meine Tips nix, da wir unterschiedliche Sprachen sprechen (Programmiersprachen). Dann kenne ich die von dir erwähnten Servos nicht, die Servos, mit denen ich bisher zu tun hatte, hielten sich alle (halbwegs) an die im Modellbau seit Jahrzehnten übliche Norm. Mit Modellbau-Servoimpulsen habe ich schon Einiges gemacht, von der Servoimpulserzeugung über die Umcodierung bis hin zu Fahrtreglern, Schaltdecodern, Servoelektronik... Ein Teil davon steht auch im Netz, bei www.hanneslux.de/avr wirst du fündig. Quelltexte gibt's da auch, aber eben nur in primitivem Assembler, für C bin ich zu doof. ...
Hey, kein Problem! Geht mir gar nicht um die Programmierung, sondern eher die allgemeine Vorgehensweise. Hab ja schon soweit erklärt, wie der Servo angesteuert werden soll. Nun stellt sich mir die Frage, wie sich das am besten realisieren lässt. Im Moment erzeug ich mir ja das Signal mit dem Timer und setze die verschiedenen Pulslängen mit bestimmten Werten im Array fest, die dann im Compare Modus verglichen werden und den Port umschaltewn. So würd ich es machen: Je nach gewünschter Stellung werden die Werte im Aray geändert. Also je nach ankommendem Wert per UART würde ich dann halt die entsprechenden Werte ins Array schreiben. Nun weiß ich nicht ob es der richtige Weg ist oder es noch andere "bessere" Möglichkeiten gibt so etwas zu machen und welche davon empfehlenswert sind. Ist halt mein erstes "größeres" Projekt und die meisten hier haben ja schon eine Menge mehr Erfahrung mit der ganzen Thematik. Daher dachte ich dass mir der eine oder andere nen guten Tip zur Vorgehensweise für mich hätte... Gruß
Abend. Björn, mach dir jetzt nicht zuviele Gedanken. Häng einfach mal dein Servo an den µC an und sieh nach ob das Ding reagiert. Wenns klappt, dann anderst du mal die Werte im Array und siehst nach wie das Servo drauf reagiert. Danach beschäftigst du dich mal mit der UART. Und zwar ohne an das Servo auch nur im entferntesten zu denken. Immer nur eine Baustelle auf einmal! Wenn du dann über die UART was empfangen kannst (Dein erstes Testprogramm könnte zum Beispiel alles Empfangene einfach wieder an den Sender zurückschicken), kannst du ja mal mit einer einfachen Auswertung des Empfangenen anfangen: Wenn ein bestimmtes Zeichen ankommt, dann LED einschalten. Bei einem anderen LED ausschalten, etc. Einfache Übungen also. Wenn du soweit bist, dann kannst du mal anfangen die beiden Welten zusammenzubringen. Eine gute Methode zur Programmentwicklung ist das Arbeiten in kleinen Schritten. Die meisten Neulinge verfransen sich ganz einfach in der Komplexität einer Aufgabe. Also muss man die Komplexität drücken. Und das macht man, indem man Dinge isoliert betrachtet. Die UART hat zunächst mal nichts mit einem Servo zu tun. Klar, dein langfristiges Ziel ist es über die UART das Servo zu steuern. Nur: Die UART kann man auch für sich alleine untersuchen und programmieren. Daher wird das langfristige Ziel zu Gunsten eines kurzfristigen Ziels zurückgestellt (mit der UATR einfach mal klarkommen). Bei der Programmierung kann man unzählige Dinge falsch machen und sich selbst in eine Sackgasse manövrieren. Das ist nicht weiter schlimm. Das ist der ganz normale Lernprozess, den wir alle durchlaufen haben. Das kann dir auch keiner abnehmen, da musst du schon selber durch.
> Nun weiß ich nicht ob es der richtige Weg ist oder es noch > andere "bessere" Möglichkeiten gibt So was wie 'eine beste' Möglichkeit gibt es ganz selten. Im Zweifelsfall ist die 'beste' Möglichkeit diejenige die du realisieren kannst und die auch funktioniert. Klar lernt man mit der Zeit dazu. Man lernt was gut funktioniert und was weniger gut funktioniert. Aber: was in einem speziellen Fall gut ist, kann im nächsten speziellen Fall schon wieder schlecht sein. Hilft alles nichts: Erfahrung erlangt man nur in dem man verschiedene Möglichkeiten ausprobiert und rausfindet womit man gut klar kommt und womit nicht.
Hallo Karl Heinz, erst mal Danke für die Tips! > Häng einfach mal dein Servo an den µC an und sieh > nach ob das Ding reagiert. Also den Servo hab ich schonm mal drangehangen, da tut tut sich auf jeden Fall was. Werd Morgen mal das Programm umschreiben und mal schauen was sich bei anderen Werten tut. Dazu hätt ich ne Frage: Kann ich direkt das ganze Array neu beschreiben. Oder muss ich das für jeden Wert einzeln z.B. "Preloads[1] = 42" tun? > Danach beschäftigst du dich mal mit der UART Das hab ich schon hinter mir und es klappt (hab mich selber gewundert" schon ziemlich gut. Hab den UART auch mit nem Interrupt realisiert und kann Daten emfangen und senden. Hab da ein kleines Programm geschrieben, welches mir bei einem bestimmten emfangenen Zeichen eine LED ein/aus schaltet und das Zeichen wieder zurück schickt. Klappt bestens! Auch einen Blinker hab ich schon programmiert, der sich bei einem bestimmten empfangenen Zeichen ein/aus schalten lässt. Nun ist halt der nächste Schritt den Servo anzusteuern....
unsigned char preloads[] = {42;32;...}; siehe auch "Die Programmiersprache C" von einem gewissen Brian Kernighan und einem Dennis Ritchie...
So erzeuge ich mir das Array ja auch, das ist kein Problem. Nur ich wollte das Array aber so auch in einer switch Anweisung bei einem bestimmten "case" mit anderen Werten beschreiben. Nur da klappt es nicht. Sobald ich das Array da so mit neuen Werten versehen will. bekomm ich ne Fehlermeldung dass sich das break nicht in einer switch Anweisung befindet. Keine Ahnung warum, vielleicht gibt es da Probleme mit den {} Klammern....
Das {} geht auch nur bei der Array-Deklaration. Im Programm selbst kannst du nur auf die einzelnen Feldeinträge zugreifen. Vielleicht solltest du dir eine andere Lösung überlegen (Bits in einem Byte auswerten). Dann bräuchte man nur eine Ausgabe-Routine, der man einen ein Byte übergibt, das dann an das Servo "weitergeleitet" wird.
Hab ich mir fast gedacht. Das mit der Ausgabe-Routine hört sich gut an! Kannste mir das genauer erklären.....
Sei mal ein bischen erfinderisch :-) Du könntest zum Beispiel mit dir selbst vereinbaren, dass das Servo nach links drehen soll, wenn immer über die Serielle ein 'L' daherkommt. Das Servo soll nach rechts drehen bei einem 'R'. Und wenn ein 'C' daherkommt, dann soll das Serve wieder in Mittelstellung gehen (C wie center). Das Drehen ist doch kein Problem. Dazu muss einfach nur ein Wert in deinem Array immer kleiner und kleiner (bzw. groesser und groesser) werden. Und bei 'C' schreibst du einfach wieder den bekannten Wert für Mittelstellung hinein. Du könntest auch die Buchstaben von '0' bis '9' nehmen und jedem dieser Buchstaben einen bestimmen Zahlenwert fürs Array zuordnen, der dann in das Timingarray geschrieben wird oder ... Da gibt es kein Richtig oder Falsch. Was immer du mit dir selbst vereinbarst ist per Definition richtig.
Hey, noch eimal vielen Dank für eure Hilfe! Hab mir nun noch nen standard Servo bei Conrad besorgt und es erst mal mit dem Probiert. Klappt alles wunderbar. Mit den anderen Servos klappt das soweit auch, nur nach dem Einschalten zittern die Servos. Wenn ich sie dann über ein gesendetes Zeichen in die gewünschte Stellung bringen will, klappt das nur ab und zu. Nach ein paar gesendeten Zeichen hört das zittern irgendwann auf und es klappt alles wie es soll! Woran kann das mit dem zittern liegen? Und eigentlich müssten die Servos doch trotz zittern in die gewünschte Position fahren. Warum machen Sie das nur ab und zu? Gruß Björn
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.