mikrocontroller.net

Forum: Compiler & IDEs RS232-Frameprotokoll transmitt Problem


Autor: Patrick B. (p51d)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
void UART_Transmit(int Num_of_bytes, int param[]){            // Anzahl Parameter und Parameters übergeben
  int frame_pos = 0;                          // Frameteile: SOH, NUM, PAR's, CRC, EOT
  int q = 0;
  int CRC = 0x45;
  int start_index = 0;
  for(frame_pos=0;frame_pos<=3;frame_pos++){              // Daten in TX Buffer füllen/1 Durchlauf mehr als Bytes
    if(index_txBuf_W == Buf_max){                  // Damit Buffer nicht überläuft
      index_txBuf_W = 0;
    }
    else{
      index_txBuf_W ++;                      // Nächste Bufferstelle
    }
    switch (frame_pos){                        // Frame generieren
      case 0:
        txBuf[index_txBuf_W] = Num_of_bytes;          // Anzahl Parameter
        start_index = index_txBuf_W;
        break;
      case 1:                            // Parameter Speichern
        for(q=0;q<Num_of_bytes;q++){
          txBuf[index_txBuf_W] = param[q];
          if(q < (Num_of_bytes - 1)){            // Bei letztem Byte nicht inkrementieren, da erneuter Durchlauf 
            if(index_txBuf_W == Buf_max){          // Damit Buffer nicht überläuft
              index_txBuf_W = 0;
            }
            else{
              index_txBuf_W ++;              // Nächste Bufferstelle
            }
          }
        }
        break;
      case 2:                            // CRC
        txBuf[index_txBuf_W] = CRC;      
        break;
    }
  }
  if(index_txBuf_R == Buf_max){                    // Buffer am überlaufen hindern  
    index_txBuf_R = 0;
  }
  else{
    index_txBuf_R ++;
  }
  UCSRB |= (1<<TXCIE);                        // Für Frame TX Complete Interrupt aktivieren
  RXTX_Reg = txBuf[start_index];                    // Com starten mit Number of Bytes senden
}
ISR(USART_TXC_vect){                          // Transmit Complete Interrupt
  if(index_txBuf_R == Buf_max){                    // Damit Buffer nicht überläuft
    index_txBuf_R = 0;
  }
  else{
    index_txBuf_R ++;                        // Index erhöhen
  }  
  if(index_txBuf_R == index_txBuf_W){                  // Damit Read Write nicht überholt
    UCSRB &= (~(1<<TXCIE));                    // Frame beendet, TX Complete Interrupt deaktivieren
    return;
  }
  else{
    RXTX_Reg = txBuf[index_txBuf_R];                // Daten senden
  }
}

int array[]={'h','a','l','l','o',' ','w','e','l','t'};      // Gibt als Parameter "hallo welt" aus
int Num_bytes = (sizeof(array)/sizeof(array[0]));          // Arrayplätze berechnen
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.
void UART_Transmit( int Num_of_bytes, int param[] )
{            // Anzahl Parameter und Parameters übergeben
  int q = 0;
  int CRC = 0x45;

  UART_StartFrame();        // Aufgepasst ein neuer Frame beginnt!

  UART_Write( Num_of_bytes );

  for( q = 0; q < Num_of_bytes; q++ )
  {
    UART_Write( param[q] );
  }

  UART_Write( CRC );

  UART_StartTransmission();  // Frame ist aufgebaut: Und raus damit!
}

static int StartIndex;

void UART_StartFrame()
{
  StartIndex = index_txBuf_W;
}

void UART_StartTransmission()
{
  UCSRB |= (1<<TXCIE);              // Für Frame TX Complete Interrupt aktivieren
  RXTX_Reg = txBuf[StartIndex];     // Com starten mit Number of Bytes senden
}

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.

Autor: Patrick B. (p51d)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
void txBuf_Write(int parameter){                    // TX-Buffer mit Werte füllen
  if(index_txBuf_W == Buf_max){                    // Damit Ringbuffer nicht überläuft
    index_txBuf_W = 0;
  }
  else{
    index_txBuf_W ++;
  }
  txBuf[index_txBuf_W] = parameter;
}
void UART_Transmit(int Num_of_bytes, int param[]){                  // Anzahl Parameter und Parameters übergeben
  int q = 0;
  static int StartIndex = 0;                      // Index wo Transmit-Frame gestartet werden soll
  int CRC = 0x45;
  txBuf_Write(Num_of_bytes);
  StartIndex = index_txBuf_W;                      // Aufgepasst ein neuer Frame beginnt!
  for(q = 0; q < Num_of_bytes; q++){
    txBuf_Write(param[q]);
  }
  txBuf_Write(CRC);
  if(index_txBuf_R == Buf_max){                    // Buffer am überlaufen hindern  
    index_txBuf_R = 0;
  }
  else{
    index_txBuf_R ++;
  }  
  UCSRB |= (1<<TXCIE);                                // Für Frame TX Complete Interrupt aktivieren
  RXTX_Reg = txBuf[StartIndex];                       // Frame ist aufgebaut/Com starten mit Number of Bytes senden
}

Ok, aber trotz allem danke für die Hilfe.
MFG
P51D

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Patrick B. (p51d)
Datum:

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

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK. nach etwas spielerei habe ich nun etwas interessantes 
herausgefunden:

Dieser Code funktioniert:
RXTX_Reg = txBuf[StartIndex];
UCSRB |= (1<<TXCIE);

Dieser nicht:
UCSRB |= (1<<TXCIE);
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

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Patrick B. (p51d)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.