Forum: Mikrocontroller und Digitale Elektronik PIC18F452 Daten auswerten


von Michi (Gast)


Lesenswert?

Hallo zusammen

Zur Aufgabenstellung:
Ich muss einen PIC18F452 für eine Überwachung programmieren. (im Grunde 
einen kleinen Watchdog).
Dieser muss von der seriellen Schnittstelle die Daten auswerten, welche 
er empfängt. Stimmen diese (Status-)Daten nicht mehr, wird einfach ein 
Alarmausgang geschaltet. Als Eingang zum PIC verwende ich einen MAX202.

Das Problem liegt daran, dass ich keinen String (mit Abschluss CR) 
empfange.
Somit muss ich doch jede Zeichenkette, welche sporadisch an meinen PIC 
gesendet werden, auswerten.
Und weil zusätzlich noch andere Daten über die RS232 Schnittstelle 
gesendet werden, muss ich auf ein spezielles Muster im Datenstring 
ausschau halten.

Wie ich das gesehen habe, werden über die serielle Schnittstelle nur 
reine HEX-Werte geschickt.
Das Statusmuster sieht folgendermassen aus: 02 03 00 FF 00 FF
Sollte nach 02 03 00 FF keine 00 erscheinen, muss der Alarmausgang 
geschaltet werden.

Nach meinen Kenntnisse benutzt man doch hierfür einen #int_rda und dann 
den Befehl getc. Leider bin ich mir nicht sicher, ob ich die Konfig 
richtig eingestellt habe.

Hier einmal ein Teil des Codes
1
#include <18F452.H>
2
#device ADC=10 *=16 
3
// #device ICD=TRUE;
4
 #fuses NODEBUG
5
// #fuses DEBUG
6
#fuses  HS,NOPROTECT ,PUT,NOLVP  
7
#fuses NOWDT,BROWNOUT,BORV42
8
#use delay(clock=20000000)
9
#use FAST_IO(C)
10
#use rs232(baud=115200,Xmit=PIN_C6, rcv=PIN_C7,stream=HostPC)  
11
12
#opt 9
13
14
15
void main(void);
16
17
#include <input.c>
18
#include "Define.h"
19
#include <stdlib.h>
20
21
22
#int_rda
23
void IntRS(void){
24
//int RSstatus=0;
25
disable_interrupts(int_rda);  // Interrupts RS232 aus
26
27
28
29
// Serielle Schnittestelle auswerten
30
  getc(HostPC);
31
  if (HostPC==0x02){          // Kontrollbyte 1 (Wert 02)
32
    getc(HostPC);
33
    RSstatus=1;          
34
    if (HostPC==0x03){         // Status RS232 OK
35
      getc(HostPC);
36
      RSstatus=2;
37
       if (HostPC==0x00){
38
        getc(HostPC);
39
        RSstatus=3;
40
        if (HostPC==0xFF){
41
          getc(HostPC);
42
          RSstatus=4;
43
          if (HostPC==0x00){  // CES-Status OK
44
             Alarm=0;
45
          }
46
          else Alarm=1;
47
        }
48
      }
49
        }
50
  }
51
52
}                    // Ende IntRS


Vielen Dank für eure Hilfe

von Severino R. (severino)


Lesenswert?

Ich nehme an, der Empfang eines Zeichens generiert einen Interrupt und 
IntRS ist Deine Interrupt-Service-Routine.

Darin liest Du aber mehrere Zeichen, die ev. noch gar nicht eingetroffen 
sind.
Du solltest in IntRS eher eine Statemachine programmieren, jeweils genau 
ein Zeichen liest und RSstatus hochzählt.
Ausserdem brauchst Du einen Timer um einen Timeout zu generieren, wenn 
nach Empfang von FF innert einer von Dir zu definierenden Zeit kein 00 
folgt.

Alles klar?

Severino

von Michi (Gast)


Lesenswert?

Hallo Severino

>Ich nehme an, der Empfang eines Zeichens generiert einen Interrupt und
>IntRS ist Deine Interrupt-Service-Routine.

Das stimmt genau. Dies ist meine Interrupt-Service-Routine.

Mein Problem liegt jetzt genau in dieser Statemachine, welche nur 1 
Zeichen liest. Nach dürchstöbern von mehreren Literaturen komme ich 
nicht auf den Befehl, mit welchem ich nur 1 Zeichen einlesen kann. 
Vielleicht kannst du mir hier weiterhelfen.

>Ausserdem brauchst Du einen Timer um einen Timeout zu generieren, wenn
>nach Empfang von FF innert einer von Dir zu definierenden Zeit kein 00
>folgt.

