Forum: Compiler & IDEs Auswertung RS232-Befehle


von Marco (Gast)


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

von Joe D. (kosmonaut_pirx)


Lesenswert?

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

von Marco (Gast)


Lesenswert?

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

von Dirk (Gast)


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

von Dirk (Gast)


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.
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
//#include "uart.h"
4
5
6
#define BUFLEN  20
7
8
#define COMMAND0 1
9
#define COMMAND1 2
10
#define COMMAND2 3
11
#define COMMAND3 4
12
13
14
volatile unsigned char rx_buffer[BUFLEN];              // Receive Buffer
15
Array
16
volatile unsigned char rx_ptr;                      // uchar pointer
17
volatile unsigned char wrx_ptr;                      // uchar pointer
18
19
20
21
22
SIGNAL(SIG_UART_RECV)
23
{
24
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
25
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
26
    wrx_ptr = 0;
27
  }
28
}
29
30
31
int main (void)
32
{
33
  if(wrx_ptr != rx_ptr){                      
34
    switch (rx_ptr)              // State Maschine to get data
35
    {
36
  
37
      case COMMAND0:  // do anything
38
                      break;
39
                      
40
      case COMMAND1:  // do anything
41
                      break;
42
                      
43
      case COMMAND3:  // do anything
44
                      break;
45
                      
46
    }
47
48
  }
49
return 0;
50
}

von Dirk (Gast)


Lesenswert?

Zeile 33 sollte so lauten
1
switch (rx_buffer[rx_ptr])

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

von Marco (Gast)


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

von Sonic (Gast)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Dirk (Gast)


Lesenswert?

Hi,

man koennte es so machen (ist ungetestet und ich hoffe es ist kein
Fehler drin)
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
//#include "uart.h"
4
5
6
#define BUFLEN  20
7
8
#define COMMAND0 1
9
#define COMMAND1 2
10
#define COMMAND2 3
11
#define COMMAND3 4
12
13
#define READY   0
14
15
16
typedef unsigned char  u8;
17
typedef unsigned short u16;
18
typedef unsigned long  u32;
19
 
20
typedef struct commands 
21
{
22
  u8  cmd;
23
  u8  data0;
24
  u8  data1;
25
  u8  data2;
26
  u8  data3;
27
}commands;
28
29
volatile unsigned char rx_buffer[BUFLEN];              // Receive Buffer
30
Array
31
volatile unsigned char rx_ptr;                      // uchar pointer
32
volatile unsigned char wrx_ptr;                      // uchar pointer
33
volatile unsigned char status_byte;                      // uchar pointer
34
35
enum states {byte0=0, byte1, byte2, byte3, byte4}state;
36
37
38
39
40
SIGNAL(SIG_UART_RECV)
41
{
42
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
43
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
44
    wrx_ptr = 0;
45
  }
46
}
47
48
49
int main (void)
50
{
51
52
  struct commands rs232data,*p;
53
  p = &rs232data;  
54
  
55
  
56
  if(wrx_ptr != rx_ptr){                      
57
    
58
    
59
    switch (state)
60
    {
61
      case byte0:  p->cmd = rx_buffer[rx_ptr];
62
                  break;
63
                  
64
      case byte1:  p->data0 = rx_buffer[rx_ptr];
65
                  break;
66
      
67
      case byte2:  p->data1 = rx_buffer[rx_ptr];
68
                  break;
69
                  
70
      case byte3:  p->data2 = rx_buffer[rx_ptr];
71
                  break;
72
                  
73
      case byte4:  p->data3 = rx_buffer[rx_ptr];
74
                  status_byte |= (1<<READY);
75
                  break;
76
                  
77
      }
78
      state++;
79
      
80
      
81
      if (status_byte & (1<<READY)){
82
        switch (p->cmd)            
83
        {
84
  
85
        case COMMAND0:  // do anything
86
                        break;
87
                      
88
        case COMMAND1:  // do anything
89
                        break;
90
                      
91
        case COMMAND3:  // do anything
92
                        break;
93
                      
94
        }
95
       status_byte &= ~(1<<READY);
96
    
97
    }
98
  }
99
return 0;
100
}

von Marco (Gast)


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

von Dirk (Gast)


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

von Dirk (Gast)


Lesenswert?

So, in meiner Version waren ein paar Fehler (Lesezeiger wurde nicht
erhoeht , Commandasuwertung nur wenn Schreibpointer erhoeht wurde).
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
//#include "uart.h"
4
5
6
#define BUFLEN  20
7
8
#define COMMAND0 1
9
#define COMMAND1 2
10
#define COMMAND2 3
11
#define COMMAND3 4
12
13
#define READY   0
14
15
16
typedef unsigned char  u8;
17
typedef unsigned short u16;
18
typedef unsigned long  u32;
19
 
20
typedef struct commands 
21
{
22
  u8  cmd;
23
  u8  data0;
24
  u8  data1;
25
  u8  data2;
26
  u8  data3;
27
}commands;
28
29
volatile unsigned char rx_buffer[BUFLEN];              // Receive Buffer
30
Array
31
volatile unsigned char rx_ptr;                      // uchar pointer
32
volatile unsigned char wrx_ptr;                      // uchar pointer
33
volatile unsigned char status_byte;                      // uchar pointer
34
35
enum states {byte0=0, byte1, byte2, byte3, byte4}state;
36
37
38
39
40
SIGNAL(SIG_UART_RECV)
41
{
42
  rx_buffer[wrx_ptr] = UDR;            // put new byte to buffer
43
  if (++wrx_ptr >= BUFLEN) {          // erhoehe den lese Buffer
44
    wrx_ptr = 0;
45
  }
46
}
47
48
49
void get_data(void)
50
{
51
  if(wrx_ptr != rx_ptr){                      
52
    switch (state)
53
    {
54
      case byte0:  p->cmd = rx_buffer[rx_ptr];
55
                  break;
56
                  
57
      case byte1:  p->data0 = rx_buffer[rx_ptr];
58
                  break;
59
      
60
      case byte2:  p->data1 = rx_buffer[rx_ptr];
61
                  break;
62
                  
63
      case byte3:  p->data2 = rx_buffer[rx_ptr];
64
                  break;
65
                  
66
      case byte4:  p->data3 = rx_buffer[rx_ptr];
67
                  status_byte |= (1<<READY);
68
                  break;
69
                  
70
    }
71
    state++;
72
    if (++rx_ptr>=BUFLEN){
73
          rx_ptr = 0;
74
    }
75
  }
76
}  
77
78
void auswertung (void)
79
{
80
    if (status_byte & (1<<READY)){
81
    switch (p->cmd)            
82
    {
83
  
84
    case COMMAND0:  // do anything
85
                    break;
86
                      
87
    case COMMAND1:  // do anything
88
                    break;
89
                      
90
    case COMMAND3:  // do anything
91
                    break;
92
                      
93
    }
94
    status_byte &= ~(1<<READY);
95
    
96
  }
97
}
98
99
100
101
int main (void)
102
{
103
104
  struct commands rs232data,*p;
105
  p = &rs232data;  
106
  
107
  get_data();
108
  auswertung();
109
  
110
return 0;
111
}

von Marco (Gast)


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!

von Dirk (Gast)


Lesenswert?

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

von Marco (Gast)


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

von Olli (Gast)


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

von Ludger (Gast)


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 :-)

von Olli (Gast)


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

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.