Forum: Mikrocontroller und Digitale Elektronik Problem Drehgeber Rechnen + Senden RS232


von PicNeuling (Gast)


Lesenswert?

1
/** I N C L U D E S **********************************************************/
2
#include "p18f2550.h"  // PIC- Header
3
#include "delays.h"     // für Warteschleifen
4
#include "usart.h"    // für RS232- Schnittstelle
5
6
/** Configuration Fuses********************************************************/
7
#pragma config FOSC = HS      // CPU=20 MHz
8
#pragma config PWRT = ON      // PowerOnTimer AN   
9
#pragma config BOR = OFF      // BOR AUS
10
#pragma config WDT = OFF      // Watchdog Timer
11
#pragma config LVP = OFF      // Low Voltage ICSP AUS
12
#pragma config PBADEN = OFF      // PORTA Analogeingänge AUS
13
#pragma config VREGEN = OFF      // VREGEN AUS
14
#pragma config MCLRE = ON      // Master Reset AN
15
#pragma config PLLDIV = 1      // PLLTeiler = 1 => 20 MHz
16
#pragma config CPUDIV = OSC1_PLL2  // OSCTeiler = 1 => 20 MHz
17
#pragma config USBDIV = 1      // CPUTeiler = 1 => 20 MHz
18
19
/** Globale Variablendeklarationen**********************************************************/
20
signed long int counter = -18946;      // Zählvariable
21
signed long int ncounter = 0;        // Hilfsvariable
22
char gray_code = 0;              // Einlesevariable
23
char gray_help = 0;              // Vergleichsvariable
24
char ch0, ch1, ch2, ch3, ch4, ch5, ch6;    // Sendevariablen
25
int s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0; // Counterstellenvariablen
26
#define Takt 200000000            // Quarz HS 20 MHZ an OSC1- OSC2
27
#define Baud 57600               // Baudrate über SPBRG eingestellt 1 BIT = 1,74 µs
28
29
/** Programm **************************************************/
30
#pragma code
31
32
void InitUsart(void)    
33
{
34
  SPBRG = 20;        // Baudrate 57600 Baud  SPBRG- Berechnung  59524 = 20000000/(16*(20+1)) = Fehlerquote unter 5%
35
  SPBRGH =  0;      // 8 Bit Übertragung      
36
  TRISCbits.TRISC6 = 1;  // Aktivierung Senden TXD
37
  TRISCbits.TRISC7 = 1;  // Aktivierung Empfangen RXD
38
  RCSTA = 0b10000100;    // Empfangsregister konfigurieren
39
  TXSTA = 0b00100100;    // Senderegister konfigurieren
40
  return 0;
41
}
42
43
void PICConfig(void)
44
{
45
  // PORTA => Ausgänge LEDs RA0, RA4, RA5
46
  TRISA = 0x00;
47
  PORTA = 0x00;
48
  LATA  = 0x00;
49
  
50
  // PORTB  => Eingänge Inkrementalgeber
51
  TRISB = 0xFF;
52
  PORTB = 0x00;
53
  LATB  = 0x00;
54
55
  // PORTC => alles Ausgänge  RC0, RC1, RC2
56
  TRISC = 0x00;   
57
  PORTC = 0x00;
58
  LATC  = 0x00;
59
  return 0;
60
}
61
62
void Senden(unsigned char byte)
63
{
64
  WriteUSART(byte);              // Byte in RS232 Sendebyte schreiben
65
  while(BusyUSART( ));            // Warten bis Sendebyte leer
66
  return 0;
67
}
68
69
signed long int Counter(void)
70
{
71
    gray_code = PORTBbits.RB0;        //A1      X
72
    gray_help = gray_code << 1;          //       X0    
73
    gray_code = gray_help | PORTBbits.RB1;  //B1     XY
74
    gray_help = gray_code << 1;         //      XY0
75
    Nop();                  // 0,2 µs Warten
76
    gray_code = gray_help | PORTBbits.RB0;  //A2    XYZ
77
    gray_help = gray_code << 1;         //     XYZ0
78
    gray_code = gray_help | PORTBbits.RB1;  //B2   XYZW
79
    Nop();                  // 0,2 µs Warten
80
    switch (gray_code)
81
    {
82
      case 0b00000001: 
83
        counter--;
84
        break;
85
      case 0b00000010: 
86
        counter++;
87
        break;
88
      case 0b00000100: 
89
        counter--;
90
        break;
91
      case 0b00000111: 
92
        counter++;
93
        break;
94
      case 0b00001101: 
95
        counter--;
96
        break;
97
      case 0b00001110: 
98
        counter++;
99
        break;
100
      case 0b00001000: 
101
        counter--;
102
        break;
103
      case 0b00001011: 
104
        counter++;
105
        break;
106
    }      // Ende Switch- Anweisung
107
    return counter;
108
}
109
110
char InttoChar(int counter)
111
{
112
   ncounter = counter;
113
   ch0 = 0;
114
   ch1 = 0;
115
   ch2 = 0;
116
   ch3 = 0;
117
   ch4 = 0;
118
   ch5 = 0;
119
     if(counter == 0 || counter > 0)
120
     {
121
                ch0 = '+';
122
     }
123
     if (counter < 0)
124
     {
125
                 ch0 = '-';
126
                 counter = (counter*(-1));
127
     }            
128
    for(;counter > 9999;counter -= 10000)
129
    {s1++;}
130
    for(;counter > 999;counter -= 1000)
131
    {s2++;}
132
    for(;counter > 99;counter -= 100)
133
    {s3++;}
134
    for(;counter > 9;counter -= 10)
135
    {s4++;}
136
    for(;counter > 0;counter -= 1)
137
    {s5++;}
138
    ch1 = s1 + 0x30;
139
    ch2 = s2 + 0x30;
140
    ch3 = s3 + 0x30;
141
    ch4 = s4 + 0x30;
142
    ch5 = s5 + 0x30;
143
  ch6 = '#'; 
144
    s1, s2, s3, s4, s5 = 0;           // Counterstellenvariablen nullen
145
    return ch0, ch1, ch2, ch3, ch4, ch5, ch6;  // Rückgabe von chars
146
}
147
148
void main(void)
149
{
150
  PICConfig();          // Konfigurieren der PORTs
151
  InitUsart();          // Start der USART- Schnittstelle
152
  PORTCbits.RC0 = 1;        // Anschalten EIN/Aus Lampe
153
  //InttoChar(counter);        // Zähler in chars zum Senden umwandeln
154
  //loop
155
  while(1)              //Hauptprogramm
156
  {
157
    //Counter();            // Zählen
158
    InttoChar(counter);          // Zähler in chars zum Senden umwandeln
159
    counter = ncounter;
160
    Nop();                // Warten
161
    Nop();                // Warten
162
    Senden(ch0);            // Senden Vorzeichen
163
    Senden(ch1);            // Senden Zehntausender
164
    Senden(ch2);            // Senden Tausender
165
    Senden(ch3);            // Senden Hunderter
166
    Senden(ch4);            // Senden Zehner
167
    Senden(ch5);            // Senden Einer
168
    Senden(ch6);            // Senden Trennzeichen
169
  }                // Ende Endlosschleife
170
  return 0;
171
 }                  /* Ende Mainfunktion */

