Forum: Mikrocontroller und Digitale Elektronik Timmer Interrupt zu langsam.


von PicNeuling (Gast)


Lesenswert?

Hi, ich habe jetzt endlich mein Projekt weitesgehnd fertig.
Doch nun habe ich noch zwei Dinge, die naja noch nicht optimal laufen.

1. Wenn ich den Drehgeber langsam dreh, habe ich pro Umdrehung 20000 
Flanken gezählt( Langsam = 1 Umdrehungen in ca .2 Sekunden), sobald ich 
schneller werde zählt er immer weniger Flanken.
Die Zählroutine habe ich in einem Interrupt.

Den Timerüberlauf habe ich so schnell wie möglich gewählt(laut sprut). 
Mein Quarz ist 20 MHz ohne Teiler. Eigentlich müsste der Interrupt so 
schnell sein, das er alle Impulse empfägt, oder habe ich einen 
Denkfehler?

Hier die Interupt und Timer Einstellung
1
// PORTB-ON-CHANGE-INTERRUPT konfigurieren
2
  INTCON  = 0b11100000;              // Interruptregister setzen
3
  INTCON2 = 0b00000000;              // Interruptregister setzen
4
  //Timer 0 Konfiguration + Starten
5
  T0CON = 0b11000000;
6
[\c]
7
8
9
Das 2 Thema ist, sobald mein Motor an ist, schwankt der Drehgeber zwischen ca. 3 Flanken. Und sendet deswegen Werte wie -00003 bis +00000  bis +00003.
10
11
Mir fhelt logisch nicht an, wie ich meine Countervariable 4 mal speichere,  und die erste und die letzte vergleiche, ob deren DIffernez größer 5 ist!?
12
Kann mir jemand auf die Sprünge helfen?
13
14
VIelen Dank schon einmal
15
16
grüße
17
18
Hier ist der komplette CODE
19
20
[c]
21
/** I N C L U D E S **********************************************************/
22
#include "p18f2550.h"                // PIC- Header
23
#include "delays.h"                   // für Warteschleifen
24
#include "usart.h"                  // für RS232- Schnittstelle
25
#include "timers.h"                  // für Timerinterrupt
26
#include "stdlib.h"                  // für Standardbibliothek
27
/** Configuration Fuses********************************************************/
28
#pragma config FOSC = HS              // Quarz HS 20 MHZ an OSC1- OSC2
29
#pragma config PWRT = ON              // PowerOnTimer AN   
30
#pragma config BOR = OFF              // BOR AUS
31
#pragma config WDT = OFF              // Watchdog Timer
32
#pragma config LVP = OFF              // Low Voltage ICSP AUS
33
#pragma config PBADEN = OFF              // PORTA Analogeingänge AUS
34
#pragma config VREGEN = OFF              // VREGEN AUS
35
#pragma config MCLRE = ON              // Master Reset AN
36
#pragma config PLLDIV = 1              // PLLTeiler = 1 => 20 MHz
37
#pragma config CPUDIV = OSC1_PLL2          // OSCTeiler = 1 => 20 MHz
38
#pragma config USBDIV = 1              // CPUTeiler = 1 => 20 MHz
39
/** Globale Variablendeklarationen*********************************************/
40
signed long int counter = 0;            // Zähler -2.147.483.648 bis 2.147.483.647
41
signed long int counter_old = 0;          // Vergleichszähler
42
signed long int counter_test = 0;
43
char ch0, ch1, ch2, ch3, ch4, ch5, ch6;
44
char ch0 = '+';                    // Vorzeichen
45
char ch1 = '\n';                  // Schlusszeichen
46
char code = 0;                    // Graycode
47
char new = 0;                    // Neuer Zustand
48
char old = 0;                    // Alter Zustand
49
/** Programm **************************************************/
50
void high_isr(void);
51
#pragma code high_vector=0x08
52
void interrupt_at_high_vector(void)
53
{
54
  _asm goto high_isr _endasm
55
}
56
#pragma code
57
#pragma interrupt high_isr
58
void high_isr (void)
59
{
60
    new = (PORTBbits.RB6<<1) | (PORTBbits.RB7); // Kanal A + B einlesen
61
    code = (old<<2) | (new);                    // Graycode bilden
62
    switch (code)                               // Gray- Code Auswertung
63
    {
64
      case 0b00000001: counter++;  break;      // Drehung im Uhrzeigersinn
65
      case 0b00000111: counter++;  break;      // Drehung im Uhrzeigersinn
66
      case 0b00001110: counter++;  break;      // Drehung im Uhrzeigersinn
67
      case 0b00001000: counter++;  break;      // Drehung im Uhrzeigersinn
68
      case 0b00000010: counter--;  break;      // Drehung gegen den Uhrzeigersinn
69
      case 0b00000100: counter--;  break;      // Drehung gegen den Uhrzeigersinn
70
      case 0b00001101: counter--;  break;      // Drehung gegen den Uhrzeigersinn
71
      case 0b00001011: counter--;  break;      // Drehung gegen den Uhrzeigersinn
72
73
    //  case 0b00000011: PORTCbits.RC2 = 1;  break;      // Fehler
74
    //  case 0b00000110: PORTCbits.RC2 = 1;  break;      // Fehler
75
    //  case 0b00001100: PORTCbits.RC2 = 1;  break;      // Fehler
76
    //  case 0b00001001: PORTCbits.RC2 = 1;  break;      // Fehler
77
    //  0000 , 1111 , 0101 , 1010 keine Veränderung des Zustandes
78
      }                                        // Ende Switch- Anweisung
79
    old = new;                                  // Neuen Zustand als Alten Zustand speichern
80
    INTCONbits.TMR0IF = 0;            // Interrupt zurücksetzen
81
}
82
void InitUsart(void)                            // Funktion USART Konfiguration            
83
{
84
  SPBRG = 20;                    // Baudrate 57600 Baud  SPBRG- Berechnung  59524 = 20000000/(16*(20+1)) = Fehlerquote unter 5% ; 1 BIT = 1,74 µs ; 57600 Baud
85
  SPBRGH =  0;                  // 8 Bit Übertragung      
86
  TRISCbits.TRISC6 = 1;              // Aktivierung Senden TXD
87
  TRISCbits.TRISC7 = 1;              // Aktivierung Empfangen RXD
88
  RCSTA = 0b10000100;                // Empfangsregister konfigurieren
89
  TXSTA = 0b00100100;                // Senderegister konfigurieren
90
}
91
void PICConfig(void)                                // Funktion PIC Konfiguration
92
{
93
                          // PORTA => Ausgänge LEDs RA0, RA4, RA5
94
  TRISA = 0x00;                  // PORT A Ausgänge deklarieren
95
  PORTA = 0x00;                  // PORT A auf Null setzen
96
  LATA  = 0x00;                  // PORT A Latchregister auf Null setzen
97
                          // PORTB  => Eingänge Inkrementalgeber
98
  TRISB = 0xFF;                  // PORT B Ausgänge deklarieren
99
  PORTB = 0x00;                  // PORT B auf Null setzen
100
  LATB  = 0x00;                  // PORT B Latchregister auf Null setzen
101
                          // PORTC => alles Ausgänge  RC0, RC1, RC2
102
  TRISC = 0x00;                   // PORT C Ausgänge deklarieren
103
  PORTC = 0x00;                  // PORT C auf Null setzen
104
  LATC  = 0x00;                   // PORT C Latchregister auf Null setzen
105
                          // PORTB-ON-CHANGE-INTERRUPT konfigurieren
106
  INTCON  = 0b11100000;              // Interruptregister setzen
107
  INTCON2 = 0b00000000;              // Interruptregister setzen
108
  //Timer 0 Konfiguration + Starten
109
  T0CON = 0b11000000;
110
  return 0;                                       // Rückgabewert (void) Konfiguration
111
}
112
void Senden(unsigned char byte)                     // Funktion für USART Senden
113
{
114
  WriteUSART(byte);                // Byte in RS232 Sendebyte schreiben
115
  while(BusyUSART( ));              // Warten bis Sendebyte leer
116
  return 0;                                       // Rückgabewert (void) Senden
117
}
118
void InttoChar(void)             // Funktion INT in CHAR Umwandlung
119
{
120
   int s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0;  // Counterstellenvariablen
121
   signed long int  ncounter = 0;         // Hilfscounter
122
   ncounter = counter/4;                            // Originalcounter zwischenspeichern
123
     if(ncounter == 0 || ncounter > 0)                // Zähler positiv?
124
     {
125
    ch0 = '+';                                  // Vorzeichen positiv
126
     }
127
     if (ncounter < 0)                               // Zähler negativ?
128
     {
129
         ch0 = '-';                                 // Vorzeichen negativ
130
         ncounter = (ncounter*(-1));                  // Invertieren
131
     }            
132
    for(;ncounter > 9999;ncounter -= 10000)           // Zehntausenderstellen
133
    {s1++;}
134
    for(;ncounter > 999;ncounter -= 1000)             // Tausenderstellen
135
    {s2++;}
136
    for(;ncounter > 99;ncounter -= 100)               // Hunderterstellen
137
    {s3++;}
138
    for(;ncounter > 9;ncounter -= 10)                 // Zehnerstellen
139
    {s4++;}
140
    for(;ncounter > 0;ncounter -= 1)                  // Einerstellen
141
    {s5++;}
142
    ch1 = s1 + 0x30;                                // Zehntausenderstelle CHAR zum Senden
143
    ch2 = s2 + 0x30;                                // Tausenderstelle CHAR zum Senden
144
    ch3 = s3 + 0x30;                                // Hunderterstelle CHAR zum Senden
145
    ch4 = s4 + 0x30;                                // Zehnerstelle CHAR zum Senden
146
    ch5 = s5 + 0x30;                                // Einerstelle CHAR zum Senden
147
  ch6 = '\n';                                      // Abschlusszeichen CHAR zum Senden
148
//  counter = ncounter;                        // Originalzähler zurückgeben
149
    return 0;                                       // Rückgabewert (void) Umwandlung
150
}
151
void main(void)                                     // Hauptprogramm starten
152
{
153
  PICConfig();                  // Konfigurieren der PORTs
154
  InitUsart();                  // Start der USART- Schnittstelle
155
  PORTCbits.RC0 = 1;                // Anschalten EIN/Aus Lampe
156
  //loop
157
  while(1)                      //Hauptschleife
158
  {
159
  //  if(counter > 0)
160
  //  { PORTAbits.RA4 = 1; }
161
  //  if(counter < 0)
162
  //  { PORTAbits.RA5 = 1; }
163
    counter_test = counter/4;
164
    while(counter_test != counter_old)
165
    {
166
    InttoChar();              // Zähler in chars zum Senden umwandeln
167
    Senden(ch0);                // Senden Vorzeichen
168
    Senden(ch1);                // Senden Zehntausender
169
    Senden(ch2);                // Senden Tausender
170
    Senden(ch3);                // Senden Hunderter
171
    Senden(ch4);                // Senden Zehner
172
    Senden(ch5);                // Senden Einer
173
    Senden(ch6);                  // Senden               
174
    counter_old = counter_test;
175
    }
176
    PORTCbits.RC1 = 0;              // LED Senden AUS
177
    PORTCbits.RC2 = 0;              // LED Fehler AUS
178
    PORTAbits.RA4 = 0;              // LED Rechts AUS
179
    PORTAbits.RA5 = 0;              // LED Links  AUS
180
  }                        // Ende Endlosschleife
181
 }                          /* Ende Mainfunktion */

