PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : 16bit UART Übertragung



opc
15.04.2016, 15:22
Hallo Community,

Auf Empfehlung sponsere ich dem Thema einen eigenen Thread^^

mein Vorhaben ganz simpel: eine Zahl (16bit) soll in 2x(8bit) zerlegt werden und via UART auf einen zweiten µC gesendet und auf einer 7-Segment LED-Anzeige ausgegeben werden.
Mit einer 8-bit Zahl habe ich die Ansteuerung des 7-Segment Displays getestet um dies schonmal abzuhaken in seiner Funktion.

Problem ist, wenn ich eine 16-bit Zahl (hier 6789) sende, gibt die LED-Anzeige lauter Zufallszahlen aus und wechselt hin- und her.

Ich stehe mit meiner Programmiererfahrung noch ziemlich am Anfang und habe versucht eine Lösung zu finde, jedoch ohne Erfolg. Ich poste die wichtigen Ausschnitte meines Codes (unwichtiges rausgelöscht) was das Senden und Empfangen angeht, wäre nett wenn mir einer helfen kann (ist ja bestimmt nichts großes) :)

SENDEN



.
.
.
.
uint16_t zahl=6789;

uint8_t HByte;
uint8_t LByte;


void daten_senden (uint8_t data)
{
while (!(UCSRA & (1<<UDRE))){} //warten bis senden möglich ist

UDR = data; //Zeichen in den Ausgabepuffer schreiben
}

void usart_init (void)
{
UCSRB |= (1<<TXEN); //Daten senden
UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}


int main(void)
{
usart_init();

while (1)
{

HByte = (zahl >> 8);
LByte = zahl & 0xff;

daten_senden(HByte);
daten_senden(LByte);
}
}




EMPFANGEN



.
.
.


uint16_t zahl;
uint8_t usart_empfang;

void usart_init (void)
{
UCSRB |= (1<<RXEN) |(1<<RXCIE) ; //Daten empfangen und Interrupt für Datenempfang enable
UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1); // Asynchron 8N1
UBRRH = UBRR_VAL >> 8;
UBRRL = UBRR_VAL & 0xFF;
}


ISR(USART_RXC_vect)
{
usart_empfang = UDR;
}


int main(void)
{
usart_init();

sei();

while (1)
{

uint8_t HByte = usart_empfang;
uint8_t LByte = usart_empfang;

zahl = (((uint16_t) HByte) << 8 ) | LByte;

zahl_ausgeben(zahl);


}
}

HaWe
15.04.2016, 15:33
welche µCs programmierst du?
Ich habe perfekt funktionierenden UART-Code in C/C++ (synchron, quasi per handshake), sowohl für Arduino-Arduino als auch Raspi-Arduino.
Arduino- und Raspi Codes sind fast 100% identisch, da ich nicht auf die super-speziellen Arduino-libs zugreife:


Arduino-Arduino
http://www.mindstormsforum.de/viewtopic.php?f=78&t=8491&start=15#p67476

Raspi-Arduino
http://www.mindstormsforum.de/viewtopic.php?f=78&t=8689&p=67907#p67815

opc
15.04.2016, 15:35
AVR ;)

Du kannst mir deinen Code ja trotzdem mal schicken, vlt. erkenne ich den Fehler denn komme einfach nicht drauf.

Wäre nett, danke.

Edit: habe die Links jetzt auch gesehen danke :D bin für jeden Tipp dankbar...

HaWe
15.04.2016, 15:36
ps, lass dich von den Arduino-Display-Treibern nicht verwirren, ich musste den Code so schreiben, dass er mit verschiedenen TFTs und ihren libs funktioniert.

BMS
15.04.2016, 15:44
Hallo,
das Problem ist wohl eher, dass hier nicht wirklich synchronisiert wird.
Die beiden Mikrocontroller starten sicher nicht gleichzeitig in die int main und schon kann es passieren, dass der Empfänger z.B. ein Byte hinterher "hinkt".
Auch bei dieser Konstruktion


uint8_t HByte = usart_empfang;
uint8_t LByte = usart_empfang;

hab ich so meine Bedenken. Ja, die globale Variable usart_empfang wird in der ISR gesetzt.
Aber wie wird sichergestellt, dass genau in dem passenden Moment auch das nächste Byte vom Sender kommt?
Es kann hier passieren, dass zweimal das gleiche Byte abgespeichert wird, weil einfach das Timing nicht hinhaut.
Überlege gerade, ob man usart_empfang nicht sogar als volatile deklarieren sollte...