Anbei mein Code!

Das Auslesen des Drehgebers funktioniert. Auch Das umrechnen von int inc 
char und auch das Senden der einzelnen Chars.


Alles funktioniert. Aber ohne die Schleife.

Sobald ich mein Inttochar() Funktion in die Schleife packe. KOmmt am PC 
nur noch Mist an. ICh weiß nicht mehr weiter. Kann sich das einer 
erklären!?

Zum Testen habe ich den Counter manuell beladen ( =-12856) for der 
Schleife umgewandelt und dann in die Schleife => Unendliches Senden. 
Funktioniert perfekt.

Doch sobal ich das Umrechnen in die Schleife schiebe, funktioniert gar 
nix mehr;-(

Hat vielleicht jemand ne Idee?

Grüße Pascal

von Josef S. (chnaideur)


Lesenswert?

InttoChar() enthaelt eindeuting fehler:
In C kann man Variablen nicht zur gleichen zeit aendern (normaler 
weise).

s1, s2, s3, s4, s5 = 0;

wird

s1 = 0;
s2 = 0;
etc.

Man kann auch nicht mehrere Variablen von einer function zurueck 
schicken - brauchst du hier sowieso nicht da ch1 - ch6 global deklariert 
sind; du kannst sie ueberall im Programm benutzen.
InttoChar() schaut sonst aus als ob es funktionieren sollte, koennte 
aber viel eleganter sein (mit for loop und einem array)

Ich hoffe das hilft!
Josef

von olaf (Gast)


Lesenswert?

Vielleicht sollte man auch mal den Compiler
wechseln. Ich finde es bemerkenswert das
bei soetwas:

>  return ch0, ch1, ch2, ch3, ch4, ch5, ch6;  // Rückgabe von chars

nicht sofort beim Compiler die Alarmglocken angehen.
Oder handelt es sich um das typische
Nachwuchsproblem? (aka: Kann posten aber nicht lesen,
z.b Fehlermeldungen)

Olaf

von Peter D. (peda)


Lesenswert?

olaf schrieb:
>>  return ch0, ch1, ch2, ch3, ch4, ch5, ch6;  // Rückgabe von chars
>
> nicht sofort beim Compiler die Alarmglocken angehen.
> Oder handelt es sich um das typische
> Nachwuchsproblem? (aka: Kann posten aber nicht lesen,
> z.b Fehlermeldungen)

Da gibt es keine Fehlermeldung.
Es ist in C erlaubt, Anweisungen durch Komma zu trennen. Der 
Rückgabewert ist dann die letzte Anweisung ch6.


Peter

von PicNeuling (Gast)


Lesenswert?

danke für die schnelle Hilfe.

Hab eureTipps befolgt und jetzt funktioniert, das immerwährende Senden.

Es lag daran, dass meine Stellenvariablen s1 - s5 global definiert waren 
und somit immer irreale zufällige Werte annahmen. Hab sie nur lokal in 
der Funktion deklariert und jetzt gehts.

Jetzt muss ich mich nur noch ans Zählen machen.
Da hätt ich auch das gleiche Problem:
Hier nochmla die FUnktion
1
void Counter(void)
2
{
3
        gray_code = PORTBbits.RB0;                //A1      X
4
        gray_help = gray_code << 1;                  //       X0       
5
        gray_code = gray_help | PORTBbits.RB1;    //B1     XY
6
        gray_help = gray_code << 1;                 //      XY0
7
        Nop();                                    // 0,2 µs Warten
8
        gray_code = gray_help | PORTBbits.RB0;    //A2    XYZ
9
        gray_help = gray_code << 1;                 //     XYZ0
10
        gray_code = gray_help | PORTBbits.RB1;    //B2   XYZW
11
        Nop();                                    // 0,2 µs Warten
12
        switch (gray_code)
13
        {
14
            case 0b00000001:
15
                counter--;
16
                break;
17
            case 0b00000010:
18
                counter++;
19
                break;
20
            case 0b00000100:
21
                counter--;
22
                break;
23
            case 0b00000111:
24
                counter++;
25
                break;
26
            case 0b00001101:
27
                counter--;
28
                break;
29
            case 0b00001110:
30
                counter++;
31
                break;
32
            case 0b00001000:
33
                counter--;
34
                break;
35
            case 0b00001011:
36
                counter++;
37
                break;
38
        }            // Ende Switch- Anweisung
39
        return counter;
40
}
Meine Codelogik funktioniert irgendwie nicht.

Was passiert, wenn ich die Eingänge in ein char speichere?

Speichert der PIC für ein High eine 1 bzw. 0b0000001 / 0x01 ? oder eine 
ASCI CODE EINS?

Daran hängts bei mir gerade noch.

Weil er bei mir willkürlich und langsam vor und zurück zählt.

Grüße

Pascal

von Karl H. (kbuchegg)


Lesenswert?

PicNeuling schrieb:

> Was passiert, wenn ich die Eingänge in ein char speichere?
>
> Speichert der PIC für ein High eine 1 bzw. 0b0000001 / 0x01 ? oder eine
> ASCI CODE EINS?

Ein char ist auch nichts anderes als eine Variable, die einen kleinen 
Integer speichern kann, lediglich bei I/O (also printf, scanf und 
Konsorten) wird ein char anders behandelt.

Du solltest dir aber angewöhnen, für alles was in Wirklichkeit Bytes 
sind, immer 'unsigned char' zu benutzen. Bei char alleine ist nicht 
festgelegt, ob da jetzt ein Vorzeichen berücksichtigt wird oder nicht. 
Je nachdem können Schiebeoperationen mächtig in die Hose gehen.

Wenn du in einen char die Zahl 1 speicherst, dann steht da auch das 
Bitmuster für die Zahl 1 drinnen.

>
> Daran hängts bei mir gerade noch.

Es hängt bei dir daran, dass du kein C-Buch hast, welches dir die 
Grundlagen von C beigebracht hat.

von Karl H. (kbuchegg)


Lesenswert?

PicNeuling schrieb:

> Weil er bei mir willkürlich und langsam vor und zurück zählt.

Sieh dir mal diese Sequenz an
1
        gray_code = PORTBbits.RB0;                //A1      X
2
        gray_help = gray_code << 1;                  //       X0       
3
        gray_code = gray_help | PORTBbits.RB1;    //B1     XY
4
        gray_help = gray_code << 1;                 //      XY0
5
        Nop();                                    // 0,2 µs Warten
6
        gray_code = gray_help | PORTBbits.RB0;    //A2    XYZ
7
        gray_help = gray_code << 1;                 //     XYZ0
8
        gray_code = gray_help | PORTBbits.RB1;    //B2   XYZW
9
        Nop();                                    // 0,2 µs Warten
10
11
        switch (gray_code)

Aha. Du denkst tatsächlich, dass dein Benutzer den Drehgeber exakt in 
den 0.2µs drehen wird, die du ihm dafür Zeit gibst, während er in den 
restlichen Millisekunden, die du fürs UART senden brauchst, drehen kann 
wie ein Verrückter und nichts passiert.

Dein Code kann nur Veränderungen feststellen, die sich zeitlich nach dem 
erstmaligen Abfragen hier
1
        gray_code = PORTBbits.RB0;                //A1      X
2
        gray_help = gray_code << 1;                  //       X0       
3
        gray_code = gray_help | PORTBbits.RB1;    //B1     XY
4
        gray_help = gray_code << 1;                 //      XY0
5
        Nop();                                    // 0,2 µs Warten

und dem nächsten Abfragen hier
1
        gray_code = gray_help | PORTBbits.RB0;    //A2    XYZ
2
        gray_help = gray_code << 1;                 //     XYZ0
3
        gray_code = gray_help | PORTBbits.RB1;    //B2   XYZW
4
        Nop();                                    // 0,2 µs Warten
5
6
        switch (gray_code)

ereignet haben.
Alles andere kriegt er nicht mit.

von PicNeuling (Gast)


Lesenswert?

Ich hab ein C- Buch.


Mir gehts es um das Problem:



char c1 = PORTBbits.RB0;
char c2 = PORTBBIts.RB1;

Wenn ein Lowpegel anliegt (0V) => Was ist in c1 und c2 gespeichert.
Wenn ein Highpegel anliegt (2,5V => Schmitttrigger erkennt es ls High) 
=> Was ist in c1 und c2 gespeichert?
Für Highpegel:
c1 = 1;
oder
c1 = '1' bzw. c1 = 0x31 bzw. c1 = 61;

Für Lowpegel:
c1 = 0;
oder
c1 = '0' oder c1 = 0x30 bzw. c1 = 60;

Welche Zeile speichert der PIC?

Ich weiß einfach nicht wie der PIC den Highpegel interpretiert und ich 
keinem Buch finde ich etwas.

von Karl H. (kbuchegg)


Lesenswert?

PicNeuling schrieb:
> Ich hab ein C- Buch.
>
>
> Mir gehts es um das Problem:
>
>
>
> char c1 = PORTBbits.RB0;
> char c2 = PORTBBIts.RB1;
>
> Wenn ein Lowpegel anliegt (0V) => Was ist in c1 und c2 gespeichert.

0

> Wenn ein Highpegel anliegt (2,5V => Schmitttrigger erkennt es ls High)
> => Was ist in c1 und c2 gespeichert?

1

> Für Highpegel:
> c1 = 1;
> oder
> c1 = '1' bzw. c1 = 0x31 bzw. c1 = 61;

Wo sollen die her komme?

Nochmal. Ein char ist ein (kleiner) Integer. Wenn du dort ein Bit setzt, 
dann setzt du ein Bit. Nur weil das Teil char heisst, tauchen da nicht 
magisch irgendwelche ASCII Zeichen auf.

von PicNeuling (Gast)


Lesenswert?

@kbuchegg

Danke für die Char Antwort. Das wollte ich wissen.

Ok das mit dem Abfragen erscheint mir logisch.

Das muss per Timerinterrupt geschehen, oder?

Doch was muss genau in die Interruptroutine und wie aktiviere ich sie!? 
Nur bei A?


grüße
PAscal

von Karl H. (kbuchegg)


Lesenswert?

PicNeuling schrieb:
> @kbuchegg
>
> Danke für die Char Antwort. Das wollte ich wissen.
>
> Ok das mit dem Abfragen erscheint mir logisch.
>
> Das muss per Timerinterrupt geschehen, oder?

Kann, muss nicht (fürs erste).
Ein erster Erfolg wäre es, wenn in der Funktion der aktuelle Zustand des 
Drehgebers mit dem Zustand des Drehgebers im vorhergehenden 
Funktionsaufruf verglichen werden würde.
Vorausgesetzt die Funktion wird häufig genug aufgerufen, kann dir somit 
keine Veränderung am Drehgeber mehr durch die Lappen gehen.

von PicNeuling (Gast)


Lesenswert?

Hier ist meine überarbeitete Funktion

char codenew=0;              // Neuer Zustand
char gray_help = 0;      // Hilfvariable
char codeold = 0;            // Alter Zustand
char code = 0;              // Graycode
void Counter(void)
{
    codenew = PORTBbits.RB0;        //A1      X
    gray_help = codenew << 1;        //       X0
    codenew = gray_help | PORTBbits.RB1;  //B1     XY
    codeold = codenew << 2;          //     XY00
    code = codeold | codenew;
    switch (code)
    {
      case 0b00000001:
        counter--;
        break;
      case 0b00000010:
        counter++;
        break;
      case 0b00000100:
        counter--;
        break;
      case 0b00000111:
        counter++;
        break;
      case 0b00001101:
        counter--;
        break;
      case 0b00001110:
        counter++;
        break;
      case 0b00001000:
        counter--;
        break;
      case 0b00001011:
        counter++;
        break;
    }      // Ende Switch- Anweisung

    return 0;
}

Ist der Code logisch korrekt?

Countr wird in einer while(1) schleife Augerufen?

grüße

PAscal

von PicNeuling (Gast)


Angehängte Dateien:

Lesenswert?

Hi @all

Danke erstmal für die Hilfe.
Mein Programm funktioniet jetzt. Doch leider ist das Auslesen des 
Drehgebers viel zu langsam. (Er hat 5000 Schritte pro Umdrehung).

Also muss ich doch auf den Interrupt umzwischen.

Ich denke mal, das logischte wäre ein PORTB-ON-CHANGE Interrupt der bei 
einem Highsignal von A (also RB0) in diese InterruptServiceroutine geht, 
oder?

zum Aktivieren der Interrupts, reicht
    // PORTB-ON-CHANGE-INTERRUPT konfigurieren
    INTCON  = 0x11001000;
    INTCON2 = 0x10000001;
oder?

nun nehme ich einfach meine Counter funktion und packe sie in die ISR, 
oder?

void Interrupt(void)
{
  new = (PORTBbits.RB0<<1) | (PORTBbits.RB1);

code = (old<<2) | (new);
  switch (code)
  {
  case 0b00000001: counter++;  break;
  case 0b00000111: counter++;  break;
  case 0b00001110: counter++;  break;
  case 0b00001000: counter++;  break;

case 0b00000010: counter--;  break;
  case 0b00000100: counter--;  break;
  case 0b00001101: counter--;  break;
  case 0b00001011: counter--;  break;
  }  // Ende Switch- Anweisung
  old = new;
 flag=1;

INTCON.RBIF = 0;
 return 0;
}

Doch wenn der PIC die Routine abgearbeitet hat, wo steht er dann 
innerhalb der main- funktion!?

Denn wenn er an die letzte Stelle vom Programm springt, an der er gerade 
war.

Brauche ich ja meine Endlosschleife noch, die immer int in char 
umrechnet und sendet,oder!?

Reicht das schon?


Echt nochmal und schonmal vielen vielen Dank für eureHilfe

Grüße

Also hat vielleicht jemand so eine allgemeine Interruptroutine und 
Konfiguration

Pascal

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.