von Ulrich (Gast)


Lesenswert?

Für den Zugriff auf die Variable counter in der Routine IntToChar 
sollten Interrupts gesperrt werden, damit zwischendurch kein Interrupt 
passieren kann.

Je nach Compiler kann es nötig sein die Variable counter als Volatile zu 
markieren.

Das case statement in der ISR kann man schneller machen, wenn man dafür 
eine Tabelle mit constaten nutzt. 16 Bytes RAM sollten wohl noch übrig 
sein. Mit Case gibt das einen kurzen Source code, aber die meisten 
Compiler machen daraus eine ganze Menge Vergleiche und Sprünge.

von PicNeuling (Gast)


Lesenswert?

Danke für die Antwort:

In IntToChar hab ich doch counter gesperrt, da ich counter in eine neue 
Variable schreibe. Und er denn Code Stück für Stück abarbeitet und so 
mit ncounter rechnet!?
Das sollte doch so stimmen, oder!?

zu den Konstanten
meinst da sowas wie

#Define RECHTS1  0b00000001

etc.??

Was wäre eine Alternative für die case Anweisung?
Ich hab schone den Drehgeber Artikel auf µcontroller.d gelesen, also die 
schnelle Variante, doch steige leider nicht genau dahinter.


grüße

von PicNeuling (Gast)


