Datum:
Angehängte Dateien:Hallo zusammen, nach einigen Tagen des Rumexperimentierens sehe ich mich gezwungen, mein Problem hier zu schildern, in der Hoffnung, jemand hat einen wertvollen Tipp für mich. Ich schlage mich mit einem ATMega328 und der aktualisiertenUART Library von Peter Fleury herum, die hier zu beziehen ist: http://beaststwo.org/avr-uart/index.shtml . Zusätzlich habe ich eine Funktion, die eine String einlesen soll und ein Gerät am UART hängen, dass stumpfsinnig zyklisch Messwerte verschickt (mit 19200 8N2) und diese Strings sind immer 16Byte lang, beginnen mit 0x96 und enden mit 0x8D. Mein gestrippter Code ist befindet sich im Amhang. Ich lasse mir nun den kompletten eingelesenen String einmal zurückschicken, also ein Echo ausgeben. Dort sehe ich, dass etweas nicht stimmt. Ist ein Byte 0x00 wird es generell nie so zurückgegeben, zwischendrin, am Anfang und manchmal auch am Ende fehlen einzelne Bytes im Echo. Die Verteilung, welche Bytes jedesmal fehlen, scheint mir zufällig. Beispielhaft habe ich einen Logic Screenshot angehängt(grün=ATMega_RX, rot=ATMega_TX) . Ich habe bereits verschiedene Implementationen von uart_gets() aus dem Forum probiert (siehe Code) und auch ein blockierendes uart_getc() versucht, anstatt das nciht blockierende von Fleury, kein Erfolg...lediglich die Charakteristik der zurückgesendeten Fehler ändert sich (mal mehr mal weniger fehlende Bytes, je nach Implementierung). Der Atmega läuft mit einem 16Mhz Quarz, CKDIV8 ist nicht gesetzt. Fuses sind: Ext:0xFF H:0xD9 L:0xFF. Ich weiß mir so langsam keinen Rat mehr. Es kann doch kein Hexenwerk sein, dass die UART Kommunikation auf 19200Baud klappt?!?! Bin sehr dankbar für jeden ernst gemeinten Hinweis. Danke und Grüße vom Rhein. Stefan
Datum:
S. G. schrieb: > Ist ein Byte 0x00 wird es generell nie so zurückgegeben, Natürlich nicht, schließlich signalisiert eine 0 das Ende eines Strings für uart_puts, also wird weder die 0, noch das, was eventuell noch danach kommt, gesendet. Da du nicht einen null-terminierten Text-String senden willst, sondern einen Datenblock fester Länge, musst du dir dafür eine eigene Funktion schreiben.
Datum:
Hallo Stefan, OK, werde mir mal fix eine basteln. Allerdings erklärt das nicht die übrigen Aussetzer, bspw. dass die ersten beiden Bytes fehlen o.ä. Der Screenshot zeigt das gerade nicht, aber ich kann gerne nochmal ein paar hochladen. Werde die Änderung auch posten. Bis dahin schon mal Danke! Grüße Stefan
Datum:
Angehängte Dateien:So, ich habe jetzt die uart_putc() angepasst, welche von uart_puts() zyklisch gerufen wird. Immer, wenn das zu versendende Byte Null ist, wird es einfach um eins inkrementiert und so zu einer Eins. Ich sehe allerdings diese Eins nirgens, nächster Versuch war dann, die Null zu überschreiben mit einer 33 (rein willkürlich gewählt). Ich sehe nirgens eine 33 im Echo, siehe Screenshot. Setze ich direkt, ohne if Abfrage alles auf 33 sieht es aus, wie im zweiten Screenshot.
/************************************************************************* Function: uart_putc() Purpose: write byte to ringbuffer for transmitting via UART Input: byte to be transmitted Returns: none **************************************************************************/ void uart_putc(unsigned char data) { unsigned char tmphead; //++++++++++++++++++++++++++++++++ if(data == 0) data=33; //++++++++++++++++++++++++++++++++ tmphead = (UART_TxHead + 1) & UART_TX_BUFFER_MASK; while ( tmphead == UART_TxTail ){ ;/* wait for free space in buffer */ } UART_TxBuf[tmphead] = data; UART_TxHead = tmphead; /* enable UDRE interrupt */ UART0_CONTROL |= _BV(UART0_UDRIE); }/* uart_putc */ /************************************************************************* Function: uart_puts() Purpose: transmit string to UART Input: string to be transmitted Returns: none **************************************************************************/ void uart_puts(const char *s ) { while (*s) uart_putc(*s++); }/* uart_puts */ |
Das verstimmt mich nur noch mehr...hmpf Könnnen denn überhaupt mehrere Nullbytes mit normalen UART Routinen dann empfangen werden, oder endet dann immer der String? Ich habe die Empfangsroutine extra erst bei Empfang von 0x96 abbrechen lassen. Viele Grüße Stefan
Datum:
S. G. schrieb: > ich habe jetzt die uart_putc() angepasst, welche von uart_puts() Das bringt doch überhaupt nichts. Bei einer 0 wird doch die uart_putc() gar nicht erst von uart_puts() aufgerufen. Du brauchst eine eigene Version von uart_puts(), nicht von uart_putc().
Datum:
Du hast Recht, wie ich selbst gesehen habe... Ich habe eine Senderoutine nach dem Datenblatt gebaut, die will aber noch keine Strings versenden, allerdings schon mal einzelne Character... Werde morgen dran schrauben, und hoffe wirklich, ich bekomme was hin. Wie sieht es denn mit dem Empfangen aus, meinst du hier liegt die gleich Problematik vor? Grüße
Datum:
z.B.
void PutBytes(char *ptr, uint8_t n) { while(n) { Sci0_PutChar(*ptr); ptr++; n--; } } |
Datum:
Angehängte Dateien:Hallo, danke für den Code. Ich habe in diesem Thread: Beitrag "0x00 über UART übertragen" ähnlichen Code gefunden und bei mir folgenden Code eingefügt und verwendet:
void uart_putx(const char *s, uint8_t n) { while (n) { uart_putc(*s++); n--; } }/* uart_putx */ |
Das funktioniert auch, allerdings gewissermaßen zu gut. Jetzt werden immer Nullen verschickt und zwischendrin steckt die eigentliche Nachricht, siehe Screenshot. Irgendwie fühlt sich der AVR jetzt genötigt laufend Nullen auf den UART zu legen und zwar ohne Pause, muss ich evtl. noch den Sendebetrieb ausschalten oder so? Das wäre mir neu. Grüße Stefan
Datum:
S. G. schrieb: > Das funktioniert auch, allerdings gewissermaßen zu gut. Jetzt werden > immer Nullen verschickt und zwischendrin steckt die eigentliche > Nachricht, siehe Screenshot. Irgendwie fühlt sich der AVR jetzt genötigt > laufend Nullen auf den UART zu legen und zwar ohne Pause, der fühlt sich nicht 'irgendwie' genötigt, sondern arbeitet dein Programm ab. Wenn du die Funktion immer wieder mit 0-Bytes aufruft, dann verschickt die UART die auch.
Datum:
S. G. schrieb: > Wie sieht es denn mit dem Empfangen aus, meinst du hier liegt die gleich > Problematik vor? Sie liegt immer dann vor, wenn du deine Daten wie Strings behandelst! Du hast keine Strings! Du hast Bytefelder mit allen überhaupt möglichen Werten drinn. Das sind keine Strings. Und solange du nicht aufhörst, nicht zwischen Strings und Bytefeldern ztu unterscheiden, wirst du dich auch niemals aus deinen Dilemmi befreien können. Nicht alles was mehrere Daten hintereinander in einem Array ablegt, ist ein String! Also behandle das auch nicht so, als ob das gleich wäre.
Datum:
Hallo Karl Heinz, danke für deine Anmerkungen. Ich denke, uns beiden ist selbstverständlich klar, dass sich ein µC nicht nach Gefühlslage verhält sondern äußerst deterministisch. Ich wollte nur meinem Unmut damit Ausdruck verleihen... Das es ein Problem mit den Strings gibt ist mir auch klar, seit Stefan das eingeschmissen hat. Mir ist im Code allerdings noch unklar, wo ich das beseitigen kann, da ich nirgens eine Stelle finde, wo bspw. nach einem \0 abgebrochen wird zu senden oder empfangen...Hier sehe ich mich noch im Wald, aber ich werde sehen, was ich noch finden kann. Mir ist wohl der Unterschied zwischen einem String udn einem uint8_t Array klar. Mir ist allerdings nicht klar, wo die Funktion mit 0 als Argument dauernd aufgerufen wird, denn das sollte sie ja nur so oft, bis die while Schleife abgelaufen ist. Danke euch allerdings nochmals für eure Hinweise. Grüße Stefan
Datum:
OK, Update, was ich jetzt hinbekommen habe, ist ein vordefiniertes uint8_t Array zu versenden, und zwar folgendes:
const uint8_t SCS_CMD_RESET[] = {0xAA, 0xAA, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x5F, 0x55, 0x55}; |
Das ist entnommen aus diesem Thread: Beitrag "Flasharray an UART ausgeben" Woran ich also noch scheitere ist anscheinend, das Einlesen der Daten vom UART.
Datum:
S. G. schrieb: > Woran ich also noch scheitere ist anscheinend, das Einlesen der Daten > vom UART. dann zeige doch bitte mal den aktuellen code vom einlesen
Datum:
Angehängte Dateien:Whop..sorry, ganz vergessen in der Hektik verwende gerade diesen Code und bekomme folgendes Echo -> Screenshot Leider finden sich hier auch die Nullen nach dem öffnenden Byte 0x96, die Anzahl der empfangenene Bytes (=16) scheint aber zu stimmen, also die Endbedingung auch zu funktionieren... EDIT: Stimmt nicht, es lag nur daran, dass ich den Puffer genau 16 Byte groß gemacht hatte, wird er vergrößert, wird er auch mit Nullen vollgefüllt bzw. befinden sich nur Nullen und die Start-0x96 drin.
void uart_putx(const uint8_t *s, uint8_t n) { while (n--) uart_putc(*s++); }/* uart_putx */ void uart_getx( uint8_t* Buffer, uint8_t MaxLen ) { uint8_t NextChar; uint8_t Len = 0; NextChar = uart_getc(); // Warte auf und empfange das nächste Zeichen if(NextChar != 0x96) return; // Sammle solange Zeichen, bis entweder das String Ende Zeichen kam oder das aufnehmende Array voll ist while( NextChar != 0x8D && Len < MaxLen - 1 ) { *Buffer++ = NextChar; Len++; NextChar = uart_getc(); } // Setze Flag "Neue Daten verfügbar" flagNewData = 1; } |
Grüße
Datum:
> NextChar = uart_getc();
Dir ist aber schon klar, dass das uart_getc aus der Fleury Lib ein
'nicht wartendes getc' ist. D.h. das wartet nicht darauf, das da ein
Zeichen eintrudelt, sondern kommt sofort wieder zurück und meldet dir
mit einem Code (daher ist der Returntyp der Funktion auch int und nicht
char), dass sie kein Zeichen für dich hat. Wenn du allerdings in deinem
Code dann so tust, als ob da was reingekommen ist, darfst du dich nicht
wundern, wenn du jede Menge Schmutz in deinen Arrays stehen hast. Denn
das Programm arbeitet nun mal wesentlich schneller als die UART
übertragen kann. Selbst wenn die UART voll ausgelastet ist, wirst du
sowas wie ungefähr 30 bis 40 getc Aufrufe haben, die einfach nur melden
'Ich hab nix'.
Peter hat so ein schönes Beispiel zu seiner Lib mitgeliefert. Hast du da
noch nie reingeschaut, wie die Funktionen zu verwenden sind?
Datum:
Hallo Karl Heinz, ich weiß, das das eine non-blocking Routine ist. Ich hatte weiter oben ja bereits beschrieben, dass ich es auch schon mit einer blockierenden probiert hatte - ohne Erfolg. Ich denke, ich werde jetzt, da es einige Fortschritte gibt nochmal fix implementieren und mich dann gleich wieder melden. Grüße
Datum:
Tip: mach dir fürs erste eine blockierende Version von uart_getc. Sonst bläht sich die uart_getx Funktion nämlich extrem auf.
char uart_getc_wait() { int c = uart_getc(); while( ..... ) { c = uart_getc(); } return c; } |
Datum:
Habe gerade verifiziern können, das die Nullen also wirklich aus dem leeren FIFO stammen und an mit UART_NO_DATA gekennzeichnet sind. Allerdings habe ich für Debug Zwecke in die Lib eingegriffen. Das will ich vermeiden. Peter fragt in seinem Testprogramm nach UART_NO_Data ab, aber bei mir scheint das keine Auswirkungen zu haben, da ich sofort ein uint8_t verwende, wo die Indikation verloren geht. Ich werde das mal ändern und dann hoffentlich diese Nullen erkennen und ignorieren können. Melde mich dann mit Code wieder. Danke und Grüße
Datum:
S. G. schrieb: > Peter fragt in seinem Testprogramm nach UART_NO_Data ab, aber bei mir > scheint das keine Auswirkungen zu haben, da ich sofort ein uint8_t > verwende, wo die Indikation verloren geht. Du lernst gerade eine Lektion. Testprogramme werden nicht nur zum Spass mitgeliefert. Und: man schaut sich die Testprogramme genau an! Inklusive Verwendung der Funktionen, inklusive der Datentypen der Variablen, inklusive der Auswertung von Returncodes, inklusive der Werte der Returncodes bzw. deren Wertebereiche (und den daraus resultierenden Anforderungen an die Datentypen von Variablen) Sowas nennt man dann: Code-Studium
Datum:
Angehängte Dateien:OK, ich denke, ich hab's. Siehe Screenshot.
void uart_putx(const uint8_t *s, uint8_t n) { while (n--){ uart_putc(*s++); if(*(s+1) == 0x8D) break; } }/* uart_putx */ //taken from: http://www.mikrocontroller.net/topic/153197 void uart_getx( uint8_t* Buffer, uint8_t MaxLen ) { uint16_t NextChar; uint8_t Len = 0; NextChar = uart_getc(); // Warte auf und empfange das nächste Zeichen if(NextChar == 0x96) { // Sammle solange Zeichen, bis entweder das String Ende Zeichen kam oder das aufnehmende Array voll ist while(NextChar != 0x8D) { if ( !(NextChar & UART_NO_DATA) ) { *Buffer++ = (uint8_t)NextChar; Len++; } NextChar = uart_getc(); } // Setze Flag "Neue Daten verfügbar" flagNewData = 1; } } |
Ich habe also - nachdem ich nochmal Peters Beispielprogramm durchforstet habe - den 16Bit Output aus uart_getc() verwendet, so konnte ich dann wieder prüfen, ob es sich um valide Daten handelt und kann diese dann verwerfen, habe so gewissermaßen eine blockierende uart_getc() erzeugt. Die Routine uart_getx() kehrt erst wieder, wenn das schließende 0x8D (in meinem Fall) gefunden worden ist. Die Längenbegrenzung habe ich komplett außer Kraft gesetzt. Herzlichen Dank nochmal an alle, die mir in der Sache weiter geholfen haben. Ich muss zugeben, ich habe nicht mehr darauf geachtet, dass ich die Plausibilitätsprüfungen selbst machen muss und auch gar nicht mehr daran gedacht, dass in dem Puffer - auch wenn es Nullen sind (irgendwas muss ja immer drinstehen...klar) - nicht empfangene Daten stehen können, sondern andere. Vielen Dank nochmal! Grüße aus dem Rheinland Stefan
Datum:
> void uart_putx(const uint8_t *s, uint8_t n) > { > while (n--){ > uart_putc(*s++); > if(*(s+1) == 0x8D) break; > } > }/* uart_putx */ kann man natürlich machen. Da bei dir aber das 0x8D dieselbe Rolle spielt, die in einem String der Bytewert 0x00 spielt, kannst du auch die Techniken der Stringprogrammierung adaptieren. Im Grunde hast du sowas ähnliches wie einen String, nur das der eben mit 0x8D terminiert ist.
void uart_putx(const uint8_t *s) { while( *s != 0x8D ) uart_putc(*s++); } |
Datum:
Karl Heinz Buchegger schrieb: > Im Grunde hast du sowas ähnliches wie einen String, nur das der eben mit > 0x8D terminiert ist. Ganz genau, daher ist mir auch wohlbekannt, welche Vorteile ich hier nutzen kann. Allerdings klappt dein Code gerade nciht. ich schaue morgen, woran das nun iweder liegt, aber an solch eine schlanke Konstruktion hatte ich eigentlich auch gedacht, musste dann aber das mit der if Abfrage und dem break() verwenden, weil nur so der gewünschte Effekt zustande kam. Grüße Stefan
Datum:
S. G. schrieb: > nutzen kann. Allerdings klappt dein Code gerade nciht. Mach mich nicht schwach. Scroll, scroll, scroll Nö, das müsste funktionieren, wenn die Daten korrekt sind. Ah. Ich seh gerade doch noch was. gewöhn dir an: char nimmst du für alles was mit Textverarbeitung zu tun hat. also alles wo Strings oder Zeichen im Spiel sind uint8_t nimmst du für alles, was einfach nur als Byte bezeichnet werden kann. Nicht mischen. Die Sichtweise 'oh, da habe ich ein paar Bytes, da nehme ich einfach einen char' kann ganz böse ins Auge gehen.
Datum:
Hallo Karl Heinz, ja,...so hab ich auch gedacht, aber ich bekomme wieder nur überall Nullen dauerhaft zurückgesendet. Egal, ich schaue morgen, was da schief läuft. Du hast vollkommen Recht mit dem char und int8...Das hatte mich schon immer irritiert, warum es so viele verschiedene Bennennungen für die vermeintlich selben Byteworte gibt. Aber jetzt hab ich da einen Einblick mehr gewonnen. :-) Danke nochmals. Grüße
Datum:
Ich habe jetzt folgenden Code, musste deinen um die Extraforderung n-- ergänzen, erst dann lief er, wie meiner vorher.
void uart_putx(const uint8_t *s, uint8_t n) { while( *s != 0x8D && n--) uart_putc(*s++); } |
Lasse ich die Prüfung der Länge weg (n--) werden dauerhaft Nullen ausgegeben. EDIT: Hm, Fehler eingeschlichen: mit diesem Code wird das schließende 0x8D nicht mehr ausgegeben.
Datum:
while(NextChar != 0x8D) { if ( !(NextChar & UART_NO_DATA) ) { *Buffer++ = (uint8_t)NextChar; Len++; } NextChar = uart_getc(); } |
Wenn du das Ende-Zeichen beim Empfangen nicht mit in den Buffer schreibst, dann kannst du es bei der Ausgabe natürlich auch nicht als Ende-Kennung verwenden.
Datum:
Hm, Hallo Stefan, da hast du natürlich Recht. Das ist mir bewusst, ich hatte jetzt allerdings bei der uart_putx das Problem, dass eine Zahlenkollone, die ein 0x8D am Ende hat (nicht also eine empfangene Kolonne, sondern eine vom AVR erzeugte) nur exklusive des letzten Bytes ausgegeben wird.
Datum:
Wenn du das Ende-Zeichen mit ausgeben willst, dann musst du halt die Schleife etwas umstellen:
void uart_putx(const uint8_t *s) { do { uart_putc(*s); } while (*s++ != 0x8D); } |
Datum:
Hey Stefan, das leuchtet mir ein und hatte ich auch schon mal probiert. Ich bekomme allerdings wiederum nur Nullen am Stück und dauerhaft gesendet und irgendwo zwischendrin die eigentlich relevanten Bytes...
Datum:
Bis jetzt konnte ich es soweit optimieren
void uart_putx(uint8_t *s) { do{ uart_putc(*s++); }while(*(s-1) != 0x8D); }/* uart_putx */ |
Aber Moment, sollte das nicht an sich das gleiche sein, wie dein letzter Code, Stefan?
void uart_putx(const uint8_t *s) { do { uart_putc(*s); } while (*s++ != 0x8D); } |
Datum:
S. G. schrieb: > das leuchtet mir ein und hatte ich auch schon mal probiert. Ich bekomme > allerdings wiederum nur Nullen am Stück und dauerhaft gesendet und > irgendwo zwischendrin die eigentlich relevanten Bytes... Innerhalb welchen Kontextes und mit welchem anderen Code zusammen? Da blickt doch keine Sau mehr durch (zumindest ich nicht), was genau du da jeweils mit welchem Code probierst. Ich kann mich z.B. nicht erinnern, hier schon mal konkreten Code hierzu > nicht also eine empfangene Kolonne, sondern eine vom AVR erzeugte gesehen zu haben.
Datum:
Ja Stefan, du hast vollkommen Recht. Ich kann allerdings nicht immer allen Code posten, da ich ja auch ständig ändere. Deswegen habe ich von dem Startcode ausgehend immer nur die Funktionen gepostet. ich lade nach dem Mittag nochmal meinen kompletten Code hoch. Jetzt gerade passiert aber alles, wo es noch Probleme gibt innerhalb der uart_putx(); Grüße
Datum:
S. G. schrieb: > Hm, Hallo Stefan, > > da hast du natürlich Recht. Das ist mir bewusst, ich hatte jetzt > allerdings bei der uart_putx das Problem, dass eine Zahlenkollone, die > ein 0x8D am Ende hat (nicht also eine empfangene Kolonne, sondern eine > vom AVR erzeugte) nur exklusive des letzten Bytes ausgegeben wird. Weißt du was einer der ganz wichtigen Punkte in der Softwareentwicklung ist? Das man sich mit sich selbst auf Konventionen einigt und die *dann auch durchzieht* Entweder alle deine Datensätze enden mit 0x8D und du kannst das daher als Endekennung benutzen oder sie tun es nicht (und du musst daher ständig eine Länge mitführen). Aber entscheide dich für eines von beiden und ZIEH DAS DANN AUCH DURCH! Mal so und mal so ist ein sicherer Weg ins Desaster! Du magst das vielleicht heute noch so lala unter Kontrolle haben aber in spätestens 2 Wochen hast du das nicht mehr.
Datum:
S. G. schrieb: > Jetzt gerade passiert aber alles, wo es noch Probleme gibt innerhalb der > uart_putx(); Eben nicht. Wenn du lauter Nullen bekommst, dann liegt das garantiert nicht an uart_putx, sondern einfach daran, dass die Daten, die der Funktion übergeben werden, nicht so aussehen, wie sie sollten. Und woran das liegt kann dir keiner sagen, wenn wir den Code nicht kennen, der die Daten produziert.
Datum:
Angehängte Dateien:Hm, Karl Heinz, ich sehe das genau wie du, daher sind alle diese Zeichenkolonnen mit 0x8D terminiert. Bisher konnte ich nicht zuverlässig mit dieser Terminierung arbeiten, daher musste ich immer einen Counter mitführen. Jetzt habe ich in allen betroffenen Routinen keinen Counter mehr, da ich mit der Terminierung arbeiten kann. Allerdings kann hier und da der Code noch effizienter sein, denke ich. Stefan, ich stimme dir da nicht vollkommen zu. Ich weiß wie die übergebenen Bytes aussehen und kann sie ausgeben. Teilweise entstehen aber noch diese Nebeneffekte. Zur Verbesserung, habe ich den kompletten Code zum aktuellen Stand angehängt. So wie er jetzt angehängt ist, funktioniert er allerdings vollkommen zufriedenstellend. Ich würde mich freuen, wenn ihr nochmals drüber schaut. Vielen Dank Stefan





