Forum: Mikrocontroller und Digitale Elektronik Relaiskarte mit ATmega16 - Problem mit falschen Steuerbefehlen


von Andreas W. (a-w)


Lesenswert?

Hallo,

ich habe frei programmierbare Relaiskarten mit ATmega16. Die Relais 
werden über die serielle Schnittstelle RS-232 vom PC aus geschalten.
Die Befehle bestehen aus 9 Zeichen, wobei das erste Zeichen als 
Adressierung einer bestimmten Karte dient, da mehrere davon 
aneinandergereiht werden können. Die restlichen 8 Zeichen sind immer die 
gleichen Befehle zum Steuern der Relais. Zu der Karte gab es eine 
Demosoftware, die ich etwas angepasst habe. (Code gekürzt)
1
/*commands
2
 *  1) cc<0x01>e  - TURN ON relay 1
3
 *  2) cc<0x02>e  - TURN ON relay 2
4
 *  3) cc<0x03>e  - TURN ON relay 3
5
 *  4) cc<0x04>e  - TURN ON relay 4
6
 *  5) cc<0x05>e  - TURN OFF relay 1
7
 *  6) cc<0x06>e  - TURN OFF relay 2
8
 *  7) cc<0x07>e  - TURN OFF relay 3
9
 *  8) cc<0x08>e  - TURN OFF relay 4
10
 *  9) cc<0x09>e  - READ AIN2-2 
11
 *  10) cc<0x1A>e  - READ AIN2-1
12
 *  11) cc<0x1B>e  - READ AIN1-3
13
 *  12) cc<0x1V>e  - READ AIN1-2
14
 *  13) cc<0x1D>e  - READ DIGITAL INPUTS
15
 * erste Stelle gibt Adresse der Platine an*/
16
17
#include <avr/eeprom.h> 
18
#include <ctype.h>
19
#include <stdlib.h>
20
#include <avr/pgmspace.h>
21
#include "avr/io.h"
22
#include<string.h>
23
#include <avr/interrupt.h>
24
#include <util/delay.h>
25
26
//target//
27
const char target='c';
28
29
volatile char var;
30
volatile char flag_address=0;
31
 const unsigned char address=0x05;
32
volatile char buffer[8];
33
volatile unsigned char test;
34
volatile char string_analog[10]="0000000000";
35
unsigned char i=0;
36
#define command_on    flag_register = 0xFF
37
#define  command_off    flag_register = 0x00
38
int digital(void);
39
int action (void);
40
void Print(char Text[80]);
41
void PrintLine();
42
void PORT_Init();
43
void USART_Init( unsigned int ubrr);
44
unsigned char UART_Receive();
45
void UART_Transmit(unsigned char Data);
46
void PrintLn(char Text[80]);
47
volatile unsigned char u=0;
48
volatile   unsigned char abba;
49
 volatile char result[10]="0000000000";
50
volatile long rezultat=0;
51
unsigned char i;
52
volatile char p,d; 
53
volatile char v[10];    
54
55
unsigned  char  flag_register=0;
56
57
#define  __AVR_ATmega16__  1
58
59
#define FOSC 8000000 /* in Hz */
60
#define BAUD 9600
61
#define MYUBRR ((FOSC/16/BAUD)-1)
62
63
char Txt[80];
64
unsigned char Action = 0;
65
volatile uint16_t adcval;
66
volatile struct status {
67
68
  unsigned command:1;
69
  unsigned end:1;
70
  unsigned addres:1;
71
  }status_bits;
72
73
  //
74
char *masiv[]={
75
76
    "C<0x01>E",
77
    "C<0x02>E",
78
    "C<0x03>E",
79
    "C<0x04>E",
80
    "C<0x05>E",
81
    "C<0x06>E",
82
    "C<0x07>E",
83
    "C<0x08>E",
84
    "C<0x09>E",
85
    "C<0x1A>E",
86
    "C<0x1B>E",
87
    "C<0x1V>E",
88
    "C<0x1D>E"
89
        };
90
        