Lesenswert?

...

von PICNeuling (Gast)


Lesenswert?

jemand eine Idee?

von Karl H. (kbuchegg)


Lesenswert?

PicNeuling schrieb:
> Danke für die Antwort:
>
> In IntToChar hab ich doch counter gesperrt, da ich counter in eine neue
> Variable schreibe. Und er denn Code Stück für Stück abarbeitet und so
> mit ncounter rechnet!?
> Das sollte doch so stimmen, oder!?

Dein Counter ist aber ein signed long, besteht also aus mehreren Bytes. 
Was passiert, wenn der Interrupt kommt, wenn von den 4 Bytes 2 bereits 
umkopiert sind und 2 noch nicht.

Was mir auch nicht gefällt:
Du fragst counter 2 mal ab. Einmal in der Hauptschleife, einmal in 
InttoChar.
Wer garantiert dir, dass da jedesmal derselbe Wert herauskommt?

von Karl H. (kbuchegg)


Lesenswert?

Deine while Schleife

    while(counter_test != counter_old)
    {

wird nur maximal 1 mal ausgeführt. Die einzige Möglichkeit für 
counter_test seinen Wert innerhalb der Schleife zu ändern, ist hier

    counter_old = counter_test;

und damit ist dann auch schon die Endbedingung für die Schleife erfüllt. 
Die Schleife ist also in Wirklichkeit keine, sondern ein simples if, 
dass sich einen Wolfspelz angezogen hat.

von Karl H. (kbuchegg)


Lesenswert?

PicNeuling schrieb:

> Den Timerüberlauf habe ich so schnell wie möglich gewählt(laut sprut).
> Mein Quarz ist 20 MHz ohne Teiler.

Ob das so ist oder nicht, kann ich mangels PIC-Kentnisse bzw. Datenblatt 
nicht sagen.

> Eigentlich müsste der Interrupt so
> schnell sein, das er alle Impulse empfägt, oder habe ich einen
> Denkfehler?

Benutzt du einen Timer Interrupt zum regelmässigen Nachsehen oder einen 
Pin-Change Interrupt?

In deinem Code kommen beide Begriffe vor und aus dem Code werd ich nicht 
schlau. Auch die ISR Namen sind so, dass mich das eher an einen 
Pin-Change Interrupt erinnert.

von Karl H. (kbuchegg)


Lesenswert?

> Mir fhelt logisch nicht an, wie ich meine Countervariable 4 mal
> speichere,  und die erste und die letzte vergleiche, ob deren
> DIffernez größer 5 ist!?
>
> Kann mir jemand auf die Sprünge helfen?

Du brauchst ein Array, welches 4 Werte speichern kann.

signed long LastCounter[4];

Wenn du reihum immer in dieses Array reinschreibst, überschreibt ein 
neuer Wert immer den ältesten Wert. Dazu musst du dir nur merken, wo als 
nächstes geschrieben werden soll

signed long LastCounter[4];
unsigned char nextLast;

....
   LastCounter[ nextLast ] = Counter;
   nextLast++;
   if( nextLast == 4 )
     nextLast = 0;
...

jetzt musst du den jüngesten Wert mit dem ältesten Wert vergleichen. 
Welcher ist der älteste? Der mit dem Index in nextLast.

Bsp.

  LastCounter[0]   1000
  LastCounter[1]   1001
  LastCounter[2]   1002
  LastCounter[3]   1003

  nextLast         0

Die Vorgeschichte der Counter war also
   1000, 1001, 1002, 10003

jetzt kommt der neueste Wert, 1004. Gespeichert wird er auf LastIndex[0] 
(weil nextLast gleich 0 ist)

  LastCounter[0]   1004
  LastCounter[1]   1001
  LastCounter[2]   1002
  LastCounter[3]   1003

  nextLast         1

-> der jüngste Wert im Array ist 1004. Das weißt du, weil du ihn ja 
gerade hineingeschrieben hast. Der älteste ist 1001 (weil LastCounter[1] 
gleich 1001) ist.

Jetzt kommt ein neuer Wert dazu: 1005

  LastCounter[0]   1004
  LastCounter[1]   1005
  LastCounter[2]   1002
  LastCounter[3]   1003

  nextLast         2

jüngster Wert: 1005. Ältester Wert 1002

von Ulrich (Gast)


Lesenswert?

Die Alternative zum Case wäre hier ein Array mit konstanten Werten 
0,1,-1. Statt dem case (Code)... also
  counter += Konstanten[code] ;

von PicNeuling (Gast)


Lesenswert?

HI,
könntest du mir das mit dem Array näher erklären!?
Das kurte "Stück Code" versteh ich zu wenig;-)