Probiere es mal mit Byte pollen an dieser Stelle.
Und überlege dir was zur Synchronisation.
Wenn der Empfänger "irgendwann" einliest, kann er am Datenstrom nicht erkennen, wo die most und least significant bytes sind.

Aber keine Panik, das bekommst du sicher hin. So wild ist das nicht ;)

Grüße,
Bernhard

HaWe
15.04.2016, 15:46
seltsam ist schon, dass du beide Bytes getrennt sendest, so kommen sie sicher verzögert an.
Besser wäre es schon mal, einen Byte-Array zu senden.

Außerdem ist UART extrem fehleranfällig. Daher verwende ich ein Start-sync-byte und eine checksum, um sicher zu sein, dass auf beiden Seiten der selbe Array rein und wieder raus kommt.

- - - Aktualisiert - - -

aaahh, haha, hat sich überschnitten... ;)

opc
15.04.2016, 16:21
Ich dachte (uSart s=synchron), dass die Synchronisierung der AVR selbst regelt.

Ok ich muss mich erstmal einlesen, was man unter Byte pollen versteht... an der Stelle sei erneut betont, mein Wissen ist eher dies eines Anfängers^^

HaWe
15.04.2016, 16:29
jo, ich weiß wohl, warum ich Arduinos und Raspis verwende ;)

opc
15.04.2016, 16:38
Ich glaube ich bin im falschen Forum ;)

HaWe
15.04.2016, 16:50
nee, wenn dann hast du eher den falschen µC ;)

wie schon in meinem eigenen Topic geschrieben:
"funktioniert auch sehr schnell, dabei scheint die Display-Ausgabe auf dem Arduino zu Debug-Zwecken noch am meisten die UART-comm auszubremsen....
Die Display Ausgabe muss künftig unbedingt als eigener Task per Multitasking (Arduino Due: <Scheduler.h> ) laufen !!"

- - - Aktualisiert - - -

