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
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.
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
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.
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
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().
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
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:
1
voiduart_putx(constchar*s,uint8_tn)
2
{
3
while(n)
4
{
5
uart_putc(*s++);
6
n--;
7
}
8
9
}/* 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
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.
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.
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
Das ist entnommen aus diesem Thread:
Beitrag "Flasharray an UART ausgeben"
Woran ich also noch scheitere ist anscheinend, das Einlesen der Daten
vom UART.
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
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.
1
voiduart_putx(constuint8_t*s,uint8_tn)
2
{
3
while(n--)
4
uart_putc(*s++);
5
}/* uart_putx */
6
7
8
voiduart_getx(uint8_t*Buffer,uint8_tMaxLen)
9
{
10
uint8_tNextChar;
11
uint8_tLen=0;
12
13
NextChar=uart_getc();// Warte auf und empfange das nächste Zeichen
14
if(NextChar!=0x96)
15
return;
16
17
// Sammle solange Zeichen, bis entweder das String Ende Zeichen kam oder das aufnehmende Array voll ist
> 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?
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
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
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
NextChar=uart_getc();// Warte auf und empfange das nächste Zeichen
17
if(NextChar==0x96)
18
{
19
// Sammle solange Zeichen, bis entweder das String Ende Zeichen kam oder das aufnehmende Array voll ist
20
while(NextChar!=0x8D){
21
if(!(NextChar&UART_NO_DATA)){
22
*Buffer++=(uint8_t)NextChar;
23
Len++;
24
}
25
NextChar=uart_getc();
26
}
27
// Setze Flag "Neue Daten verfügbar"
28
flagNewData=1;
29
}
30
}
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
> 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.
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
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.
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
Ich habe jetzt folgenden Code, musste deinen um die Extraforderung n--
ergänzen, erst dann lief er, wie meiner vorher.
1
voiduart_putx(constuint8_t*s,uint8_tn)
2
{
3
while(*s!=0x8D&&n--)
4
uart_putc(*s++);
5
}
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.
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.
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.
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...
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.
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
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.
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.
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