vielen dank;-)

grüße

von Ulrich (Gast)


Lesenswert?

Der Case Teil soll ja die Variabel Counter entweder gleich lassen, einen 
hochzählen, oder einen Runterzählen. Man kann das auch so sehen, das 
immer etwas zu Counter addiert wird, nämlich entweder 0, 1 oder -1. Die 
Wert 0,1,-1 kann man in einem konstanten Array als Tabelle (Lookup 
Table) haben.

von PicNeuling (Gast)


Lesenswert?

ah jetzt versteh ich was du meinst.

Gut ich werde das mal so schreiben und probieren.

danke

von PicNeuling (Gast)


Lesenswert?

1
static char code graytab[] = { 0,1,-1,0, -1,0,0,1, 1,0,0,-1, 0,-1,1,0 };
2
unsigned char graycode = 0;
3
unsigned char = 0;
4
unsigned old = 0;
5
unsigned new = 0;
6
7
8
9
10
void high_isr (void)
11
{
12
    new = (KANALA<<1) | (KANALB);         // Kanal A + B einlesen
13
    graycode = ((old<<2) | (new));                     // Gray- Code bilden (2 Zustände)
14
    counter += graytab[graycode];                          // Graycode Auswertung
15
    old = new;                                      // Neuen Zustand als Alten Zustand speichern
16
    INTCONbits.TMR0IF = 0;          // Interrupt zurücksetzen
17
}
Das ist mein, so müsste es doch klappen, oder?

