www.mikrocontroller.net

Forum: GCC Auswertung RS232-Befehle

Autor: Marco (Gast)
Datum:

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:

hallo,
hast du die anforderung, das die befehle strings sein müssen?
bye kosmo
Autor: Marco (Gast)
Datum:

Muss nicht unbedingt sein, aber was sollte anderes über RS232 kommen?
Autor: Dirk (Gast)
Datum:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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




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 erkennst du die Nutzungsbedingungen an.

webmaster@mikrocontroller.netImpressumNutzungsbedingungenWerbung auf Mikrocontroller.net