91
/*  Print new line to the Hyper Terminal */  
92
void PrintLine()
93
{
94
  UART_Transmit(10);
95
  UART_Transmit(13);
96
}
97
98
/*  PORT_Init */  
99
void PORT_Init()
100
{
101
  PORTA = 0b00000000;
102
  DDRA = 0b00001111;
103
104
  PORTB = 0b00000000;
105
  DDRB = 0b11111000;    
106
107
  PORTC = 0b00000000;
108
  DDRC = 0b11111111;
109
110
  PORTD = 0b00000000;
111
  DDRD = 0b11100010;    
112
}
113
114
/*  USART_Init */      
115
void USART_Init( unsigned int ubrr)
116
{
117
  /* Set baud rate */
118
  UCSRA = 0x00;
119
  /* Enable receiver and transmitter */
120
  UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
121
  /* Set frame format: 8data, 1stop bit */
122
  UCSRC = 0b10000110;
123
  
124
  UBRRH = (unsigned char)(ubrr>>8);
125
  UBRRL = (unsigned char)ubrr;
126
}
127
128
/* Read ANALOG inputs */
129
void analog(unsigned char channel) {
130
//-- funktioniert--  
131
                  }
132
133
/* Read DIGITAL inputs */
134
int digital(void)  {
135
//-- funktioniert--  
136
          }
137
138
/* action finction */
139
 int action ()
140
{    
141
    char i=0;
142
    result[0]=0;
143
  
144
  for(i=0;i<10;i++) {result[i]=result[i+1];}
145
  result[8]='\0';
146
147
    for(unsigned char zar=0;zar<13;zar++)   {
148
        
149
            if(!(strcasecmp(result,masiv[zar])))
150
                {
151
                switch(zar)  {
152
                case 0:PORTA|=0b00001000;break;
153
                case 1:PORTA|=0b00000100;break;
154
                case 2:PORTA|=0b00000010;break;
155
                case 3:PORTA|=0b00000001;break;
156
                case 4:PORTA&=0b11110111;break;
157
                case 5:PORTA&=0b11111011;break;
158
                case 6:PORTA&=0b11111101;break; 
159
                case 7:PORTA&=0b11111110;break;
160
                case 8:analog(4);break;
161
                case 9:analog(5);break;
162
                case 10:analog(6);break;
163
                case 11:analog(7);break;
164
                case 12:digital();break;
165
                      }          
166
                }
167
                        }
168
    for(unsigned char per=0;per<10;per++)
169
      {result[per]='\0';}                                            
170
    SREG|=0b10000000;              
171
    test=0;
172
    return 0;        
173
}
174
  
175
/*  UART Recieve Interrupt */              
176
ISR(USART_RXC_vect)
177
{   
178
  result[u++]=UDR;
179
  
180
  if(u==9)       {
181
182
  result[9]='\0';
183
  u=0;
184
  SREG&=0b01111111;
185
  var = eeprom_read_byte ((const uint8_t *)(0x00));
186
  if(result[0]==var) 
187
    {
188
    PrintLine();
189
    test=1;
190
    UCSRA&=0b01111111;
191
    return;
192
    }
193
    
194
  SREG|=0b10000000;
195
            }    
196
  UCSRA&=0b01111111;
197
  return 0;
198
}
199
200
/*  UART Receive */  
201
unsigned char UART_Receive()
202
{
203
  if (UCSRA & 0b10000000)
204
    return UDR;
205
  else
206
    return 0;
207
}
208
209
/*  UART Transmit */
210
void UART_Transmit(unsigned char Data)
211
{
212
  
213
  /* Wait for empty transmit buffer */
214
  
215
  while ( !( UCSRA & (1<<UDRE)) ){}
216
  /* Put data into buffer, sends the data */
217
  UDR = Data;
218
}
219
/**************** M A I N ******************/
220
int main(void)
221
{
222
  eeprom_is_ready();
223
  eeprom_write_byte ((uint8_t *)(0x00), (uint8_t)(target));
224
  PORT_Init();
225
  USART_Init (MYUBRR);
226
  test=0;
227
  PrintLine();
228
  sei();
229
    
230
    while(1){
231
        if (test!=0)
232
          {
233
          action();
234
          test=0;
235
          }
236
        }
237
  return 0;        
238
}