anhand der Tabelle
+ Recht
- Links
0 keine Ändernung
F Fehler => 0
binär  Bedeutung
00000000  0
00000001  ++
00000010  --
00000011  0
00000100  --
00000101  F
00000110  F
00000111  ++
00001000  ++
00001001  F
00001010  F
00001011  --
00001100  0
00001101  --
00001110  ++
00001111  0


mfg Pascal

von PicNeuling (Gast)


Lesenswert?

*push;-)*

von Karl H. (kbuchegg)


Lesenswert?

> Das ist mein, so müsste es doch klappen, oder?

Ist ganz einfach:
Brenns in den Prozessor und probiers aus.
Das ist ja einer der grossen Vorteile in der Programmierung: Es geht 
sehr wenig kaputt. Entweder ein Programm funktioniert oder es 
funktioniert nicht :-)


PS:
> habe ich pro Umdrehung 20000 Flanken gezählt
Das kommt mir ein bischen viel vor. Ein bischen sehr viel!

von PicNeuling (Gast)


Lesenswert?

Hi,

ja ich zähle pro Umdrehunge 20000 fallende und steigende Flanken, je 
5000 A und 5000 B. Ist sehr genau für eine Art "Drosselklappenstuerung".

Habs so programmiert und ist auch schneller, aber noch etwas zu langsam.

Ich hab einen 20 MHz Quarz => 1 Befehlt dauert 0,2 µs ( 1/20MHz * 4)
Mein TimerO zählt bis 256 => 256 * 0,2µs = 51,2 µs

die Interuptroutine 5 Zeilen Code * 0,2µs =>   1µs

Habe ich alle 53 µs einen Interrupt, oder irr ich mich da?


Ist es möglich, dass ich über die Config folgendes mache?!
#pragma config PLLDIV = 5
#pragma config CPUDIV = OSC1_PLL2
#pragma config USBDIV = 1

erst 20MHz durch 5 = 4MHZ für USB darausfolgt 96MHZ USBTakt / 2 => 
PICTakt 48 MHZ?
Daher dann ein Befehlt 0,08µs
.

Stimmt dies Logik, denn so interpretiere ich es auf S. 26 vom Datenblatt 
18F2550?!

Ist vielleicht ein Interupt PORTB- On- Change Interrupt schneller?

Kann mir da jemand einen Codefetzen geben, für das Problem!?

mfg

von Klaus F. (kfalser)


Lesenswert?

> die Interuptroutine 5 Zeilen Code * 0,2µs =>   1µs

Für Hochsprachen wie C/C++ geht diese Gleichung aber nicht auf !!
Jeder Assembler Befehl dauert (vielleicht, ich kenne den PIC nicht gut 
genug) gleich lang, aber eine Zeile C wird in viele Assembler Befehle 
umgesetzt!

von PicNeuling (Gast)


Lesenswert?

OK.

Dann nehmen wir mal 5µs für die Routine an .

Wie isehts mit der anderen Frage aus?
mfg

von gast (Gast)


Lesenswert?

sichrlich hat C etwas overhead ... aber wieviel kann man sich im 
avrstudio unter disassebly ansehen ...

so viel mehr code ist das nicht
wenn man in C habwegs vernünftig schreibt ist der resultierende assebler 
ebenso klein

von Ulrich (Gast)


Lesenswert?

Wenn der Interrupt vom Timer erzeugt wird. Dann kommt der Interrupt wohl 
alle 51.2 µS. Problematisch wird es erst wenn die ISR länger dauert. Das 
sollte bei dem Code wohl noch nicht der Fall sein.