böde "Powered by Google"-Reklame immer ! :(

Peter(TOO)
16.04.2016, 10:53
Ich dachte (uSart s=synchron), dass die Synchronisierung der AVR selbst regelt.

Das S steht für synchron, das A aber für asynchron. Der USART versteht zwei unterschiedliche Modi, w4elche aber nur die Bitübertragung betreffen. Bei S braucht es eine zusätzliche Taktleitung, bei A dann Start- und Stopp-Bits.


Ok ich muss mich erstmal einlesen, was man unter Byte pollen versteht... an der Stelle sei erneut betont, mein Wissen ist eher dies eines Anfängers^^
Zuerst einmal ist so ein USART ein Schieberegister.
Beim Sender schreibst du dein Datum in das Schieberegister rein und die Bits werden dann einzeln über die Leitung geschoben. Beim Empfänger geht es umgekehrt.
Wenn du jetzt neue Daten ins Schieberegister schreibst, während die alten noch nicht draussen sind, gibt es Datensalat.
Praktisch spendet man dem Schieberegister noch ein Buffer-Register in welches du eigentlich deine Daten schreibst. Wenn das Schieberegister leer ist, übernimmt es die Daten aus dem Buffer. Damit du weisst, wann das Buffer-Register geleert ist, gibt es das TE-Bit. Sobald das Buffer-Register geleert ist, wird dieses Bit gesetzt, schreibst du dann was in den Buffer, wird das TE-Bit wieder zurückgesetzt.

Der Empfänger ist eigentlich gleich aufgebaut. Wenn das Schieberegister alle Bist eingesammelt hat, werden diese in den Buffer kopiert und das RE-Bit gesetzt. Jetzt kann das Schieberegister schon den nächsten Datenstrom empfangen. Beim Lesen des Buffers wird dann das RE-Bit zurückgesetzt oder man muss es extra zurücksetzen.
Will das Schieberegister Daten in den Buffer schreiben und das RE-Bit ist noch gesetzt, gibt einen Buffer-Overrun-Fehler.

Je nach Bitrate dauert das Schieben der Daten aber. Bei 9600 Bit/s und asynchron, werden jeweils 10 Bit übertragen, also maximal 960 Zeichen/s. Das sind dann 1.04ms/Zeichen.

In 1ms kann aber eine CPU eine Menge Code abarbeiten.
Du musst also in deinem Code sicherstellen, dass beide Bytes auch schon übertragen wurden!




while (1)
{

uint8_t HByte = usart_empfang;
uint8_t LByte = usart_empfang;


Hier steckt der Wurm drin!
Bei der zweiten Abfrage von usart_empfang weisst du gar nicht ob da schon was neues drin steht.

MfG Peter(TOO)

opc
16.04.2016, 12:43
Hallo Peter,

vielen Dank für die ausführliche und gute Erkärung! Jetz habe ich auch ziemlich verstanden wie der U(S)ART funktioniert.
Ich verwende dann wohl UART also asynchron ohne extra Taktleitung mit diesen Stop-Bits.

In welche Richtung sollte ich nun versuchen am Code etwas zu ändern? Wenn ich das richtig verstehe muss eine Zeit lang gewartet werden, bis das erste Byte übertragen ist bevor ich das zweite senden kann oder?
Also sollte beim Empfänger sichergestellt sein, dass das erste Byte komplett eingegangen ist bevor das zweit angesetzt wird... puh

Peter(TOO)
16.04.2016, 22:58
Hallo,

vielen Dank für die ausführliche und gute Erkärung! Jetz habe ich auch ziemlich verstanden wie der U(S)ART funktioniert.
Ich verwende dann wohl UART also asynchron ohne extra Taktleitung mit diesen Stop-Bits.
Richtig!
Das U steht für universal, weil das Ding eben mehrere Modis kann.
Es gibt auch UARTs, die können dann kein synchron.


In welche Richtung sollte ich nun versuchen am Code etwas zu ändern? Wenn ich das richtig verstehe muss eine Zeit lang gewartet werden, bis das erste Byte übertragen ist bevor ich das zweite senden kann oder?
Also sollte beim Empfänger sichergestellt sein, dass das erste Byte komplett eingegangen ist bevor das zweit angesetzt wird... puh

Zuerst einmal hast du ein Problem mit deinem Übertragungsprotokoll! Wenn du den Datenstrom loggst siehst du folgendes:
.... Byte, Byte, Byte, Byte, Byte ....
Tja, nun weiss aber keiner, und dein µC erst recht nicht, welche zwei Bytes zusammen gehören!
Ein weiteres Problem ist noch, dass auch mal ein Byte verloren gehen kann oder gestört ist, dann wird eine andere ISR aufgerufen, welche die ganzen Fehler behandelt.

Man braucht also etwas in der Form:
.... Byte, sync, Byte, Byte, sync, Byte, Byte ....
Dann weiss man, dass immer die zwei Bytes nach sync ein Paar bilden.
Und wenn eine Byte verloren geht erkennt man das auch, dann fehlt entweder das sync oder nach sync kommt nur ein Byte.

Jetzt kommt das nächste Problem:
Du willst binäre Daten übertragen, die Datenwerte können also zwischen 0x00 und 0xFF liegen, das geht so nicht, weil du kein eindeutiges Zeichen für sync verwenden kannst.
Das einfachste ist, die Daten als Hex-Werte zu übertragen, dazu gibt es auch eine Menge Funktionen in C, um diese umzuwandeln. Dann hast du nur die Werte '0' ... '9', 'A' ... 'F' belegt kannst aber, theoretisch alle anderen als sync verwenden.
Der Nachteil bei ASCII-Hex ist halt, dass du doppelt so viele Zeichen senden musst, wie binär.
Bei ASCII-Hex bietet es sich dann aber an, CR oder LF als Sync-Zeichen zu verwenden, diese werden bei Text als Zeilenabschluss verwendet. Ein weiterer Vorteil ist, dass du zum Testen ein Terminal anschliessen kannst (PC mit Terminal-Emulation) und alles im Klartext lesen kannst.

MfG Peter(TOO)

HaWe
17.04.2016, 10:22
warum denn nicht gleich so wie ich es mache?
array um 2 länger als Daten gebraucht werden, für TCP-Zwecke,
also 2Bytes Daten + 2 TCP-Bytes = 4 Bytes

uint8_t msgarr[4];

msgarr[0] = 0xff;
msgarr[1] = chksum(); // Zellen 2+3 aufaddieren, dann davon lowbyte bilden
msgarr[2]= ... dein erstes Daten-Byte
msgarr[3]= ... dein zweites Daten-Byte

beim Empfang kontrollieren:

if((msgarr[0]==0xff) && (msgarr[1]== chksum())) {...} // hier jetzt Bytes zu Int zusammensetzen und dann zum Display schicken.

So geht es ganz easy auf Raspi und Arduino, da muss ich mich nicht um sync oder stop-Bits kümmern, das ist schon alles fix und fertig.

Aber vlt ist ja tatsächlich dein AVR komplizierter.

opc
17.04.2016, 13:42
Man braucht also etwas in der Form:
.... Byte, sync, Byte, Byte, sync, Byte, Byte ....
Dann weiss man, dass immer die zwei Bytes nach sync ein Paar bilden.


ok habe ich nun auch endlich verstanden. Da sonst endlos Bytes übertragen werden ohne dass der Empfänger weiß wo Anfang und Ende ist... so ist eine Addition natürlich sinnfrei (logisch). Ich dachte leider bislang, dass dies der UART bei den AVR´s automatisch erledigt, allerdings ging ich auch fälschlicherweise davon aus, dass ich den USART verwende (was mit jetzt aber klar ist).

für das "Protokoll" hat HaWe ja eine gute Lösung für seinen Rpi/Arduino:





uint8_t msgarr[4];

msgarr[0] = 0xff;
msgarr[1] = chksum(); // Zellen 2+3 aufaddieren, dann davon lowbyte bilden
msgarr[2]= ... dein erstes Daten-Byte
msgarr[3]= ... dein zweites Daten-Byte

beim Empfang kontrollieren:

if((msgarr[0]==0xff) && msgarr[1]== chksum())) {...} // hier jetzt Bytes zu Int zusammensetzen und dann zum Display schicken.




ich fange an den Sender mit dieser Methode aufzubauen und der Code sieht nun in diesem Ausschnitt folgendermaßen aus:

uint8_t msgarr[4];

msgarr[0] = 0xff;
msgarr[1] = chksum(); // Zellen 2+3 aufaddieren, dann davon lowbyte bilden
msgarr[2]= zahl >> 8;
msgarr[3]= zahl & 0xff;

daten_senden(msgarr);


Eine Frage habe ich noch zur Funktion chksum()... was beinhaltet diese bzw. welche Zellen 2+3 sind hier gemeint? Die Inhalte 2+3 vom Array oderwie?

Wenn mir das noch klar ist habe ich meinen Fehler "fehlendes Protokoll/Sync" beim Byte übertragen auch verstanden und mach mich an den Empfänger.

Danke für euren Support! Hat mir rein vom Verständnis her schon viel gebracht und versuche dies umzusetzen.

HaWe
17.04.2016, 14:49
genau, für chksum werden alle Array-Zellen ab [2] aufaddiert, 0 ist ja konstant und 1 ist ja die überstellte chksum, die es zu kontrollieren gilt

bei mir geht die chksum so:


uint8_t calcchecksum(uint8_t array[]) {
int32_t sum=0;
for(int i=2; i<MSGSIZE; ++i) sum+=(array[i]);
return (sum & 0x00ff);
}


arrrrgggghhhh !! wieder keine Code Tags!

bei dir wäre
#define MSGSIZE 4



auf Richtigkeit der Chksum rüfe ich per


bool checksumOK(uint8_t array[]) {
return (calcchecksum(array)==array[1]);
}

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8491&start=15#p67476

Peter(TOO)
17.04.2016, 20:07
Hallo HaWe,


[CODE]uint8_t msgarr[4];

msgarr[0] = 0xff;
msgarr[1] = chksum(); // Zellen 2+3 aufaddieren, dann davon lowbyte bilden
msgarr[2]= ... dein erstes Daten-Byte
msgarr[3]= ... dein zweites Daten-Byte

beim Empfang kontrollieren:

if((msgarr[0]==0xff) && msgarr[1]== chksum())) {...} // hier jetzt Bytes zu Int zusammensetzen und dann zum Display schicken.

[/CODE
Und wie fängt sich das Ganze wieder, wenn z.B. ein Byte verloren geht?
Sich also dann in msgarr[3] dein 0xff befindet.

MfG Peter(TOO)

HaWe
18.04.2016, 11:09
dann wird der Array nicht ausgewertet sondern auf den nächsten Array gewartet (i.P. handshake).
siehe meinen sourcecode für Raspi und Arduino (codes fast fast identisch)!

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8689&p=67907#p67815

Peter(TOO)
18.04.2016, 13:55
Hallo,

dann wird der Array nicht ausgewertet sondern auf den nächsten Array gewartet (i.P. handshake).
siehe meinen sourcecode für Raspi und Arduino (codes fast fast identisch)!

http://www.mindstormsforum.de/viewtopic.php?f=78&t=8689&p=67907#p67815

Eben, auf diesen Teil hast du den TO nicht hingewiesen :-)

Bei mir enden solche Protokollhandler immer als State Machine.

MfG Peter(TOO)

HaWe
18.04.2016, 14:13
ja und? Mach dus doch wie du willst!
außerdem hatte ich bereits mehrfach den TO auf meinen Code hingewiesen und meinen Code verlinkt als Erklärung, samt Kommentaren.