Die Software funktioniert soweit eigentlich. Ansprechen bestimmter 
Teilnehmer und die Steuerbefehle funktionieren. Werden jedoch mehr oder 
weniger als 9 Zeichen gesendet, hängt sich der Controller komplett auf 
und nimmt keine Befehle mehr an. Es bleibt nur der Reset. Das Problem 
ist, dass der Fehlerfall auch eintritt, sobald die Leitung zum PC 
getrennt wird. Der offene Eingang (ca. 2cm Leiterbahn bis zum 
Controller) reicht aus um falsche Steuerbefehle zu erzeugen. Selbst 
Pulldown-Widerstände änderten daran nichts. Jetzt wollte ich 
software-seitig versuchen diese Fehler zu ignorieren, aber alle Versuche 
sind bis jetzt gescheitert. Nach meinem Verständnis sollte das Programm 
auf Befehle, die nicht genau 9 Zeichen haben, eigentlich nicht 
reagieren. Wo liegt der Fehler?

von STK500-Besitzer (Gast)


Lesenswert?

Kommentier den Code einfach mal vernünftig - vielleicht kommst du dann 
selber auf den Fehler.

von STK500-Besitzer (Gast)


Lesenswert?

Du solltest einfach noch ein Timeout einbauen.

von Otto (Gast)


Lesenswert?

Die Schleife sollte auch verlassen werden:

a) wenn es mehr als 9 Zeichen sind (bisherige Botschaft wird verworfen)
b) durch einen Timeout (s.o.)

Otto

von Karl H. (kbuchegg)


Lesenswert?

Und PS:

Kannst du mir mal erklären, was dieser Blödsinn
>   SREG&=0b01111111;
hier sein soll?

von Karl H. (kbuchegg)


Lesenswert?

Andreas W. schrieb:

> Werden jedoch mehr oder
> weniger als 9 Zeichen gesendet, hängt sich der Controller komplett auf
> und nimmt keine Befehle mehr an. Es bleibt nur der Reset.

das verwundert nicht wirklich, wenn ...
1
ISR(USART_RXC_vect)
2
{   
3
  result[u++]=UDR;
4
  
5
  if(u==9)       {
6
7
  result[9]='\0';
8
  u=0;
9
  SREG&=0b01111111;
10
  var = eeprom_read_byte ((const uint8_t *)(0x00));
11
  if(result[0]==var) 
12
    {
13
    PrintLine();
14
    test=1;
15
    UCSRA&=0b01111111;
16
    return;
17
    }
18
    
19
  SREG|=0b10000000;
20
            }    
21
  UCSRA&=0b01111111;
22
  return 0;
23
}
... deine einzige Protokollvereinbarung darin besteht, dass jeweils 9 
Zeichen zu einem Befehl zusammengefasst werden. Das ist untauglich um 
auch nur den einfachsten Fehler abzufangen bzw. neu zu synchronisieren.


PS: Das hast du nicht besonders schlau gemacht, dass du im ersten 
Zeichen die 'Adressierung' der jeweiligen Karte untergebracht hast. 
Besser wäre es gewesen, wenn du als erstes Zeichen ein immer gleiches 
Zeichen benutzt hättest, welches in den restlichen Kommandos nicht 
vorkommt. Denn dann hättest du hier in der Empfangsroutine vereinbaren 
können, dass wenn immer dieses Zeichen hereinkommt, ein neues Kommando 
beginnt. Dann sammelst du solange Zeichen bis
* das allen Kommandos gemeinsame Abschlusszeichen 'E' eintrifft
* oder 9 Zeichen beisammen sind.

Und natürlich: Siehst du beim Empfänger dieses speziell vereinbarte 
Startzeichen, dann wird sofort alles bisher empfangene verworfen und die 
Sammlerei von Buchstaben zu einem Kommando beginnt wieder von vorne.

Kriterium für die Auswertung sind
* ist das erste Zeichen im String das von dir vereinbarte Sonderzeichen?
* ist das letzte Zeichen im String ein 'E'?
* Hat der String eine komplette Länge von 9 Zeichen?
nur dann, wenn all diese Kriterien erfüllt sind, dann kann es sich um 
ein gültiges Kommando handeln.
Du holst dir aus dem String von der 2. Position den Empfänger raus und 
vergleichst mit dem eigenen Empfänger
Und wenn auch das noch übereinstimmt, dann holst du dir aus dem String 
den Kommandoanteil raus und wertest ihn aus.