So gut kenne ich den PIC nicht, aber da sollte man auch schnellere 
Interrupts hinkriegen. Ein Möglichkeit sollte es sein in der ISR den 
Timer auf einen Wert voreinzustellen, so dass der Überlauf früher kommt.

Um zu sehen wie lang die ISR wirklich dauern kann man sich den ASM Code 
ansehen, oder das gamze im Simulator laufen lassen. Sollte es für PICs 
doch wohl geben.

von PicNeuling (Gast)


Lesenswert?

Update:

So mein Interupr ist auf jeden Fall schnell genug, da ich nur 10 
Umdrehungen ( 20000 Flanken pro Umdrehung also 200000) auswerten 
muss(jeweils auf die Sekunde bezogen). Und mein Interrupt alle 2,6µs 
kommt und die Interruptroutine auf jeden Fall unter 2,4 µs benötigt. Ist 
dies schnell genug.

Nun ist aber das Problem. Er verzählt sich. Vorallem bei langen Wegen.

Daher ich starte wenn die Klappe auf dem Boden liegt. Dreh ca. 1 
Achtelumdreung ( Wert ist egal), hau sie zurück. Er zeigt mir als 
Umdrehungen +0000 an. Sobald ich weiter drehe( Dreiviertelumdrehung), 
verzählt er sich. Daher beim wieder Anfahren meinter Nullpostiion zeigt 
er -00010. jewils Unterschiede um bis zu 20 - 30 Inkremnte. Er zählt 
immer zu viel.

Ich weiß nicht woran es liegen kann ,hat irgendjemand noch eine Idee?

