Forum: Compiler & IDEs RS232-Frameprotokoll transmitt Problem


von Patrick B. (p51d)


Angehängte Dateien:

Lesenswert?

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
void UART_Transmit(int Num_of_bytes, int param[]){            // Anzahl Parameter und Parameters übergeben
2
  int frame_pos = 0;                          // Frameteile: SOH, NUM, PAR's, CRC, EOT
3
  int q = 0;
4
  int CRC = 0x45;
5
  int start_index = 0;
6
  for(frame_pos=0;frame_pos<=3;frame_pos++){              // Daten in TX Buffer füllen/1 Durchlauf mehr als Bytes
7
    if(index_txBuf_W == Buf_max){                  // Damit Buffer nicht überläuft
8
      index_txBuf_W = 0;
9
    }
10
    else{
11
      index_txBuf_W ++;                      // Nächste Bufferstelle
12
    }
13
    switch (frame_pos){                        // Frame generieren
14
      case 0:
15
        txBuf[index_txBuf_W] = Num_of_bytes;          // Anzahl Parameter
16
        start_index = index_txBuf_W;
17
        break;
18
      case 1:                            // Parameter Speichern
19
        for(q=0;q<Num_of_bytes;q++){
20
          txBuf[index_txBuf_W] = param[q];
21
          if(q < (Num_of_bytes - 1)){            // Bei letztem Byte nicht inkrementieren, da erneuter Durchlauf 
22
            if(index_txBuf_W == Buf_max){          // Damit Buffer nicht überläuft
23
              index_txBuf_W = 0;
24
            }
25
            else{
26
              index_txBuf_W ++;              // Nächste Bufferstelle
27
            }
28
          }
29
        }
30
        break;
31
      case 2:                            // CRC
32
        txBuf[index_txBuf_W] = CRC;      
33
        break;
34
    }
35
  }
36
  if(index_txBuf_R == Buf_max){                    // Buffer am überlaufen hindern  
37
    index_txBuf_R = 0;
38
  }
39
  else{
40
    index_txBuf_R ++;
41
  }
42
  UCSRB |= (1<<TXCIE);                        // Für Frame TX Complete Interrupt aktivieren
43
  RXTX_Reg = txBuf[start_index];                    // Com starten mit Number of Bytes senden
44
}
45
ISR(USART_TXC_vect){                          // Transmit Complete Interrupt
46
  if(index_txBuf_R == Buf_max){                    // Damit Buffer nicht überläuft
47
    index_txBuf_R = 0;
48
  }
49
  else{
50
    index_txBuf_R ++;                        // Index erhöhen
51
  }  
52
  if(index_txBuf_R == index_txBuf_W){                  // Damit Read Write nicht überholt
53
    UCSRB &= (~(1<<TXCIE));                    // Frame beendet, TX Complete Interrupt deaktivieren
54
    return;
55
  }
56
  else{
57
    RXTX_Reg = txBuf[index_txBuf_R];                // Daten senden
58
  }
59
}
60
61
int array[]={'h','a','l','l','o',' ','w','e','l','t'};      // Gibt als Parameter "hallo welt" aus
62
int Num_bytes = (sizeof(array)/sizeof(array[0]));          // Arrayplätze berechnen
63
UART_Transmit(Num_bytes, array);
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.

von Karl H. (kbuchegg)


Lesenswert?

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
void UART_Transmit( int Num_of_bytes, int param[] )
2
{            // Anzahl Parameter und Parameters übergeben
3
  int q = 0;
4
  int CRC = 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
static int StartIndex;
21
22
void UART_StartFrame()
23
{
24
  StartIndex = index_txBuf_W;
25
}
26
27
void UART_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.

von Patrick B. (p51d)


Angehängte Dateien:

Lesenswert?

>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
void txBuf_Write(int parameter){                    // 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
void UART_Transmit(int Num_of_bytes, int param[]){                  // Anzahl Parameter und Parameters übergeben
11
  int q = 0;
12
  static int StartIndex = 0;                      // Index wo Transmit-Frame gestartet werden soll
13
  int CRC = 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

von Karl H. (kbuchegg)


Lesenswert?

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?

von gast (Gast)


Lesenswert?

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

von Patrick B. (p51d)


Lesenswert?

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
int array[]={'T','i','m','e','o','u','t'};          // Gibt als Parameter "Timeout" aus
2
int Num_bytes = (sizeof(array)/sizeof(array[0]));      // Arrayplätze berechnen
3
UART_Transmit(Num_bytes, array);


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

von Patrick B. (p51d)


Lesenswert?

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

von gast (Gast)


Lesenswert?

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 ?

von Patrick B. (p51d)


Lesenswert?

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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.