Hallo, weil mich das interessiert und man ohne Quarz 2 Pins mehr hat am ATtiny, gehe ich das mal an. Nun liest man ja darüber, dass man mittels 0x55 übertragen "einen Takt" auf der seriellen erzeugt und beim sich zu syncronisierenden µC die Zeiten ausmisst und damit am Ende den OSCCAL Wert korrigiert. Das bin ich gedanklich durchgegangen. Mich stört daran, dass man vom idealen Quarztakt beim Sender ausgeht. Sonst stimmt die gesamte Rechnung im Empfänger nicht mehr. Ich möchte aber eine universelle Syncronierung programmieren. Egal welcher Takt der Sender hat. Wenn man zum Bsp. 2 ATtinys mit internen RC aufeinander loslässt und die sollen sich erstmal im Takt syncronisieren bevor sie serielle Daten austauschen. Jetzt dachte ich mir, ich sende eine Weile mit kurzer Pause immer 10x 0x55 hintereinander. Dabei testet der Empfänger selbstständig mit - und + seines OSCCCAL Wertes beide Extreme aus, bis er das 10x 0x55 nicht mehr fehlerfrei empfängt. Dann bilde ich den Mittelwert von beiden extremen OSCCAL Werten und habe den perfekten "Korrektor"-Wert ermittelt. Unabhängig vom Takt. Wäre doch einfach und perfekt. Nur warum macht das so niemand?
Du übersiehst dabei zwei Dinge: 1. Die Frequenzen bis zu denen der UART die Daten noch fehlerfrei empfangen kann, liegen nicht unbedingt symmetrisch um die ideale Frequenz. 2. Der Zusammenhang zwischen dem OSCCAL-Wert und der resultierenden Frequenz ist nicht linear und bei manchen AVRs noch nicht einmal stetig. Übrigens geht die "herkömmliche" Methode keineswegs von einer idealen externen Taktquelle aus, sondern nähert sich immer dem gegebenen Takt soweit als möglich an. Der OSCCAL-Wert wird ja nicht aus dem Timer-Wert errechnet, sondern so lange verändert, bis der Wert gefunden ist, bei dem der lokale Takt der gegebenen Taktquelle am nächsten kommt.
:
Bearbeitet durch User
Ich hab mal den Osccal auf einen 32kHz Quarz synchronisiert. Das geht auch sehr gut. Der Osccal lief dann stabilisiert bei ca 7.xx MHz.
Hallo, das die serielle Baudrate zum µC Takt passen muß, ist mir bewußt. Ich möchte nur sicherstellen, um beim Bsp. mit 2 ATtinys zu bleiben, dass eben dann der Empfänger auch mit 8,2MHz taktet wenn der Sender mit 8,2MHz taktet. Und nicht mit angenommenen 8MHz. Weil zum Bsp. die Spannung statt 3,3V eben 4,5V beträgt oder was auch immer. Aber eines habe ich wohl übersehen. Die Nichtlinearität zwischen OSCCAL und µC Takt. Also muß man vorher den Sender µC Takt ausmessen und dann wie üblich beschrieben im Empfänger die Taktzeiten ermitteln und rechnen? Weil wenn ich im Empfänger µC mit idealen 8MHz rechne aber der Sender mit 8,2MHz taktet nützt mir das ja auch nichts. Ich dachte ich hätte die übliche Methode verstanden. Scheinbar doch nicht. Wenn ich im Empfänger mittels Timer die eingehenden Takte ermittel, dann rechne ich ja mit idealen Werten. Weil ich meinen eigenen Takt ja nicht genau kenne. Ich gehe davon aus, dass so und soviel Timercounts der und der Zeit entsprechen. Irgendeine genaue Taktangabe zum rechnen benötige ich doch? Am besten den µC Takt vom Sender. Mit ganz im dunkeln stochern und darauf syncronisieren möchte mir nicht in den Kopf. Irgendeine Größe muß doch vorher bekannt sein.
Veit D. schrieb: > Weil wenn > ich im Empfänger µC mit idealen 8MHz rechne aber der Sender mit 8,2MHz > taktet nützt mir das ja auch nichts. Das verstehe ich nicht ganz. Genau das willst Du doch. Wenn der Sender 200khz schneller läuft, muss der Empfänger doch auch entsprechend schneller laufen. Selbst wenn der Sender nur mit 4Mhz laufen würde, hättest Du mit dieser Methode eine funktionierende Kommunikation. Gruß, Stefan
Ich wuerd den Osccal in die Mitte Setzen undvon dort aus zu synchronisieren versuchen. Der Empfaenger synchronisiert auf den Sender. Eine Referenz braucht man nicht, aber ein Protokoll.
Veit D. schrieb: > Das bin ich gedanklich durchgegangen. Mich stört daran, dass man vom > idealen Quarztakt beim Sender ausgeht. Wie kommst du auf diesen Unsinn? Natürlich geht niemand in diesem Fall von einem idealen Quarztakt des Senders aus. Schlicht und einfach schon deshalb, weil das für das Ziel der erfolgreichen Kommunikation keine Rolle spielt. Dafür muss nur der Takt von Sender und Empfänger genau genug übereinstimmen, die absolute Größe dieses Taktes ist völlig irrelavant. > Egal welcher Takt der Sender hat. Wenn man zum Bsp. 2 > ATtinys mit internen RC aufeinander loslässt und die sollen sich erstmal > im Takt syncronisieren bevor sie serielle Daten austauschen. Ist doch kein Problem. Es darf eben nur einer der beiden nachkalibrieren. Nun muss man sich nur noch ein kleines Protokoll ausdenken, mit dem ausgehandelt wird, wer der "Master" sein soll, der seinen Takt konstant hält. Das Protokoll muss natürlich so gestaltet sein, dass es keine bereits funktionierende serielle Verbindung voraussetzt. > Jetzt dachte ich mir, ich sende eine Weile mit kurzer Pause immer 10x > 0x55 hintereinander. Dabei testet der Empfänger selbstständig mit - und > + seines OSCCCAL Wertes beide Extreme aus, bis er das 10x 0x55 nicht > mehr fehlerfrei empfängt. Dann bilde ich den Mittelwert von beiden > extremen OSCCAL Werten und habe den perfekten "Korrektor"-Wert > ermittelt. Unabhängig vom Takt. Da solltest du nochmal ernsthaft drüber nachdenken...
Normales Autobaud funktioniert über die Länge des Startbit, denn die Autobaud Routine sollte nicht von einem bestimmten Zeichen abhängen. Die Länge wird einmalig mit einem Timer oder so gemessen und dann in die richtige UART Baudrate umgeschnurzelt.
Hallo, irgendwie verstehen wir uns noch nicht. Wie soll ich den mit dem Timer irgendwas vermessen, wenn mein Takt nicht stimmt bzw. ich den Takt nicht kenne? Mit welchen bekannten Werten wird denn gerechnet wenn nicht mit dem µC Takt?
Der Punkt ist, Du mußt weder Deinen eigenen Takt kennen, noch den des Masters. Du mußt nur feststellen können, ob Du schneller oder langsamer bist, als der Master. Lassen wir den UART mal für einen Moment bei Seite und nehmen an, der Master sendet ein Rechtecksignal, dessen Periode dem 128. Teil seiner Taktfrequenz entspricht. Dieses Signal mißt Du mit einem Counter im Capture-Mode der mit Deiner eigenen Taktfrequenz getaktet wird. Hat der Counter am Ende der Periode einen Wert größer als 128, weißt Du daß Deine CPU schneller läuft als die des Masters und mußt entsprechend den OSCCAL-Wert verringern. Ist das Ergebnis kleiner als 128 läufst Du zu langsam und mußt OSCCAL erhöhen. Das Ganze wird so lange wiederholt, bis Du die 128 triffst oder zwei benachbarte OSCCAL-Werte gefunden hast, die um die 128 herumtanzen. Nun kennst Du zwar weder die absolute Frequenz des Masters noch Deine eigene, aber Du weißt, daß die Frequenzen der beiden AVRs im Rahmen der Möglichkeiten des RC-Oszillators übereinstimmen und sie damit bei gleicher Baudraten-Einstellung problemlos miteinander kommunizieren können. Dabei kann es durchaus sein, daß Deine tatsächliche Baudrate so weit neben dem nominellen Wert liegt, so daß eine Kommunikation mit einem "bequarzten" UART, der die offiziellen Toleranzen einhält, nicht möglich wäre. Dadurch, daß aber beide Kommunikationsparter sich auf einen gemeinsamen Takt geeinigt haben, interessiert die Abweichung nicht. Wird die Sache damit klarer?
Die Einfachheit der UART wird eben erkauft mit der Veinbarung in etwa im gleichen Zeitraster zu arbeiten, das ist der (virtuelle) Takt. Du könntest auch ne Taktleitung mitführen, will man oft nicht, manchmal geht es auch nicht. Alternative wäre z.B. der Manchestercode, da ist der Takt im Signal enthalten, eigene Taktfrequenz spielt keine Rolle (solange genug Zeit zum Codieren und Dekodieren bleibt). Nachteil doppelte Signalfrequenz. Einen Tod muss man immer sterben :-)
Jaja, bei solchen Problemstellungen melden sich immer viele hauptamtliche Bedenkenträger, die nur in starren Rastern denken können... MÖGLICH ist das - und hat auch den Reiz eine "lebendige" Kommunikation zu programmieren, die sich auf ihre Kommunikationspartner einstellt, anstatt sich darauf zu verlassen, dass jeder Teilnehmer Quarz-genau funktioniert. Eine zusätzliche Taktleitung ist überflüssig, wenn man etwas Zeit im Programm-Ablauf für die Synchronisation übrig hat/lässt. Vielleicht nicht für den Büro-Alltag, aber für's wahre Leben! Vereinfachend kommt dazu, dass man sich nur auf Abweichungen von vielleicht +/-10% einstellen muss. Dein Ansatz ist auch garnicht sooo verkehrt, aber du solltest dir eine idiotensichere (fehlertolerante) STRATEGIE ausdenken, wie du 2, oder auch mehrere µCs aufeinander synchronisierst! 1a: Soll ein bestimmter µC der Chef sein (einfacher zu programmieren), oder 1b: Soll das der erste µC sein, der eingeschaltet wird? (Aufwändiger zu programmieren, aber flexibler.) 2: Ein Einwurf kam schon: Die OSCCAL-Geschichte ist nicht ganz einfach (dazu noch AVR-Typ-abhängig), aber mit Sachkenntnis durchaus beherrschbar. Dafür gibt es auch eine Anleitung im Netz! Ich habe mir danach schon mal eine Synchronisation auf die 50 Hz Netzfrequenz programmiert, um Blind-/Wirk/Schein-Leistung mit recht exakt N Samples/Netzperiode zu erfassen. Die Einstellgenauigkeit erreichte < 0,5%, was bei serieller Kommmunikation vollkommen ausreichend sein sollte. Die Erfassungung des abschließenden Stopp-Bits (8N1) erfolgt nicht bei halber Bitweite (50%), sondern bei 55% - da schaudert es nur Dummköpfen! Mal sehen, ob ich den Link zur Anleitung wiederfinde...
Das es geht stand ja schon fest. Was hat Alternativen aufzeigen mit Bedenkenträger zu tun? Auf Netzfrequenz synchronisieren ist erstens weder neu und zweitens nur noch in Ausnahmefällen realisierbar. Der klassische Trafo ist so gut wie ausgestorben, das Netz selbst will man nicht im Gerät haben.
@ H.Joachim Seifert (crazyhorse) Der TO will nun mal Port-Pins sparen, da ist es doch eher zielführend, ihn darauf hinzuweisen, dass er zum Zeitverlust für die Synchronisierung, den er ja schon eingeplant hat, auch noch eine Strategie für den Clock-Master braucht. Deine Taktleitung kostet dagegen je einen Port-Pin. Die Netzsynchronisierung war ein BEISPIEL für die erreichbare Genauigkeit, kein Vorschlag für die Takt-Referenz.
Taktleitung? Hatte ich doch geschrieben, das man das meist nicht will und oft auch gar nicht kann... Eigentlich gings um Manchester, funktioniert auch mit nur einer Leitung und ist eben nicht drauf angewiesen, einen rel. genauen lokalen Takt zu haben. Andere Gründe können durchaus dagegen sprechen, sowas zu benutzen. Aber eine Alternative zum Takt kalibrieren/nachführen ist es schon.
H.Joachim S. schrieb: > Taktleitung? [...] Eigentlich gings um Manchester Es ging weder um Taktleitungen noch um Manchester, sondern darum, daß der TO ein Verständnisproblem mit der Taktsynchronisation über OSCCAL hatte und sich nicht erklären konnte, wie man einen Slave mit einem Master synchronisieren kann, ohne den genauen Takt des Masters zu kennen. Die letzte On-Topic-Antwort dazu war meine von gestern 20:59. Seitdem hat der TO sich nicht mehr gemeldet und alle weiteren Beiträge gingen am Thema vorbei.
Ist ja auch egal. Ich hatte es so verstanden, dass es primär um den Austausch serieller Daten geht und das nur mit dem internen Oszillator. Die OSCCAL-Geschichte nur mittel zum Zweck. Veit D. schrieb: > Wenn man zum Bsp. 2 > ATtinys mit internen RC aufeinander loslässt und die sollen sich erstmal > im Takt syncronisieren bevor sie serielle Daten austauschen.
Hallo, sorry, aber ich kann nicht ständig online sein. :-) ja genau, ohne Takt oder sonstwas. Die Erklärung von rmax 20:59 ist schon gut. Muß sie nur noch nachvollziehen können. Das schwankt z.Z. zwischen verstanden und doch wieder nicht. Ich muß mir das aufmalen und etwas rechnen. Danke erstmal bis hierher.
Hallo, ich denke ich habs in der Theorie durchdrungen. :-) Jetzt programmiere ich mir erstmal einen Frequenzzähler, die Vorstufe dessen und dann möchte ich darauf aufbauen um letztlich den OSCCAL nachzustellen. Manuelle OSCCAL Änderungen ergaben, dass ich später bei einem Wert von 36 landen müßte. Default sind 41. Bei Ub 5V. ATTiny841. Hatte einen Timer im CTC Mode programmiert mit Prescaler 1 und Top 1, sodass der Pin mit 2MHz taktete. Das habe ich am Oszi gemessen. Für ideale 8MHz µC Takt natürlich nur. Danke soweit.
Hallo, muß doch nochmal darauf zurückkommen. Folgende Überlegung steckt noch in mir. Master: 8MHz (nominell) Taktausgabe zum syncronisieren: 1kHz macht einen positive Flanke zum zählen aller 1ms Zählt der Slave eine Sekunde lang, muß er 1000 Counts haben damit er weis das er syncron läuft. Damit ich als Programmierer aber weis das ich auf 1000 Counts achten muß, muß ich vorher wissen das der Master mit 8MHz bzw. der Syncronisierungstakt mit 1kHz taktet. Ohne diese Angabe geht das nicht. Und wenn der Master mit ungenauen 8,5MHz läuft und damit der Syncronisierungstakt mit 1062,5Hz, dann dauert es zwischen den zählbaren Flanken 0,9411ms. Dann muß ich beim zählen auf 1062 Counts achten und nicht nur auf 1000. Das weis ich aber vorher nicht. Das syncronisieren mit dieser Methode funktioniert laut meiner Meinung nach nur mit einem Master mit Quarz und bekannten Takt. Mit internen RC Oscills funktioniert das nicht. Wenn es wie bei modernen ATtinys ein Temperatur kompensierter Takt ist, wo man sagen kann der bleibt ausreichend stabil, dann kann man den ausmessen und damit rechnen. Ansonsten sehe ich da noch schwarz. Versteht ihr jetzt mein Gedankenproblem besser? Wenn die Nichtlinearität zwischen OSCCAL und Takt nicht wäre, dann wäre meine obige Methode das Mittel der Wahl. Weil das ist wirklich unabhängig vom Takt. Nur die Baudraten müssen auf beiden Seiten gleich eingestellt sein. Moment: Das muß ich jetzt mit dem Baudratentakt machen. Nicht mit dem µC Takt. Wenn ich zum Bsp. 250kBit/s einstelle, dann dauert ein Bit 4µs, heißt aller 8µs eine positive Flanke zum zählen. Wenn ich nun soviele "0x55" hintereinander für 1ms Dauer ohne Pause rausschicken würde, dann müßte ich innerhalb der 1ms auf 125 Counts abgleichen im Empfänger. Jetzt muß es stimmen, meine Denkweise. Ja / Nein?
Fuer den Baudraten Slave musst du eigentlich nur wissen wie viele Clock counts du pro Bit, oder pro 10 bit zaehlen musst. Welcher Baudrate das entspricht ist weniger wichtig.
Veit D. schrieb: > Jetzt muß es stimmen, meine Denkweise. Das war ja schon oben mein Vorschlag. 1/(gemessene Länge des Startbit) = Baudrate. Wenn du eine vorher vereinbarte Baudrate kennst, dann lässt sich daraus der MC Takt regenerieren.
:
Bearbeitet durch User
Hallo, du meinst sicherlich: "wie viele Clock counts du pro Byte, oder pro 10 bit zählen mußt ..." "Welcher Baudrate das entspricht ist weniger wichtig." Nur ich muß doch vorher wissen auf wieviele Takte hin ich zählen/ablgeichen muß. Und das ist abhängig von der Baudrate wieviele µC Takte während dessen vergehen. Egal ist ist die Baudrate laut meiner Meinung nicht. okay, sorry wenn ich das dann doch nicht verstanden hatte und schon erklärt wurde. So langsam komme ich dahinter. Die Baudrate muß ja auf allen beteiligten µC gleich eingestellt sein, sonst wird das ja nichts. Das ist meine einzigste bekannte konstante Größe. Danke und erstmal schönes Wochenende.
:
Bearbeitet durch User
Veit D. schrieb: > Master: 8MHz (nominell) > Taktausgabe zum syncronisieren: 1kHz > macht einen positive Flanke zum zählen aller 1ms > > Zählt der Slave eine Sekunde lang, muß er 1000 Counts haben damit er > weis das er syncron läuft. Soweit richtig. > Damit ich als Programmierer aber weis das ich > auf 1000 Counts achten muß, muß ich vorher wissen das der Master mit > 8MHz bzw. der Syncronisierungstakt mit 1kHz taktet. Ohne diese Angabe > geht das nicht. Falsch. Du musst wissen, dass Dein Slave so schnell laufen soll, dass der Puls 1000 Counts dauert. > Und wenn der Master mit ungenauen 8,5MHz läuft und damit der > Syncronisierungstakt mit 1062,5Hz, dann dauert es zwischen den zählbaren > Flanken 0,9411ms. Und wenn Du derer 1000 nimmst, läuft Dein Slave exakt so schnell wie der Master. Ziel erreicht. > Das syncronisieren mit dieser Methode funktioniert laut meiner Meinung > nach nur mit einem Master mit Quarz und bekannten Takt. Mit internen RC > Oscills funktioniert das nicht. Warum soll das nicht funktionieren? Das Ziel der Synchronisation ist, dass der Slave genauso schnell läuft wie der Master. Wenn der Master heute mit 7,9 und morgen mit 8,1 MHz läuft, hat der Slave das auch zu tun. > Moment: > Das muß ich jetzt mit dem Baudratentakt machen. Nicht mit dem µC Takt. Läuft auf das gleiche hinaus. Du stellst Deinen Slave so ein, dass dessen Startbit genauso lange dauert wie das vom Master. Dann laufen beide gleich schnell und können Daten austauschen.
Veit Devil (devil-elec) schrieb: > Das muß ich jetzt mit dem Baudratentakt machen. Nicht mit dem µC Takt. > ... Ja / Nein? Da würde ich (gerade bei 250 kBd) NEIN sagen. Beim Tiny2313, bei 8 MHz muss UBRR = 1 für 250 kBd sein. Das Verändern um +/-1 halbiert/verdoppelt die Baudrate. Selbst mit U2X = 1 ist jeder UBRR-Step ein Vielfaches der Clockabweichung der RC-Oszillatoren. GEHT NICHT! Verlier mal die Scheu vorm OSCCAL! Falls du den 2313 hast, (mir fällt grad kein anderer Tiny mit USART ein,) ist die OSCCAL-Einstellung sogar recht einfach, weil es dort NICHT den Frequenzsprung bei OSCCAL von 127 auf 128 gibt.
Hallo, @ soul: "Falsch. Du musst wissen, dass Dein Slave so schnell laufen soll, dass der Puls 1000 Counts dauert." hmm, irgendwie denke ich wohl immer noch von der anderen Seite "des Problems". Aber stimmt du/ihr habt recht, man kann und sollte es von der anderen Seite betrachten. Also in meiner bisherigen Denkweise andersherum. Ihr lagt ja schon immer richtig. Ich schiebe den OSCCAL solange hin und her bis ich den Count vom nominellen Takt gefunden habe. In dem 1kHz Bsp. eben 1000 und mit dem 250k Baudratentakt eben 125. @ Jakob: ich verstelle nicht die Baudrate. Ich bleibe schon beim OSCCAL. Nur mit dem erzeugten Takt auf der seriellen (permanent 0x55 ausgeben) kalibriere ich den Slave mittels OSCCAL nach. Die Baudrate lasse ich unverändert bzw. immer ein ganzes vielfaches von 8MHz. Ist übrigens ein ATtiny841. Wenn ihr vom Startbit redet, meint ihr dann wirklich, dass ihr nur die Zeit dieses einen Bits vermessen tut? Das wäre mir ehrlich gesagt zu kurz und zu ungenau. Ich wollte 1sec. lang einen Takt ausgeben worauf sich der Slave syncronisieren kann. Danach abfragen ob es geklappt hat. Wenn keine Antwort kommt oder ein nein, dann wiederholen. Und das alles auch nur einmalig beim einschalten. In der Annahme das der temperatur kompensierte RC im ATtiny841 stabil genug bleibt. Ich freue mich auch darüber das ihr am Ball bleibt und mich nicht dumm sterben lasst. Schriftlicher Austausch ist öfters nicht einfach.
:
Bearbeitet durch User
Veit D. schrieb: > Wenn ihr vom Startbit redet, meint ihr dann wirklich, dass ihr nur die > Zeit dieses einen Bits vermessen tut? Das wäre mir ehrlich gesagt zu > kurz und zu ungenau. Das ist einfach genau genug. Du darfst nicht vergessen, dass auch eine noch so genaue Messung dir keinerlei Vorteile mehr bringt, sobald sie deutlich genauer ist, als du die eigene Frequenz über dein Steuerglied (also das OSCCAL-Register) überhaupt einstellen kannst. Jeglicher Mehraufwand für die Messung ist also völlig huppse, für'n Arsch, oder wie immer man das gepflegter ausdrücken mag... Worüber du dir also viel mehr Gedanken machen solltest, als über die Messgenauigkeit, ist die Sache, wenn zwei potentiell adaptive Teilnehmer aufeinander treffen. Das erfordert viel mehr geistigen Aufwand und ist der eigentliche Knackpunkt für eine wirklich universelle Auto-Kalibrierung. Vor allem dann, wenn man das auf eine Kette oder gar einen Ring solcher Teilnehmer erweitert... Darauf solltest du deine geistigen Potenzen konzentrieren. Das, woran du rumpfriemelst, ist doch dagegen absolut trivial...
Neee, davon haben wir ihn ja gerade abgebracht. Da sollen nicht zwei Teilnehmer miteinander in Regelschwingung geraten, sondern einer ist der Master und der hat immer Recht. Auch wenn dessen 8 MHz in Wahrheit nur 7,9 MHz sind.
soul e. schrieb: > Neee, davon haben wir ihn ja gerade abgebracht. Da sollen nicht zwei > Teilnehmer miteinander in Regelschwingung geraten, sondern einer ist der > Master und der hat immer Recht. Das ist aber eben gerade nicht universell. Universell wird es erst, wenn es ein Protokoll gibt, über den die Partner aushandeln, wer der Master sein soll. Das habe ich schon gefühlte 1000 Postings vorher erklärt.
Hallo, mit der universellen Syncronisierung meinte ich ganz zu Anfang bezogen meinen Unverständnis der Methode bzw. dessen Ablauf. Weil ich immer davon ausging man muß den Takt vorher kennen. Das ist aber nun geklärt. Zum Glück. :-) In meinen Aufbau bleibe ich aber dabei das ein bestimmter µC der Chef im Ring ist und alle anderen müssen sich danach richten. Fertig. Ist für mich einfacher und logischer. Wenn die sich jetzt noch untereinander aushandeln sollen wer sich nach wen richten soll, dann werde ich nie fertig und sprengt vielleicht auch mein derzeitiges Rechenzentrum zwischen den Ohren. Ich mach das alles rein Hobby mäßig. Wegen dem nur einen Bit vermessen. Ich versuche das zu erfassen ... im Moment sehe ich viele neue Fragen ... ich komme nochmal darauf zurück.
Veit D. schrieb: > In meinen Aufbau bleibe ich aber dabei das ein bestimmter µC der Chef im > Ring ist und alle anderen müssen sich danach richten. Fertig. Ist für > mich einfacher und logischer. Das zeigt eigentlich nur eins: Du hast die Sache wirklich noch nicht durchdacht, dich von trivialen Nebensächlichkeiten von den eigentlichen Problemen abhalten lassen. Z.B. diesem: willst du es in den Griff bekommen, dass nach n Ringgliedern am Ende des Ringes für den "Master" der den den Anfang füttert, immer noch was verständliches ankommt... DAS sind die eigentlichen Probleme einer solchen Architektur. Da solltest du drüber nachdenken...
Veit D. schrieb: > Master: 8MHz (nominell) > Taktausgabe zum syncronisieren: 1kHz > macht einen positive Flanke zum zählen aller 1ms > > Zählt der Slave eine Sekunde lang, muß er 1000 Counts haben damit er > weis das er syncron läuft. Damit ich als Programmierer aber weis das ich > auf 1000 Counts achten muß, muß ich vorher wissen das der Master mit > 8MHz bzw. der Syncronisierungstakt mit 1kHz taktet. Ohne diese Angabe > geht das nicht. Nein. Denn dein Ziel ist nicht, den Slave auf eine bestimmte absolute Frequenz einzustellen, sondern auf eine Frequenz, die möglichst genau mit der des Masters übereinstimmt. Das ist ein viel einfacheres Ziel. Löse dich mal von absoluten Frequenzen. Wenn der Master mit nominal 8MHz läuft und ein 1kHz Synchronisierungssignal sendet, dann dauert eine Periode dieses Signals 8000 Taktzyklen. Der Slave zählt jetzt auch die Länge der Periode des Synchronisierungs- signals, aber mit seiner Taktfrequenz. Wenn er mehr als 8000 Takte herausbekommt, dann läuft sein lokaler Takt offensichtlich schneller als der des Masters. Sind es weniger, dann läuft er zu langsam. Jetzt kann der Slave sein OSCCAL Register passend verändern und die Messung wiederholen. So oft, bis er zwei benachbarte OSCCAL-Werte gefunden hat die jeweils knapp mehr oder weniger als 8000 Takte ergeben. Den Wert der näher liegt, stellt er dann ein. > Und wenn der Master mit ungenauen 8,5MHz läuft Dann hat eben der > Syncronisierungstakt 1062,5Hz Aber trotzdem immer noch 8000 Takte pro Periode. Der Slave stellt sich dann auch auf 8.5MHz ein.
Hallo, @ Axel: das ist schon geklärt wurden und ich habs gerafft, Danke dennoch fürs mitdenken. @ der C freundliche: darüber mußte dir in dem Thread keine Sorgen machen. Die serielle Verbindung erfolgt am Ende mittels RS485. Tests mit MAX485 ICs erfolgten schon. Zudem möchte ich keine Kilometer überbrücken sondern nur paar wenige Meter. Mich interessiert hier die Funktionsweise der automatischen Syncronisierung und ob ich das hinbekomme. Ich denke nun ja. Das darf ich doch mal ausprobieren. Meinste nicht auch? Jeder fängt mal mit irgendwas neuen an. Lass mich mal machen. Wenn was nicht klappt, frage ich und du darfst mich gern korrigieren. Auch deinen Rat weiß ich immer zu schätzen, auch wenn er manchmal für mich nicht sofort ersichtlich ist. :-)
Hallo, ich stecke noch in der Umsetzung fest. Laut Atmel Application Note 2563 bzw. AVR054 muß man neben den UART Pins noch einen zusätzlichen Interrupt Pin verschwenden. Wäre für mich später dann etwas sinnlos, wenn ich von 2 gewonnenen Pins (ohne Quarz) einen wieder abgeben müßte. Jetzt bin ich am überlegen die UART vorrübergehend abzuschalten, also die UART Pins als normale Pins zuverwenden, auf beiden Seiten und nach hoffentlich erfolgreicher OSCCAL Korrektur wieder als UART umzuschalten. Ist das ein gangbarer Weg?
Controller mit PinChange-Interrupt-Möglichkeit können denselben Pin für USART und Startbitzeitmessung benutzen.
Knut B. schrieb: > Controller mit PinChange-Interrupt-Möglichkeit können denselben Pin für > USART und Startbitzeitmessung benutzen. Das kann jeder AVR, weil dazu schlicht kein Interrupt nötig ist. Der RxD-Pin ist natürlich jederzeit auch als stinknormaler Input abfragbar.
Hallo, gut, dann ist die Beschreibung wohl schon etwas älter. Danke.
Nötig ist ein Interruptpin nicht, aber hilfreich, weil das ständige Pollen des Pins wegfällt. Und man kann kürzere Startbits einfacher detektieren.
:
Bearbeitet durch User
Bist du schon EINEN Schritt weiter gekommen? Sollte mich bei den bisherigen Beiträgen wundern... Mittlerweile ist dir wohl klar, dass du zu Beginn über eine Ableichroutine die Slave-CPU-Frequenz an die Master-CPU-Frequenz angleichen musst. Ob 7,x MHz, oder 8,x MHz sind, ist WURSCHT. Beim Tiny2/4/861 hast du aber keine USART, sondern eine USI. Mit der kann man seriell kommunizieren. Aber nicht so ohne Weiteres nach RS232-Norm... Willst du den 2-Wire-, oder den 3-Wire-Mode nutzen? Also DI/SDA und SCL, oder DI/SDA, DO und SCL? Mit der Quarz-Einsparung hast du im 3-Wire-Mode gerade mal EINEN Pin gewonnen... Jedenfalls hast du damit nicht so einfach das RS232-Protokoll: Startbit - n Datenbits - Stoppbit Also kannst du auch kein Startbit ausmessen. Schon garnicht genau genug, um bei 250 kBd mit der Messung der Dauer eines Bits die CPU-Frequenz auf die erforderliche Genauigkeit von 1..2% nachzustellen. Zahlenbeispiel für 8 MHz: T(250 kBd) = 4 µs = 32 Takte. Auflösung = 1/32 > 3% Beim Tiny2/4/861 hast du die Möglichkeit, den DI/SDA-Pin des Slaves in der Abgleichphase als INT1-Pin zu nutzen. Sendet der Master auf seinem Out-Pin ein Abgleich-Bitmuster, wie 0b00000001 (ohne Pause dazwischen!), könntest du über die Messung des Abstands der positiven, oder negativen Flanke am INT1-Pin des Slaves die Messung schon 8 mal genauer machen. Für die Messung erfasst der Slave im INT1-Interrupt die Differenz des Zählerstands eines ohne Vorteiler laufenden Timer-Counters mit dem Wert beim vorigen INT1-Interrupt. 8 * 32 Takte = 256 Takte: Messauflösung = 0,4% Mit OSCCAL regelst du den Systemtakt des Slaves möglichst nah an den Takt des Masters. Dies ist erreicht, wenn über mehrere Interrupt-Perioden möglichst genau 256 +/- 1..2 Takte für dieses Abgleich-Bitmuster zu messen sind. Anschließend stellst du den Slave auf USI-Mode um und sendest eine gut erkennbare "Ich-Bin-Bereit-Meldung" über USI an den Master. Wenn der Master diese Meldung erkennen kann, antwortet er mit einer gut erkennbaren "Legen-Wir-Los"-Meldung. Dann kann's logehen. :-) (Sonst sendet der Master weiter das Testmuster.)
Ojeh, da habe ich mich auf die ganze Clock-Synchronisierungs- Debatte eingelassen... Aber: Das ganze Takt-Abgleich-Problem ist überhaupt nicht vorhanden! Da du beim Tiny2/4/861 keine USART, sondern eine USI hast, wird das RS232-Protokoll mit Start / n * Data / evtl. Parity / Stop bei vorgegebener Baudrate mitsamt erforderlicher Taktübereinstimmung etc. p.p. überhaupt nicht unterstützt! Da wirst du wohl den USI-2-wire-Mode (TWI) nutzen müssen. Und dabei taket der jeweils sendende µC mit seinem SCL-Signal seine Daten beim Empfänger ein. Egal, ob der Master, oder der Slave am Senden/Empfangen ist. Egal, ob der Master, oder der Slave mit dem Systemtakt +/-10...??% daneben liegt. Vergiss den Frequenzabgleich, sondern kümmere dich um das TWI-Protokoll! Die Vorgehensweise bei mehr als 2 Kommunikationspartnern ist damit auch schon (fast) erledigt. Wenn du unbedingt Frequenz-Abgleichen willst: Benutze µCs mit USART.
Hallo, ich hatte weder von einer USART noch von I2C geschrieben. Bringe das nicht unnötig durcheinander. Außerdem mache ich das in meiner Freizeit. Reines Hobby. Da dauert das nun einmal länger. Nur kein Stress.
Jakob schrieb: > Mittlerweile ist dir wohl klar, dass du zu Beginn über eine > Ableichroutine die Slave-CPU-Frequenz an die Master-CPU-Frequenz > angleichen musst. > > Beim Tiny2/4/861 hast du aber keine USART, sondern eine USI. > Mit der kann man seriell kommunizieren. Aber nicht so ohne > Weiteres nach RS232-Norm... Stimmt. Direkt nicht. Mit Tricks schon (Appnote AVR307) > Willst du den 2-Wire-, oder den 3-Wire-Mode nutzen? Wenn er das wöllte, dann müßte er die Taktfrequenzen der beiden µC nicht angleichen. Denn beide Protokolle führen den Takt mit. Die einzige Einschränkung, die bei asynchronem Systemtakt bleibt, ist daß er nicht die maximal mögliche Geschwindigkeit ausnutzen kann. Das dürfte in der Praxis irrelevant sein, weil beide synchronen Modi viel schneller sein können als ein UART. Wenn man also lediglich einen UART ersetzen will ... > Also kannst du auch kein Startbit ausmessen. Das eine folgt nicht aus dem anderen. Es ist kein Naturgesetz, daß er das letztendliche Kommunikationsprotokoll für die Taktsynchronisierung nutzen muß. Er kann genauso gut die entsprechenden Pins erst rein in Software zur Synchronisation nutzen und erst wenn die Taktfrequenzen abgeglichen sind, auf das USI/USART umschalten. > Schon garnicht genau genug, um bei 250 kBd mit der Messung > der Dauer eines Bits die CPU-Frequenz auf die erforderliche > Genauigkeit von 1..2% nachzustellen. > > Zahlenbeispiel für 8 MHz: > T(250 kBd) = 4 µs = 32 Takte. Auflösung = 1/32 > 3% Genau deswegen macht man das ja auch nicht so.
Hallo, sorry, um eine UART bzw. USART gehts es schon. Nur nicht um I2C oder andere. War gestern Abend etwas forsch. Es geht mir hierbei nicht in erster Linie darum womit ich schneller Daten übertragen kann, sondern darum ob ich es schaffe den einen µC mit dem anderen zu syncronisieren. Bin gerade bei der Codeumsetzung. USART könnten übrigens beide. ATmega2560 und ATtiny841.
Jakob schrieb: > Mittlerweile ist dir wohl klar, dass du zu Beginn über eine > Ableichroutine die Slave-CPU-Frequenz an die Master-CPU-Frequenz > angleichen musst. Ob 7,x MHz, oder 8,x MHz sind, ist WURSCHT. Vorher muss er erstmal klarkriegen, wer der Master sein soll... DAS wurde bereits als das ultimative Kriterium für eine wirklich universelle Sache herausgearbeitet... > Beim Tiny2/4/861 hast du aber keine USART, sondern eine USI. > Mit der kann man seriell kommunizieren. Aber nicht so ohne > Weiteres nach RS232-Norm... Natürlich muss man die mit einem USI als Hardwareunterstützung selber sachgerecht umsetzen. Genauso dann, wenn es um eine reine Soft-UART geht. Bezüglich der Kalbrierung auf den Takt eines Masters gibt es aber absolut keinen Unterschied. In ALLEN Fällen muß man man zuerst mal aushandeln, wer der Clock-Master sein soll, und in ALLEN Fällen muß man dan den eigenen Takt an den des Masters so gut wie möglich angleichen, wenn man bei der Aushandlung die Client-Rolle erwischt hat. Es spielt hierfür absolut KEINE Geige, ob man die eigentliche Kommunikation ganz, teilweise oder garnicht mittels Hardwareunterstützung abwickelt. Es geht hier in diesem Thread eigentlich auch nur um genau den Teil der Sache, der VOR der eigentlichen Kommunikation passieren muss. Das hast du wohl nicht ganz gerafft... > Also kannst du auch kein Startbit ausmessen. Kann man zwar, ist aber bei hohen Bitraten und relativ dazu geringem Takt tatsächlich nicht hinreichend genau (und obendrein sehr anfällig gegen Störungen). Deswegen würde das natürlich auch niemand mit gesundem Menschenverstand und grundlegenden mathematischen Fähigkeit so machen... > Beim Tiny2/4/861 hast du die Möglichkeit, den DI/SDA-Pin > des Slaves in der Abgleichphase als INT1-Pin zu nutzen. > Sendet der Master auf seinem Out-Pin ein Abgleich-Bitmuster, > wie 0b00000001 (ohne Pause dazwischen!), könntest du über die > Messung des Abstands der positiven, oder negativen Flanke am > INT1-Pin des Slaves die Messung schon 8 mal genauer machen. Das ist nur eine Möglichkeit von sehr vielen. Letztlich beruhen alle vernünftigen Lösungen darauf, dass man bei im Vergleich zum Takt hohen Bitraten mehr als nur ein Bit vermessen muss, um eine hinreichende Genauigkeit zu erzielen. Das ist trivial. Man muss wirklich kein Mathematiker sein, um das erkennen zu können... Der eigentliche Trick steckt halt im Protokoll. Nachdem ausgehandelt ist, wer Master und wer Slave sein soll, muss halt der Master dem Slave hinreichend lange ein hinreichend gut zur Messung geeignetes Muster senden. Das war's. Der eigentlichen Knackpunkt ist und bleibt: Der Aushandlung der Rollen. Wie soll das Protokoll unter der Bedingung eine nicht funktionsfähigen seriellen Verbindung ablaufen und wie genau wird bestimmt, wer Master und wer Slave sein soll. Und letztlich: wie minimiert man die Zeit für dieses Protokoll. Denn es ist ja nur ein Helper für die eigentliche Aufgabe der Installation. Die Minimierung der Zeit für den Ablauf des Protokolls wird vor allem dann wichtig, wenn man auch zur Laufzeit ständig damit rechnen muss, das Kommunikationsteilnehmer "aus dem Takt geraten". Dann muss man nämlich diese Protokoll erneut starten. Das ist z.B. immer dann der Fall, wenn die Teilnehmer RC-Oszillor-getrieben sind und ihre Temperatur sich nennenswert ändern kann...
Hallo, der Master ist bei mir immer der Gleiche. Spätere Wiederholung wegen Temperaturschwankungen sollten beim ATtiny841 eigentlich keine Rolle spielen. Das macht es für mich erstmal einfacher. Kann man später vielleicht noch einbauen. Jetzt erstmal nicht. Der Code auf dem Master für erstmal einmalige Sync-Protokol Übertragung ist fertig. 1ms High, 1ms Low zur Erkennung das es jetzt los geht, dann folgen 20 Takte mit 100kHz. Wenn der Slave dann wie gewünscht darauf reagiert, wird es mehrfach wiederholt. Jetzt muß ich den Slave ATtiny841 programmieren. Dazu muß ich nun doch zwischendurch eine Frage stellen. Gehe ich richtig mit der Annahme das ich folgende Register setzen muß, wenn ich den RxD0 Pin auf steigende Flanke zählen einstellen möchte?
1 | PCMSK0 = (1<<PCINT2); // pin change mask für RxD0 Pin |
2 | GIMSK = (1<<PCIE0); // pin change interrupt enable 0 |
3 | MCUCR = (1<<ISC01) | (1<<ISC00); // rising edge |
:
Bearbeitet durch User
Hallo, das mit dem rising edge trifft wohl nur für "External Interrupt Request 0" INT0 zu. ATtiny841 Ich kann für RxD0 nur setzen
1 | PCMSK0 = (1<<PCINT2); // RxD0 Pin Interrupt enable |
2 | GIMSK = (1<<PCIE0); // pin change interrupt group enable |
und Interrupt Handler PCINT0_ISR auswerten. In dem Fall kann ich einfach alle Flankenänderungen zählen. Wie könnte ich die steigende/fallende Flanken detektieren?
Ich wiederhole mich: mit Manchester wärst du schon lange fertig mit der ganzen Kacke. Gibts als fertigen Code und funktioniert. Aber ne, theoretisch ist es ja möglich mit Oszillator nachziehen. Naja - nicht so einfach, aber muss ja gehen...
Kommt drauf an, ob du den DEFAULT-, oder den ALTERNATIVE- RXD0-Eingang benutzen willst. PA2 = Default-RXD0 kann auf PCINT2 konfiguriert werden, PB2 = Alternative-RXD0 kann auf PCINT10 konfiguriert werden. Mit dem Tiny841 hab ich noch nix gemacht, aber bei den mir bekannten Tinys führt JEDE Flanke zum PCINT-Interrupt. Die Richtung muss man in der ISR durch Abfrage des nun vorhandenen Logik-Pegels bestimmen. Jetzt Hi: Es war eine positive Flanke. Übrigens: Wenn man per PCINTx Manchester-Code auwerten will, hat man das gleiche Problem. ;-)
Hallo, @Joachim: meinst du das aus Application Node 9164? Sieht sehr sehr umfangreich aus der ganze Code und überblicke ich eh erstmal nicht. Müßte mich auch da wieder reindenken usw. und lenkt mich aktuell von meinem ab. Aber gute Idee, zugegeben. Selbst den Ansatz den ich verfolge hättet ihr in paar Stunden fertig, denke ich. Ich muß viel im Datenblatt lesen, Register und Bits raussuchen usw. Am Ende generiere ich auch einen Takt zum auswerten. Wichtig für mich ist, dass ich meinen eigenen Code verstehe. Wenn etwas nicht klappt oder angepaßt werden muß kenne ich den Code. Würde ich als wichtig betrachten. @ Jakob: Pegelauswertung in der ISR ist ein gutes Stichwort. Danke. Merke ich mir. Wegen der Alternative. Alle PCINTx Pins reagieren auf alle Pegeländerungen. Nur die INTx Pins kann man genauer konfigurieren. Der ATtiny841 hat nur einen davon, INT0 an PB1. Aber gut, benötige ich jetzt nicht unbedingt wie ich das aktuell sehe. Ich mach heute mal weiter ...
INT0 an PB1 wäre ein verschwendeter Pin, wenn der Serial In PA2 (PB2), oder PA4 ist. Dass die Manchester-Codierung/Decodierung auch nicht ganz ohne Aufwand zu machen ist, hast du richtig gesehen. Die AN 9164 geht dabei von bekanntem "data rate clock" aus, was du ja gerade nicht hast... Eine Software-UART ist nur WENIG aufwändiger. So sehr ich viele schlaue Beiträge vom c-hater schätze - hier hat er sich nach meiner (nicht maßgeblichen) Meinung etwas festgebissen. LANGSAM möchte man aber Erfolgsmeldungen hören! Es ist machbar!
Veit D. schrieb: > seines OSCCCAL ... Warum eigentlich OSCCCAL? Man kann doch auch das Baudrateregister passend setzen und OSCCCAL lassen, wie es ist. MfG Klaus
Hallo, die Slave Erkennungssequence hatte ich erstmal auf dem ATmega2560 geschrieben. Master und Slave vertauscht. Funktioniert. Fiel mir irgendwie leichter, weil ich den schon länger habe. Jetzt sollte es möglich sein das auf den ATtiny zu übertragen. Das zum Thema halbe "Erfolgsmeldung". :-)
Hallo, es läuft erstmal halbwegs auf dem ATtiny841. Eine böse Falle waren die Namen der Interrupt Vectoren. Im Datenblatt stehen die falsch drin. Das dazu. Bsp. "TIM2_COMPA" statt "TIMER2_COMPA", Seite 49, 50 Die gesamte Erkennungs Sequence funktioniert damit einmalig. Jetzt muß ich eine Wiederholung einbauen. Was mich aber jetzt schon wundert ist, wenn ich den Timer 2 nicht stoppe (im ISR(PCINT0_vect) Interrupt Handler), dann müßte doch der Compare Match ISR zuschlagen und meine 3. Led leuchten. Die leuchtet aber nicht. Warum weiß ich noch nicht. Vielleicht ihr? Ablauf: Vom Master steht TxD0 auf High nach Reset, dann für 1,5ms auf LOW und dann für 1,5ms wieder auf HIGH, danach folgen 20 Takte mit am Ende wieder auf HIGH. Nach Low/High Wechel-Längenerkennung leuchtet LED_0. Wenn die 20 Takte erkannt wurden leuchtet LED_1. LED_2 soll nur leuchten wenn etwas schief ging, zu lange dauert und damit der Overflow ISR zuschlägt.
1 | /*
|
2 | * ATtiny841_Takt_Syncronisierung_002.cpp
|
3 | *
|
4 | * Created: 03.10.2016 20:50:04
|
5 | * Author: Devil-Elec
|
6 | * µC : ATtiny841 mit internen 8MHz Oszillator
|
7 | *
|
8 | * Interrupt-Vector Namen Übersicht:
|
9 | * C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\avr8\avr8-gnu-toolchain\avr\include\avr\iotn841.h
|
10 | */
|
11 | |
12 | #include <avr/io.h> |
13 | #include <avr/interrupt.h> |
14 | #include <util/atomic.h> // für cli() und sei() mit SREG Sicherung |
15 | |
16 | #define F_CPU 8000000UL // Systemtakt in Hz
|
17 | |
18 | #ifndef sbi
|
19 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
|
20 | #endif
|
21 | #ifndef cbi
|
22 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
|
23 | #endif
|
24 | |
25 | /*
|
26 | UART Berechnungen des Wertes für das Baudratenregister aus Taktrate und gewünschter Baudrate
|
27 | */
|
28 | #define BAUD 250000UL // gewünschte Baudrate
|
29 | #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // clever runden
|
30 | #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // reale Baudrate
|
31 | #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
|
32 | #if ((BAUD_ERROR<995) || (BAUD_ERROR>1005))
|
33 | #error Systematischer Fehler der Baudrate greosser 0,5% und damit zu hoch!
|
34 | #endif
|
35 | |
36 | // Definitionen und Initialisierungen
|
37 | #define Pin_TxD0_OUT sbi (DDRA,1) // TxD0 Ausgang
|
38 | #define Pin_RxD0_IN cbi (DDRA,2) // RxD0 Eingang
|
39 | #define Pin_RxD0_PULL sbi (PUEA,2) // RxD0 Pullup aktiv
|
40 | #define Pin_Led0_OUT sbi (DDRB,0) // PB0 Ausgang
|
41 | #define Pin_Led0_EIN sbi (PORTB,0) // PB0 einschalten
|
42 | #define Pin_Led0_AUS cbi (PORTB,0) // PB0 ausschalten
|
43 | #define Pin_Led1_OUT sbi (DDRB,1) // PB1 Ausgang
|
44 | #define Pin_Led1_EIN sbi (PORTB,1) // PB1 einschalten
|
45 | #define Pin_Led1_AUS cbi (PORTB,1) // PB1 ausschalten
|
46 | #define Pin_Led2_OUT sbi (DDRA,7) // PA7 Ausgang
|
47 | #define Pin_Led2_EIN sbi (PORTA,7) // PA7 einschalten
|
48 | #define Pin_Led2_AUS cbi (PORTA,7) // PA7 ausschalten
|
49 | #define Pin_Takt_OUT sbi (DDRB,2) // PB2 Ausgang, Pin 5
|
50 | |
51 | volatile unsigned int Count_TCNT2; |
52 | volatile bool Sync_Ende = false; |
53 | volatile uint32_t millis; |
54 | volatile uint32_t second; |
55 | |
56 | /* *** Funktion Deklarationen *** */
|
57 | void UART0_Init(); // Pin 11,12, TxD0/RxD0 |
58 | void USART_sendMsg(const uint8_t * data, const uint8_t length); |
59 | void UART0_senden(uint8_t data); |
60 | unsigned char UART0_empfangen(); |
61 | uint32_t millis_value(); |
62 | ISR(TIMER0_COMPA_vect); |
63 | ISR(PCINT0_vect); |
64 | ISR(TIMER2_COMPA_vect); |
65 | void set_Timer0(); |
66 | void set_Timer1(); // Timer 1, CTC, Mode 4, Pin 5, TOCC7 |
67 | void preSet_Timer_2(); // Timer 2, CTC, Mode 4 |
68 | void set_PCINT2(); |
69 | void del_PCINT2(); |
70 | void Sync_Starterkennung(); |
71 | |
72 | int main(void) |
73 | {
|
74 | |
75 | Pin_TxD0_OUT; // TxD0 Ausgang |
76 | Pin_RxD0_IN; // RxD0 Eingang |
77 | Pin_RxD0_PULL; // RxD0 Pullup aktiv |
78 | Pin_Led0_OUT; // PB0 Ausgang |
79 | Pin_Led1_OUT; // PB1 Ausgang |
80 | Pin_Led2_OUT; // PA7 Ausgang |
81 | Pin_Takt_OUT; // PB2 Ausgang, für Kontrollmessung ob OSCCAL stimmt |
82 | |
83 | set_Timer0(); |
84 | set_Timer1(); |
85 | preSet_Timer_2(); |
86 | Sync_Starterkennung(); |
87 | |
88 | while (1) |
89 | {
|
90 | |
91 | }
|
92 | }
|
93 | |
94 | |
95 | /* *** Funktionen *** */
|
96 | |
97 | void UART0_Init() |
98 | {
|
99 | /* Set baud rate */
|
100 | UBRR0H = UBRR_VAL >> 8; |
101 | UBRR0L = UBRR_VAL & 0xFF; |
102 | /* Enable receiver and transmitter */
|
103 | UCSR0B = (1<<RXEN0)|(1<<TXEN0); |
104 | /* Set frame format: 8data, 1stop bit */
|
105 | UCSR0C = (1<<UCSZ01)|(1<<UCSZ00); |
106 | }
|
107 | |
108 | |
109 | void USART_sendMsg(const uint8_t *data, const uint8_t length) { |
110 | |
111 | for (uint8_t i = 0; i < length; i++) { |
112 | UDR0 = (data [i]); |
113 | }
|
114 | }
|
115 | |
116 | |
117 | void UART0_senden( uint8_t data ) |
118 | {
|
119 | /* Wait for empty transmit buffer */
|
120 | //while ( !( UCSR0A & (1<<UDRE0)) )
|
121 | //;
|
122 | /* Put data into buffer, sends the data */
|
123 | UDR0 = data; |
124 | while ( !( UCSR0A & (1<<UDRE0)) ) {} |
125 | }
|
126 | |
127 | |
128 | unsigned char UART0_empfangen() |
129 | {
|
130 | /* Wait for data to be received */
|
131 | while ( !(UCSR0A & (1<<RXC0)) ) |
132 | ;
|
133 | /* Get and return received data from buffer */
|
134 | return UDR0; |
135 | }
|
136 | |
137 | |
138 | ISR(TIMER0_COMPA_vect) |
139 | {
|
140 | static uint32_t local_millis = 0; |
141 | |
142 | millis++; |
143 | local_millis++; |
144 | |
145 | if(local_millis > 999) { |
146 | second++; |
147 | local_millis = 0; |
148 | }
|
149 | }
|
150 | |
151 | |
152 | void set_Timer0 () // millis |
153 | {
|
154 | cli(); // Interupts ausschalten |
155 | TCNT0 = 0; // Register Reset |
156 | TCCR0A = 0; |
157 | TCCR0B = 0; |
158 | TIMSK0 = 0; |
159 | TCCR0A = (1 << WGM01); // CTC Modus |
160 | OCR0A = 124; // TOP, (F_CPU/PRESCALER)/1000-1 |
161 | TIMSK0 |= (1 << OCIE0A); // Interupts einschalten |
162 | TCCR0B |= (1 << CS01) | (1 << CS00); // Prescaler 64, Timer starten |
163 | sei(); |
164 | }
|
165 | |
166 | |
167 | void set_Timer1() // CTC, Mode 4, für Kontrollmessung ob OSCCAL stimmt |
168 | {
|
169 | cli(); // Interrupts ausschalten |
170 | TCCR1A = 0; // Reset TCCR1A Register |
171 | TCCR1B = 0; // Reset TCCR1B Register |
172 | TIMSK1 = 0; // Reset TIMSK1 Register (disable Timer Compare Interrupts) |
173 | TCNT1 = 0; // Start 0 |
174 | OCR1A = 399; // TOP Wert bestimmt mit Prescaler Takt, 10kHz (399) |
175 | TOCPMSA0 = (1<<TOCC7S0); // OC1A Routing Select |
176 | TOCPMCOE = (1<<TOCC7OE); // TOCC7 Output enable |
177 | TCCR1A = (1<<COM1A0); // Toggle OC1A Pin on compare match |
178 | TCCR1B = (1<<WGM12) | (1<<CS10); // Prescaler 1 |
179 | sei(); // Interrupts einschalten |
180 | } // end Funktion |
181 | |
182 | |
183 | void preSet_Timer_2() // CTC, Mode 4 |
184 | {
|
185 | cli(); // Interrupts ausschalten |
186 | TCCR2A = 0; // Reset Register |
187 | TCCR2B = 0; // Reset Register |
188 | TIMSK2 = 0; // Reset Register (disable all Interrupts) |
189 | TCNT2 = 0; // Zähler nullen |
190 | TCCR2B = (1<<WGM22); // CTC, Mode 4 |
191 | TIMSK2 = (1<<OCIE2A); // enable Compare Match A Interrupt |
192 | OCR2A = 65535; |
193 | sei(); // Interrupts einschalten |
194 | } // end Funktion |
195 | |
196 | |
197 | void set_PCINT2() |
198 | {
|
199 | GIMSK = (1<<PCIE0); // PCINT 7:0 Gruppe Trigger aktiv |
200 | PCMSK0 = (1<<PCINT2); // PCINT2, Pin Einzelauswahl |
201 | }
|
202 | |
203 | |
204 | void del_PCINT2() |
205 | {
|
206 | GIMSK = 0; // PCINT 7:0 Gruppe deaktivieren |
207 | PCMSK0 = 0; // PCINT2 deaktivieren |
208 | }
|
209 | |
210 | |
211 | void Sync_Starterkennung() |
212 | {
|
213 | int temp_millis = 0; |
214 | bool SignalPegel = false; |
215 | |
216 | // LOW/High Pegeldauer messen
|
217 | // Flanke High > LOW
|
218 | while ( PINA & (1<<PA2) ) {} // wenn RxD0 Pin auf HIGH, dann warten bis wieder LOW |
219 | temp_millis = millis_value(); // aktuelle Zeitnahme |
220 | |
221 | while ( !(PINA & (1<<PA2)) && (SignalPegel == false) ) { // wenn RxD0 auf LOW, warten bis wieder HIGH |
222 | if ((millis_value() - temp_millis) > 1) { // wenn Low-Pegel länger wie 1ms war ... |
223 | SignalPegel = true; |
224 | }
|
225 | }
|
226 | |
227 | temp_millis = millis_value(); // aktuelle Zeitnahme |
228 | SignalPegel = false; |
229 | |
230 | while ( PINA & (1<<PA2) && (SignalPegel == false) ) { // wenn RxD0 Pin auf HIGH, dann warten bis wieder LOW |
231 | if ((millis_value() - temp_millis) > 1) { // wenn High-Pegel länger wie 1ms war ... |
232 | SignalPegel = true; |
233 | }
|
234 | }
|
235 | |
236 | set_PCINT2(); // PCINT2 Interrupt aktivieren |
237 | TCCR2B = (1<<CS20); // Timer 2 starten, Prescaler 1 |
238 | Pin_Led0_EIN; // Debug |
239 | |
240 | }
|
241 | |
242 | |
243 | ISR(PCINT0_vect) // Interrupt Handler PCINT0 |
244 | {
|
245 | static unsigned int Counter = 0; |
246 | |
247 | Counter++; // zählt die Pegelwechsel |
248 | |
249 | if (Counter > 19) { // 20 Pegelwechsel |
250 | //TCCR2B = 0; // Timer stoppen, Reset TCCR2B Register
|
251 | TIMSK2 = 0; // Reset TIMSK2 Register (disable Timer Compare Interrupts) |
252 | Count_TCNT2 = TCNT2; // Zählerstand speichern |
253 | TCNT2 = 0; // Zähler nullen |
254 | Sync_Ende = true; |
255 | Counter = 0; |
256 | Pin_Led1_EIN; // Debug |
257 | }
|
258 | }
|
259 | |
260 | |
261 | ISR(TIMER2_COMPA_vect) // Timer2 Compare Match A |
262 | {
|
263 | // wenn ich aktiv werde ging was schief
|
264 | Pin_Led2_EIN; // Debug, PA7 einschalten |
265 | }
|
266 | |
267 | |
268 | uint32_t millis_value() |
269 | {
|
270 | uint32_t value = 0; |
271 | ATOMIC_BLOCK (ATOMIC_RESTORESTATE) // cli und sei mit SREG Sicherung |
272 | {
|
273 | value = millis; |
274 | }
|
275 | return value; |
276 | }
|
:
Bearbeitet durch User
Veit D. schrieb: > Eine böse Falle waren die Namen der Interrupt Vectoren. > Im Datenblatt stehen die falsch drin. Das dazu. > Bsp. "TIM2_COMPA" statt "TIMER2_COMPA", Seite 49, 50 Vorsicht, du musst hier das Datenblatt genau lesen. Das Codebeispiel auf Seite 50 definiert ja die Labels entsprechend sodass es wieder passt ;)
Als Hobby sollte man sich den naechst Groesseren goennen, der eine richtige UART, hinreichend Pins und Quarz besizt goennen. Ein Mega 644 kostet weniger als 5 euro. Wegen den paar Euros den Aufstand ?
Hallo, auf Seite 50 stehen Code Bsp., okay. Aber auf Seite 49 in der Tabelle "Interrupt Vectors" sollten doch die Namen so stehen wie man sie schreiben muß. Plus den Zusatz _vect. Im Datenblatt vom ATmega2560 steht z.Bsp. in der Tabelle "TIMER2_COMPA". Deswegen war ich verwirrt und mußte in der Definitionsdatei nachschauen. Ich dachte wenn ich das Datenblatt von Atmel habe und die Software von Atmel, dann muß man das so schreiben wie im Datenblatt angegeben? Und das Code Bsp. von Seite 50 sollte ja das wiedergeben was in der Definitionsdatei drin steht. Meine ich. Wo hättest du nachgeschaut, wenn du merkst, dass "TIM2_COMPA_vect" falsch ist. Beim ATtiny841. Wir müssen aber nicht unbedingt wegen der Kleinigkeit diskutieren. :-) @ oh doch: Du hast leider nicht gelesen worum es geht.
Hallo, seit zwei Wochen bin ich wieder am Thema. Der ATtiny bekommt von einem anderen 16MHz µC mit jeden Tastendruck 100 Takte über die serielle geliefert. Das wird mittels einer for Schleife und 20x 'U' gemacht mit einer Baudrate von 50k. Was 25kHz entspricht. Der ATtiny wertet die Low Pegel aus. Wenn er das erste Low erkennt, wird der Timer 2 gestartet. Nach 100 erkannten Low Pegeln stoppt der Timer. Der TCNT2 Count wird ausgewertet. Idealerweise müsste er bis 32000 zählen. Ich habe eine Toleranz von +/- 60 eingebaut. Ist der Zählerstand größer 32060 taktet der ATtiny zu schnell, OSCCAL--. Ist der Zählerstand kleiner 31940, taktet der ATtiny zu langsam, OSCCAL++. Obwohl die Sache erstmal funktioniert, bin ich noch nicht zufrieden. Er kalibriert mir immer etwas daneben. Auf beiden µC lasse ich einen Kontrolltakt von 10kHz erzeugen mittels Timer. Am Oszi messe ich vom Taktgeber perfekte 10kHz. Der ATtiny stellt sich am Ende auf 10,1kHz ein. Sein OSCCAL steht dann auf 37.
1 | T[µs] TCNT2 OSCCAL |
2 | 102,4 30946 30 |
3 | 101,8 31130 31 |
4 | 101,4 31259 32 |
5 | 101,0 31306 33 |
6 | 100,4 31524 34 |
7 | 100,0 31667 35 |
8 | 99,6 31797 36 |
9 | 99,2 31927 37 |
10 | 98,8 32074 38 |
11 | 99,2 31936 37 |
12 | 98,8 32088 38 |
13 | 99,2 31950 37 |
Eigentlich müsste er einen OSCCAL von 35 finden. Allerdings ist da der TCNT2 Wert weit weg. Den Takt mit manuell vergebenen OSCCAL hatte ich vorher einmal ausgemessen.
1 | Cal MHz kHz % TCNT2 diff |
2 | 41 8,20 10,20 2,50 32800 |
3 | 40 8,16 10,20 2,00 32640 160 |
4 | 39 8,12 10,20 1,50 32480 160 |
5 | 38 8,10 10,10 1,25 32400 80 |
6 | 37 8,06 10,10 0,75 32240 160 |
7 | 36 8,02 10,00 0,25 32080 160 |
8 | 35 8,00 9,98 0,00 32000 80 |
9 | 34 7,96 9,94 0,50 31840 160 |
10 | 33 7,94 9,90 -0,75 31760 80 |
11 | 32 7,90 9,86 -1,25 31600 160 |
TCNT2 sind berechnet: xMHz / 25kHz * 100 = TCNT2 (die 100 sind meine gezählten Low-Pegel. Warum findet er sich bei 10kHz nicht wieder? Warum liegt der TCNT2 daneben? Liegt das am Code oder habe ich das falsch umgesetzt?
1 | #define F_CPU 8000000UL // Systemtakt in Hz
|
2 | |
3 | #include <avr/io.h> |
4 | #include <stdio.h> |
5 | #include <stdlib.h> |
6 | #include <avr/interrupt.h> |
7 | #include <util/atomic.h> // für cli() und sei() mit SREG Sicherung |
8 | #include <util/delay.h> |
9 | |
10 | #ifndef sbi
|
11 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
|
12 | #endif
|
13 | #ifndef cbi
|
14 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
|
15 | #endif
|
16 | |
17 | /*
|
18 | UART Berechnungen des Wertes für das Baudratenregister aus Taktrate und gewünschter Baudrate
|
19 | */
|
20 | //#define BAUD 250000UL // gewünschte Baudrate
|
21 | #define BAUD 50000UL // gewünschte Baudrate
|
22 | #define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // sauber runden
|
23 | #define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1))) // reale Baudrate
|
24 | #define BAUD_ERROR ((BAUD_REAL*1000)/BAUD) // Fehler in Promille, 1000 = kein Fehler.
|
25 | #if ((BAUD_ERROR<995) || (BAUD_ERROR>1005))
|
26 | #error Systematischer Fehler der Baudrate greosser 0,5% und damit zu hoch!
|
27 | #endif
|
28 | |
29 | // Definitionen und Initialisierungen
|
30 | #define Pin_TxD0_OUT sbi (DDRA,1) // TxD0 Ausgang
|
31 | #define Pin_RxD0_IN cbi (DDRA,2) // RxD0 Eingang
|
32 | #define Pin_RxD0_PULL sbi (PUEA,2) // RxD0 Pullup aktiv
|
33 | #define Pin_TxD1_OUT sbi (DDRA,5) // TxD1 Ausgang
|
34 | #define Pin_RxD1_IN cbi (DDRA,4) // RxD1 Eingang
|
35 | #define Pin_RxD1_PULL sbi (PUEA,4) // RxD1 Pullup aktiv
|
36 | #define Pin_Led0_OUT sbi (DDRB,0) // PB0 Ausgang
|
37 | #define Pin_Led0_EIN sbi (PORTB,0) // PB0 einschalten
|
38 | #define Pin_Led0_AUS cbi (PORTB,0) // PB0 ausschalten
|
39 | #define Pin_Led1_OUT sbi (DDRB,1) // PB1 Ausgang
|
40 | #define Pin_Led1_EIN sbi (PORTB,1) // PB1 einschalten
|
41 | #define Pin_Led1_AUS cbi (PORTB,1) // PB1 ausschalten
|
42 | #define Pin_Led2_OUT sbi (DDRA,7) // PA7 Ausgang
|
43 | #define Pin_Led2_EIN sbi (PORTA,7) // PA7 einschalten
|
44 | #define Pin_Led2_AUS cbi (PORTA,7) // PA7 ausschalten
|
45 | #define Pin_Led3_OUT sbi (DDRA,6) // PA6 Ausgang
|
46 | #define Pin_Led3_EIN sbi (PORTA,6) // PA6 einschalten
|
47 | #define Pin_Led3_AUS cbi (PORTA,6) // PA6 ausschalten
|
48 | #define Pin_Led4_OUT sbi (DDRA,3) // PA3 Ausgang
|
49 | #define Pin_Led4_EIN sbi (PORTA,3) // PA3 einschalten
|
50 | #define Pin_Led4_AUS cbi (PORTA,3) // PA3 ausschalten
|
51 | |
52 | #define Pin_Takt_OUT sbi (DDRB,2) // PB2 Ausgang, Pin 5
|
53 | |
54 | typedef enum {STOP, OKAY, TIMEOUT, READY, RUNNING, REPEAT, FINISH} SyncStates; // Steuerzustände |
55 | SyncStates state_Cal = STOP; |
56 | |
57 | unsigned int Count_TCNT2; |
58 | |
59 | |
60 | /* *** Funktion Deklarationen *** */
|
61 | void UART1_Init(); // Pin 8,9, TxD1/RxD1 |
62 | void UART1_sendChar(unsigned char data); |
63 | void UART1_sendString (const char *s); |
64 | void UART1_send_uint32(uint32_t zahl); |
65 | unsigned char UART1_empfangen(); |
66 | ISR(PCINT0_vect); |
67 | ISR(TIMER2_COMPA_vect); |
68 | void set_Timer1(); // Timer 1, CTC, Mode 4, Pin 5, TOCC7 |
69 | void preSet_Timer_2(); // Timer 2, CTC, Mode 4 |
70 | void set_PCINT2(); |
71 | void del_PCINT2(); |
72 | void Calibration_Routine(); |
73 | void Debug_LED(); |
74 | |
75 | |
76 | int main(void) |
77 | {
|
78 | // µC Takt langsam verstellen zum testen
|
79 | OSCCAL0 = 40; _delay_ms(500); |
80 | OSCCAL0 = 38; _delay_ms(500); |
81 | OSCCAL0 = 36; _delay_ms(500); |
82 | OSCCAL0 = 34; _delay_ms(500); |
83 | OSCCAL0 = 32; _delay_ms(500); |
84 | |
85 | Pin_TxD0_OUT; // TxD0 Ausgang |
86 | Pin_RxD0_IN; // RxD0 Eingang |
87 | Pin_RxD0_PULL; // RxD0 Pullup aktiv |
88 | Pin_TxD1_OUT; // TxD1 Ausgang |
89 | Pin_RxD1_IN; // RxD1 Eingang |
90 | Pin_RxD1_PULL; // RxD1 Pullup aktiv |
91 | Pin_Led0_OUT; // PB0 Ausgang |
92 | Pin_Led1_OUT; // PB1 Ausgang |
93 | Pin_Led2_OUT; // PA7 Ausgang |
94 | Pin_Led3_OUT; // PA6 Ausgang |
95 | Pin_Led4_OUT; // PA3 Ausgang |
96 | Pin_Takt_OUT; // PB2 Ausgang, für Kontrollmessung ob OSCCAL stimmt |
97 | |
98 | UART1_Init(); |
99 | set_Timer1(); |
100 | UART1_sendString("ATtiny841" "\r\n"); |
101 | preSet_Timer_2(); |
102 | |
103 | while (1) |
104 | {
|
105 | if (state_Cal != FINISH) { |
106 | Calibration_Routine(); |
107 | Debug_LED(); |
108 | }
|
109 | }
|
110 | }
|
111 | |
112 | |
113 | /* *** Funktionen *** */
|
114 | |
115 | void UART1_Init() |
116 | {
|
117 | /* Set baud rate */
|
118 | UBRR1H = UBRR_VAL >> 8; |
119 | UBRR1L = UBRR_VAL & 0xFF; |
120 | /* Enable receiver and transmitter */
|
121 | UCSR1B = (1<<RXEN1)|(1<<TXEN1); |
122 | /* Set frame format: 8data, 1stop bit */
|
123 | UCSR1C = (1<<UCSZ11)|(1<<UCSZ10); |
124 | }
|
125 | |
126 | |
127 | void UART1_sendChar( unsigned char data ) |
128 | {
|
129 | while ( !( UCSR1A & (1<<UDRE1)) ) // wait for empty transmit buffer |
130 | ;
|
131 | UDR1 = data; // Put data into buffer, sends the data |
132 | }
|
133 | |
134 | |
135 | void UART1_sendString (const char *string) |
136 | { // Vergleich auf ASCII Zeichen '\0' (NUL) |
137 | while (*string != 0x00) { // "String-Endezeichen" (Null-Terminator) |
138 | UART1_sendChar(*string); // vorderstes Zeichen senden |
139 | string++; |
140 | }
|
141 | }
|
142 | |
143 | |
144 | void UART1_send_uint32(uint32_t zahl) |
145 | {
|
146 | // convert eine long Zahl zu einem String
|
147 | char string[11]; |
148 | ultoa(zahl, string, 10); // convert unsigned long to string, radix=10 |
149 | UART1_sendString(string); |
150 | }
|
151 | |
152 | |
153 | unsigned char UART1_empfangen() |
154 | {
|
155 | /* Wait for data to be received */
|
156 | while ( !(UCSR1A & (1<<RXC1)) ) |
157 | ;
|
158 | /* Get and return received data from buffer */
|
159 | return UDR1; |
160 | }
|
161 | |
162 | |
163 | void set_Timer1() // CTC, Mode 4, für Kontrollmessung ob OSCCAL stimmt |
164 | {
|
165 | cli(); // Interrupts ausschalten |
166 | TCCR1A = 0; // Reset TCCR1A Register |
167 | TCCR1B = 0; // Reset TCCR1B Register |
168 | TIMSK1 = 0; // Reset TIMSK1 Register (disable Timer Compare Interrupts) |
169 | TCNT1 = 0; // Start 0 |
170 | OCR1A = 399; // TOP Wert bestimmt mit Prescaler Takt, 10kHz (399) |
171 | TOCPMSA1 = (1<<TOCC7S0); // OC1A Routing Select |
172 | TOCPMCOE = (1<<TOCC7OE); // TOCC7 Output enable (Pin PB2) |
173 | TCCR1A = (1<<COM1A0); // Toggle OC1A Pin on compare match |
174 | TCCR1B = (1<<WGM12) | (1<<CS10); // Prescaler 1 |
175 | sei(); // Interrupts einschalten |
176 | } // end Funktion |
177 | |
178 | |
179 | void preSet_Timer_2() // CTC, Mode 4 |
180 | {
|
181 | cli(); // Interrupts ausschalten |
182 | TCCR2A = 0; // Reset Register |
183 | TCCR2B = 0; // Reset Register |
184 | TIMSK2 = 0; // Reset Register (disable all Interrupts) |
185 | TCNT2 = 0; // Zähler nullen |
186 | TCCR2B = (1<<WGM22); // CTC, Mode 4 |
187 | TIMSK2 = (1<<OCIE2A); // enable Compare Match A Interrupt |
188 | OCR2A = 65000; // OVF ... TimeOut |
189 | sei(); // Interrupts einschalten |
190 | } // end Funktion |
191 | |
192 | |
193 | void set_PCINT2() |
194 | {
|
195 | GIMSK = (1<<PCIE0); // PCINT 7:0 Gruppe Trigger aktiv |
196 | PCMSK0 = (1<<PCINT2); // PCINT2, Pin Einzelauswahl |
197 | }
|
198 | |
199 | |
200 | void del_PCINT2() |
201 | {
|
202 | GIMSK = 0; // PCINT 7:0 Gruppe deaktivieren |
203 | PCMSK0 = 0; // PCINT2 deaktivieren |
204 | }
|
205 | |
206 | |
207 | void Calibration_Routine() |
208 | {
|
209 | const uint8_t Hysterese = 60; |
210 | |
211 | if (state_Cal == STOP) { |
212 | set_PCINT2(); |
213 | state_Cal = READY; |
214 | }
|
215 | |
216 | if (state_Cal == TIMEOUT) { |
217 | TCCR2B &= ~(1<<CS20); // Timer stoppen |
218 | }
|
219 | |
220 | if (state_Cal == OKAY) { // TCNT2 auswerten und OSCCAL nachstellen |
221 | ATOMIC_BLOCK (ATOMIC_RESTORESTATE) { |
222 | Count_TCNT2 = TCNT2; // Zählerstand auslesen/speichern |
223 | TCNT2 = 0; // Zähler nullen |
224 | }
|
225 | |
226 | UART1_sendString("alt OSCCAL "); UART1_send_uint32(OSCCAL0); UART1_sendString("\r\n"); |
227 | UART1_sendString("TCNT2 "); UART1_send_uint32(Count_TCNT2); UART1_sendString("\r\n"); |
228 | |
229 | if (Count_TCNT2 > (32000+Hysterese)) { |
230 | OSCCAL0--; |
231 | state_Cal = REPEAT; |
232 | }
|
233 | |
234 | if (Count_TCNT2 < (32000-Hysterese)) { |
235 | OSCCAL0++; |
236 | state_Cal = REPEAT; |
237 | }
|
238 | |
239 | // liegt TCNT2 im Limit ?
|
240 | if ( (Count_TCNT2 < (32000+Hysterese)) && (Count_TCNT2 > (32000-Hysterese)) ) { |
241 | state_Cal = FINISH; |
242 | del_PCINT2(); |
243 | }
|
244 | |
245 | UART1_sendString("state "); UART1_send_uint32(state_Cal); UART1_sendString("\r\n"); |
246 | UART1_sendString("new OSCCAL "); UART1_send_uint32(OSCCAL0); UART1_sendString("\r\n"); |
247 | |
248 | if (state_Cal == REPEAT) { |
249 | state_Cal = READY; |
250 | }
|
251 | }
|
252 | |
253 | }
|
254 | |
255 | |
256 | ISR(PCINT0_vect) // Interrupt Handler PCINT0 |
257 | {
|
258 | static unsigned int Counter = 0; |
259 | |
260 | uint8_t reg = PINA; // Portzustand sichern |
261 | |
262 | if (state_Cal == READY) { |
263 | state_Cal = RUNNING; |
264 | TCCR2B |= (1<<CS20); // Timer 2 starten mit Prescaler 1 |
265 | }
|
266 | |
267 | if ( (~reg & 0b00000100) & (1<<PINA2) ) { // prüft ob Pin Bit.2 auf Low ist |
268 | Counter++; |
269 | }
|
270 | |
271 | if (Counter > 99) { // 100 Pegelwechsel |
272 | TCCR2B &= ~(1<<CS20); // Timer stoppen |
273 | state_Cal = OKAY; |
274 | Counter = 0; |
275 | }
|
276 | |
277 | }
|
278 | |
279 | |
280 | ISR(TIMER2_COMPA_vect) // Timer2 Compare Match A |
281 | {
|
282 | // wenn ich aktiv werde ging was schief
|
283 | state_Cal = TIMEOUT; |
284 | }
|
285 | |
286 | |
287 | void Debug_LED () |
288 | {
|
289 | if (state_Cal == READY) { |
290 | Pin_Led0_EIN; Pin_Led1_AUS; Pin_Led2_AUS; Pin_Led3_AUS; Pin_Led4_AUS; |
291 | }
|
292 | if (state_Cal == RUNNING) { |
293 | Pin_Led1_EIN; Pin_Led0_AUS; Pin_Led2_AUS; Pin_Led3_AUS; Pin_Led4_AUS; |
294 | }
|
295 | if (state_Cal == REPEAT) { |
296 | Pin_Led2_EIN; Pin_Led0_AUS; Pin_Led1_AUS; Pin_Led3_AUS; Pin_Led4_AUS; |
297 | }
|
298 | if (state_Cal == OKAY) { |
299 | Pin_Led3_EIN; Pin_Led0_AUS; Pin_Led1_AUS; Pin_Led2_AUS; Pin_Led4_AUS; |
300 | }
|
301 | if (state_Cal == TIMEOUT) { |
302 | Pin_Led4_EIN; Pin_Led3_AUS; Pin_Led0_AUS; Pin_Led1_AUS; Pin_Led2_AUS; |
303 | }
|
304 | }
|
Edit: den Takt der Baudrate habe ich soeben kontrolliert, sind exakt 25kHz. Hätte mich auch gewundert bei dem glatten Teilerfaktor wenn das anders wäre.
:
Bearbeitet durch User
Veit D. schrieb: > Er kalibriert mir immer etwas daneben. Das ist völlig normal. Die Auflösung des Stellgliedes (OSCCAL) ist einfach nicht sehr hoch. Es wäre reiner Zufall, wenn sich ein exakter Treffer ergäben würde. > Der ATtiny stellt sich am Ende auf 10,1kHz ein. Das sind 1%. Viel besser geht's eben wegen der begrenzten Auflösung von OSCCAL sowieso nicht. > Ich habe eine Toleranz von +/- 60 eingebaut. Das allein sind schon 0,2%. Der Rest dürfte in den Latenzen deines Programms stecken und der Nichtberücksichtigung der dynamischen Eigenschaften von OSCCAL. Es ist nämlich nicht so, dass eine Änderung des Wertes darin quasi instantan die Frequenz des Oszillators ändert. Das dauert eine gewisse Zeit.
Hallo, mich wundert eben dass ich bei meinen statischen Messungen andere Werte bekommen habe wie jetzt mit der automatischen Kalibrierung. Wieviel ms benötigt denn der µC nach Änderung am OSCCAL um stabil zu laufen? Ich meine wenn ich messe bleiben zum Bsp. die 10,1kHz absolut stabil. Da ändert sich auch Sekunden später nichts. Wenn ich dich richtig verstehe geht etwas schief sobald er zum nächsten OSCCAL umschaltet, weil er quasi sofort wieder den Takt messen tut. Ich müsste demzufolge ein delay einbauen nach OSCCAL Änderung? Würde aktuell betrachtet bedeuten, wenn ich nach der ersten Kalibrierrunde noch eine starte, müßte sich nochmal was ändern? Im Datenblatt lese ich nur das man den OSCCAL nicht um mehr als 2% verstellen darf von Änderung zu Änderung. Also ich darf nicht von default 41 direkt auf 35 gehen. Was kann ich wegen den Latenzen im Code verbessern? Paar Statusvariablen kommen noch raus, sind nur zum testen und debuggen soviele drin. Im Grund geht es doch nur darum den Timer exakt zu starten und exakt zu stoppen. Der Rest sollte nebensächlich sein.
Die Ausgabefrequenz ist in der Frequenzschrittweite durch die Koernigkeit der Ansteuerung begrenzt. Allenfalls kann man laenger messen, und allenfalls kann man das Nachstellen als langsamer Prozess nebenbei mitlaufen lassen. zB einmal alle Minute.
Hallo, Moment. Derzeit gebe noch manuell eine neue Taktsequence raus per Tastendruck vom Taktgeber (Master). Das heißt, der ATtiny hat nach OSCCAL Änderung genügend Zeit sich zu stabilisieren. Der Ablauf ist wie folgt. - der ATtiny empfängt die 100 Takte - wertet diese an Hand vom TCNT Counter aus und ändert darauf sofort den OSCCAL und wartet wieder auf neue 100 Takte, Zeit zum stabilisieren - außer die Auswertung vom TCNT Counter liegt im Limit, dann wartet er nicht mehr und meldet "fertig". Scheinbar drehen sich meine Gedanken im Kreis. Jedoch hatte ich den ATtiny vorher wie gesagt mit verschiedenen OSCCAL ausgemessen. Demzufolge sollte er doch einen Wert von 35 oder 36 finden und nicht 37 oder 38. Wo liegt mein Denkfehler?
Hallo, während ich tippte kam deine Antwort. Ich hatte vor das sich beim einschalten aller µC die ATtinys am Bus kalibrieren der Reihe nach. Da diese keine Temperaturschwankungen ausgesetzt sind, wollte ich es erstmal bei der einen Einschalt-Kalibrierung belassen. Zwischendurch wäre nur erstmal eine Option. Wegen der Genaugikeit. Das ist ja gerade das verrückte. Ich weiß ja das er mit 35 oder 36 genauer takten könnte, aber er bekommt das alleine nicht hin. Ich könnte den Zählumfang erhöhen, also größer 32000. Mit Ansteuerung meinst du den PCINT oder den OSCCAL? Edit: Wieviel % Taktabweichung der UARTs sind denn überhaupt tolerierbar? Ziel ist es mit 250k zu kommunizieren.
:
Bearbeitet durch User
Hallo, Zählumfang erhöhen brachte keine Besserung. Mit dem Code von oben und ohne künstliche Vorabverstellung des OSCCAL, ist der OSCCAL default nach Reset erstmal zu hoch und muss runter. Ergibt:
1 | TCNT2 OSCCAL |
2 | 32453 41 |
3 | 32284 40 |
4 | 32142 39 |
5 | 32014 38 |
Jetzt habe ich den ISR Code geändert auf (gekürzt):
1 | ISR(PCINT0_vect) // Interrupt Handler PCINT0 |
2 | {
|
3 | static unsigned int Counter = 0; |
4 | |
5 | if (state_Cal == READY) { |
6 | TCCR2B |= (1<<CS20); // Timer 2 starten mit Prescaler 1 |
7 | state_Cal = RUNNING; |
8 | }
|
9 | |
10 | Counter++; |
11 | |
12 | if (Counter > 199) { // 200 Pegelwechsel |
13 | TCCR2B &= ~(1<<CS20); // Timer stoppen |
14 | state_Cal = OKAY; |
15 | Counter = 0; |
16 | }
|
17 | }
|
ergibt:
1 | TCNT2 OSCCAL |
2 | 32620 41 |
3 | 32475 40 145 |
4 | 32330 39 145 |
5 | 32185 38 145 |
6 | 32061 37 124 |
7 | 32188 38 -127 |
8 | 32052 37 136 |
9 | 32192 38 -140 |
10 | 32046 37 146 |
11 | 32193 38 -147 |
12 | 32038 37 155 |
Der Timer zählt plötzlich ca. 200 Counts höher wie vorher. Was mir nicht klar ist, warum zählt er dadurch höher und nicht niedriger, wenn er schneller durch die ISR kommt? Kostet der Vergleich wirklich 200 Takte?
1 | if ( (~reg & 0b00000100) & (1<<PINA2) ) { // prüft ob Pin Bit.2 Low |
2 | Counter++; |
3 | PulsCounter = Counter; |
4 | }
|
Danach habe ich die Verzögerung zum Timer stoppen verkürzt.
1 | ISR(PCINT0_vect) // Interrupt Handler PCINT0 |
2 | {
|
3 | static unsigned int Counter = 0; |
4 | |
5 | if (state_Cal == READY) { |
6 | TCCR2B |= (1<<CS20); // Timer 2 starten mit Prescaler 1 |
7 | state_Cal = RUNNING; |
8 | }
|
9 | |
10 | Counter++; |
11 | |
12 | if (Counter > 199) { // 200 Pegelwechsel |
13 | TCCR2B = 0; // Timer stoppen (könnte schneller gehen) |
14 | TCCR2B = (1<<WGM22); // CTC, Mode 4 neu vorbereiten |
15 | state_Cal = OKAY; |
16 | Counter = 0; |
17 | }
|
18 | }
|
Der Timer stoppt ca. 100 Takte eher.
1 | TCNT2 OSCCAL |
2 | 32527 41 |
3 | 32384 40 143 |
4 | 32231 39 153 |
5 | 32080 38 151 |
6 | 31930 37 150 |
7 | 32074 38 -144 |
8 | 32227 39 -153 |
9 | 32083 38 144 |
10 | 31944 37 139 |
11 | 32077 38 -133 |
12 | 31935 37 142 |
Hier ein paar weitere mögliche Fehlerquellen: 1. Du erzeugst den Takt per UART. Die Bits innerhalb eines Bytes sind da immer gleich lang, aber ist auch sichergestellt, daß zwischen dem Stop-Bit eines und dem Start-Bit des nächsten Bytes nicht etwas mehr Zeit vergeht, als die Länge eines Bits? 2. Du startest und stoppst den Timer in Software. Das ist mit systematischen Ungenauigkeiten verbunden. Besser wäre es, nur einen einzigen Takt zu senden (z.B. ein Nullbyte) und dessen Länge per Input-Capture in Hardware auszumessen. 3. Ich würde die 60 Takte Toleranz weglassen. Kalibriere auf den Wert, der die geringste Abweichung vom Sollwert hat.
Veit D. schrieb: > Zählumfang erhöhen brachte keine Besserung. Array of uint16_t [99] anlegen oder malloc(100 * sizeof(uint16_t)). TCNT2 in Normal Mode. In PCINT0_ISR nur TCNT2 ins Array reinschreiben und Pointer ++ Wenn Pointer > 99 Auswerteroutine aufrufen. Erst in der Auswerteroutine werden die Überläufe berücksichtigt und die Abstände berechnet. Wert in Array[0] wird nur als Anfangswert benutzt, deswegen wird in der ISR auch nur auf Pointer > 99 geprüft und sonst nichts. Fehler ist damit max. +/- 1 Takt. P.S. Selbstverständlich ist das alles unnötig. Die Tinys sind vom Werk schon ausreichend genau. Die Temperatur- und Spannungsabweichungen sind dein eigentliches Problem.
:
Bearbeitet durch User
Marc V. schrieb: > Die Temperatur- und Spannungsabweichungen > sind dein eigentliches Problem. sehe ich auch so und es ist ein Unterschied ob es eine echter Kaltstart nach Power ON ist oder ein Warmstart nach Reset! Das könnte ja noch in der Selbstkalibration eingebaut werden.
Hallo, ganz ehrlich, ich kann zwar lesen was ihr mir sagen wollt, aber warum verstehe ich nicht wirklich. Momentan inkrementiere ich in der PCINT0 ISR nur einen Counter. Der zählt die Pegelwechsel. Und nun soll ich statt dessen jedesmal den Timercounter in ein Array schreiben? Dauert doch viel länger als jetzt. Was soll ich mit 100 Timercounterwerten anfangen? Genauigkeit +/- ein einzigen Takt? Wirklich? Da würde es doch ausreichen die Überläufe zu zählen und dazu den letzten Stand des Timercounters zur Gesamtberechnung. Nur wie lange soll den gemessen werden wenn du von Überläufen redest? Bei 100 Überläufen würde das alles ca. 800ms dauern für eine Messung. Aktuell messe ich die Zeit in Timercounts, die vergeht bis 100 Low-Pegel bzw. 200 Pegelwechsel vorbei sind. Alles innerhalb von 8ms, also innerhalb eines möglichen Überlaufs. Die Spannung ist stabil und es ist Zimmertemperatur. Der Effekt tritt auch auf wenn der ATtiny schon paar Stunden unter Spannung steht. Das ein absoluter Kaltstart und ein erwärmter ATTiny Unterschiede machen ist mir bewusst, kann ich später einbauen mit Nachkalibrierung. Nur erstmal sollte ich verstehen was jetzt falsch läuft. Die PCINT0 ISR wird doch im Grunde sofort aufgerufen, der Counter++ und später unmittelbar der Timer gestoppt. Oder wollt ihr mir sagen das meine Messung um Größenordnungen viel zu kurz ist? Nur dann wären ja solche Aussagen wie, ich messe einen Frame während der Übertragung aus um Größenordnungen zu kurz und zu ungenau. Manche wollen nur das Startbit ausmessen. Wäre noch ungenauer. Deswegen dachte ich meine ca. 4ms wären schon übertrieben lang. R.Max.: ob die Stopbits gleich lang sind kann ich mal ausmessen bzw. mir anschauen. Ein einziger Takt? Ist das nicht zu ungenau? Die Toleranz kann ich nicht weglassen. Den Zielwert trifft man nie genau, ich kann 1000 Messungen mit unveränderten OSCCAL machen, es gibt immer Schwankungen.
Hallo, habe die High und Low Pegel zeitlich angeschaut. Liegt zwischen 20µs und 19,96µs. Mal die eine mal die andere, wird an der Messungenauigkeit vom Oszi liegen, nehme ich. Also von daher würde ich sagen, dass alle Bits gleich lang sind. Wegen der Werkskalibrierung. Ja die ist genau. Aber nur gültig für 3,3V. Ich betreibe den mit 5V, weshalb er mit 8,2MHz taktet.
:
Bearbeitet durch User
Hallo, nach x-mal lesen ... Kann es sein das du Marc, mit der mehrfachen und längeren Messung die Messschwankungen der einzelnen TCNT Messungen ausgleichst? Ich weis zwar noch immer nicht was du mit den gespeicherten TCNT Einzelwerten machst, klingt aber für mich so, also könnte ich den Gesamtwert des TCNT inkl. Überläufen durch die Anzahl der Messungen teilen und eleminiere damit zu einem Großteil dessen Einzelschwankungen. Meinst du sowas in der Art?
Veit D. schrieb: > Kann es sein das du Marc, mit der mehrfachen und längeren Messung die > Messschwankungen der einzelnen TCNT Messungen ausgleichst? Unter anderem. > Ich weis zwar noch immer nicht was du mit den gespeicherten TCNT > Einzelwerten machst, klingt aber für mich so, also könnte ich den > Gesamtwert des TCNT inkl. Überläufen durch die Anzahl der Messungen > teilen und eleminiere damit zu einem Großteil dessen Einzelschwankungen. > Meinst du sowas in der Art? Ja, ausserdem hast du eine Routine mit ausreichend Zeit um alles genau zu vergleichen und zu prüfen, ev. Glitches zu eliminieren, Min. und Max Dauer zu finden, usw. Das alles kann man in Echtzeit nicht oder nicht ausreichend genau machen. Timer2 kann beliebig schnell bzw. langsam laufen, solange der nicht in einer Halbperiode überläuft. Timer2 Overflow ISR wird natürlich nicht benutzt, ev. wird die Gesamtdauer oder die Zeitüberschreitung der Messung mit einem anderen Timer kontrolliert. P.S. Veit D. schrieb: > Momentan inkrementiere ich in der PCINT0 ISR nur einen Counter. Der > zählt die Pegelwechsel. Und nun soll ich statt dessen jedesmal den > Timercounter in ein Array schreiben? Dauert doch viel länger als jetzt. Uninteressant, solange es immer die selbe Dauer ist. > Was soll ich mit 100 Timercounterwerten anfangen? Prüfen, addieren, teilen, vergleichen... > Genauigkeit +/- ein einzigen Takt? Wirklich? Ja. Hängt nur vom Befehl ab, in welchem der Interrupt zugeschlagen hat.
:
Bearbeitet durch User
Hallo, ich habe die Methode erstmal gelassen und die Anzahl der Messungen verzehnfacht. Danach berechne ich die Gesamt Timercounts und teile durch 10. Damit erhalte ich schon, aus meiner Sicht, sehr genaue und wiederholbare Werte. Wenn ich das umstelle und runterbreche auf die Timercounts eines UART Frames, dann wird es bestimmt noch genauer.
1 | OSCCAL TCNT2 OSCCAL TCNT2 OSCCAL TCNT2 |
2 | 41 32822 41 32819 41 32820 |
3 | 40 32664 40 32673 40 32669 |
4 | 39 32522 39 32529 39 32527 |
5 | 38 32375 38 32372 38 32373 |
6 | 37 32231 37 32228 37 32230 |
7 | 36 32082 36 32081 36 32089 |
8 | 35 31941 35 31944 35 31947 |
9 | 36 32077 36 32087 36 32081 |
10 | 35 31943 35 31944 35 31938 |
11 | 36 32088 36 32080 36 32081 |
12 | 35 31949 35 31943 35 31943 |
13 | 36 32079 36 32083 36 32085 |
14 | 35 31945 35 31948 35 31945 |
15 | 36 32088 36 32090 36 32084 |
16 | 35 31942 35 31940 35 31939 |
17 | 36 32102 36 32109 36 32079 |
18 | 35 31943 35 31968 35 31946 |
19 | 36 32084 36 32081 |
20 | 35 31949 35 31945 |
21 | 36 32079 36 32082 |
1 | OSCCAL min max delta mittel |
2 | 36 32077 32109 32 32085 |
3 | 35 31938 31968 30 31945 |
Den Timer OVF nutze ich zur Zeit nach der derzeitigen Messmethode um ein eventuelles Zählproblem abzufangen. Nach deiner Methode nicht notwendig. Aktuell nutze ich den um die Überläufe zu zählen. Wenn du die einzelnen Messwerte speicherst, kann es sein, dass du dann irgendwelche Statistiken anwendest? Zum Bsp. die 5 größten und 5 kleinsten Werte verwirfst und nur mit dem Rest letztlich den Mittelwert bildest? Das wäre aktuell meine Idee das umzusetzen.
Veit D. schrieb: > Wenn du die einzelnen Messwerte speicherst, kann es sein, dass du dann > irgendwelche Statistiken anwendest? Zum Bsp. die 5 größten und 5 > kleinsten Werte verwirfst und nur mit dem Rest letztlich den Mittelwert > bildest? Das wäre aktuell meine Idee das umzusetzen. ich würde auch eine Toleranzgrenze festlegen +-1% oder +-5% also nur den Mittelwert der Werte nehmen die innerhalb der 95(99)% bis (101)105% liegen und den Rest ignorieren.
Hallo, Hab die Messung und Teilung umgestellt, sodass ich den Durchschnittswert vom Timercounter eines UART-Frames bekomme über die Messzeit. Punktlandung wäre 3200.
1 | OSCCAL TCNT2 |
2 | 41 3280 |
3 | 40 3264 |
4 | 39 3251 |
5 | 38 3236 |
6 | 37 3221 |
7 | 36 3207 |
8 | 35 3192 |
9 | 36 3207 |
10 | 35 3192 |
11 | 36 3207 |
12 | 35 3193 |
13 | 36 3206 |
14 | 35 3192 |
15 | 36 3207 |
16 | 35 3193 |
17 | 36 3207 |
18 | 35 3193 |
19 | 36 3207 |
20 | 35 3193 |
21 | 36 3207 |
22 | |
23 | OSCCAL TCNT2 |
24 | 41 3280 |
25 | 40 3265 |
26 | 39 3250 |
27 | 38 3235 |
28 | 37 3221 |
29 | 36 3207 |
30 | 35 3193 |
31 | 36 3206 |
32 | 35 3192 |
33 | 36 3206 |
34 | 35 3192 |
35 | 36 3206 |
36 | 35 3192 |
37 | 36 3206 |
38 | 35 3191 |
39 | 36 3206 |
40 | 35 3192 |
41 | 36 3206 |
42 | 35 3192 |
43 | 36 3206 |
1 | OSCCAL min max delta mittel |
2 | 36 3206 3207 1 3207 |
3 | 35 3191 3193 2 3192 |
Das sieht schon alles perfekt aus. Wenn ich nun wie du alle Einzelwerte aufaddiere und teile und dann korrekt runde, dann wäre das der krönende Abschluss. Und man bräuchte wirklich keine Hysterese, welch ein Wunder. Manches muss man erst probieren um es zu verstehen. :-)
Veit D. schrieb: > Das sieht schon alles perfekt aus. Wenn ich nun wie du alle Einzelwerte > aufaddiere und teile und dann korrekt runde, dann wäre das der krönende > Abschluss. Und man bräuchte wirklich keine Hysterese, und wenn man das für den kalten Chip, kurz nach power on und für den warmen Chip nach einiger Laufzeit macht ist man noch besser
Hallo, genau. Das werde ich so machen. Dann gibts aller halben Stunde oder so "eine Runde" Kalibrierung spendiert. :-) Dann werde ich das mal weiter umsetzen. Ich hatte am Anfang nicht daran geglaubt diese Meßgenaugikeit zu erhalten. Ich danke euch allen erstmal an der Stelle für die sehr informative Unterhaltung und den sehr guten Hinweisen zur Umsetzung. Prinzipielle Ablaufpläne sind das eine, die Umsetzung mit höherer Genauigkeit das andere. Bin einfach nur glücklich. :-)
Hallo, nachdem das nun schon geschmeidig läuft mit Wiederholungsanforderung senden, dann neue Takte zum einmessen empfangen usw. muss ich nochmal fragen wie man das Kaltstartproblem angeht? Also wenn Master und ATtiny frisch eingschalten werden. Dann kann der ATtiny keine Befehle senden, würden eh nur verstümmelt ankommen ohne Wirkung. Jetzt fällt mir nur ein, dann ich den ATtiny pauschal in den Empfangsmodus setze und der Master leicht verzögert erstmal paar Salven Takte rausfeuert. Lieber mehr als zu wenig, sodass der ATtiny die Chance sich zu kalibrieren. Danach wird angenommen das er sich wenigstens soweit selbst kalibrieren konnte, dass eine Kommunikation möglich ist, die der Master versteht, eben weil der ATtiny µC Takt schon im brauchbaren Toleranzfenster der UART liegt. Geht man so ran? Bis jetzt habe ich mir beholfen indem ich den OSCCAL zum Start um einen Wert von Hand nach unten korrigiert habe, der Rest folgt automatisch.
Veit D. schrieb: > Geht man so ran? Aha, jetzt bist du also doch schon bei dem Problem angelangt, auf das ich dich bereits im 21. September letzten Jahres in diesem Thread hingewiesen hatte. Hättest du man gleich auf die Erwachsenen gehört... Ich könn't mich totlachen, wenn's nicht so traurig wäre.
Hallo, @ c Liebhaber: Ich weiß nicht was es da zu lachen gibt. Das Problem war von Anfang an klar. Jetzt ist die Kalibrierung selbst fertig. Und jetzt gehts nur noch darum den Kaltstart sicher zugestalten. Ein Idee hatte ich geäußert. Eine andere Idee kam als Antwort. Ich sehe jetzt nicht das Problem der Unlösbarkeit. Danke R.Max, ich überlege mir das mal in Ruhe. Weil ich habe später nur noch eine einzige UART am ATtiny die ich umschalten muss zwischen Zeichen senden/empfangen und der Kalibrierung.
Veit D. schrieb: > Danke R.Max, ich überlege mir das mal in Ruhe. Weil ich habe später nur > noch eine einzige UART am ATtiny die ich umschalten muss zwischen > Zeichen senden/empfangen und der Kalibrierung. Um den Break auszulösen, mußt Du am Slave den UART nur kurzzeitig auf eine deutlich langsamere Baudrate setzen und ein Nullbyte senden. Es reicht ja, wenn Du das beim Kaltstart machst, solange der UART ohnehin potentiell nicht für die Kommunikation zu gebrauchen ist. Für die Anforderung einer Rekalibrierung im laufenden Betrieb kannst Du ja dann dir reguläre Kommunikation verwenden.
Hallo, mit Frame Error abfragen geht das für einen einzelnen. Später sollen mehrere ATtiny an den seriellen RS485 Bus mit Max485. Dabei dürfen die ATtinys nicht ungefragt senden. Nur auf Anforderung vom Master. Das heißt ich muss im Master die Logik einbauen mit Antwort Timeout. Und der ATtiny muss im Notfall auf Modus "Kalibrierungsempfang" zurückschalten. Mein Plan ist aktuell folgender. Beim Kaltstart werden mehrere Runden Kalibrierungstakte auf den Bus gesendet, vom Master. Danach fragt der Master alle ATtinys der Reihe nach ab auf "Finish" Antwort. Ist eine "Repeat" Antwort dabei, gibt es einzelne Kalibrierungstaktsequencen als Bonus auf den Bus, solange bis alle Finish gemeldet haben. Resetet man einen einzelnen ATtiny oder was auch immer, dann muss das der Master in einem Antwort Timeout erkennen, weil nur Datenmüll gesendet werden kann wenn der Takt nicht stimmt. Dann sendet der Master wieder Kalibrierungstakte und alle sind wieder im Rennen. Die ATtiny reagieren im Normalfall nur auf Anfragen wenn ihre Adresse im Protokol enthalten ist. Die ATtinys können ihre Temperatur messen und eine Kalibrierung anfordern. Der Master muss ständig aller Zeit x alle ATtinys abfragen ob sie noch da sind. Übersehe ich irgendwas? Man lernt wegen 2 Pins mehr ganz schön viel dazu. :-) Solange es der Ram/Flash hergibt für die Kalibrierung.
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.