Hab schon die Eingangsignale mit Optokopplern aufbereitet( selbes 
Problem)


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
#include "timers.h"                              // für Timerinterrupt
6
#include "stdlib.h"                              // für Standardbibliothek
7
#include "stdio.h"                              // Für Standardein- und ausgabe
8
/** Configuration Fuses*********************************************************************************************************************************************************************************/
9
#pragma config FOSC = HS                          // Quarz HS 20 MHZ an OSC1- OSC2
10
#pragma config PWRT = ON                          // PowerOnTimer AN   
11
#pragma config BOR = OFF                          // BOR AUS
12
#pragma config WDT = OFF                          // Watchdog Timer AUS
13
#pragma config LVP = OFF                          // Low Voltage ICSP AUS
14
#pragma config PBADEN = OFF                          // PORTA Analogeingänge AUS
15
#pragma config VREGEN = OFF                          // VREGEN AUS
16
#pragma config MCLRE = ON                          // Master Reset AN
17
#pragma config PLLDIV = 1                          // PLLTeiler = 1 => 20 MHz
18
#pragma config CPUDIV = OSC1_PLL2                      // OSCTeiler = 1 => 20 MHz
19
#pragma config USBDIV = 1                          // CPUTeiler = 1 => 20 MHz
20
/** Globale Variablendeklarationen und Definierungen****************************************************************************************************************************************************/
21
signed long volatile int counter = 0;                    // Zähler -2.147.483.648 bis 2.147.483.647                  
22
signed long int counter_state1   = 0;                    // Counter Zustand 1
23
unsigned char ch0, ch1, ch2, ch3, ch4, ch5;                  // Sendevariablen                      
24
unsigned char new = 0;                            // Neuer Zustand der Kanaele A und B
25
unsigned char old = 0;                            // Alter Zustand der Kanaele A und B
26
unsigned char graycode = 0;                          // Graycode
27
signed static int graytab[] = { 0,1,-1,0, -1,0,0,1, 1,0,0,-1, 0,-1,1,0 };  // Array mit allen 16 Drehgeberzuständen (Graycode)
28
#define KANALSENSEVCC (PORTBbits.RB0)                    // Kanal SenseVCC  liegt an Bit 0 des Port B
29
#define KANALSENSEGND (PORTBbits.RB1)                    // Kanal SenseGND  liegt an Bit 1 des Port B
30
#define KANALNN       (PORTBbits.RB2)                    // Kanal N_Negiert liegt an Bit 2 des Port B
31
#define KANALN      (PORTBbits.RB3)                    // Kanal N         liegt an Bit 3 des Port B 
32
#define KANALNA     (PORTBbits.RB4)                    // Kanal A_Negiert liegt an Bit 4 des Port B
33
#define KANALNB     (PORTBbits.RB5)                    // Kanal B_Negiert liegt an Bit 5 des Port B
34
#define KANALA      (PORTBbits.RB6)                    // Kanal A         liegt an Bit 6 des Port B 
35
#define KANALB      (PORTBbits.RB7)                    // Kanal B         liegt an Bit 7 des Port B
36
/** Interrupt ******************************************************************************************************************************************************************************************/
37
void high_isr(void);                            // High Interrupt bekannt machen
38
#pragma code high_vector=0x08                        // Speicherbereich Interrupt definieren
39
void interrupt_at_high_vector(void)                      // Interrupt (High) Funktion
40
{
41
  _asm goto high_isr _endasm                        // Assembler Funktion high_isr = Interrupt Service Routine
42
}
43
#pragma code                                // Compilerbefehl Beginn Code
44
#pragma interrupt high_isr                          // Compilerbefehl Beginn InterruptCode
45
46
void high_isr (void)                            // Interrupt Routine
47
{
48
    new = (KANALA<<1) | (KANALB);                     // Kanal A + B einlesen                      
49
    graycode = ((old<<2) | (new));                             // Gray- Code bilden (2 Zustände)                
50
    counter += graytab[graycode];                                // Graycode Auswertung
51
    old = new;                                              // Neuen Zustand als Alten Zustand speichern
52
    TMR0L = 242;                            // Timer0 Register aufladen auf 242, Zähler zählt nur noch 13 Prozessorschritte => 2,6µs Auflösung für 10 Umdrehungen pro s
53
    INTCONbits.TMR0IF = 0;                        // Interrupt zurücksetzen
54
}
55
/** Programm *******************************************************************************************************************************************************************************************/
56
void InitUsart(void)                                        // Funktion USART Konfiguration            
57
{
58
  SPBRG = 20;                                // Baudrate 57600 Baud  SPBRG- Berechnung  59524 = 20000000/(16*(20+1)) = Fehlerquote unter 5% ; 1 BIT = 1,74 µs ; 57600 Baud
59
  SPBRGH =  0;                              // 8 Bit Übertragung      
60
  TRISCbits.TRISC6 = 1;                          // Aktivierung Senden TXD
61
  TRISCbits.TRISC7 = 1;                          // Aktivierung Empfangen RXD
62
  RCSTA = 0b10000100;                            // Empfangsregister konfigurieren
63
  TXSTA = 0b00100100;                            // Senderegister konfigurieren
64
}
65
void PICConfig(void)                                             // Funktion PIC Konfiguration
66
{
67
  // PORTA => Ausgänge
68
  TRISA = 0x00;                              // PORT A Ausgänge deklarieren
69
  PORTA = 0x00;                              // PORT A auf Null setzen
70
  LATA  = 0x00;                              // PORT A Latchregister auf Null setzen
71
  // PORTB  => Eingänge Inkrementalgeber
72
  TRISB = 0xFF;                              // PORT B Ausgänge deklarieren
73
  PORTB = 0x00;                              // PORT B auf Null setzen
74
  LATB  = 0x00;                              // PORT B Latchregister auf Null setzen
75
  // PORTC => Ausgänge 
76
  TRISC = 0x00;                               // PORT C Ausgänge deklarieren
77
  PORTC = 0x00;                              // PORT C auf Null setzen
78
  LATC  = 0x00;                               // PORT C Latchregister auf Null setzen
79
  // TIMER-INTERRUPT konfigurieren
80
  INTCON  = 0b11100000;                          // Interruptregister setzen
81
  INTCON2 = 0b00000000;                          // Interruptregister setzen
82
  T0CON =   0b11000000;                          // Timer 0 Konfiguration + Starten
83
}
84
char InttoChar(signed long int counter_state)                       // Funktion zur Umwandlung von INT in CHAR 
85
{
86
   int s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0;              // Variablen, die die einzelnen Stellen des Counter darstellen
87
    ch0 = '+';                                                // Positives Vorzeichen als Default                
88
     if (counter_state < 0)                                         // Falls Zähler negativ...
89
     {
90
         ch0 = '-';                                             // ...Minus als Vorzeichen
91
         counter_state = (-counter_state);                          // Vorzeichen entfernen                          
92
     }            
93
    for(;counter_state > 9999;counter_state -= 10000)            // Zehntausenderstelle
94
    {s1++;}
95
    for(;counter_state > 999;counter_state -= 1000)                     // Tausenderstelle
96
    {s2++;}
97
    for(;counter_state > 99;counter_state -= 100)                       // Hunderterstelle
98
    {s3++;}
99
    for(;counter_state > 9;counter_state -= 10)                         // Zehnerstelle
100
    {s4++;}
101
    for(;counter_state > 0;counter_state -= 1)                         // Einerstelle
102
    {s5++;}
103
                                      // Konvertierung
104
                                      // "Verschiebung" nach ASCII-Tabelle um Zeichen aus Zahlen zu erhalten
105
  ch1 = s1 + 0x30;                                            // Zehntausenderstelle
106
    ch2 = s2 + 0x30;                                             // Tausenderstelle 
107
    ch3 = s3 + 0x30;                                                    // Hunderterstelle
108
    ch4 = s4 + 0x30;                                                  // Zehnerstelle 
109
    ch5 = s5 + 0x30;                                              // Einerstelle 
110
  return ch0, ch1, ch2, ch3, ch4, ch5;                  // Rückgabe der zu sendenden Zeichen
111
}
112
void main(void)                                                  
113
{
114
  PICConfig();                              // Konfiguration der Ports
115
  InitUsart();                              // Start der USART-Schnittstelle
116
  Delay10KTCYx(10);                            // Wartezeit
117
  printf("#CONNECTED#\n");                        // Anfangsintialiserung senden
118
  while(1)                                  // Hauptschleife
119
  {
120
    INTCONbits.TMR0IE = 0;                        // Interrupts ausschalten für Variablenübergabe
121
    counter_state1 = counter/4;                      // Vergleichsvariable1 zwischenspeichern und Auflösung reduzieren 5000 Schritte pro Umdrehung
122
    INTCONbits.TMR0IE = 1;                        // Interrupts wieder aktivieren
123
    InttoChar(counter_state1);                      // Zähler in chars zum Senden umwandeln
124
    printf("%c%c%c%c%c%c\n",ch0, ch1, ch2, ch3, ch4, ch5);        // Senden des Counters  
125
  }                                    // Ende Endlosschleife
126
 }                                      /* Ende Mainfunktion */

