Hallo zusammen, ich brauche einen Befehl für mein Assemblerprogramm, der das Programm für eine definierte Zeit (z.B. 500ms) anhält. (Ein Relais soll für eine kurze Zeit anziehen und wieder loslassen.) Oder mache ich das über einen Timer? Gibt es eine Art Delay-Befehl (nicht den nop)? Danke im voraus! :-) Sebastian
Also mir ist kein solcher Befehl bekannt. Du kannst entweder den Delay Schleifen generator(irgendwo zu downloaden, such in den Beiträgen)verwenden oder den Timer. Beides ist ziemlich einfach. Um nur schnell etwas mit dem Relais zu machen ist der Schleifengenerator ideal. Man kan genau eingeben wie lange gewartet werden soll und der Code wird generiert. Fritz 7
Wenn du während des Wartens was anderes zu tun hast bzw ein Interrput oder so ausgelöst werden könnte und die Zeit eher kritisch ist, ist die Lösung über den Timer weitaus eleganter als den Prozessor nop'en zu lassen, bis die 500ms vorbei sind (denn was anderes zum warten gibts eigentlich nicht).
Hi, danke für den Link. Allerdings geht es da ja meistens um einen Generator, der den Code erzeugt (die meistens Links sind defekt). Ist es denn wirklich so schwer? Gibt es nicht ein paar gut kommentierte Zeilen, daß ich auch weiß, was ich mache? Kennt noch jemand einen funktionierenden Link zu einem Generator? Danke! Sebastian
Der Generator baut dir einfach aus ein paar Befehlen (rjmp, cmp, inc...) eine Schleife mit der gewünschten Länge zusammen. Natürlich kann man das auch von Hand machen, nur wozu? http://www.home.unix-ag.org/tjabo/avr/AVRdelayloop.html
so als perfektionist: was mache ich denn wenn ich es ganz genau haben will? soweit ich das ueberblickt habe, ist in dem generierten code die zeit, die die schleifenverwaltung benötigt nicht mit einbezogen. da stellt sich natürlich die frage: war ich nur unfähig die anleitung zu lesen oder fehlt da eine liste mit der zyklenanzahl, die pro befehl benötigt werden? antworten werden dankbar entgegengenommen... Holger
Ich denke schon das die Takte für die Sprungbefehle usw mit eingerechnet werden. Ansonsten bekommt man ja völlig falsche Werte. Die Genauigkeit der Zeitmessung hängt auch mit der Taktfrequenz zusammen.
> Ich denke schon das die Takte.. Und ich weiß es. (Hab nachgesehen). > Die Genauigkeit der Zeitmessung hängt auch mit der Taktfrequenz zusammen. Nein. Vielleicht meinst du die Auflösung - die Genauigkeit jedenfalls hängt nicht mit der Taktfrequenz zusammen. Schmittchen.
jo, jetzt hab ich das auch kapiert, stimmt also wirklich genau. und hab auch inzwischen die zyklenanzahlen gefunden... instructions.pdf sei dank...
Hi Also mit den Schleifen beim AVR ist das so eine Sache. Als Beispiel sei die RC5 Fernbedienung genannt. Wenn man den Anhang anschaut, so ist das eine simpelst Geschichte bei der C-Control von Conrad. Programmiert man das Gleiche beim AVR, so erhält man alles, nur keine RC5 Befehle. Wenn man sich dann den generierten Code von Compilern (Bascom etc.) oder was zu RC5 an Codebeispielen zum AVR anschaut, so stellt man schnell fest, dass hier mit ober und unter-limits gearbeitet wird. Warum, wenn die Befehle immer die gleiche Zeit benötigen? Also ich habe meine absoluten Zweifel an Schleifen oder auch Timer Routinen beim AVR Peter
Hi Peter, ehrlich gesagt versteh ich das nicht so ganz was Du meinst. Wieso soll es Probleme geben mit Delays oder Schleifen beim AVR? Gruß Markus
Hi Markus Ich habe jetzt mal das umgeschriebene Prog für AVR angehängt. Ich weis zwar nicht mehr, auf welchem Versuchsstand das Prog ist, aber wenn du die beiden Progs vergleichst, siehst du, dass sie das gleiche machen. Der Unterschied, mal von Kleinigkeiten abgesehen, besteht darin, das Prog für C-Control funzt, das Prog für AVR funzt nicht. Ich habe dann das Prog für den AVR in alle möglichen Richtungen geändert, um zu erfahren, wie lange ein Biphasenslot dauert (mit Schleifen). Es kam nur selten der gleiche Schleifenzähler dabei raus. Wenn du dir dann mal die funktionierenden Progs für den AVR anschaust, siehst du, dass diese mit Zeitunter- und Zeitobergrenzen für einen Biphasenslot arbeiten. Ich habe jedenfalls kein funktionierendes RC5 Prog für den AVR gefunden, dass wie bei der C-Control mit festen Schleifenzählern läuft. lg Peter
Hi Peter, kenn mich mit RC5 nicht aus. Weiß auch nicht was mit Bipahsenlot gemeint ist. Aber ich sehe trotzdem keinen Grund warum man mit dem AVR keine Schleifen oder sowas machen können soll. Vielleicht liegt es einfach daran das der AVR um einiges schneller ist als die C-Control und daher bestimmte Programmiertechniken, die mit der C-Control noch funktionieren, mit dem AVR nicht mehr möglich sind. Deshalb evt die von Dir angesprochene Lösung mit den Ober- und Untergrenzen. Vielleicht um Signalungenauigkeiten, die die C-Control noch nicht registrieren würde, auszugleichen. Gruß Markus
> Der Unterschied [...] besteht darin, das Prog für C-Control funzt, das Prog für AVR funzt nicht Aha, und den Grund dafür suchst du dann am AVR? Seltsame Logik. Im Netz gibts genug RC5-Beispiele für AVRs. Die meisten bauen auf Peter Fleurys Implementation auf. <http://www.mysunrise.ch/users/pfleury/avr-software.html> Das Problem bei der Fernbedienungssoftware ist nicht AVR oder RC5, sondern daß nicht alle Hersteller sich streng an RC5-Specs halten. (Viele Fernbedienungen benutzen auch andere, RC5-inkompatible Protokolle - das nur nebenbei). Oftmals wird auch der EmpfägerIC (TSOP17xx) mit der falschen Frequenz eingesetzt. Das gleiche Problem sehe ich übrigens beim Empfang/Auswertung der DCF77-Signale. Wirklich idiotensichere AVR-Software dazu gibt es nicht... aber auch den AVR als solchen würde ich das nicht schieben. Schmittchen.
Hi erklärt mir doch einmal, was an meiner Logik falsch ist. Ein Bit dauert Biphasencodiert 1778 us. Bei 8MHz dauert ein Takt 0,000000125 s. 1778us = 0,001778 s. 0,001778 / 0,000000125 = 14224. Wenn nun das Biphasensignal in 4 Teile zerlegt wird, dann 14224 / 4 = 3556 Takte bis zur Abfrage des Pegels. Bei dem AVR vergehen bei mir zwischen 11000 und 17000 Takten pro Bit. Bei der C-Control ist die Anzahl der Takte immer gleich. Wie gesagt, ich möchte den AVR nicht madig machen. Wenn ich den nicht gut finden würde, dann würde ich ihn nicht nutzen. Tue ich aber. Ich warne nur vor Schleifenzählern. Möglicherweise treibt der AVR durch INTs etc. andere Dinge die Zeit brauchen. Dann ist mein Schleifenzähler makulatur. Was lernt man aus der von Schmittchen angegebenen Referenz? if ( (timer > RC5BITREF1) && (timer < RC5BITREF2) ) { // startbit ok, decode Nun, es wird ein Zeitslot für die Codierung verwendet, also kein fester Zeitwert wie bei der C-Control. Allein die Ermittlung des Zeitslots ist fast so aufwendig, wie der gesamte RC5-Code der C-Control. Das heist meiner Meinung nach aber nicht, dass die C-Control dem AVR überlegen ist. lg Peter
Vorweg: Mein "Peter-Fleury-Link" war leider nicht der, den ich zu diesem Thema bringen wollte (hab mich in meinen Bookmarks verirrt). Eigentlich wollte ich auf Bray verweisen: http://bray.velenje.cx/avr/ Und der verwendet einen Interrupteingang und den Timerinterrupt zur Dekodierung. > Bei dem AVR vergehen bei mir zwischen 11000 und 17000 Takten pro Bit. Nein, sondern genau 14224, wie du auch oben ausgerechnet hast (@8MHz). Nochmal: 1Bit ist 1778us lang. Bei 8MHz (=125ns) werden genau 14424 Takte ausgeführt. Das was du beobachtet hast, war wahrscheinlich bei einer AVR-RC5-Version, die nicht auf die fallende Flanke des IR-Empfängerausgangssignal reagiert. So können in deinem Falle noch alle möglichen Interrupts dazwischenkommen bis das Hauptprogramm per Polling merkt, daß da was ansteht. Oder je nach Zustand des AVRs/ext.Hardware kann die Ausführung einzelner Hauptprogrammteile unterschiedlich viel Zeit in Anspruch nehmen. Das kann dann zu den 11000-17000 Takten führen. Aber das hast du ja schon selbst bemerkt: > Möglicherweise treibt der AVR durch INTs etc. andere Dinge die Zeit brauchen... Wie RC5 am besten dekodiert wird, beschreibt Ralf Spettel auf seiner Seite sehr anschaulich: http://www.spettel.de/ralf/index_reload.html?http://www.spettel.de/ralf/projekte/avr_rc5/index.shtml Zieh dir die Seite mal rein, dann dürften auch deine Bedenken/Probleme bzgl. unterschiedlicher Taktzahlen im Prinzip geklärt sein. > Was lernt man aus der von Schmittchen angegebenen Referenz? Wie mans besser nicht machen sollte. ;-) oder besser :-( Können bei der C-Control keine "unvorgesehenen" Dinge (wie z.B. INTs oder je nach Situation zeitlich unterschiedlich lange Programmteile) auftreten? Das hieße dann ja, daß während RC5 dekodiert wird, nichts anderes ausgeführt werden kann? (Anm.: Ich kenne die C-Control nicht). Schmittchen.
Hallo Schmittchen Erst mal danke für die links. ich denke, die sind für alle interessant. weiter denke ich, dass wir den disput hier beenden können, denn mit deinem satz >Oder je nach Zustand des AVRs/ext.Hardware kann die Ausführung einzelner Hauptprogrammteile unterschiedlich viel Zeit in Anspruch nehmen. Das kann dann zu den 11000-17000 Takten führen. Aber das hast du ja schon selbst bemerkt:< haben wir ja doch noch einigkeit erzielt. ursprünglich wollte ich ja lediglich zur vorsicht bei der verwendung von schleifen mit schleifenzählern warnen. die rc5 problematik war nur ein beispiel zur verdeutlichung. viele standartroutinen zur zeitvertrödelung benutzen aber genau diesen schleifentyp. auch delay.zip auf der homepage von bray (http://bray.velenje.cx/avr/) verwendet diesen typ. dein zweiter link ist ebenfalls sehr hilfreich in der angelegenheit. er besagt, grob vereinfacht, vertraue nicht auf einen zähler, sondern synchronisiere dich mit hilfe von steigenden oder fallenden flanken. er weist auch darauf hin, dass die methode von bray problematisch sein kann. kann, nicht muss. lg peter
Hi Einen kleinen Einwurf habe ich noch. Wiso gibt es keine Idiotensichere SW für DCF77, wie meinst du das Schmittchen? Erst solltest du testen und dann drüber herfallen. Schleifen benutze ich übrigens nur um eine min. benötigte Zeit zu überbrücken, niemals aber um Zeiten zu messen,(naja fast nie) wozu sind denn sonst Timer da. Gruss Uwe
Hallo Uwe als ich die implementation der rc5 routine gestartet habe, war ich noch neu beim avr. was lag da näher, als mit schleifen zu arbeiten. der timer kam erst später. wenn man neu an einem prozessor rangeht, fängt man halt mit dem leichten (schleifen) an. wenn das dann schon nicht klappt, naja dann kann es sein, dass einem das selbstvertrauen zur timerprogrammierung fehlt. ging mir jedenfalls so. lg peter
> Wiso gibt es keine Idiotensichere SW für DCF77 Ich schrieb: "Probleme beim Empfang/Auswertung [...]. Idiotensichere Software gibts nicht..." Naturbedingt ist das DCF77-Signal nicht immer frei von Störungen und der Empfang ist auch nicht immer perfekt. Wenn man den TTL-Ausgang der gängigen DCF-Empfänger betrachtet, so erscheint das eigentlich immer sauber. Timingkritisch sollte die DCF-Kodierung im Gegensatz zu RC5 auch nicht sein. Dennoch schaffen es die meisten Softwareimplementationen nicht daraus die richtige Zeit zu erzeugen. Die meisten im Netz verfügbaren Sourcen bauen auf Volker Oths Code auf. Er verzichtet auf Plausibilitätsprüfungen, die meiner Meinung nach bei schlechtem Empfang wichtig sind. Z.B. wenn eben noch der 25.Aug war, dann kann in 5min nicht der 23.Aug sein usw.. Aber dann besteht ja auch das Problem, daß man anfangs eine verläßliche Zeit/Datumsangabe braucht, um solche Prüfungen durchzuführen. Zusammenfassend: Das Problem liegt im (teilweise) schlechten Empfang mit fehlender Plausibilitätskontrolle. Eine Software, die man irgendwo hernimmt, wie sie z.B. auf jeder AVR-Seite für LCDs bereitsteht, gibts für DCF77 ((noch)) nicht. Vom Gegenteil lasse ich mich natürlich gern überzeugen. Ich muß allerdings auch dazusagen, daß seit meinen Erfahrungen inzwischen über ein Jahr vergangen ist. Schmittchen.
Hi! Dann schaue dir bitte "DCF77-Uhr mit LCD" in der Codesammlung an, dann reden wir nochmal. Gruss Uwe
@Uwe: So wie ich deinen Assemblercode lese hast du auch keine Plausibilitätsprüfungen integriert (habe den für mich schwer lesbaren Ass.Code nur überflogen, falls doch implementiert, bitte nicht schlagen :-)). Und das Problem mit der Anfangsuhrzeit bleibt auch bestehen - irgendwann braucht man eine Uhrzeit, für die man sicher sein kann, daß sie stimmt, sonst sind alle Plausibilitätskontrollen unnütz. Dreimalige "gültige", aufeinanderfolgende Uhrzeit könnte dafür reichen. (Bei mir dauert das dann ca. 30min - 3h, bis 3x gültige Uhrzeit kommt; mit Volker Oths C-Code). Allgemein: Die Uhrzeit der Uhr per Taster (oder sonstwie) vorsagen, finde ich am Ziel einer Funkuhr vorbei. Ich habe hier auch eine gekaufte fertige Funkuhr (mit Funkthermometer), die auch manchmal die falsche Zeit anzeigt. Meist werden die Stunden als 0 erkannt. Dann ist um 16:54 -> 0:54. Ich bin hier ca. 150km südwestlich von Frankfurt. Zwischen Frankfurt und der Funkuhr (auch dem Modul) stehen noch ein paar Bildschirme und die Gebäudeaußenwand - evtl. wirkt sich das auf meinen schlechten Empfang aus? Schmittchen.
Hi Habe leider momentan etwas wenig Zeit. Ich mache einen Quersummentest zwischen alter und neuer Zeit. Da sich pro Übertragung meistens nur die Minute ändert ist das sehr wirkungsvoll. Will sagen: alte Zeit + 1 mit neue Zeit muss gleich sein, sonst war es fehlerhaft. Damit konnte ich bis jetzt alle fehlerhaften Zeichen verhindern. Das Ding läuft bei mir als Schaltuhr und da wären falsche Zeiten fatal. Ich mache es noch mal optisch: 12:20 30.8.02 Quersumme = 18+1 = 19 12:21 30.8.02 Quersumme = 19 Wenn das OK ist kann die Zeit synchronisiert werden. Es wird automatisch ein Fehler bei jedem Übergang der Minutenzehner erzeugt (19->20 Q1=3 Q2=2) Für 1.Synchr.braucht man, wenn Sign.OK 2 Minuten, wenn Sign. Müll halt länger. Ich habe schon mal ne 1/2h gewartet. Wie gesagt die Sache ist sehr sicher. Gruss Uwe
Ist zwar schon etwas her dieser Beitrag, aber vielleicht schaut einer von Euch doch nochmal vorbei. Hallo Sebastian, wie schon gesagt wurde, kann man Wartezeiten sehr gut und sehr genau über Zählschleifen realisieren, jedoch mit 2 Nachteilen: 1. man kann dabei nichts anderes machen. 2. wenn die maximale Zeit auch eingehalten werden soll, müssen alle Interrupts gesperrt sein. Deshalb macht man das üblicher Weise nur bei sehr kurzen Zeiten. Z.B. benötigt meine 1-Wire-Routine (z.B. für die Maxim DS18B20 Temperatursensoren) Zeiten von 15µs und 60µs. Bei Deiner Anwendung kommt es warscheinlich nicht darauf an, ob es 500 oder 600ms sind. Da würde ich dann einen Timerinterrupt nehmen, der einen Takt von etwa 100ms erzeugt. Und wenn das Relais eingeschaltet wurde, wird ein Register auf 6 gesetzt und der Timerinterrupt zählt es runter und schaltet das Relais ab, wenn der Wert 0 erreicht wurde. Hallo Peter, daß das AVR-Programm nicht funktioniert liegt bestimmt nicht an der Delay-Routine sondern daß es eben doch nicht gleich ist. Z.B. ist mir sofort ins Auge gesprungen, daß das SREG nicht gesichert wird, es ist doch aber ein Interrupt (endet mit RETI). D.h. Dein Hauptprogramm hat eigentlich gar keine andere Chance, als gegen den Baum zu laufen. Außerdem springst Du immer zum Ende (rjmp zurueck), das C-control aber zum Anfang (brset 0,PC,rc5). RC5-Dekodierung: Der RC5-Kode synchronisiert sich ja bei jedem Bit neu. Damit sind exakte Zeiten völlig unnötig. Abweichungen von 20% sind kein Problem. Man kann deshalb sogar ohne Quarz arbeiten und z.B. den Tiny15 nehmen. Der Trick ist natürlich, man synchronisiert bei jedem Bit und nicht nur beim Startbit. Mein Beispiel könnt Ihr im Anhang finden. Kurz zum Prinzip: Es wird mit dem Timerinterrupt die Zeit bis zum nächsten Pegelwechsel gemessen. Der Bittakt ist ja etwa 1,8ms. Alles kleiner als 0,75 * Bittakt ist nur der zusätzliche Puls, wenn das gleiche Bit nochmal kommt. Alles größer 1,25 * Bittakt ist ne Störung. Alles dazwischen ist dann das Bit. Der Pegel nach dem Wechsel entspricht dem Bitwert und wird in das Ergebnis reingeschoben. Das ganze 13 mal und fertig. Falls noch weitere Fragen sind, fragt ruhig. DCF77-Dekodierung: also meine Routine ist 100%-ig störsicher (das war doch wohl gemeint mit "idiotensicher"). Mir ist jedenfalls noch nie eine falsche Zeit untergekommen. Mann muß natürlich trotzdem noch eine normale Digitaluhr programmieren. Z.B. solange der Fernseher oder der PC-Monitor an sind, empfängt meine Routine absolut nichts. Ich benutze den Conrad-Empfangsmodul. Die Routine ist auf meiner Webseite: http://www.specs.de/~danni/appl/soft/c51/thclock/index.htm Die einzelnen Zeiten messe ich mit einem Timerinterrupt von 64Hz. Dann ergeben sich folgende Zählerwerte: 0-Bit: 6 (6,4) 1-Bit: 12 (12,8) Pulsabstand: 64 Synchronimpulsabstand: 128 Das ganze wird also in ein Byte gezählt und dann getestet: Puls < 5: Fehler 4 < Puls < 10: 0-Bit 10: Fehler 10 < Puls < 20: 1-Bit Puls > 20: Fehler Pulsabstand < 60: Fehler 60 < Pulsabstand < 70: O.K. 70 < Pulsabstand < 120: Fehler 120 < Pulsabstand < 140: Synchronimpuls Pulsabstand > 140: Fehler Zusätzlich werden noch die 3 Paritätsbits geprüft und ob es auch genau 59 Impulse waren. Alle diese Tests sind nur Byte-Vergleiche, d.h. der Rechenaufwand ist absolut minimal. Dafür spare ich ne Menge Code bei der Auswertung: Üblicher Weise sieht man oft riesige Programme, die jeden Impuls extra behandeln. Aber es wird eigentlich immer nur das gleiche gemacht: Es wird ein Wert (1,2,4,8,10,20,40,80) zu einer Speicherstelle addiert. Es gibt also nur eine kleine Tabelle, in der steht, welcher Wert auf welcher Adresse addiert werden muß. Und immer, wenn eine 1 addiert wird, wird vorher gelöscht. Und der Tabellenwert FF bedeutet, daß die Parität geprüft wird. Das ganze sind dann gerade mal lächerliche 250Byte an Code. Ich würde mich über Rückmeldungen freuen, d.h. ob Ihr mit Routinen nach diesem Prinzip immer noch diese Probleme habt. Peter
Hallo Peter >>daß das AVR-Programm nicht funktioniert liegt bestimmt nicht an >>der Delay-Routine sondern daß es eben doch nicht gleich ist. >>Z.B. ist mir sofort ins Auge gesprungen, daß das SREG nicht >>gesichert wird, es ist doch aber ein Interrupt (endet mit RETI). >>D.h. Dein Hauptprogramm hat eigentlich gar keine andere Chance, >>als gegen den Baum zu laufen. Wie ich schon schrieb, handelt es sich um einen Auszug des Programmes. Weiter schrieb ich, dass ich nicht mehr weis, welcher Stand der vielen Testversuche es ist. Aber egal. Tatsache ist, dass man mit Zeitschleifen über vertrödeln mit "nop" oder ähnliches keine genauen Zeiten hinkriegt. Versuche doch mal bitte die Routine von der C-Control in AVR (AT90S*) Assembler zu programmieren. Ich wünsche viel Freude. Weiter mußte ich feststellen, dass auch der Timer nicht besonders genau ist. Beispiel: Die Routine von Volker Oth zum DCF77 Signal prüft bei einem AVR mit 8 MHz und eingestelltem Teiler von 1024 mit dem Timer 1 und INT1 bei der 0 auf Timercounter 1 kleiner 1171, bei einer 1 auf kleiner 1953 und beim Startbit auf größer 11718. Bei mir wurde gemessen: Für eine 0 zwischen 600 und 960 Für eine 1 zwischen 1300 und 1600 Für das Startbit 14000 und 15500 also bei zwei Sekunden ungenau auf etwa 200 ms. Bei Zeitkritischen Sachen wohl etwas zu ungenau. Frage mich bitte nicht, woran das liegt. Die Funkuhr von Volker Oth funktioniert einwandfrei. Auch bei laufendem PC und laufendem Fernseher. Es sollte aber klar sein, dass Zeitschleifen ohne Sync über ein externes Signal problematisch sind. Welchen Grund das hat, ob z.B. die Taktfrequenz des Quarzes schwankt, oder bei Schaltvorgängen die Spannung kurz zusammenbricht, oder, oder, oder, muß man wohl von Fall zu Fall untersuchen. lg Peter
Hallo Peter, es gibt überhaupt keinen Grund, warum Verzögerungsschleifen oder Timer nicht genau sein sollen. Bei einem 2313 mit 10MHZ sind sie auf 100ns genau. Und die Quarze schwingen auch auf mindestens 6 Stellen genau. Wenn Du aber analoge Signale messen willst, können diese natürlich erheblich schwanken. Z.B. durch wechselnde Empfangssignalstärke und auch durch die Tiefpässe zum Ausfiltern der Trägerfrequenz kriegst Du automatisch erhebliche Schwankungen draufaddiert. Deshalb muß man bei Signalen aus der analogen Welt immer mit unteren und oberen Schwellen arbeiten. Wenn Du dagegen mit einem 2. AVR die Impulse erzeugen würdest, kriegst Du auch immer ein konstantes Meßergebnis geliefert. Die Programme habe ich mir nur auf Unterschiede und Fehler hin angesehen. Es macht ja auch keinen Sinn, ein Assemblerprogramm zu schreiben, wenn ich schon ein super funktionierendes C-Programm fix und fertig habe. Peter
Welche Probleme? Bitte ein konkretes _Minimal_-Beispiel. Die Ausführungszeiten der Befehle sind im Instruction Set genau angegeben, z.B. bei sbrc: | Cycles: | 1 if condition is false (no skip) | 2 if condition is true (skip is executed) and the instruction skipped is 1 word | 3 if condition is true (skip is executed) and the instruction skipped is 2 words Bei welchem Befehl ist diese Angabe falsch?
Hallo Andreas Ich weis nicht, ob Du mich meinst. Ich habe nicht behauptet, dass die Angabe der Anzahl der Takte pro Befehl falsch ist. Ich habe behauptet, dass die Ausführung von Schleifen unterschiedlich lange dauern kann. Auch der Timer 1 liefert unterschiedlich Timercounts bei der DCF77 Uhr von Volker Oth. Ich denke weiter, dass das Thema nun ausgereizt und ausdiskutiert ist. Wer glaubt, man könne den Werten trauen, der soll sie halt nutzen. Ich jedenfalls traue ihnen nicht mehr von 12:00 bis Mittag. Wer meint ich wäre im Unrecht, naja der soll mir eine funktionierende Umsetzung der C-Control Routine zeigen. Die ist wie gesagt winzig und funktioniert. Alle RC5 Programme des AVR sind wahre Monster im Vergleich dazu. Wenn ich so im unrecht bin, warum sind die Programme für den AVR so riesig? Warum arbeiten sie mit Bereichswerten und nicht mit starrem Schleifenzähler, meinetwegen auch festen Timercounts? Ich habe technisch nicht die Möglichkeit zu prüfen, was denn nun zu den Unterschieden führt. Ich denke aber mal, wenn ein und derselbe RC5 Empfänger an der C-Control feste Zeiten pro Bit hat und beim AVR nicht, wenn die Zeiten bei der DCF77 Uhr dermaßen schwanken, dann liegt für mich die Ursache eindeutig beim AVR. Wenn man dies berücksichtigt, dann kann man denke ich mit dem AVR alles machen. Die C-Control hat bei weitem nicht die Leistungsfähigkeit des AVR. In Assembler geschriebene Programme dürfen nur einige wenige Byte groß werden. Der Basic Interpreter ist extrem fehlerhaft, und, und, und. lg Peter
Die unterschiedlich lange Schleifendauer ist nur durch dazwischenkommende Interrupts erklärbar. Die Timer liefern auch genaue Werte, allerdings kann bei manuellem Reload Jitter auftreten, weswegen man bei hohen Frequenzen den Compare-Interrupt von Timer 1 verwenden sollte. Jitter gibt es zwar auch beim Aufruf dieses Interrupts, aber da der Timerwert hier nicht von Hand geändert werden muss, sondern automatisch zurückgesetzt wird, addiert sich dieser Fehler nicht. Ich kann deine Probleme nicht nachvollziehen. Das DCF77-Programm kann ich leider nicht ausprobieren, da ich kein DCF77-Modul habe (bzw. meinen Wecker nicht zerlegen will). Gruß Andreas
Hallo Andreas Leider war nur der Int0 für die RC5 aktiv. Somit schwer erklärbar. Bei der DCF77 war nur INT 1 aktiv und an dem hing die DCF77. Wie gesagt, mir fehlen die technischen Möglichkeiten, um abzuklären, was die unterschiedlichen Timercounts/Zeitschleifen erklärt. Ich denke, ich habe sogar Ints abgeklemmt, gepollt und trotzdem keine korrekte Erkennung der RC5 mit Zeitschleifen hinbekommen. Aber egal, die AVR's sind super und ich bleibe dabei. Probleme sind keine mehr, wenn man sie als Problem erkannt hat und einen Ausweg dafür gefunden hat. Dies ist sowohl bei RC5 als auch mit DCF77 gelungen. Man verwendet eben Zeitbereiche. Auch wenn das vielleicht für einen Perfektionisten nicht optimal erscheint, mir reicht es, wenn es funktioniert. Ich muß nicht für alles eine hieb und stichfeste Erklärung haben. Wenn mir einer eine gibt, super, wenn nicht, na dann eben klapt oder klapt nicht. Wenns klapt, bin ich zufrieden. lg Peter
Ich hab jetzt mal mein C-Programm in AVR Assembler umgesetzt. Ich kann nicht behaupten, daß es mit 30 Zeilen (=60 Bytes) sehr lang ist. Wie gesagt, alle Werte aus der Analogwelt können schwanken. Deshalb wird für ein Bit ein Zeitfenster von 0,75 ... 1,5 Bitzeiten getestet. Ich habs ausprobiert, es läuft sowohl mit einem 8MHz als auch mit einem 12MHz Quarz einwandfrei. Aber man sollte trotzdem die richtige Quarzfrequenz (z.B. 10MHz) eintragen. Dein Assemblerprogramm habe ich nicht verwendet, da Du ja die gesamte Zeit im Interrupt verwartest. Und das sollte man besser vermeiden, schließlich soll die CPU ja noch anderes zu tun. Peter
Hallo Peter damit wären wir ja mal wieder einig. Ein Zeitfenster muß her. Feste Werte klappen nicht. Mit Routinen, die beim AVR Zeitfenster verwenden, kannst Du die Strasse pflastern. Es ist mir aber kein Programm bekannt, welches mit festen Werten (ob über Schleifen oder über Timercounter) realisiert wurde. lg Peter
Das liegt aber, wie gesagt, am analogen Charakter der Übertragungsstrecke und daran, daß in den Fernbedienungen billige unpräzise Schwinger verwendet werden. Wenn Du Dir das C-Control-Programm genauer ansiehst, wartet die Funktion getbit auch innerhalb eines Zeitfensters auf einen Pegelwechsel und synchronisiert sich darauf neu. Und dieses Zeitfenster beginnt, genau wie bei mir, bei 3/4 * Bitzeit. Nichts ist es mit festen Werten. Wenn das in Deinem AVR-Programm nicht so ist, dann kann es auch nicht funktionieren. Wenn die Timer im AVR nicht auf den Quarztakt genau wären, dann müßte ja meine Digitaluhr ohne DCF77 nach dem Mond gehen und mein Frequenzmesser müßte auch ständig falsche Werte anzeigen, tun sie aber nicht. Der Frequenzmesser ist 5-stellig und auf alle Stellen genau. Und bei der Uhr sind erst nach einem Monat ein paar Sekunden Abweichung feststellbar. Der Mikrokontroller ist nun mal kein Mensch und braucht deshalb für die selben Befehle immer genau die gleiche Zeit. Peter
Och Peter guck mal richtig hin. >>;--------------------------------------------------------------------- -------- >>; Warteroutine fuer 1/4 eine Biphasenbits = 444us = 444T bei 12Mhz >>; ;Aufrufcall 2T >>w444 lda #147 ; 3 >>loop deca ; 3 A -1 >> bne loop ; 3 6*0,5µs = 3µs/Schleife >> rts Ich übersetze es Dir aber gerne. w444 = Label lda #147 = lade Akkumulator decimal 147 loop = label deca = akkumulator -- bne loop = wenn akkumulator > 0 dann springe zum Label loop rts = return subroutine Wo ist das Zeitfenster? Hier nochmal das Prog der C-Control ; RC5-Unterprogramm RC5.ASm ; nach einer Vorlage von Olaf Kaluza PC .equ $01 Cnt .equ $0A3 Kon .equ $0A4 Adr .equ $0A5 Tas .equ $0A6 .org $101 Clr Kon Clr Adr Clr Tas brset 0,PC,rc6 rc5 brset 0,PC,rc5 ;Warten auf Startbit jsr w444 ;warten brset 0,PC,rc5 ;Stoerimpuls jsr w444 ;Naechsten Biphasenpegel jsr w444 brclr 0,PC,rc5 ;Stoerimpuls jsr w444 jsr w444 brset 0,PC,rc5 ;Stoerimpuls jsr w444 ;Ok weiter jsr w444 ;Kontrollbit einlesen jsr getbit ;Kontrollbit hier im Carry rol Kon ldx #5 ;5 Adressbits rcadr jsr w444 jsr w444 jsr getbit rol Adr ;Bit merken dex bne rcadr ldx #6 ;6 Datenbits rcdat jsr w444 jsr w444 jsr getbit rol Tas ;merken dex bne rcdat rc6 rts ;Zurueck zum Hauptprogramm ;----------------------------------------------------------------------- ------ ;Einlesen eines Bits, Rueckgabe in Carry getbit lda #255 ;Timeout festlegen sta Cnt brset 0,PC,gethi ;Wird es ein Lowbit? gbl1 brset 0,PC,gbl2 ;Auf Biphasensprung syncronisieren dec Cnt bne gbl1 bra biterr ;Timeout gbl2 jsr w444 brclr 0,PC,biterr ;Fehler clc ;Lowbit rts gethi brclr 0,PC,gethi2 ;auf Phasensprung syncronisieren dec Cnt bne gethi bra biterr ;Timeout gethi2 jsr w444 brset 0,PC,biterr ;Fehler? sec ;Highbit rts biterr Clr Kon ;Fehler, alles löschen clr Adr clr Tas clr Cnt rts ;----------------------------------------------------------------------- ------ ; Warteroutine fuer 1/4 eine Biphasenbits = 444us = 444T bei 12Mhz ; ;Aufrufcall 2T w444 lda #147 ; 3 loop deca ; 3 A -1 bne loop ; 3 6*0,5µs = 3µs/Schleife rts .END Mann, wenn Du meinst, in der Proc getbit wird ein Zeitfenster abgefragt, i9rrst Du. Es wird lediglich abgefragt, ob eine Störung auf der Leitung war. Wie gesagt, nicht reden machen. Bitte mach das doch bitte im AVR eins zu eins. Sind doch nur ein paar Befehle. Solltest Du Fragen zu einem Befehl haben, ich sage Dir gerne was welcher Befehl macht. lg Peter
Also vor getbit wird 3 * w444 aufgerufen, damit ist erst 3/4 der Zeit rum. Richtig ? Dann in getbit steht: getbit lda #255 ;Timeout festlegen was soviel heißen soll, wie oberes Zeitlimit. Dann kommt: gethi brclr 0,PC,gethi2 ;auf Phasensprung syncronisieren was soviel heißen soll, wie auf die Flanke synchronisieren. Und darin wird wohl auch Dein Fehler liegen. Wenn, wie Du sagst, der AVR 100-mal schneller ist, müßte Dein Timeoutzähler dann ja auf 25500 gesetzt werden, d.h. ein Byte reich da nicht mehr. Peter
Hallo Peter wo liegt das Problem? Machs doch genau so im AVR. Und damit soll es nun auch genug sein. Ich weis nicht, vielleicht drücke ich mich unverständlich aus. Mach es genau so wie bei der C-Control. Wenn es dann funktioniert, dann gebe ich mich geschlagen. lg Peter
Ich will Dich nicht schlagen. Ich machs doch im Prinzip wie bei der C-Control. Ob nun Timerinterrupt oder Delay-Loop ändert doch am Prinzip überhaupt nichts. Es macht nur die Implementierung leichter. Statt: .equ XTAL = 10000000 ;10Mhz kann ich auch schreiben: .equ XTAL = 4000000 ;4MHz und es funktioniert auch sofort mit dem anderen Quarz. Versuch mal, das C-Control-Programm nachzuvollziehen. Dann wirst Du sehen, daß es genauso arbeitet, wie meine Routine. Peter
Lieber Peter ich habe das C-Control Programm erst exakt, dann in ich weis nicht wie vielen Variationen umgesetzt. Es klapt eben nicht. Die Diskusion geht langsam in eine Richtung, die keinen Erfolg verspricht. Also, wenn du eine exakte Umsetzung des Programmes für den AVR hast, und nicht etwas mit Zeitfenstern oder ähnliches, dann bin ich der Erste, der zugibt, dass ich im Unrecht bin. Solange ich das nicht sehe, sage ich: 1. Delays über Zeitschleifen mit festem Schleifenzähler kannst Du vergessen. Dies wird auch unisono in den diversen Foren bestätigt. Lies Dir vielleicht noch mal die Beiträge durch. 2. Auch mit dem Timercounter ist Vorsicht geboten. Auch dies wird in anderen Foren bestätigt. 3. Ich sage nicht, woran das liegt, denn das weis ich nicht. Es gibt eine Reihe von Möglichkeiten woran es liegen könnte. Auch diese sind in den Beiträgen zu Warte-Befehl in Assembler aufgeführt. lg Peter
Daß Dein AVR-Programm gar nicht gehen kann, weil der Timeout-Zähler für die obere Zeitschwelle viel zu klein ist, hast Du wohl überlesen. (Letzter Satz im 4.Beitrag über diesem). Das ist diese Zeile in Deinem Programm: ser r24 ;Cnt=255 Nötig wäre >700, d.h. 2 Register. Peter
Langsam wird die Diskussion hier etwas diffus. Am Ende weiss wohl keiner mehr wie die Timer und Interrupts bei der AVR Familie wirklich funtionieren. Ich möchte an dieser Stelle nur folgendes anmerken sowohl mit reinen Zählschleifen als auch mit Timer-gesteuerten Routine sind auf 1clk genaue Delays möglich es kommt immer auf die Programmstruktur an.
@Peter: 1. Zu den Zeitschleifen: Mach das Programm aus dem Anhang in einen AVR. Schließ ein Oszi an PB0 an. Freu dich über das wunderhübsche, jitterfreie Rechteck. 2. Zu den Timern: Toggel ein Pin im Compare-Interrupt von Timer 1, schreib irgendwas sinnloses kompliziertes in die Hauptschleife. Freu dich über das wunderhübsche, mit vorhersagbarem Jitter versehene Rechteck. Mehr gibt es zu dem Thema nicht mehr zu sagen.
Wenn dem so wäre, dass ich beim Timer gesteuerten Delay immer mit einem Jitter leben müßte, hätte ich den Einsatz Einsatz von AVR Prozessoren längst be- endet bzw. hätte die Prozessorarchitektur nicht richtig verstanden.
Du mußt mit dem Jitter leben. Er entsteht dadurch, dass beim Auftreten des Interrupts der aktuelle Befehl noch fertig abgearbeitet wird. Die Zeitunterschiede sind natürlich minimal, aber bei sehr hohen Interruptfrequenzen durchaus spürbar. Wenn du den Timer 0 ohne Prescaler und mit manuellem Reload verwendest, setzt sich der Fehler sogar fort. Das hat mich schonmal mehrere Stunden Fehlersuche gekostet...
Also ich nicht. Der Unterschied zwischen den Aus- führungszeiten 1/2 Takte ist ja sehr gering und in einer Interruptroutine sehr leicht ausgleichbar. Ohne Prescaler ist auch das manuelle Reload ohne Jitter möglich. Da ich ja immer eine genaue Kontrolle über jeden einzelnen Taktimpuls habe. Etwas komplexer werden die Korrekturen bei eingeschaltetem Prescaler. Bei den neueren AVRs der MEGA-Familie hat Atmel zusätzlich die Möglichkeit eines Prescaler Reset spen- diert, welchses die Programmierung an dieser Stelle erheblich vereinfacht.
In meinem Programm (DDS) wird der Interrupt alle 24 Takte ausgelöst, da fallen ein oder zwei Takte Unterschied schon ins Gewicht. Mit Prescaler hast du doch ewig Zeit den Timerwert zu setzen, ohne Prescaler läuft der Wert schon weiter während du ihn ausliest. Wenn man anstatt einen festen Wert zu laden den Zählerstand mit subi ändert, sollte sich der Jitter theoretisch trotzdem eliminieren lassen. Ich hab das damals erst so probiert, es gab jedoch irgendwelche Probleme die mich zum Umstieg auf Timer 1 gezwungen haben. Müsste das Ganze vielleicht irgendwann nochmal simulieren und vielleicht einen "Artikel" dazu schreiben...
Ohne Prescaler und ohne weitere aktive Interrupts sorgt dieser Kunstgriff für einen Ausgleich der 2 unterschiedlich Befehlslängen. Allerdings sind die Möglichkeiten wie zusätzliche WAIT-States bei externem Speicher (ist mir sowie zu langsam und maximal 4K internes SRAM dürften wohl in jedem Fall reichen) nicht berücksichtigt. in R16,TCNTx(L) ;R16 = aktueller Wert Timer 0,1,2 sbrc R16,0 ;Bit0 = 0 -> jump = 2 Takte sonst ;no JUMP = 1 Takt sbrs R16,0 ;immer 2 Takte Bei aktivem Prescaler ist die Sache mit der taktge- nauen Synchronisation etwas aufwendiger, da ja hier keinerlei Möglichkeit besteht, den Zählerstand des Prescalers auszulesen.
Hi! gerade mit Prescalers geht es am besten. bei zb.einem Prescalers von 1024 hast du genau 1024 Takte Zeit um dich um den Timerwert zu kümmern ohne das Verfälschungen auftreten. Gruss Uwe
Wenn der Wert des Timer-Register TCNTx von Bedeutung ist, hast du recht. Wenn du aber z.B. ein Rechteck mit exaktem 50/50 Tastverhältnis an einem Port via Timer-Interrupt erzeugen willst und z.B. noch Interrupts vom UART und SPI autreten, wird's bei aktivem Prescaler richtig schwierig dieses zu realisieren, da man ja mit unterschiedlichen Response-Zeiten beim Timer-Interrupt rechnen muß.
Hallo, wow was für thread ! ich fand es hochspannend die Beiträge zu verfolgen eine Frage: könnte von euch jemand die DCF-Decodierroutine so in assembler schreiben das man sie in ein Bascom-Basicprogramm einbinden kann? Es gibt allerding einige Dinge die man dabei beachten muß Die Hilfebeschreibung dazu weiter unten eine andere Möglichkeit wäre das Assemblerprogramm einmal ausführlich zu kommentieren so das man es auch als Basic/Turbo-Pascal-Programmierer versteht. Dann könnte ich etwas äquivalentes in Basic programmieren oder wäre der Basic-code nicht schnell genug? Vielen Dank schon im Voraus viele herzliche Grüße Stefan Mixing ASM and BASIC BASCOM allows you to mix BASIC with assembly. This can be very useful in some situations when you need full control of the generated code. Almost all assembly mnemonics are recognized by the compiler. The exceptions are : SUB, SWAP,CALL and OUT. These are BASIC reserved words and have priority over the ASM mnemonics. To use these mnemonics precede them with the ! - sign. For example : Dim a As Byte At &H60 'A is stored at location &H60 Ldi R27 , $00 'Load R27 with MSB of address Ldi R26 , $60 'Load R26 with LSB of address Ld R1, X 'load memory location $60 into R1 !SWAP R1 'swap nibbles As you can see the SWAP mnemonic is preceded by a ! sign. Another option is to use the assembler block directives: $ASM Ldi R27 , $00 'Load R27 with MSB of address Ldi R26 , $60 'Load R26 with LSB of address Ld R1, X 'load memory location $60 into R1 SWAP R1 'swap nibbles $END ASM A special assembler helper function is provided to load the address into the register X or Z. Y can may not be used because it is used as the soft stack pointer. Dim A As Byte 'reserve space LOADADR a, X 'load address of variable named A into register pair X This has the same effect as : Ldi R26 , $60 'for example ! Ldi R27, $00 'for example ! Some registers are used by BASCOM R4 and R5 are used to point to the stack frame or the temp data storage R6 is used to store some bit variables: R6 bit 0 = flag for integer/word conversion R6 bit 1 = temp bit space used for swapping bits R6 bit 2 = error bit (ERR variable) R6 bit 3 = show/noshow flag when using INPUT statement R8 and R9 are used as a data pointer for the READ statement. All other registers are used depending on the used statements. To Load the address of a variable you must enclose them in brackets. Dim B As Bit Lds R16, {B} 'will replace {B} with the address of variable B To refer to the bitnumber you must precede the variable name by BIT. Sbrs R16 , BIT.B 'notice the point! Since this was the first dimensioned bit the bit number is 7. Bits are stored in bytes and the first dimensioned bit goes in the LS bit. To load an address of a label you must use : LDI ZL, Low(lbl * 1) LDI ZH , High(lbl * 1) Where ZL = R30 and may be R24, R26, R28 or R30 And ZH = R31 and may be R25, R27, R29 or R31. These are so called register pairs that form a pointer. When you want to use the LPM instruction to retrieve data you must multiply the address with 2 since the AVR object code consist of words. LDI ZL, Low(lbl * 2) LDI ZH , High(lbl * 2) LPM ; get data into R0 Lbl: Atmel mnemonics must be used to program in assembly. You can download the pdf from www.atmel.com that shows how the different mnemonics are used. Some points of attention : * All instructions that use a constant as a parameter only work on the upper 16 registers (r16-r31) So LDI R15,12 WILL NOT WORK * The instruction SBR register, K will work with K from 0-255. So you can set multiple bits! The instruction SBI port, K will work with K from 0-7 and will set only ONE bit in a IO-port register. The same applies to the CBR and CBI instructions. You can use constants too: .equ myval = (10+2)/4 ldi r24,myval+2 '5 ldi r24,asc("A")+1 ; load with 66 Or in BASIC with CONST : CONST Myval = (10+2) / 4 Ldi r24,myval How to make your own libraries and call them from BASIC? The files for this sample can be found as libdemo.bas in the SAMPLES dir and as mylib.lib in the LIB dir. First determine the used parameters and their type. Also consider if they are passed by reference or by value For example the sub test has two parameters: x which is passed by value (copy of the variable) y which is passed by reference(address of the variable) In both cases the address of the variable is put on the soft stack which is indexed by the Y pointer. The first parameter (or a copy) is put on the soft stack first To refer to the address you must use: ldd r26 , y + 0 ldd r27 , y + 1 This loads the address into pointer X The second parameter will also be put on the soft stack so : The reference for the x variable will be changed : To refer to the address of x you must use: ldd r26 , y + 2 ldd r27 , y + 3 To refer to the last parameter y you must use ldd r26 , y + 0 ldd r27 , y + 1 Write the sub routine as you are used too but include the name within brackets [] [test] test: ldd r26,y+2 ; load address of x ldd r27,y+3 ld r24,x ; get value into r24 inc r24 ; value + 1 st x,r24 ; put back ldd r26,y+0 ; address of y ldd r27,y+1 st x,r24 ; store ret ; ready [end] To write a function goes the same way. A function returns a result so a function has one additional parameter. It is generated automatic and it has the name of the function. This way you can assign the result to the function name For example: Declare Function Test(byval x as byte , y as byte) as byte A virtual variable will be created with the name of the function in this case test. It will be pushed on the softstack with the Y-pointer. To reference to the result or name of the function (test) the address will be: y + 0 and y + 1 The first variable x will bring that to y + 2 and y + 3 And the third variable will cause that 3 parameters are saved on the soft stack To reference to test you must use : ldd r26 , y + 4 ldd r27 , y + 5 To reference to x ldd r26 , y + 2 ldd r27 , y + 3 And to reference y ldd r26 , y + 0 ldd r27 , y + 1 When you use exit sub or exit function you also need to provide an additional label. It starts with sub_ and must be completed with the function / sub routine name. In our example: sub_test: When you use local variables thing become more complicated. Each local variable address will be put on the soft stack too When you use 1 local variable its address will become ldd r26, y+0 ldd r27 , y + 1 All other parameters must be increased with 2 so the reference to y variable changes from ldd r26 , y + 0 to ldd r26 , y + 2 ldd r27 , y + 1 to ldd r27 , y + 3 And of course also for the other variables. When you have more local variables just add 2 for each. Finally you save the file as a .lib file Use the library manager to compile it into the lbx format. The declare sub / function must be in the program where you use the sub / function. The following is a copy of the libdemo.bas file : 'define the used library $lib "mylib.lib" 'also define the used routines $external Test 'this is needed so the parameters will be placed correct on the stack Declare Sub Test(byval X As Byte , Y As Byte) 'reserve some space Dim Z As Byte 'call our own sub routine Call Test(1 , Z) 'z will be 2 in the used example End When you use ports in your library you must use .equ to specify the address: .equ EEDR=$1d In R24, EEDR This way the library manager know the address of the port during compile time. As an alternative precede the mnemonic with a * so the code will not be compiled into the lib. The address of the register will be resolved at un time in that case. This chapter is not intended to learn you ASM programming. But when you find a topic is missing to interface BASCOM with ASM send me an email.
Hallo Stefan, mal als Tip: Die Diskussion wird dadurch nicht gerade gut lesbar, wenn man riesige Textbandwürmer mittendrin einfügt. Besser ist es, dafür die Option "Dateianhang" anzuklicken. Peter
Dem kann ich nur zustimmen. Zumal es sich hier um Auszüge aus einem Handbuch handelt, würde ein entsprechender Link ausreichen. Auf der BASCOM Hompage findet sich übrigens auch ein entsprechendes DEMO-Programm zur DCF77 Decodierung in BASIC
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.