Ich möchte einen ATmega128 und einen ATtiny12 miteinander verbinden. Der ATmega128 ist Empfänger und der Tiny12 ist der Sender. Ich dachte mir, dass ganze über einen serielle Verbindung laufen zu lassen. Der Mega128 wird einen RXD opfern und der Tiny12 einen Pin, den ich als Ausgang definiere. ICh nutze auf dem ATmega128 die UART-Lib von Peter Fleury. Nun kommen die Zeichen aber nicht wirklich an oder zumindest ist das Error-Flag immer gesetzt. Senden tue ich Start-Bit (0), 8 Datenbits und 1 Stopp-Bit (1). Ist das grundsätzlich so korrekt oder ist da schon ein gedanklicher Fehler drin?
Welche Baudrate benutzt du denn? Hälst du die auch auf beiden Seiten genau ein? RS232 ist da recht empfindlich.
Extra nicht so viel: 9600 Baud. Auf der ATmega-Seite ist das ja kein Problem, da ich den Hardware-UART nehme. Auf der Tiny-Seite habe ich mal durchgerechnet und durchlaufen lassen. Auch unter AVR-Studio in der Simulation nochmal die Zeit gemessen. 104us muss ein Bit anliegen, was so um ca. 1us differiert. Wäre das zu viel? Auf der anderen Seite, weiss ich natürlich nicht, ob der Takt des ATtiny12 (1.2 MHz) wirklich exakt stimmt. Ich habe das Calibration-Byte ausgelesen und gesetzt, was vom Hersteller ja schon voreingestellt ist.
Versuch mal, den Tiny mit Quarz zu takten, dann weißt Du schon mal, daß Dein Takt immer annähernd genau ist. Serieller Datentransfer mit internem RC-Oszillator ist nicht zu empfehlen.
Zum testen könnte ich mir einen Oszillator besorgen. Grundsätzlich geht es aber nicht, da ich alle 6 Portpins benötige. Evtl. sollte ich erst einmal eine niedrigere Baudrate verwenden (evtl. 2400?). Das reicht auch. Weil ich immer nur 5 Bytes pro Sekunde übertragen will. Durch den FIFO im ATMEGA128 habe ich auch keine Pollingzeiten zum abholen.
Auch für niedrige Baudraten ist der interne Oszillator zu ungenau für UART. ...
Also ich hab hier einen Mega8 laufen, dessen USART mit der RS232 des PCs verbunden ist. Getaktet ist das Ding mit den internen 1MHz, und die Übertragung mit 4800 Baud läuft fehlerfrei. Und dabei läuft der Controller spannungsmäßig sogar außerhalb der Spezifikation (3,3V - der Mega8 ohne L geht laut Datenblatt eigentlich erst ab 4,5V). Extra Kalibrierung hab ich da auch keine gemacht. Also ganz so schlimm kann's wohl nicht sein.
>Auch für niedrige Baudraten ist der interne Oszillator zu ungenau für UART. Wenn die Kurven im Datenblatt korrekt sind, dann ist diese Aussage falsch: Ist die Spannung stabilisiert, und man läd den OSCCAL mit einem Wert, so dass der AVR bei etwa 35°C mit der Sollfrequenz läuft, dann bleibt man im Bereich von etwa 0-70°C innerhalb von +/-2° Ich verwende seit Jahren einen mega8 der Daten mit 500kBit/s überträgt, und zwar immer 512kByte auf einmal. Mit dem internen Oszillator kein problem, solange man die Schaltung nur im Haus betreibt. Hier hat man nämlich nie weniger als 0°C oder mehr als 60°C.
Da ist u.U. auch schon meine Fehler. Die Betriebsspannung kommt direkt vom PC-Netzteil und liefert etwa 5.2 V. Wirkt sich das auf den RC Oscillator aus?
Ronny, hast du ein Oszi? Einfach mal messen wenn du die ganze zeit 0x55 sendest wie lange ein bit tatsaechlich ist. RC Oscillatoren sind je nach Bauart mehr temperatur oder mehr spannungsempfindlich. Es gibt fuer beides interne Kompensationsmoeglichkeiten aber die kosten Chipflaeche und Chipflaeche kostet Geld. Die AVR sind sehr preisguenstig und das ist einer der Punkte an denen gespart wurde. Versuch mal zu messen, falls der Puls zu kurz ist einfach um ein Inkrement in deinem Software UART verlangsamen. Das waere ueberhaupt noch eine Moeglichkeit, programmier den Tiny so, dass er deiner Meinung nach ca. 5% zu schnell ist, dann mach ihn mit jedem Schleifendurchlauf etwas langsamer, solange bis die richtigen Daten empfangen werden, dann mach ihn weiter langsamer bis die Daten wieder Schrott sind. Von den richtigen Daten nimm den Mittelwert. Hoffentlich war das jetzt nicht zu kryptisch. Gruss, Robert
Uart und RC-Osz - das ist einfach nicht tot zu hauen. Immer wieder diesselbe Geschichte, einer hat Probleme damit, wers schon durchhat oder drüber nachdenkt, sagt, dass das nicht richtig funktioniert - es kommen aber auch immer wieder Leute: geht problemlos, seit Jahren, nie Fehler. Und so hält sich das ewig...
@crazy horse: Danke... - Ich dachte schon, ich stehe mit Meiner Meinung als Depp da... ...
5 Bytes pro Sekunde? Ich würde mit dem Tempo heruntergehen, z.B. auf 110Baud. Da ist dan der Teilerfaktor so gross, dass auch ein stark driftender RC-Oszillator kaum noch Probleme macht. Ich behaupte mal, so ein ATTiny ist immer noch frequenzstabiler als ein mechanischer Fernschreiber. Und die hatten bei dem Tempo keine Probleme fehlerfrei mitzuschreiben. Gruss Jadeclaw.
Ja bitte - was ändert das denn am Problem? Da kannst du teilen, bis du auf 1Bit/Tag bist, das wird kein Stückchen besser dadurch.
Denk mal scharf nach: Was passiert wohl, wenn der Oszillator um einen bestimmten Betrag wandert? Und dann überlege mal, welchen Auswirkung diese Drift bei 9k6 hat und welche Wirkung diese Drift bei 110 Baud hat. Und du wirst erkennen, dass eine Oszillatordrift, die bei 9k6 noch Probleme bereitet, sich bei 110 Baud überhaupt nicht mehr bemerkbar macht. Gruss Jadeclaw.
@Robert: Hab ich verstanden. Super erklärt. Ich probiere da mal einiges von aus. Vielleicht ist die günstigste Lösung eine automatische Anpassung der Baudraten... Ich sehe gerade einiger sind der Meinung, dass es garnicht geht..... In diesem Fall sollte ich mir ein anderes Protokoll ausdenken, was nicht so stark auf Timings basiert. So 1 Wire, wie beim DS1820 wäre ja auch eine Möglichkeit.
Also ich vermute mal: 1.) OSCAL Wert ? 2.) Baudrate falsch ? 3.) Soft-UART im Tiny12 fehlerhaft ? 4.) HW-fehler ? und dann erst das bei 9600 der interne RC Proleme macht. Auch wenn ich der Meinung bin mit Quarz ist auf der sicheren Seite zu sein, so muß ich denoch bestätigen das alle meine UARTs zuerstmal mit dem internen RC-Osz. ohne Probleme liefen. Das ist zwar kein Beweis aber ein Indiz. Dabei spielte die Baudrate ansich nicht die größte Rolle. Gruß Hagen
Serienproduktion: Atmega8, interner Osci (4 MHz), Baudrate 9600. Nie irgendwelche Fehler registriert. Voraussetzung: Register OSCAL bei jedem einzelnen MCU sehr exakt setzen. MfG
"Denk mal scharf nach: Was passiert wohl, wenn der Oszillator um einen bestimmten Betrag wandert? Und dann überlege mal, welchen Auswirkung diese Drift bei 9k6 hat und welche Wirkung diese Drift bei 110 Baud hat. Und du wirst erkennen, dass eine Oszillatordrift, die bei 9k6 noch Probleme bereitet, sich bei 110 Baud überhaupt nicht mehr bemerkbar macht." Vielleicht solltest du mal nachdenken..., und wenn du nicht draufkommst, rechne es einfach mal nach.
So gesehen um alles auf einen Nenner zu bringen, sollte diese Übertragung grundsätzlich funktionieren, wenn man die Baudrate wie oben von Robert beschrieben automatisch anpassen lässt. Das habe ich auch ausprobiert. Aber irgendwie kommt bei mir nur Müll an. Mit dem Oszilloskop gemessen habe ich. Und das sah ganz gut aus. Ich machte bloss ne Schleife die immer 0x55 sendet. Die Datei ist im Anhang. Unter C auf dem ATmega128 läuft dann folgende Prozedur: void fan_rotation(void) { unsigned int data; char cbuf[10]; if (!((data = uart_getc()) & UART_NO_DATA)) { itoa((data>>8) & 0x00FF, cbuf, 16); if (cbuf[1] == '\0') { cbuf[1] = cbuf[0]; cbuf[0] = '0'; cbuf[2] = '\0'; } uart1_puts(cbuf); itoa(data & 0x00FF, cbuf, 16); if (cbuf[1] == '\0') { cbuf[1] = cbuf[0]; cbuf[0] = '0'; cbuf[2] = '\0'; } uart1_puts(cbuf); uart1_puts(" baud: "); itoa(fbaud, cbuf, 10); uart1_puts(cbuf); if (data == 0x55) uart1_puts(" OK!"); uart1_puts("\r\n"); if (fbaud < FAN_BAUD) { if (fbaud == FAN_BAUD - FAN_BAUD / 5) fbaud = FAN_BAUD + 1; fbaud -= 1; } else { if (fbaud == FAN_BAUD + FAN_BAUD / 5) fbaud = FAN_BAUD - 2; fbaud += 1; } uart_init(UART_BAUD_SELECT(fbaud, SYSCLOCK)); } return; } Klar das vorher uart_init() schon aufgerufen wurde und die Routine hier immer wieder angesprungen wird.
Um das ganze nochmal aufzufrischen. Welche anderen Möglichkeiten der Übertragung stehen mir denn offen, wenn ich nur einen Portpin zur Verfügung habe? Theoretisch fiele mir dir eine Bit- für Bit-Übertragung ein, wo ich jedes Bit einzeln anfordere, indem ich den "Master" einfach auf Pullup setze und dann vom "Slave" innerhalb eine Zeit x zurücklese. Oder was meint ihr grundsätzlich?
UART benötigt deshalb einen genauen Takt, weil nur im Startbit synchronisiert wird, es also für 9 weitere Bits ausreichen muss. Mit jedem Bit steigt jedoch die Fehlerwahrscheinlichkeit. Alternative: Andere Codierung, bei der mit jedem Bit ein Takt mitläuft (FM, Manchester, ...). Ist allerdings meist reine Software. Im einfachsten Fall nimmt man Hardware-UARTs mit grob übereinstimmendem Takt, überträgt aber pro Byte nur 1 Bit. Um 0 zu senden, sendet man 0xFE, um 1 zu senden 0xE0. Beim Empfänger kommt dann je nach Taktrelation bei 0 irgendwas aus 0xFF,0xFE,0xFC an, bei 1 irgendwas aus 0xF8,0xF0,0xE0,0xC0,0x80,0x00.
Das mit dem UART leuchet mir ein. Also send ich das ganze achtmal, um mein Byte komplett zu übertragen. Also Startbit, Daten, Stoppbit: 0-x-1 Das werde ich auf alle Fälle mal ausprobieren. Die andere Sache interessiert mich auch. Wie kann ich denn noch einen Takt mitlaufen lassen, wenn ich nur eine Leitung habe. Bei 2 Leitungen ist das ja einfach. Aber auf einer?
Beispiel: http://www.the-starbearer.de/Praxis/datenfunk/manchestercode.htm Implementiert in Atmels SAM7S Controllern.
Ach ja, nachdem eine der Seiten ein Controller ohne Hardware-UART ist: Das ganze ist ja nichts anderes als eine Pulslängencodierung, auch als PWM bekannt. Kurzer 0-Impuls steht hier für 0, ein langer für 1 (andersrum ist logischer). Du musst also im Tiny12 keine Soft-UART dafür vergewaltigen, einfach Pulse der richtigen Länge und in hinreichendem Abstand tun es auch, und wenn noch ein PWM-Timer frei ist... Da kannst Du nun, wie von dir beschrieben, das Protokoll einer asynchronen Schnitstelle drauflegen, oder gänzlich eigene Phantasie walten lassen.
Ja das mit der Pulslängencodierung war so eine Idee. Das habe ich schon im Protokoll vom DS1820 gesehen. Da wird das auch über Zeiten geregelt. Dennoch probiere ich es erstmal pber den Software-UART aus. Für mich hat das den Vorteil, dass die Daten erstmal im Puffer landen, wenn die reinkommen und ich die dann später auswerten kann. Der Hardware-UART auf der anderen Seite kümmert sich eben komplett darum und entlastet in dem Moment natürlich den Controller. Und mit dem Manchestercode habe ich auch schon wieder was dazu gelernt. Fallende und steigende Flanken definieren 1 oder 0.
So ich könnte heute mal was von dem gesagten umsetzen. Ich werde da sicher noch weiter machen,. aber für den ersten Test lief es ganz gut. Ich sende jetzt jedes Bit als Byte. So dass es so aussieht: Startbit -> 0 serial delay Datenbit serial delay * 8 Stoppbit -> 1 serial delay Das ganze wird dann für jedes Bit wiederholt. Der Empfänger schaut dann. Wenn 0 ankommt ist es 0, wenn was anderes kommt ist es 1.
So vielen Dank nochmal. Das ganze Thema ist jetzt soweit gelöst, dass die Version funktioniert in der ich nur ein Datenbit als Byte verpacke. Ich habe das ganze sogar heute noch optimiert, indem ich die Baudrate nicht vorgebem sondern die vom Master setzen lasse. Der Master sendet am Start einen Low-Impuls der genau so lang ist, wie ein delay in der serial_send-Routine. Somit kann ich die Baudrate vom Master einstellen lassen. Und egal, wie ungenau der Slave ist - es ist egal. Wenn ich den Tiny13 mal zum laufen bekomme, könnte ich evtl. die Genauigkeit so weit erhöhen (8 MHz), dass ich vielleicht auch richtig senden kann. Dann liegt es am Master, wie genau das delay am Start ist.
Irgendwie funzt das doch nicht so recht. Ich habe schon den halben Tag hier verbracht zu simulieren mit AVR-Studio und die Timings nachzuprüfen. Aber anscheinend haben sich die AVRs zu zickig und wollen nicht so mit der Genauigkeit. Besonders der Tiny13 macht Probleme. Inzwischen läuft er und auch mit 9,6 MHz. Meine Initialisierung am Master sieht so aus: cli(); DDRE &= ~(1<<PE0); /* set uart-pin to input */ PORTE &= ~(1<<PE0); /* disable pullups */ DDRE |= 1<<PE0; /* start synchronize */ delay_us(1000000 / FAN_BAUD); /* synchronize delay */ DDRE &= ~(1<<PE0); uart_init(UART_BAUD_SELECT(FAN_BAUD, SYSCLOCK)); /* init fan uart */ Extern hängt ein 10k-Pullup dran, damit ich da nicht in Schwierigkeiten beim High-Pegel komme. Grundsätzlich muss das ganze doch perfekt funktionieren, wenn ich die Zeit vom Master schicken lasse. Also wo ist die Problematik? Schwankt die Frequenzabweichung vielleicht beim AVR.
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.