Dieser Timer für den Timeout habe ich implementiert.


Danke für deine Bemühungen

von Severino R. (severino)


Lesenswert?

getc() liest meines Wissens genau ein Zeichen ein.
(Was geschieht, wenn gar kein Zeichen da ist, weiss ich nicht auswendig, 
ich denke, getc() wartet dann bis eines eintrifft).

IntRS liest mehrere Zeichen ein, solange es sich um die erwarteten 
Zeichen handelt und sie in der richtigen Reihenfolge eintreffen.
Was andernfalls geschieht, bleibt offen (kein else). Alarm ist dann 
undefiniert (übrigens, wo wird die Variable definiert?)
Mindestens sollte Alarm vor dem ersten if auf 1 gesetzt werden, damit es 
(nur) im korrekten Fall gegen Ende auf 0 zurückgesetzt wird.

Bleibt das Problem, dass Du in der ISR mehrere getc() hast, und das 
Programm dort wartet, bis ein weiteres Zeichen eintrifft. In einer ISR 
sollte man nie warten.

Prüfe mal in der Doku zu getc(), was geschieht, wenn gar kein Zeichen 
ansteht. Wenn getc() ewig wartet, brauchst Du eine Funktion, die Dir 
sagt, ob überhaupt ein Zeichen im Empfangspuffer steht. Dann könntest Du 
das Ganze sogar ohne Interrupts lösen.

Severino

von Michi (Gast)


Lesenswert?

getc() liest ein Zeichen, dies funktioniert jetzt auch. Schon einmal 
hier ein Dankeschön Severino.

Nun habe ich dies folgendermassen gelöst: Ich verwende keinen Interrupt 
für die Datenauswertung. Sondern löse dies über eine Schlaufe. Dort 
wartet der PIC mit dem Einlesen (getc();) bis Daten auch vorhanden sind.
Das ist für mich so in Ordnung.

Nun habe ich nur noch ein anderes Problem.
Nach dem dritten Einlesen steht der PIC still. Ich kann weder Daten 
einlesen, noch welche aussenden. Überhaupt passiert nichts mehr.

Kann es sein, dass die Daten langsamer/schneller empfangen werden 
müssen, als dies ich eingestellt habe (RS232 Geschwindigkeit stimmt mit 
der anderen überein)?

Hier nochmals der Code von der Schlaufe.
Danke
1
// Von RS232 lesen
2
3
boolean IntRS(void){
4
boolean OK=true;
5
//char RSBuf;              // RS232-Zeichenlänge definieren
6
disable_interrupts(int_rda);    // Interrupts RS232 aus
7
//enable_interrupts(int_timer2);  // Timer2 "Stringlänge empfangen" ein
8
9
10
if (Out_Counter2<3){
11
// Serielle Schnittestelle auswerten
12
printf("Lesen    ");
13
  while (!kbhit()){} if (getc()==0x02){          // Kontrollbyte 1 (Wert 02)
14
    RSstatus=1;
15
    printf("Zahl_1 (02) OK   ");          
16
    while (!kbhit()) {} if (getc()==0x03){         // Status RS232 OK
17
      RSstatus=2;
18
      printf("Zahl_2 (03)  OK   ");
19
       while (!kbhit()) {} if (getc()==0x00){
20
        RSstatus=3;
21
        printf("Zahl_3 (00)  OK   ");
22
        while (!kbhit()) {} if (getc()==0x00){      // --> ACHTUNG falsche Zahl
23
          RSstatus=4;
24
          printf("Zahl_4 (FF)  OK   ");
25
          while (!kbhit()) {} if (getc()==0x00){  // CES-Status OK
26
            Out_Counter2=0;  // Reboot-Counter rücksetzen
27
               set_timer1(0);
28
               timer1Count=0;
29
            RSstatus=0;
30
            printf("Statuszahl (00)  OK   ");
31
          }
32
          else Menu=4; 
33
        } else printf("   FF false ");  
34
      }  
35
        }  
36
  }  
37
 }                    // Ende Out_Counter
38
//enable_interrupts(int_rda);
39
40
  return(OK);
41
}

von Severino R. (severino)


Lesenswert?

Bei der vierten Zahl prüfst Du zwar auf ==0x00, schreibst dann aber:
      printf("Zahl_4 (FF)  OK   ");
(Schönheitsfehler?)

Sendest Du dem PIC überhaupt eine vierte (und fünfte) Zahl?
Wenn nicht, "hängt" er in while (!kbhit()).

Hast Du einen ICD2 oder sonst ein Debug-Werkzeug, um zu sehen, was der 
PIC macht?
Oder könntest Du das Ding in MPLABs Simulator testen (weiss nicht, wie 
weit die UART-Simulation läuft).

