Hallo miteinander
Ich habe von einem Lehrer der Auftrag erhalten, ein RS232-Frameprotokoll
zu schreiben, welches in etwa so aussieht:
ENQ ->
<- ACK
-> Request
<- ACK
<- Response
ACK ->
EOT ->
Das Frame gab er so als vor:
SOH
Command
Number of Bytes
Parameter 1
...
Parameter n
Checksum
EOT
Eine weitere Einschränkung ist, dass alles selber geschrieben werden
muss, und keine Fertiglösungen benützt werden dürfen, dass heisst printf
wird nicht erwünscht, aber geduldet.
Beim Protokoll läuft alles so weit so gut, ich kann ein Frame an den
MCU(ATmega32) senden, und dort wird alles so gespeichert wie es soll.
An den PC senden funktioniert auch gut, aber sobald ich im Protokoll die
Antwort oder zum Beispiel ein Timeout senden möchte, kommt am PC nicht
das an, was soll.
Diese Funktion nütze ich um Daten an den PC zu senden:
1
voidUART_Transmit(intNum_of_bytes,intparam[]){// Anzahl Parameter und Parameters übergeben
ausserhalb des Protokoll kommt am PC das an:
Number of Bytes
Parameter 1
...
Parameter n
Checksum
Im Protokoll kommt dann sowas an:
Paramter 1
Number of Bytes
Parameter 2
...
Paramter n
CRC
oder
Parameter 3
...
Paramter n
CRC
Ich weiss überhaupt nicht mehr weiter und bin für jeden Vorschlag offen.
Danke für eure Antworten MFG
P51D
Im Anhang habe ich noch den kompleten Source-Code.
Trenn das alles doch mal vernünftig in Funktionen auf.
Das ist doch Schei... da mit deiner for-Schleife.
Schreib eine Funktion, die EIN Byte in den Ausgabebuffer stellt. Nicht
mehr und nicht weniger. Diese Funktion sorgt dafür, dass der Ringbuffer
sauber rundum läuft und sich nicht selbst einholt.
Und diese Funktion benutzt du dann in der Frameausgabefunktion. Die ist
dann linear runtercodiert.
1
voidUART_Transmit(intNum_of_bytes,intparam[])
2
{// Anzahl Parameter und Parameters übergeben
3
intq=0;
4
intCRC=0x45;
5
6
UART_StartFrame();// Aufgepasst ein neuer Frame beginnt!
7
8
UART_Write(Num_of_bytes);
9
10
for(q=0;q<Num_of_bytes;q++)
11
{
12
UART_Write(param[q]);
13
}
14
15
UART_Write(CRC);
16
17
UART_StartTransmission();// Frame ist aufgebaut: Und raus damit!
18
}
19
20
staticintStartIndex;
21
22
voidUART_StartFrame()
23
{
24
StartIndex=index_txBuf_W;
25
}
26
27
voidUART_StartTransmission()
28
{
29
UCSRB|=(1<<TXCIE);// Für Frame TX Complete Interrupt aktivieren
30
RXTX_Reg=txBuf[StartIndex];// Com starten mit Number of Bytes senden
31
}
Siehst du, wie einfach nachvollziehbar und offensichtlich korrekt die
Frame-Sendefunktion UART_Transmit ist?
Du schreibst jetzt die UART_Write. Ihre Aufgabe: EIN Zeichen in den
Ausgabebuffer stellen und sich um den index_txBuf_W kümmern.
>Siehst du, wie einfach nachvollziehbar und offensichtlich korrekt die>Frame-Sendefunktion UART_Transmit ist?
nachvollziehbar, ja ok, aber offensichtlich korrekt würde ich nicht
sagen. So wie du UART_Transmit geschrieben hast, würde das Frame mit dem
letzten Byte des vorhergegangenen Frame starten.
Doch leider läuft es noch immer nicht. Der Fehler ist nach wie vor der
gleiche:
Ausserhalb der RS232 kommunikation läuft die Funktion ohne Probleme,
aber sobald eine Antwort oder ein Timeout gesendet werden soll, verliert
er entweder am Anfang des TX Frames Number of Bytes oder stellt diese an
2. Stelle hin, was für mich gegen jede Logik verstösst.
hier ist nochmals der abgeänderte TX-Teil und im anhang nochmals die
neue Source.
1
voidtxBuf_Write(intparameter){// TX-Buffer mit Werte füllen
2
if(index_txBuf_W==Buf_max){// Damit Ringbuffer nicht überläuft
3
index_txBuf_W=0;
4
}
5
else{
6
index_txBuf_W++;
7
}
8
txBuf[index_txBuf_W]=parameter;
9
}
10
voidUART_Transmit(intNum_of_bytes,intparam[]){// Anzahl Parameter und Parameters übergeben
11
intq=0;
12
staticintStartIndex=0;// Index wo Transmit-Frame gestartet werden soll
13
intCRC=0x45;
14
txBuf_Write(Num_of_bytes);
15
StartIndex=index_txBuf_W;// Aufgepasst ein neuer Frame beginnt!
16
for(q=0;q<Num_of_bytes;q++){
17
txBuf_Write(param[q]);
18
}
19
txBuf_Write(CRC);
20
if(index_txBuf_R==Buf_max){// Buffer am überlaufen hindern
21
index_txBuf_R=0;
22
}
23
else{
24
index_txBuf_R++;
25
}
26
UCSRB|=(1<<TXCIE);// Für Frame TX Complete Interrupt aktivieren
27
RXTX_Reg=txBuf[StartIndex];// Frame ist aufgebaut/Com starten mit Number of Bytes senden
28
}
Ok, aber trotz allem danke für die Hilfe.
MFG
P51D
Patrick B. schrieb:
>>Siehst du, wie einfach nachvollziehbar und offensichtlich korrekt die>>Frame-Sendefunktion UART_Transmit ist?>> nachvollziehbar, ja ok, aber offensichtlich korrekt würde ich nicht> sagen. So wie du UART_Transmit geschrieben hast, würde das Frame mit dem> letzten Byte des vorhergegangenen Frame starten.
Das hängt jetzt ganz davon ab, was die Bedeutung von index_txBuf_W ist.
Bei dir ist das anscheinend: Es enthält immer den Index, an den das
nächste asuzugebende Byte abgelegt wird.
Wenn ich mir also diesen Wert in einer Hilfsvariable merke, noch ehe ich
irgendwas in den Buffer stelle, dann habe ich nachdem der Frame fertig
im Buffer steht, in der Hilfsvariable den Index des ersten Framebytes.
> aber sobald eine Antwort oder ein Timeout gesendet werden soll,
Wie sendest du die Antwort?
dann solltest du DAS
ENQ ->
<- ACK
-> Request
<- ACK
<- Response
ACK ->
EOT ->
Das Frame gab er so als vor:
SOH
Command
Number of Bytes
Parameter 1
...
Parameter n
Checksum
EOT
mal näher erläutern
Karl heinz Buchegger schrieb:
> Das hängt jetzt ganz davon ab, was die Bedeutung von index_txBuf_W ist.> Bei dir ist das anscheinend: Es enthält immer den Index, an den das> nächste asuzugebende Byte abgelegt wird.>> Wenn ich mir also diesen Wert in einer Hilfsvariable merke, noch ehe ich> irgendwas in den Buffer stelle, dann habe ich nachdem der Frame fertig> im Buffer steht, in der Hilfsvariable den Index des ersten Framebytes.
index_txBuf_W ist ein Pointer, der mir zeigt, wo ich im Ringbuffer schon
etwas relevantes abgelegt habe.
Zum angesprochenen Fehler:
Damit nicht der Wert des vorhergegangenen Bytes überschrieben wird, muss
der Pointer zur nächsten Stelle wandern. Und erst dann kann ich ihn als
Variable merken, befor ich ihn wieder inkrementiere, damit auf der
nächsten Bufferstelle das nächste Byte gespeichert werden kann.
> Wie sendest du die Antwort?
Ich rufe einfach die Funktion UART_Transmit so auf:
1
intarray[]={'T','i','m','e','o','u','t'};// Gibt als Parameter "Timeout" aus
gast schrieb:
>dann solltest du DAS>ENQ ->><- ACK>-> Request><- ACK><- Response>ACK ->>EOT ->>Das Frame gab er so als vor:>SOH>Command>Number of Bytes>Parameter 1>...>Parameter n>Checksum>EOT
Ok, ich versuchs mal:
der PC sendet ein ENQ bis der MCU ein ACK sendet.
Der Request ist dann in etwa so aufgebaut:
SOH|Command|Number of Bytes|Parameters|CRC|
Wenn der MCU bis hier alles richtig erhalten hat und die CRC stimmt
sendet er ein ACK und anschliessend falls nötig die Antwort, welche etwa
so aussieht:
Number of Bytes|Parameters|CRC
Hat der PC alles von der Antwort erhalten und die CRC stimmt, so sendet
er ein ACK und beendet die Kommunikation mit EOT.
Ich denke ich habs in etwa so geschrieben, wie es die Aufgabe verlangt.
Danke für eure Hilfe
MFG
P51D
OK. nach etwas spielerei habe ich nun etwas interessantes
herausgefunden:
Dieser Code funktioniert:
1
RXTX_Reg=txBuf[StartIndex];
2
UCSRB|=(1<<TXCIE);
Dieser nicht:
1
UCSRB|=(1<<TXCIE);
2
RXTX_Reg=txBuf[StartIndex];
Was passiert wenn ich den Interrupt aktiviere?? springt er dann in die
ISR??
bei der jetzt funktionierenden Variante, kann da nicht ein Problem
auftreten, wenn ich den TXC Interrupt erst aktiviere nachdem ich das UDR
geupdatet habe, bei sehr hohen baudraten??
Hoffe ihr könnt mir da etwas erklährunen
MFG
P51D
wenn da so aussehen soll versteh ich deinen verwuselen aufbau nicht
ich hätte grundlegend erstmal uart senden und empfangen gemacht
8bit rein und 8bit senden
damit kann man erstmal bytes verschicken .. und auch bytes empfangen
dann entsprechend eine statemaschine bauen
das ganze ist ja ein frage - antwortspielchen
zu dem :
>ENQ ->><- ACK>-> Request><- ACK><- Response>ACK ->>EOT ->
der PC sendet ein ENQ
der µC antwortet mit ACK
daraufhin weiß der µC jetz kommen daten
steht jetz konsequent auf empfang
PC sendet nach vorlage des frames
µC empfängt entsprechend des längenbytes
prüft und sendet bei korrektheit ein ACK
das spielchen kann sich jetz wiederholen ...
PC sendet EOT .. µC kann sleep oder sonstwas tun ..
Rx ist jetz keine hohe prio mehr
hab ich das so mitschneiden können ? ^^
und sendest du für das ENQ , ACK , NACK , EOT volle frames ? oder nur
ein byte ?
gast schrieb:
> der PC sendet ein ENQ> der µC antwortet mit ACK> daraufhin weiß der µC jetz kommen daten> steht jetz konsequent auf empfang>> PC sendet nach vorlage des frames> µC empfängt entsprechend des längenbytes> prüft und sendet bei korrektheit ein ACK>> das spielchen kann sich jetz wiederholen ...>> PC sendet EOT .. µC kann sleep oder sonstwas tun ..> Rx ist jetz keine hohe prio mehr>> hab ich das so mitschneiden können ? ^^
Fast perfekt: Der Frame ist abgeschlossen, und es kann sich nicht's im
Frame selber wiederholen.
Nach Empfang, Überprüfung und Bestätigung kommt je nach Kommando eine
Antowort im Frame wie schon erwähnt (Anzahl Bytes, Parameters, CRC) dies
wird wiederum vom PC empfangen nach Länge "Anzahl Bytes", geprüft und
wenn korrekt sendet der PC ein ACK mit anschiessendem EOT als Abschluss.
Falls jetzt nochmals Daten ausgetauscht werden, so beginnt das Spiel von
vorne. Der MCU ist hier der totale Slave und macht nichts ohne Anweisung
vom PC
> und sendest du für das ENQ , ACK , NACK , EOT volle frames ? oder nur> ein byte ?
Dafür sende ich immer nur je 1 Byte.