von Gangs S. (gangsripoche)


Lesenswert?

push;-)

von geb (Gast)


Lesenswert?

Mir scheint, die Interruptroutine hat deutlich mehr als 5 Assembler- 
Befehle.
Zur bestimmung in "real" würde ich in der Interruptroutine einen Pin 
toggeln und mit dem Oszi die wirkliche Zeitdauer zu bestimmen.
Ich glaube aber an ein anderes Problem: Der Inkrementalgeber kommt in 
eine Endstellung, wo ein Ausgangssignal wechselt dh. kleine Bewegung um 
die Ruheposition.Der Fall scheint mir im code nicht berücksichtigt.
Würde auch besser deine Beobachtung erklären, denn wenn die Interrupts 
nicht mehr mitkämen, dann würde ja weniger gezählt.

Grüße gebhard

von gangsripoche (Gast)


Lesenswert?

Hi,

vielen Dank für die Antwort.

Wie würde ich den diesen Zustand abfangen im Code?
Das wäre ja noch ein paar Fragen in der Interuproutine.


Anderes Thema.

Was passiert, wenn mein Interrupt greift und die Kanäle einliest während 
einer Flanke!? Vielleicht passiert da der Fehler?

mfg

von geb (Gast)


Lesenswert?

Also ich hab auch mal einen drehgeber ausgewertet, da nahm ich 
2-Flankengesteuerte interrups(interrupt bei steigender und fallender 
Flanke). Das ging gut. War aber ein Siemens C161 ,weiß nicht ob du 
2Flankengetriggerte Interrupts hast.Sonst mußt halt 2 Interrupteingänge 
auswerten, geht auch.

Ich hab den Code noch gefunden, ist eigentlich ganz simpel


void resolver_trap(void) interrupt 0x1A
{
#define TIM_COUNT 0xFFFF-18430
static uint old_system_counter;
static uint old_tim_counts;
uint system_counts;
static ulong speed_counts;
if((P2&0x0400)==0){//Wenn A=0
    if((P3&0x0010)==0)res_pos--;//Wenn B=0 runterzählen
    else res_pos ++;//Wenn B=1 raufzählen
          }
else{if((P3&0x0010)==0)res_pos++;//A=1,B=0,raufzählen
     else res_pos --;//A=1,B=1,runterzählen
          }

system_counts=(system_counter-old_system_counter);
speed_counts=(ulong)((ulong)(T2-old_tim_counts)+(ulong)(TIM_COUNT*system 
_counts));
old_system_counter=system_counter;
old_tim_counts=T2;
}


kanst du sinngemäß übernehmen

Grüße Gebhard

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.