www.mikrocontroller.net

Forum: Compiler & IDEs Auswertung RS232-Befehle


Autor: Marco (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade dabei für ein etwas grösseres Projekt eine Kommunikation
mit einem AVR (ATMEGA 8535) auf die Beine zustellen.

Ich habe bereits Code für die Auswertung geschrieben, allerdings
gefallen mir ein paar Dinge daran noch nicht so ganz. Weiss im Moment
aber auch nicht wie ich es besser machen kann. Hier erhoffe ich mir
Feedback/Hilfe von euch.

1. Für jeden Befehl den der Controller erkennen soll benutze ich ein
Array. Am liebsten währe mir ein grosses Array wo alle Strings
drinstehen.

2. Die Auswertung mache ich zurzeit mit strcmp_P(...). Momentan
übermittle ich auch noch keine Werte, d.h. ich müsste sowieso auf
strstr_P(...) ausweichen und mit atoi(...) den Wert rausfischen.

Ist das so gängige Praxis, oder gibt es da bessere Lösungen?

Hier mal ein wenig Code wie es im Moment aussieht:

char cmd_1[] PROGMEM = "Befehl_1";
char cmd_2[] PROGMEM = "Befehl_2";
char cmd_3[] PROGMEM = "Befehl_3";
char cmd_4[] PROGMEM = "Befehl_4";

while(1) {
    if (cmd_rcv == TRUE) {
        if (strcmp_P(cmd, cmd_1) == 0) {
            do_something(...);
        }
        else if (strcmp_P(cmd, cmd_2) == 0) {
            do_something(...);
        }
        else if (strcmp_P(cmd, cmd_3) == 0) {
            do_something(...);
        }
        else if (strcmp_P(cmd, cmd_4) == 0) {
            do_something(...);
        }
        else
            send_text("Fehler\n\r");
        cmd_rcv = FALSE; ISR
    }
    if (--delay == 0) {
        ausgabe(...);
    }
}

Der Empfang der Befehle wird in einer ISR(...) erledigt, dort kommt
auch das Flag 'cmd_rcv' und die String-Variable 'cmd' her.

Bin mal gespannt auf eure Vorschläge/Feedback.

MfG
Marco

Autor: Joe Die (kosmonaut_pirx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
hast du die anforderung, das die befehle strings sein müssen?
bye kosmo

Autor: Marco (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Muss nicht unbedingt sein, aber was sollte anderes über RS232 kommen?

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

müssen es unbedingt "String" Commandos sein? Ich finde es besser
einfach ein uChar als Commando zu nehmen, somit hast du mit nur einem
Byte 256 Befehle.

Gruß,
DIrk

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

hier ein kleines Beispiel. Die RS232 Daten werden erstmal in ein FIFO
geschrieben und in der Mainroutine wird geprueft, ob der Schreibezeiger
!= Lesezeiger ist. Die Auswertung des Commando wird dann in der Switch
Anweisung bearbeitet.
#include <avr/io.h>
#include <avr/interrupt.h>
//#include "uart.h"


#define BUFLEN  20

#define COMMAND0 1
#define COMMAND1 2
#define COMMAND2 3
#define COMMAND3 4


volatile unsigned char rx_buffer[BUFLEN];              // Receive Buffer
Array
volatile unsigned char rx_ptr;                      // uchar pointer
volatile unsigned char wrx_ptr;                      // uchar pointer




SIGNAL(SIG_UART_RECV)
{
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
    wrx_ptr = 0;
  }
}


int main (void)
{
  if(wrx_ptr != rx_ptr){                      
    switch (rx_ptr)              // State Maschine to get data
    {
  
      case COMMAND0:  // do anything
                      break;
                      
      case COMMAND1:  // do anything
                      break;
                      
      case COMMAND3:  // do anything
                      break;
                      
    }

  }
return 0;
}



Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zeile 33 sollte so lauten
switch (rx_buffer[rx_ptr]) 

und natuerlich muss der Lesepointer jedesmal erhoeht werden.
if (++rx_ptr>=BUFLEN)
{      
 rx_ptr = 0;
}

Autor: Marco (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok so geht es auch, habe dann 255 Befehle. Ich muss aber auch Werte
übertragen.

Das ganze soll zunächst als Fernsteuerung für einen Roboter dienen.
Habe also mehrere Aktoren die Angesteuert werden wollen. Folgende
Möglich keit hätte ich dann.

a = Servo 1
b = Servo 2
c = Servo 3
d = Motor

und der Wert von 0 bis 255 in ASCII codiert, also nur 2 Bytes.

Oder

S = Servo
dann Kanal von 1 bis 4 und den Wert als ASCII codiert. z.B. S1a

wie würdest du sowas dann mit deiner Routine realisieren ?

MfG
Marco

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie wär's wenn du ein Datenpaket festgelegter Länge schickst, evtl. mit
CRC-Prüfung, dann kannst du die Bytes genau zuordnen und auswerten.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für lesbare Befehlstexte kann man immer noch auf richtige Parser
umsteigen, also irgendwas mit lex und yacc.  Ab einem bestimmten
Umfang werden derartige Parser (die intern als state machines
implementiert sind) kleiner und schneller als aufwändige
Zeichenkettenvergleiche.

Für deinen Fall ist aber wahrscheinlich die Variante mit dem
1-Byte-Befehl und einem festen Befehlsformat zweckmäßiger.

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

man koennte es so machen (ist ungetestet und ich hoffe es ist kein
Fehler drin)
#include <avr/io.h>
#include <avr/interrupt.h>
//#include "uart.h"


#define BUFLEN  20

#define COMMAND0 1
#define COMMAND1 2
#define COMMAND2 3
#define COMMAND3 4

#define READY   0


typedef unsigned char  u8;
typedef unsigned short u16;
typedef unsigned long  u32;
 
typedef struct commands 
{
  u8  cmd;
  u8  data0;
  u8  data1;
  u8  data2;
  u8  data3;
}commands;

volatile unsigned char rx_buffer[BUFLEN];              // Receive Buffer
Array
volatile unsigned char rx_ptr;                      // uchar pointer
volatile unsigned char wrx_ptr;                      // uchar pointer
volatile unsigned char status_byte;                      // uchar pointer

enum states {byte0=0, byte1, byte2, byte3, byte4}state;




SIGNAL(SIG_UART_RECV)
{
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
    wrx_ptr = 0;
  }
}


int main (void)
{

  struct commands rs232data,*p;
  p = &rs232data;  
  
  
  if(wrx_ptr != rx_ptr){                      
    
    
    switch (state)
    {
      case byte0:  p->cmd = rx_buffer[rx_ptr];
                  break;
                  
      case byte1:  p->data0 = rx_buffer[rx_ptr];
                  break;
      
      case byte2:  p->data1 = rx_buffer[rx_ptr];
                  break;
                  
      case byte3:  p->data2 = rx_buffer[rx_ptr];
                  break;
                  
      case byte4:  p->data3 = rx_buffer[rx_ptr];
                  status_byte |= (1<<READY);
                  break;
                  
      }
      state++;
      
      
      if (status_byte & (1<<READY)){
        switch (p->cmd)            
        {
  
        case COMMAND0:  // do anything
                        break;
                      
        case COMMAND1:  // do anything
                        break;
                      
        case COMMAND3:  // do anything
                        break;
                      
        }
       status_byte &= ~(1<<READY);
    
    }
  }
return 0;
}

Autor: Marco (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sieht sehr gut aus, so werde ich es machen.

Die switch(state) werde ich aber lieber in die Interrupt-Routine
unterbringen, damit ich Empfang und Verarbeitung getrennt habe.

Sieht dann wohl so aus:

<code>
#include <avr/io.h>
#include <avr/interrupt.h>

#define BUFLEN  20

#define SERVO    'S'
#define COMMAND1 2
#define COMMAND2 3
#define COMMAND3 4

#define CHANNEL1 '1'
#define CHANNEL2 '2'
#define CHANNEL3 '3'
#define CHANNEL4 '4'

#define READY    0

typedef unsigned char  u8;
typedef unsigned short u16;
typedef unsigned long  u32;
typedef struct commands {
  u8  cmd;
  u8  data0;
  u8  data1;
  u8  data2;
  u8  data3;
}commands;

volatile unsigned char rx_buffer[BUFLEN];
volatile unsigned char rx_ptr;
volatile unsigned char wrx_ptr;
volatile unsigned char status_byte;

enum states {byte0=0, byte1, byte2, byte3, byte4}state;

SIGNAL(SIG_UART_RECV){
  rx_buffer[wrx_ptr] = UDR;
  if (++wrx_ptr >= BUFLEN) {
      wrx_ptr = 0;
  }

  switch (state) {
    case byte0:
      p->cmd = rx_buffer[rx_ptr];
      break;
    case byte1:
      p->data0 = rx_buffer[rx_ptr];
      break;
    case byte2:
      p->data1 = rx_buffer[rx_ptr];
      break;
    case byte3:
      p->data2 = rx_buffer[rx_ptr];
      break;
    case byte4:
      p->data3 = rx_buffer[rx_ptr];
      status_byte |= (1<<READY);
      break;
  }
  state++;
}

int main (void){
  struct commands rs232data,*p;

  p = &rs232data;

  if (status_byte & (1<<READY)) {
    switch (p->cmd) {
      case COMMAND0:
        // Beispiel für Ausertung data0
        switch (p->data0) {
          case CHANNEL1:
            stelle_Servo(p->data1);
            break;
        }
        break;
      case COMMAND1:  // do anything
        break;
      case COMMAND2:  // do anything
        break;
      case COMMAND3:  // do anything
        break;
    }

    status_byte &= ~(1<<READY);
  }

  return 0;
}
</code>

Ist das so OK oder ist die Interrupt-Routine dann zu lang?

MfG
Marco

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich wuerde die State Maschine nicht in der ISR laufen lassen, sondern
irgendwo in der Main prüfen ob neue Zeichen im Buffer sind, weil so
kannst du das komplette FIFO auch rausnehmen.

GRuß,
Dirk

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, in meiner Version waren ein paar Fehler (Lesezeiger wurde nicht
erhoeht , Commandasuwertung nur wenn Schreibpointer erhoeht wurde).
#include <avr/io.h>
#include <avr/interrupt.h>
//#include "uart.h"


#define BUFLEN  20

#define COMMAND0 1
#define COMMAND1 2
#define COMMAND2 3
#define COMMAND3 4

#define READY   0


typedef unsigned char  u8;
typedef unsigned short u16;
typedef unsigned long  u32;
 
typedef struct commands 
{
  u8  cmd;
  u8  data0;
  u8  data1;
  u8  data2;
  u8  data3;
}commands;

volatile unsigned char rx_buffer[BUFLEN];              // Receive Buffer
Array
volatile unsigned char rx_ptr;                      // uchar pointer
volatile unsigned char wrx_ptr;                      // uchar pointer
volatile unsigned char status_byte;                      // uchar pointer

enum states {byte0=0, byte1, byte2, byte3, byte4}state;




SIGNAL(SIG_UART_RECV)
{
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
    wrx_ptr = 0;
  }
}


void get_data(void)
{
  if(wrx_ptr != rx_ptr){                      
    switch (state)
    {
      case byte0:  p->cmd = rx_buffer[rx_ptr];
                  break;
                  
      case byte1:  p->data0 = rx_buffer[rx_ptr];
                  break;
      
      case byte2:  p->data1 = rx_buffer[rx_ptr];
                  break;
                  
      case byte3:  p->data2 = rx_buffer[rx_ptr];
                  break;
                  
      case byte4:  p->data3 = rx_buffer[rx_ptr];
                  status_byte |= (1<<READY);
                  break;
                  
    }
    state++;
    if (++rx_ptr>=BUFLEN){
          rx_ptr = 0;
    }
  }
}  

void auswertung (void)
{
    if (status_byte & (1<<READY)){
    switch (p->cmd)            
    {
  
    case COMMAND0:  // do anything
                    break;
                      
    case COMMAND1:  // do anything
                    break;
                      
    case COMMAND3:  // do anything
                    break;
                      
    }
    status_byte &= ~(1<<READY);
    
  }
}



int main (void)
{

  struct commands rs232data,*p;
  p = &rs232data;  
  
  get_data();
  auswertung();
  
return 0;
}

Autor: Marco (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Argh, jetzt wo du FIFO erwähnst, habe ich nochmal genauer hin gesehen.

Da is mir doch glatt entgangen das du ein Schreibzeiger (wrx) und einen
Lesezeiger(rx).

Ok dan macht das in der ISR wenig Sinn!

Autor: Dirk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Man koennte auch die beiden if abfragen aus den funktionen nehmen dann
spart man sich die Sprungbefehle falls sich nix geaendert hat.

Autor: Marco (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe es jetzt ausprobiert, funktioniert wunderbar.

2 Fehler habe ich aber doch noch entdeckt.

1. commands rs232data,*p muss global sein, sonst kann die ISR und die
Funktionen nicht drauf zugreifen.

2. state muss irgendwo zurück gesetzt werden. Ich habe es in
Auswertung, direkt nach Zeile 95 gemacht.

Vielen Dank für deine Hilfe
MfG
Marco

Autor: Olli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo
bin auf diesen Beitrag gestossen auf der Suche nach einer Lösung wie ich 
über die RS232 Schnittstelle mit meinem µC "sprechen" kann.

Also verstehe ich das richtig?
[c]
#include <avr/io.h>
#include <avr/interrupt.h>
//#include "uart.h"


#define BUFLEN  20

#define COMMAND0 1
#define COMMAND1 2
#define COMMAND2 3
#define COMMAND3 4

#define READY   0


typedef unsigned char  u8;
typedef unsigned short u16;
typedef unsigned long  u32;

struct commands rs232data,*p;

typedef struct commands
{
  u8  cmd;
  u8  data0;
  u8  data1;
  u8  data2;
  u8  data3;
}commands;

volatile unsigned char rx_buffer[BUFLEN];       // Receive Buffer Array
volatile unsigned char rx_ptr;                  // uchar pointer
volatile unsigned char wrx_ptr;                 // uchar pointer
volatile unsigned char status_byte;             // uchar pointer

enum states {byte0=0, byte1, byte2, byte3, byte4}state;




SIGNAL(SIG_UART_RECV)
{
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
    wrx_ptr = 0;
  }
}


void get_data(void){
  if(wrx_ptr != rx_ptr){
    switch (state)    {
      case byte0:  p->cmd = rx_buffer[rx_ptr];
                  break;

      case byte1:  p->data0 = rx_buffer[rx_ptr];
                  break;

      case byte2:  p->data1 = rx_buffer[rx_ptr];
                  break;

      case byte3:  p->data2 = rx_buffer[rx_ptr];
                  break;

      case byte4:  p->data3 = rx_buffer[rx_ptr];
                  status_byte |= (1<<READY);
                  break;
    }
    state++;
    if (++rx_ptr>=BUFLEN){
          rx_ptr = 0;
    }
  }
}

void auswertung (void){
    if (status_byte & (1<<READY)){
      switch (p->cmd){
      case COMMAND0:
    // do anything
        break;
  case COMMAND1:
    // do anything
        break;
      case COMMAND3:
    // do anything
        break;
      }
  state = 0;
    status_byte &= ~(1<<READY);
  }
}



int main (void){

  p = &rs232data;

  get_data();
  auswertung();

return 0;
}
[\c]

So muss der Code aussehen um mit meinem µC zu "sprechen"?
Die String-Befehle heißen "COMMAND0" - "COMMAND3" die ich an den µC 
senden muss?
Wie läuft das Programm ab?
Also ich kenne es so das das Programm in einer Endlosschleife läuft und 
in dieser Befehle abarbeitet.(Bin absoluter µC neuling).
Ich gehe gerade davon aus das es über ein Interupt gestartet wird aber 
was muss ich dann machen das mein µC wenn der PC über RS232 
angeschlossen ist die Befehle abarbeitet welche ihm der PC liefert und 
wenn der PC nicht mehr angeschlossen ist er seine eigenen Befehle 
abarbeitet? Wenn es nicht anders möglich ist auch das ich einen Schalter 
habe mit dem ich bestimmen kann ob der µC seine eigenen oder die PC 
operationen machen soll.

Danke schonmal
Olli

Autor: Ludger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

guck mal hier hinein:

Beitrag "Re: uShell - ein universeller Parser für uCs"

Dort gibt es in einer Antwort von Peter Dannegger (peda) ein COMMAND.C 
als Anhang. Das muesste Dir weiterhelfen :-)

Autor: Olli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi
danke mein Problem ist quasi einer seiner ersten Sätze. Ich würde es 
gerne in einen ATmega8 reinbekommen und ohne großen aufwand das ganze 
verwenden und wiegesagt ich bin absoluter neuling was µC Programmierung 
angeht daher weiß ich nicht ob ich es falsch Verstanden habe aber so wie 
ich das erkenne macht der programmtext nichts anderes als eingehende 
Signale zu verarbeiten.

Meine Frage war eher ob der Quelltext so funktionieren kann, dass ich 
wahlweise den µC die Arbeit machen lassen kann (z.B. einen Motor 
ansteuern  oder auf Eingangssignale von Tastern reagieren kann)
und wenn ich an die RS232 Schnittstelle einen PC anschließe der µC die 
Signal vom PC verarbeitet.

Bzw. wie ich es Implementieren kann das der Quelltext und somit der µC 
das kann.

Ich gehe im Moment davon aus das der Quelltext der hier steht 
folgendermaßen funktioniert:

der µC bekommt über die RS232 Schnittstelle ein Signal.
Dieses löst ein Interrupt aus und lässt die Auswertung dieses Signals 
starten.
Nach der Auswertung macht er irgendetwas was in der Case-Anweisung 
steht.
Danach legt er sich schlafen bis das nächste Signal kommt.

Lieg ich bis dahin richtig?

Was ich jetzt möchte ist, dass der µC auf irgendeine Weise mitbekommt ob 
ein PC angeschlossen ist (meinetwegen auch über einen Schalter o.ä.)
und wenn kein PC angeschlossen ist dann soll er sein eigenes Programm 
abarbeiten.

THX und LG
Olli

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.