Forum: Mikrocontroller und Digitale Elektronik internen RC Osci syncronisieren


von Veit D. (devil-elec)


Lesenswert?

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?

von R. M. (rmax)


Lesenswert?

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
von Pandur S. (jetztnicht)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Stefan K. (stefan64)


Lesenswert?

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

von Pandur S. (jetztnicht)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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?

von R. M. (rmax)


Lesenswert?

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?

von H.Joachim S. (crazyhorse)


Lesenswert?

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 :-)

von Jakob (Gast)


Lesenswert?

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...

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Jakob (Gast)


Lesenswert?

@ 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.

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von R. M. (rmax)


Lesenswert?

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.

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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?

von Pandur S. (jetztnicht)


Lesenswert?

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.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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
von Veit D. (devil-elec)


Lesenswert?

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
von Soul E. (Gast)


Lesenswert?

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.

von Jakob (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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
von c-hater (Gast)


Lesenswert?

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...

von Soul E. (Gast)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.  :-)

von Veit D. (devil-elec)


Lesenswert?

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?

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Controller mit PinChange-Interrupt-Möglichkeit können denselben Pin für 
USART und Startbitzeitmessung benutzen.

von c-hater (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

gut, dann ist die Beschreibung wohl schon etwas älter.
Danke.

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

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
von Jakob (Gast)


Lesenswert?

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.)

von Jakob (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von c-hater (Gast)


Lesenswert?

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...

von Veit D. (devil-elec)


Lesenswert?

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
von Veit D. (devil-elec)


Lesenswert?

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?

von H.Joachim S. (crazyhorse)


Lesenswert?

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...

von Jakob (Gast)


Lesenswert?

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. ;-)

von Veit D. (devil-elec)


Lesenswert?

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 ...

von Jakob (Gast)


Lesenswert?

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!

von Klaus (Gast)


Lesenswert?

Veit D. schrieb:
> seines OSCCCAL ...

Warum eigentlich OSCCCAL? Man kann doch auch das Baudrateregister 
passend setzen und OSCCCAL lassen, wie es ist.

MfG Klaus

von H.Joachim S. (crazyhorse)


Lesenswert?

Nein, das geht nicht. Zu grob die EInstellungsmöglichkeiten.

von Veit D. (devil-elec)


Lesenswert?

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".  :-)

von Veit D. (devil-elec)


Lesenswert?

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
von M. K. (sylaina)


Lesenswert?

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 ;)

von Pandur S. (jetztnicht)


Lesenswert?

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 ?

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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
von c-hater (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Pandur S. (jetztnicht)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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?

von Veit D. (devil-elec)


Lesenswert?

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
von Veit D. (devil-elec)


Lesenswert?

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

von R. M. (rmax)


Lesenswert?

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.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Joachim B. (jar)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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
von Veit D. (devil-elec)


Lesenswert?

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?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

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
von Veit D. (devil-elec)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.  :-)

von Joachim B. (jar)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.  :-)

von Veit D. (devil-elec)


Lesenswert?

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.

von R. M. (rmax)


Lesenswert?

Laß doch den Slave als Anforderung ein BREAK senden.

von c-hater (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von R. M. (rmax)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das ist eine gute Idee und lösbare Aufgabe. Vielen Dank.

von Veit D. (devil-elec)


Lesenswert?

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
Noch kein Account? Hier anmelden.