Ich bin auf der suche nach einer schöneren Lösung als ein stumpfes
delay()
Ich habe einen Atmega2560 mit 4 USARTs, der µC wird über einen USART
gesteuert und hat an 2 weiteren USARTs externe Geräte. Die externen
Geräte erhalten über den Steuer-USART ihre Befehle, dazu wird den
Befehlen ein @0 oder @3 vorangestellt.
Wenn die externen Geräte selbst etwas senden, stellt die Software im µC
den Daten ebenfalls ein @0 oder @3 vor.
Die komplette USART-Übertragung läuft mit Interrupts und FIFO-Speichern.
Ich habe nun das Problem, dass die Hauptschleife die eingehenden Daten
schneller verarbeitet als neue Daten nachkommen.
Wenn das externe Gerät "Test" sendet, wird "@3T@3e@3s@3t" ausgegeben.
Die Kennung brauche ich damit die Software auf dem PC unterscheiden kann
von welchem Gerät die Daten kommen.
Handshake oder warten auf Abschlusszeichen geht leider auch nicht, da
die Geräte keine Handshake-Leitungen haben und auch unterschiedliche
Geräte angeschlossen werden können.
Was bisher funktioniert ist ein Delay nach jedem kopierten Zeichen. Das
bremmst sowohl die Hauptschleife als auch die serielle Kommunikation aus
:/
Hier mal der Teil der Hauptschleife um die es geht.
1
// Eingehende Daten der USARTs über TCP versenden.
2
// Gegenrichtung in ethertest.c zu finden.
3
// Ist kein Ethernet verfügbar, erfolgt die Steuerung über den USART fürs Debuggen.
Das Projekt ist schon etwas größer, deshalb hänge ich es vorerst mal
nicht mit an.
Was ich eigtl. bräuchte währe ein Flag welches anzeigt, dass gerade
Daten emfangen werden.
Vielleicht hat jemand von euch noch eine Idee was ich machen könnte.
Und was soll passiert wenn beide Geräte gleichzeitig senden ?
Also :
@3Test
@0Hallo
dann
@3Te@0Halstlo
besser wäre wohl
@3Test@0Hallo
Dann mußt du aber den einen puffern wenn der andere gerade sendet, und
woher weißt du das ernicht mehr sendet ohne Steuerzeichen für das Ende ?
Da würde doch die jetzige Version vorziehen :
@3T@3e@0H@0a@0l@3s@3t@0l@0o
da kann nichts durcheinander kommen. Mußt du halt auf dem PC wieder
zusammenbasteln.
Weis ja nich was an Geräten drannehängt, aber die haben doch sicher ne
Kennung wann die Nachricht abgeschlossen ist.
Daher erst wiedern @ vorhängen wenn dieser Abschluss vorkam.
Vllt auch noch komplett Zwischenspeichern um Kollisionen zu vermeiden.
Oder du machst jedem UART einen eigenen Puffer für RX und TX und mußt
halt mit Timeout erkennen wenn jemand nicht mehr sendet. Das m it dem
Timeout ist aber doof.
@ M. K. (avr-frickler) Benutzerseite
>Die komplette USART-Übertragung läuft mit Interrupts und FIFO-Speichern.
Klingt gut.
>Ich habe nun das Problem, dass die Hauptschleife die eingehenden Daten>schneller verarbeitet als neue Daten nachkommen.
Kaum, dann dann wäre es kein FIFO.
>Wenn das externe Gerät "Test" sendet, wird "@3T@3e@3s@3t" ausgegeben.
Dann hast du einen Programmierfehler.
>Die Kennung brauche ich damit die Software auf dem PC unterscheiden kann>von welchem Gerät die Daten kommen.>Handshake oder warten auf Abschlusszeichen geht leider auch nicht, da>die Geräte keine Handshake-Leitungen haben und auch unterschiedliche>Geräte angeschlossen werden können.
Müssen sie auch nicht. Du musst meldungsbasiert denken und arbeiten,
nicht zeichenbasiert.
>Was bisher funktioniert ist ein Delay nach jedem kopierten Zeichen.
Delay ist meistens eine Zeichen für Murks.
> Das>bremmst sowohl die Hauptschleife als auch die serielle Kommunikation aus>:/
Macht man so nicht, sondern Multitasking. Ist gar nicht so schwer.
>Was ich eigtl. bräuchte währe ein Flag welches anzeigt, dass gerade>Daten emfangen werden.
Nö, keineswegs. Du musst interruptgesteuert komplette Meldungen
empfangen und dann per Flag den anderen Programmteilen (=
statemachine) das mitteilen, dort kann dann die Meldung komplett
verarbeitet werden.
Uwe schrieb:> Und was soll passiert wenn beide Geräte gleichzeitig senden ?> Also :> @3Test> @0Hallo> dann> @3Te@0Halstlo
Nein das kann nicht passieren, in diesem Fall sähe das so aus:
@3Te@0Hal@3st@0lo
> besser wäre wohl> @3Test@0Hallo
Das währe mein Wunschergebniss!
Uwe schrieb:> Dann mußt du aber den einen puffern wenn der andere gerade sendet, und
Die UARTs haben alle 2(RX/TX) eigene Puffer(FIFO). Der RXC-Interrupt
stellt die Daten immer in den RX-Puffer und der UDRE-Interrupt sendet
die Daten aus dem TX-Puffer.
> woher weißt du das ernicht mehr sendet ohne Steuerzeichen für das Ende ?
Wissen tue ich das nicht, ich schreibe einfach nur alle Daten aus den
Empfangspuffer raus bis der Puffer leer ist. Durch das Delay verzögere
ich das senden nur soweit das nach dem Delay das nächste Zeichen im
Puffer steht. Ist der Puffer nach dem Delay immer noch leer, springe ich
zum nächsten USART.
Uwe schrieb:> Da würde doch die jetzige Version vorziehen :> @3T@3e@0H@0a@0l@3s@3t@0l@0o> da kann nichts durcheinander kommen. Mußt du halt auf dem PC wieder> zusammenbasteln.
Hmm das währe auch noch eine Variante.
Martin Wende schrieb:> Weis ja nich was an Geräten drannehängt, aber die haben doch sicher ne> Kennung wann die Nachricht abgeschlossen ist.> Daher erst wiedern @ vorhängen wenn dieser Abschluss vorkam.
Im Moment sind es 2 verschiedene Netzteile das eine hat als Endung <cr>
das andere <cr><lf>.
@ M. K. (avr-frickler) Benutzerseite
>Die UARTs haben alle 2(RX/TX) eigene Puffer(FIFO). Der RXC-Interrupt>stellt die Daten immer in den RX-Puffer und der UDRE-Interrupt sendet>die Daten aus dem TX-Puffer.
Dieser Minipuffer reicht hier nicht.
>Wissen tue ich das nicht, ich schreibe einfach nur alle Daten aus den>Empfangspuffer raus bis der Puffer leer ist. Durch das Delay verzögere>ich das senden nur soweit das nach dem Delay das nächste Zeichen im>Puffer steht. Ist der Puffer nach dem Delay immer noch leer, springe ich>zum nächsten USART.
Total unbrauchbar.
Falk Brunner schrieb:>>Ich habe nun das Problem, dass die Hauptschleife die eingehenden Daten>>schneller verarbeitet als neue Daten nachkommen.>> Kaum, dann dann wäre es kein FIFO.
Doch es ist einer, nur er wird halt nicht voll weil die Hauptschleife
ihn zu schnell wieder leert.
Falk Brunner schrieb:>>Wenn das externe Gerät "Test" sendet, wird "@3T@3e@3s@3t" ausgegeben.>> Dann hast du einen Programmierfehler.
Nö denn es macht genau das was es soll.
Falk Brunner schrieb:> Müssen sie auch nicht. Du musst meldungsbasiert denken und arbeiten,> nicht zeichenbasiert.
Würde ich ja gerne, nur woher soll ich wissen wann eine Meldung zuende
ist. Es gibt unzählige Variaten wie ein Gerät eine Meldung abschliessen
kann, <cr>, <cr><lf>, <lf><cr>, <null>, feste Länge, usw.
Falk Brunner schrieb:> Delay ist meistens eine Zeichen für Murks.
Ein Grund warum ich es gerne weg hätte.
Falk Brunner schrieb:> Macht man so nicht, sondern Multitasking. Ist gar nicht so schwer.
Die Software macht Multitasking. Was in diesem Fall mit zu dem Problem
beiträgt.
Falk Brunner schrieb:> Nö, keineswegs. Du musst interruptgesteuert komplette Meldungen> empfangen und dann per Flag den anderen Programmteilen (=> statemachine) das mitteilen, dort kann dann die Meldung komplett> verarbeitet werden.
Der µC soll die Meldungen nicht verarbeiten, er soll Sie lediglich an
den PC weiterleiten.
Falk Brunner schrieb:>>Die UARTs haben alle 2(RX/TX) eigene Puffer(FIFO). Der RXC-Interrupt>>stellt die Daten immer in den RX-Puffer und der UDRE-Interrupt sendet>>die Daten aus dem TX-Puffer.>> Dieser Minipuffer reicht hier nicht.
Wie groß soll er denn sein? Ich kann ihn theoretisch 64kByte groß
machen, praktisch 256Byte im Moment sind es 50Bytes RX und 50Bytes TX,
was dicke reicht, wie schon geschrieben der Puffer läuft nicht voll,
sondern er wird zu schnelle geleert.
M. K. schrieb:>> Müssen sie auch nicht. Du musst meldungsbasiert denken und arbeiten,>> nicht zeichenbasiert.>> Würde ich ja gerne, nur woher soll ich wissen wann eine Meldung zuende> ist. Es gibt unzählige Variaten wie ein Gerät eine Meldung abschliessen> kann, <cr>, <cr><lf>, <lf><cr>, <null>, feste Länge, usw.
Dann muss man das eben in deiner Software konfigurieren können, wenns
denn universell sein soll. Wenns an spezielle Geräte angepasst wird,
dann programmiert man dann eben deren Verhalten rein. So viele
verschiedene Varianten gibt es dann auch wieder nicht.
@ M. K. (avr-frickler) Benutzerseite
>> Dann hast du einen Programmierfehler.>Nö denn es macht genau das was es soll.
Warum dann dein Posting?
>> Müssen sie auch nicht. Du musst meldungsbasiert denken und arbeiten,>> nicht zeichenbasiert.>Würde ich ja gerne, nur woher soll ich wissen wann eine Meldung zuende>ist.
Tja, wie soll es der Mikrocontroller wissen? Der tut nur das, was du ihm
sagst.
> Es gibt unzählige Variaten wie ein Gerät eine Meldung abschliessen>kann, <cr>, <cr><lf>, <lf><cr>, <null>, feste Länge, usw.
Dann musst du das berücksichtigen, in welcher Forma auch immer.
>> Macht man so nicht, sondern Multitasking. Ist gar nicht so schwer.>Die Software macht Multitasking. Was in diesem Fall mit zu dem Problem>beiträgt.
Nicht wenn man es richtig (tm) macht. Siehe oben, meldungsbasiert, nicht
zeichenbasiert.
>Der µC soll die Meldungen nicht verarbeiten, er soll Sie lediglich an>den PC weiterleiten.
Er muss aber verschiedene Meldungen verschiedener Geräte ohne
Verwurstung in EINEN Datenstrom zusammenfügen, aka Multiplexer. Dazu
muss er wisssen, was ein vollständiger Datenblock ist, aka Kommando bzw.
String. Einfach planlos Zeichen zusammwürfeln kann jeder Depp.
Falk Brunner schrieb:> @ M. K. (avr-frickler) Benutzerseite>>>>> Dann hast du einen Programmierfehler.>>>Nö denn es macht genau das was es soll.>> Warum dann dein Posting?
Weil es nicht das ist was ich will, sind halt 2 verschiedene Paar Schuhe
:P
Karl Heinz Buchegger schrieb:> Dann muss man das eben in deiner Software konfigurieren können, wenns> denn universell sein soll. Wenns an spezielle Geräte angepasst wird,> dann programmiert man dann eben deren Verhalten rein. So viele> verschiedene Varianten gibt es dann auch wieder nicht.
Was dann zu Falks Meldungsbasiertem Interrupt führt. Scheint wohl der
einzigst sinvolle Weg zu sein. Werde es dann wohl so machen:
Kommando erzeugen fürs setzen Meldungsende-Makierung. 2 Bytes sollten
reichen denke ich.
Im RXC-Interrupt die Meldungsende-Makierung erkennen und Flag setzen, in
der Hauptschleife das Flag auswerten und darauf reagieren.
Für den Fall das kein Makierungsende gesetzt wurde oder der FIFO voll
läuft den Puffer leeren, dann müssen die Daten am PC halt richtig
zusammengesetzt werden.
Wenn mir am Wochenende nichts besseres einfällt werde ich das am Montag
mal so umsetzen.
M. K. schrieb:> Würde ich ja gerne, nur woher soll ich wissen wann eine Meldung zuende> ist. Es gibt unzählige Variaten wie ein Gerät eine Meldung abschliessen> kann, <cr>, <cr><lf>, <lf><cr>, <null>, feste Länge, usw.
Wieviel verschiedene Geräte mit verschiedenen Protokollen sind es denn
wirklich?
Ich mach das immer so, daß CR oder LF als Ende angesehen werden und ein
folgendes CR oder LF wird ignoriert.
Ein anderer Trick wäre, Du setzt ein Timeout auf. Z.B. wenn 3 Bytezeiten
kein neues Zeichen eintrudelt, wird das Paket als beendet angenommen und
bis dahin gesendet.
Blöd wäre dann aber, wenn die Geräte ihr Senden manchmal durch einen
langen Interrupt unterbrechen oder ohne Pause senden.
Man kann auch, wenn das Ende erkennen nicht klappt, dann spätestens bei
50% Füllgrad des FIFO senden.
Peter
Man hat ja 4 UARTs. Daher kann der Controller ja wissen woher welche
Meldung kommt. Also der Controller wartet parallel auf allen UARTs ab,
bis eine komplette Meldung da ist und sendet sie dann als komplette
Meldung weiter. Alles andere ist Murks. Denk dir eine Zustandsmaschine
aus, die das leistet.
Wie ich's verstanden habe sind die Gerate verschieden und haben bereits
feste Protokolle. Also der controller muss diese Geraete natuerlich
verstehen, um die einzelnen Meldungen zu entschluesseln und zu wissen ,
wann eine Meldung fertig ist.
Das Protokoll zum PC ist noch unbestimmt. Dabei sollte man etwas
waehlen, das eine Absenderkennung, und eine Laenge hat. Allenfalls noch
einen CRC, um sicherzustellen, das nur gueltige Daten verwendet werden.
Die einzelnen Meldungen der Geraete werden dann einfach als einzelne
Meldungen aneinander gefuegt.
@ M. K. (avr-frickler) Benutzerseite
>Kommando erzeugen fürs setzen Meldungsende-Makierung. 2 Bytes sollten>reichen denke ich.
Warum zwei Bytes? Es sind ASCII-Strings in C, da kann man das alles
standardkonform machen, 0x00 = Stringende.
Ein Byte reicht als Flag. Deine einzelnen RX-Interrupts müssen nur das
Datenendezeichen erkennen, eine 0x00 in den FIFO schreiben und das Flag
setzen. Die Hauptschleife mit ihrer Statemachine prüft diese Flags. Wenn
eins gesetzt ist, wird nur aus diesem FIFO der komplette String
gesendet, danach das Flag gelöscht, dann geht es weiter zum nächsten
Flag eines anderen Kanals. GGf. nutz man das Flag als Zähler für mehrere
Strings im Puffer, falls die Datenraten der UARTs arg verschieden sind.
Aber Vorsicht mit atomarem Zugriff!
>Im RXC-Interrupt die Meldungsende-Makierung erkennen und Flag setzen, in>der Hauptschleife das Flag auswerten und darauf reagieren.
Ja, siehe Interrupt.
>Für den Fall das kein Makierungsende gesetzt wurde oder der FIFO voll>läuft den Puffer leeren, dann müssen die Daten am PC halt richtig>zusammengesetzt werden.
Wie denn, wenn dort Fragmente ankommen? Ein voller FIFO darf eigentlich
nie vorkommen, es sein denn, die Datenrate aller Eingänge in Summe ist
größer als die Datenrate zum PC. Das kann man leicht verhindern.
Ich verstehe ehrlich gesagt nicht so ganz wo das Problem liegt? Wenn eh
von den Geräten alles "zu langsam" kommt ist es ja egal das du immer
doppelt soviele Zeichen überträgst. Falls die Geräte mal schneller sind
wird es ja automatisch mehr Daten pro "Paket" übertragen...
> Warum zwei Bytes? Es sind ASCII-Strings in C, da kann man das alles> standardkonform machen, 0x00 = Stringende.
Ich denke er will alle möglichen Geräte dranmachen. Da geht gar kein
Endzeichen, weil die geräte könnten ja auch binäre Daten senden.
Also für eigenes Protokoll :
Startbyte - Nummer der des ports - Länge der Daten - Checksumme
Aber ich glaube er wird sowieso viele Probleme bekommen, denn was
passiert wenn ein Gerät was sendet und ne antwort in 100ms erwartet,
aber erst mal ein anderes gerät bedient wird bzw. die Statemachine
weiter die puffer füllt usw.
Das einzige was geht ist :
@0T@1H@2J@3µ@0e...
Dann die Baudrate zum PC vier mal so hoch wählen bzw. den Overhead ala
"@0" muß man auch noch dazurechen, also pro Zeichen 2 extra Zeichen also
3x4=12.
Mindestens 12 fache Baudrate einstellen. dann brauch man auch keine
großen Puffer.
@ Uwe (Gast)
>> Warum zwei Bytes? Es sind ASCII-Strings in C, da kann man das alles>> standardkonform machen, 0x00 = Stringende.>Ich denke er will alle möglichen Geräte dranmachen. Da geht gar kein>Endzeichen, weil die geräte könnten ja auch binäre Daten senden.
Eas dürfte mit dem Konzept schwiegig bis unmöglich werden, denn er
sendet ja ASCII zum PC, mit der Kennung @1 für Gerät 1 etc.
Und eine eierlegende Wollmilchsau ist selten gut.
>Aber ich glaube er wird sowieso viele Probleme bekommen, denn was>passiert wenn ein Gerät was sendet und ne antwort in 100ms erwartet,>aber erst mal ein anderes gerät bedient wird bzw. die Statemachine>weiter die puffer füllt usw.
Das ist eine andere Frage.
>Das einzige was geht ist :>@0T@1H@2J@3µ@0e...>Dann die Baudrate zum PC vier mal so hoch wählen bzw. den Overhead ala>"@0" muß man auch noch dazurechen, also pro Zeichen 2 extra Zeichen also>3x4=12.>Mindestens 12 fache Baudrate einstellen. dann brauch man auch keine>großen Puffer.
Dann kann man sich den Käse gleich schenken und vier RS232 Ports direkt
an den PC klemmen, sei es per USB oder wasauchimmer. Spart haufenweise
Zurückfriemeln der Daten.
Also binäre Daten werden nicht übertragen, es sind schon einfache
ASCII-Strinngs.
Falk Brunner schrieb:>>Aber ich glaube er wird sowieso viele Probleme bekommen, denn was>>passiert wenn ein Gerät was sendet und ne antwort in 100ms erwartet,>>aber erst mal ein anderes gerät bedient wird bzw. die Statemachine>>weiter die puffer füllt usw.>> Das ist eine andere Frage.
So etwas ist bisher noch nicht vorgekommen.
Falk Brunner schrieb:> Warum zwei Bytes? Es sind ASCII-Strings in C, da kann man das alles> standardkonform machen, 0x00 = Stringende.
Ja so meinte ich es auch nur halt auf 2 Zeichen begrenzt .. obwohl wenn
ich recht überlege ist es eigtlich egal, wenn ich im Interrupt nen
Zähler habe wieviele Zeichen des Meldungsende-Strings bereits
übereinstimmen brauche ich nur noch gegen die Stringlänge testen und das
Flag setzen.
1
ISR(USART0_RX_vect){
2
>>staticuint8_tcount_endl;
3
uint8_ti=com0.rx_buffer.in;
4
charc=UDR0;
5
6
WRAPBUFFER(i,com0.rx_buffer.length);
7
8
// Puffer Überlauf
9
if(i==com0.rx_buffer.out)
10
return;
11
12
com0.rx_buffer.data[com0.rx_buffer.in]=c;
13
com0.rx_buffer.in=i;
14
15
>>if(c==com0.endl[count_endl])
16
>>count_endl++;
17
>>else
18
>>count_endl=0;
19
20
>>if(count_endl==strlen(com0.endl))
21
>>com0.msgComplete=1;
22
23
if(com0.setting.flow==FLOW_RTS_CTS){
24
c=0;
25
do{
26
WRAPBUFFER(i,com0.rx_buffer.length);
27
}while(i!=com0.rx_buffer.out&&c++<=5);
28
29
if(c<5){
30
PORT_HANDSHAKE0|=(1<<RTS0);
31
}
32
}
33
}
* Die mit >> makierten Zeilen sind neu.
static-Variablen werden vom Compiler mit 0 initialisert? Arbeite kaum
mit static-Variablen daher die Frage.
strlen kann noch gegen eine Variable ersetzt werden die beim
initialiseren des USARTs gesetzt wird, msgComplete könnte auch ein
Zähler anstatt eines Flags sein.
Splitte doch dein Byte in ein 4-Bit-Teil mit Adresse und einen
4-Bit-Teil mit Daten. Musst du dein Protokoll nur ein bisschen umbauen
(also zwei Mal senden für 1 Byte), aber dann ist es eindeutig, wenn
keine Übertragungsfehler dazukommen.