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
hallo, hast du die anforderung, das die befehle strings sein müssen? bye kosmo
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
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 | }
|
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 | }
|
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
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.
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.
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 | }
|
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
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
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 | }
|
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!
Man koennte auch die beiden if abfragen aus den funktionen nehmen dann spart man sich die Sprungbefehle falls sich nix geaendert hat.
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
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
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 :-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.