Und dann kannst du auch nach Belieben das Kabel abziehen und wieder 
anstecken, ohne dass etwas durcheinander kommt.

von Andreas W. (a-w)


Lesenswert?

Zunächst mal Danke für die schnellen Antworten.
Das Programm war wie schon gesagt eine Demosoftware. Der Teil mit dem 
UART-Interrupt ist original. Ursprünglich streikte die Software schon, 
sobald das erste Zeichen für die Adresse nicht stimmte. Das konnte ich 
aber beheben. Es blieb nur der Fehler mit Befehlen größer/kleiner 9 
Zeichen.
Ich dachte im Abschnitt der UART-Interrupt muss dazu nur noch die 
Abfrage eingebaut werden, dass wenn weniger als 9 Zeichen kommen nichts 
gemacht wird außer das Resultat-Array zu leeren und u=0 zusetzen. Habe 
ich auch erfolglos probiert.

Prinzipiell ist die Auswertung doch nicht schlecht. Es wird als erstes 
geprüft, ob die Adresse stimmt und dann der Rest weitergeprüft. Ob ein 
Sonderzeichen oder ein beliebiges anders Zeichen als erstes geprüft wird 
ist egal.
Die Befehlslänge mit 9 Zeichen wird auch abgefragt.
Problematisch habe ich nur gesehen, dass der "Gegenfall" nicht 
abgesichert wird. Das bei weniger als 9 Zeichen das Resultat-Array 
geleert und u Null gesetzt wird, damit alles für das Empfangen neuer 
Zeichen vorbereitet ist.
1
> result[u++]=UDR;
 füllt sich ja mit jedem empfangen Zeichen. Bei 9 Zeichen mit richtiger 
Adresse wird der Befehl ausgewertet und das Array gelöscht. Besteht der 
Befehl nur aus 3 Zeichen, füllt sich das Array mit 3 Zeichen und es 
passiert nichts. Es wird auch nichts gelöscht. Damit bleiben 3 Zeichen 
im Array und u=2. Kommt jetzt ein richtiger Befehl mit 9 Zeichen, dann 
werden die ersten 6 Zeichen übernommen, damit ist u=9 und Befehl wird 
ausgewertet und das Resultat-Array gelöscht. Und es werden die 
restlichen 3 Zeichen des eigentlich richtigen Befehls gleich wieder 
eingelesen. Somit kommt kein richtiger Befehl mehr an.

Ich habe es probiert und nach zu kurzen Befehlen solange die Adresse als 
einzelne Zeichen gesendet, bis wieder eine Antwort (neue Zeile im 
Hyperterminal)kam, da die Adresse stimmte und genau 9 Zeichen vorhanden 
waren. result und u wurden gelöscht, sodass richtige Befehle wieder 
angenommen wurden.

Da bei 9 Zeichen alles wieder zum Empfangen zurückgesetzt wird, muss nur 
das Detektieren von zu wenige empfangenen Zeichen in einem bestimmten 
Zeitraum gemacht werden. Timeout ist da das richtige, aber ich weiß 
nicht, wie und wo ich das unterbringen muss. Timeout wäre ja ein 
Interrupt im UART-Interrupt.
Das geht schon nicht, weil mit
1
> SREG&=0b01111111;
 die Interrupts vorübergehend ausgeschalten werden. Wobei ich mich auch 
schon gefragt habe wozu das gut sein soll.

Ich werde mal nach Infos stöbern und versuchen den Timeout einzubauen. 
Wäre nett wenn mir jemand hier vielleicht direkt ein Tipp geben könnte, 
wie man das macht.
Im Prinzip muss ich einen Interrupt-Timer bauen, den ich aktiviere 
sobald etwas empfangen wird. Ist der Timer abgelaufen und noch kein 
neuntes Zeichen da, wird der Befehl verworfen. Umgekehrt muss der Timer 
zurückgesetzt werden, sobald das neunte Zeichen empfangen wurde.

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.