Jedenfalls denke ich, dass in der while-Schleife zusätzlich nach einem 
Timeout abgefragt werden sollte, z.B.
1
while ( !kbhit() & !TimerExpired() ) {}
2
if TimerExpired() { /* Timeout behandeln (Fehler?) */ }
3
else { /* Zeichen einlesen */ }

Severino

von Severino R. (severino)


Lesenswert?

Sorry, ich meinte:
1
while ( !kbhit() && !TimerExpired() ) {}
2
statt
3
while ( !kbhit() & !TimerExpired() ) {}

von Michi (Gast)


Lesenswert?

Hallo Severino.
(sorry die Verspätung, war unvorhersehbar geschäftlich unterwegs)

Nun zu meinem Programm:
Ich habe zwar ein ICD2, kann diese Anwendung jedoch dort nicht 
einsetzen. Die Überwachung muss in einem Gerät (mit einer bereits 
angefertigten Plattine) eingesetzt werden.
Ich sehe also nur, ob das Programm läuft (oder eben nicht :-(  )


Ich habe die serielle Schnittstelle über einen Interrupt eingelesen:
Und wie ich dies gesehen habe, funktioniert das Einlesen bis zur Zahl 03 
recht gut. Im Moment habe ich noch zwei Probleme:
1. Wenn ich das Programm laufen lasse, bleibt er mir beim Wert 00 
"stehen".
2. Wenn ich die dritte Überprüfung (0x00) nicht vornehme (also 
auskommentiert) und ich die Zeile "RS232 Counter reset" gesendet habe, 
bleibt das Programm auch stehen.
Es müsste doch mindestens noch den printf("Durchlauf OK"); gesendet 
werden, was leider nicht der Fall ist.
1
#int_rda
2
void IntRS(void){
3
disable_interrupts(int_rda);
4
5
if (Out_Counter2<3){
6
// Serielle Schnittstelle auswerten
7
timerRSCount=0;
8
timerRSOut=0;
9
10
while (timerRSCount<=0x0032){                      // Timeout 35 sek
11
while ((!kbhit()) && (timerRSCount<=0x000A)){}     // Timeout 2 sek. 
12
    if (timerRSCount>=0x000A) { Set_tris_c(0x80);  // Eingänge reset
13
        timerRSOut=1;}      
14
    else {}
15
     if ((!timerRSOut) && (getc()==0x02)){
16
        printf("Startzahl 02 OK\n\r");
17
        while ((!kbhit()) && (timer1Count<=0x0014)){}  //Timeout 4 sek.
18
        if (timerRSCount>=0x0014) { Set_tris_c(0x80); 
19
           timerRSOut=1;}
20
        else{}
21
        if ((!timerRSOut) && (getc()==0x03)){
22
           printf("Startzahl 03 OK\n\r");
23
           while ((!kbhit()) && (timerRSCount<=0x001E)){} // 6 sek.
24
           if (timerRSCount>=0x001E) { Set_tris_c(0x80); 
25
               timerRSOut=1;}
26
           else{}
27
           if ((!timerRSOut) && (getc()==0x00)){
28
               printf("Startzahl 00 OK\n\r");
29
               while ((!kbhit()) && (timerRSCount<=0x0028)){}  // 8 sek
30
               if (timerRSCount>=0x0028) { Set_tris_c(0x80);   
31
                   timerRSOut=1;}
32
               else {}
33
               if ((!timerRSOut) && (getc()==0x00)){
34
                   Out_Counter2=0;          // Reboot-Counter rücksetzen
35
                   set_timer1(0);
36
                   timer1Count=0;
37
                   Set_tris_c(0x80);
38
                   printf("RS232 Counter reset\n\r");
39
                   }
40
                else { printf("Reboot\n\r");
41
                       Menu=4;
42
                      }    
43
                }                        // Ende 0x00
44
                else  printf("Fehler 00\n\r");
45
            }                            // Ende 0x00
46
            else  printf("Fehler 03\n\r");
47
        }                                // Ende 0x03
48
        else  printf("Fehler 02\n\r");
49
}                                        
50
if (timerRSCount>=0x0032){                            // Timeout 35 sek.
51
  timerRSCount=0;
52
  Set_tris_c(0x80);
53
  printf("Timerueberlauf");
54
  Delay_ms(50);
55
  }
56
}
57
Set_tris_c(0x80);
58
printf("Durchlauf OK");
59
enable_interrupts(int_rda);
60
}

Nochmals vielen Dank für Deine Bemühungen

Michi

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.