mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Timmer Interrupt zu langsam.


Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
// PORTB-ON-CHANGE-INTERRUPT konfigurieren
  INTCON  = 0b11100000;              // Interruptregister setzen
  INTCON2 = 0b00000000;              // Interruptregister setzen
  //Timer 0 Konfiguration + Starten
  T0CON = 0b11000000;
[\c]


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.

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?

VIelen Dank schon einmal

grüße

Hier ist der komplette CODE

[c]
/** I N C L U D E S **********************************************************/
#include "p18f2550.h"                // PIC- Header
#include "delays.h"                   // für Warteschleifen
#include "usart.h"                  // für RS232- Schnittstelle
#include "timers.h"                  // für Timerinterrupt
#include "stdlib.h"                  // für Standardbibliothek
/** Configuration Fuses********************************************************/
#pragma config FOSC = HS              // Quarz HS 20 MHZ an OSC1- OSC2
#pragma config PWRT = ON              // PowerOnTimer AN   
#pragma config BOR = OFF              // BOR AUS
#pragma config WDT = OFF              // Watchdog Timer
#pragma config LVP = OFF              // Low Voltage ICSP AUS
#pragma config PBADEN = OFF              // PORTA Analogeingänge AUS
#pragma config VREGEN = OFF              // VREGEN AUS
#pragma config MCLRE = ON              // Master Reset AN
#pragma config PLLDIV = 1              // PLLTeiler = 1 => 20 MHz
#pragma config CPUDIV = OSC1_PLL2          // OSCTeiler = 1 => 20 MHz
#pragma config USBDIV = 1              // CPUTeiler = 1 => 20 MHz
/** Globale Variablendeklarationen*********************************************/
signed long int counter = 0;            // Zähler -2.147.483.648 bis 2.147.483.647
signed long int counter_old = 0;          // Vergleichszähler
signed long int counter_test = 0;
char ch0, ch1, ch2, ch3, ch4, ch5, ch6;
char ch0 = '+';                    // Vorzeichen
char ch1 = '\n';                  // Schlusszeichen
char code = 0;                    // Graycode
char new = 0;                    // Neuer Zustand
char old = 0;                    // Alter Zustand
/** Programm **************************************************/
void high_isr(void);
#pragma code high_vector=0x08
void interrupt_at_high_vector(void)
{
  _asm goto high_isr _endasm
}
#pragma code
#pragma interrupt high_isr
void high_isr (void)
{
    new = (PORTBbits.RB6<<1) | (PORTBbits.RB7); // Kanal A + B einlesen
    code = (old<<2) | (new);                    // Graycode bilden
    switch (code)                               // Gray- Code Auswertung
    {
      case 0b00000001: counter++;  break;      // Drehung im Uhrzeigersinn
      case 0b00000111: counter++;  break;      // Drehung im Uhrzeigersinn
      case 0b00001110: counter++;  break;      // Drehung im Uhrzeigersinn
      case 0b00001000: counter++;  break;      // Drehung im Uhrzeigersinn
      case 0b00000010: counter--;  break;      // Drehung gegen den Uhrzeigersinn
      case 0b00000100: counter--;  break;      // Drehung gegen den Uhrzeigersinn
      case 0b00001101: counter--;  break;      // Drehung gegen den Uhrzeigersinn
      case 0b00001011: counter--;  break;      // Drehung gegen den Uhrzeigersinn

    //  case 0b00000011: PORTCbits.RC2 = 1;  break;      // Fehler
    //  case 0b00000110: PORTCbits.RC2 = 1;  break;      // Fehler
    //  case 0b00001100: PORTCbits.RC2 = 1;  break;      // Fehler
    //  case 0b00001001: PORTCbits.RC2 = 1;  break;      // Fehler
    //  0000 , 1111 , 0101 , 1010 keine Veränderung des Zustandes
      }                                        // Ende Switch- Anweisung
    old = new;                                  // Neuen Zustand als Alten Zustand speichern
    INTCONbits.TMR0IF = 0;            // Interrupt zurücksetzen
}
void InitUsart(void)                            // Funktion USART Konfiguration            
{
  SPBRG = 20;                    // Baudrate 57600 Baud  SPBRG- Berechnung  59524 = 20000000/(16*(20+1)) = Fehlerquote unter 5% ; 1 BIT = 1,74 µs ; 57600 Baud
  SPBRGH =  0;                  // 8 Bit Übertragung      
  TRISCbits.TRISC6 = 1;              // Aktivierung Senden TXD
  TRISCbits.TRISC7 = 1;              // Aktivierung Empfangen RXD
  RCSTA = 0b10000100;                // Empfangsregister konfigurieren
  TXSTA = 0b00100100;                // Senderegister konfigurieren
}
void PICConfig(void)                                // Funktion PIC Konfiguration
{
                          // PORTA => Ausgänge LEDs RA0, RA4, RA5
  TRISA = 0x00;                  // PORT A Ausgänge deklarieren
  PORTA = 0x00;                  // PORT A auf Null setzen
  LATA  = 0x00;                  // PORT A Latchregister auf Null setzen
                          // PORTB  => Eingänge Inkrementalgeber
  TRISB = 0xFF;                  // PORT B Ausgänge deklarieren
  PORTB = 0x00;                  // PORT B auf Null setzen
  LATB  = 0x00;                  // PORT B Latchregister auf Null setzen
                          // PORTC => alles Ausgänge  RC0, RC1, RC2
  TRISC = 0x00;                   // PORT C Ausgänge deklarieren
  PORTC = 0x00;                  // PORT C auf Null setzen
  LATC  = 0x00;                   // PORT C Latchregister auf Null setzen
                          // PORTB-ON-CHANGE-INTERRUPT konfigurieren
  INTCON  = 0b11100000;              // Interruptregister setzen
  INTCON2 = 0b00000000;              // Interruptregister setzen
  //Timer 0 Konfiguration + Starten
  T0CON = 0b11000000;
  return 0;                                       // Rückgabewert (void) Konfiguration
}
void Senden(unsigned char byte)                     // Funktion für USART Senden
{
  WriteUSART(byte);                // Byte in RS232 Sendebyte schreiben
  while(BusyUSART( ));              // Warten bis Sendebyte leer
  return 0;                                       // Rückgabewert (void) Senden
}
void InttoChar(void)             // Funktion INT in CHAR Umwandlung
{
   int s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0;  // Counterstellenvariablen
   signed long int  ncounter = 0;         // Hilfscounter
   ncounter = counter/4;                            // Originalcounter zwischenspeichern
     if(ncounter == 0 || ncounter > 0)                // Zähler positiv?
     {
    ch0 = '+';                                  // Vorzeichen positiv
     }
     if (ncounter < 0)                               // Zähler negativ?
     {
         ch0 = '-';                                 // Vorzeichen negativ
         ncounter = (ncounter*(-1));                  // Invertieren
     }            
    for(;ncounter > 9999;ncounter -= 10000)           // Zehntausenderstellen
    {s1++;}
    for(;ncounter > 999;ncounter -= 1000)             // Tausenderstellen
    {s2++;}
    for(;ncounter > 99;ncounter -= 100)               // Hunderterstellen
    {s3++;}
    for(;ncounter > 9;ncounter -= 10)                 // Zehnerstellen
    {s4++;}
    for(;ncounter > 0;ncounter -= 1)                  // Einerstellen
    {s5++;}
    ch1 = s1 + 0x30;                                // Zehntausenderstelle CHAR zum Senden
    ch2 = s2 + 0x30;                                // Tausenderstelle CHAR zum Senden
    ch3 = s3 + 0x30;                                // Hunderterstelle CHAR zum Senden
    ch4 = s4 + 0x30;                                // Zehnerstelle CHAR zum Senden
    ch5 = s5 + 0x30;                                // Einerstelle CHAR zum Senden
  ch6 = '\n';                                      // Abschlusszeichen CHAR zum Senden
//  counter = ncounter;                        // Originalzähler zurückgeben
    return 0;                                       // Rückgabewert (void) Umwandlung
}
void main(void)                                     // Hauptprogramm starten
{
  PICConfig();                  // Konfigurieren der PORTs
  InitUsart();                  // Start der USART- Schnittstelle
  PORTCbits.RC0 = 1;                // Anschalten EIN/Aus Lampe
  //loop
  while(1)                      //Hauptschleife
  {
  //  if(counter > 0)
  //  { PORTAbits.RA4 = 1; }
  //  if(counter < 0)
  //  { PORTAbits.RA5 = 1; }
    counter_test = counter/4;
    while(counter_test != counter_old)
    {
    InttoChar();              // Zähler in chars zum Senden umwandeln
    Senden(ch0);                // Senden Vorzeichen
    Senden(ch1);                // Senden Zehntausender
    Senden(ch2);                // Senden Tausender
    Senden(ch3);                // Senden Hunderter
    Senden(ch4);                // Senden Zehner
    Senden(ch5);                // Senden Einer
    Senden(ch6);                  // Senden               
    counter_old = counter_test;
    }
    PORTCbits.RC1 = 0;              // LED Senden AUS
    PORTCbits.RC2 = 0;              // LED Fehler AUS
    PORTAbits.RA4 = 0;              // LED Rechts AUS
    PORTAbits.RA5 = 0;              // LED Links  AUS
  }                        // Ende Endlosschleife
 }                          /* Ende Mainfunktion */

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
...

Autor: PICNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jemand eine Idee?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ulrich (Gast)
Datum:

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

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ah jetzt versteh ich was du meinst.

Gut ich werde das mal so schreiben und probieren.

danke

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
static char code graytab[] = { 0,1,-1,0, -1,0,0,1, 1,0,0,-1, 0,-1,1,0 };
unsigned char graycode = 0;
unsigned char = 0;
unsigned old = 0;
unsigned new = 0;




void high_isr (void)
{
    new = (KANALA<<1) | (KANALB);         // Kanal A + B einlesen
    graycode = ((old<<2) | (new));                     // Gray- Code bilden (2 Zustände)
    counter += graytab[graycode];                          // Graycode Auswertung
    old = new;                                      // Neuen Zustand als Alten Zustand speichern
    INTCONbits.TMR0IF = 0;          // Interrupt zurücksetzen
}
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

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
*push;-)*

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Klaus Falser (kfalser)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK.

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

Wie isehts mit der anderen Frage aus?
mfg

Autor: gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Ulrich (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: PicNeuling (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)


/** I N C L U D E S ************************************************************************************************************************************************************************************/
#include "p18f2550.h"                            // PIC- Header
#include "delays.h"                               // für Warteschleifen
#include "usart.h"                              // für RS232- Schnittstelle
#include "timers.h"                              // für Timerinterrupt
#include "stdlib.h"                              // für Standardbibliothek
#include "stdio.h"                              // Für Standardein- und ausgabe
/** Configuration Fuses*********************************************************************************************************************************************************************************/
#pragma config FOSC = HS                          // Quarz HS 20 MHZ an OSC1- OSC2
#pragma config PWRT = ON                          // PowerOnTimer AN   
#pragma config BOR = OFF                          // BOR AUS
#pragma config WDT = OFF                          // Watchdog Timer AUS
#pragma config LVP = OFF                          // Low Voltage ICSP AUS
#pragma config PBADEN = OFF                          // PORTA Analogeingänge AUS
#pragma config VREGEN = OFF                          // VREGEN AUS
#pragma config MCLRE = ON                          // Master Reset AN
#pragma config PLLDIV = 1                          // PLLTeiler = 1 => 20 MHz
#pragma config CPUDIV = OSC1_PLL2                      // OSCTeiler = 1 => 20 MHz
#pragma config USBDIV = 1                          // CPUTeiler = 1 => 20 MHz
/** Globale Variablendeklarationen und Definierungen****************************************************************************************************************************************************/
signed long volatile int counter = 0;                    // Zähler -2.147.483.648 bis 2.147.483.647                  
signed long int counter_state1   = 0;                    // Counter Zustand 1
unsigned char ch0, ch1, ch2, ch3, ch4, ch5;                  // Sendevariablen                      
unsigned char new = 0;                            // Neuer Zustand der Kanaele A und B
unsigned char old = 0;                            // Alter Zustand der Kanaele A und B
unsigned char graycode = 0;                          // Graycode
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)
#define KANALSENSEVCC (PORTBbits.RB0)                    // Kanal SenseVCC  liegt an Bit 0 des Port B
#define KANALSENSEGND (PORTBbits.RB1)                    // Kanal SenseGND  liegt an Bit 1 des Port B
#define KANALNN       (PORTBbits.RB2)                    // Kanal N_Negiert liegt an Bit 2 des Port B
#define KANALN      (PORTBbits.RB3)                    // Kanal N         liegt an Bit 3 des Port B 
#define KANALNA     (PORTBbits.RB4)                    // Kanal A_Negiert liegt an Bit 4 des Port B
#define KANALNB     (PORTBbits.RB5)                    // Kanal B_Negiert liegt an Bit 5 des Port B
#define KANALA      (PORTBbits.RB6)                    // Kanal A         liegt an Bit 6 des Port B 
#define KANALB      (PORTBbits.RB7)                    // Kanal B         liegt an Bit 7 des Port B
/** Interrupt ******************************************************************************************************************************************************************************************/
void high_isr(void);                            // High Interrupt bekannt machen
#pragma code high_vector=0x08                        // Speicherbereich Interrupt definieren
void interrupt_at_high_vector(void)                      // Interrupt (High) Funktion
{
  _asm goto high_isr _endasm                        // Assembler Funktion high_isr = Interrupt Service Routine
}
#pragma code                                // Compilerbefehl Beginn Code
#pragma interrupt high_isr                          // Compilerbefehl Beginn InterruptCode

void high_isr (void)                            // Interrupt Routine
{
    new = (KANALA<<1) | (KANALB);                     // Kanal A + B einlesen                      
    graycode = ((old<<2) | (new));                             // Gray- Code bilden (2 Zustände)                
    counter += graytab[graycode];                                // Graycode Auswertung
    old = new;                                              // Neuen Zustand als Alten Zustand speichern
    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
    INTCONbits.TMR0IF = 0;                        // Interrupt zurücksetzen
}
/** Programm *******************************************************************************************************************************************************************************************/
void InitUsart(void)                                        // Funktion USART Konfiguration            
{
  SPBRG = 20;                                // Baudrate 57600 Baud  SPBRG- Berechnung  59524 = 20000000/(16*(20+1)) = Fehlerquote unter 5% ; 1 BIT = 1,74 µs ; 57600 Baud
  SPBRGH =  0;                              // 8 Bit Übertragung      
  TRISCbits.TRISC6 = 1;                          // Aktivierung Senden TXD
  TRISCbits.TRISC7 = 1;                          // Aktivierung Empfangen RXD
  RCSTA = 0b10000100;                            // Empfangsregister konfigurieren
  TXSTA = 0b00100100;                            // Senderegister konfigurieren
}
void PICConfig(void)                                             // Funktion PIC Konfiguration
{
  // PORTA => Ausgänge
  TRISA = 0x00;                              // PORT A Ausgänge deklarieren
  PORTA = 0x00;                              // PORT A auf Null setzen
  LATA  = 0x00;                              // PORT A Latchregister auf Null setzen
  // PORTB  => Eingänge Inkrementalgeber
  TRISB = 0xFF;                              // PORT B Ausgänge deklarieren
  PORTB = 0x00;                              // PORT B auf Null setzen
  LATB  = 0x00;                              // PORT B Latchregister auf Null setzen
  // PORTC => Ausgänge 
  TRISC = 0x00;                               // PORT C Ausgänge deklarieren
  PORTC = 0x00;                              // PORT C auf Null setzen
  LATC  = 0x00;                               // PORT C Latchregister auf Null setzen
  // TIMER-INTERRUPT konfigurieren
  INTCON  = 0b11100000;                          // Interruptregister setzen
  INTCON2 = 0b00000000;                          // Interruptregister setzen
  T0CON =   0b11000000;                          // Timer 0 Konfiguration + Starten
}
char InttoChar(signed long int counter_state)                       // Funktion zur Umwandlung von INT in CHAR 
{
   int s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0;              // Variablen, die die einzelnen Stellen des Counter darstellen
    ch0 = '+';                                                // Positives Vorzeichen als Default                
     if (counter_state < 0)                                         // Falls Zähler negativ...
     {
         ch0 = '-';                                             // ...Minus als Vorzeichen
         counter_state = (-counter_state);                          // Vorzeichen entfernen                          
     }            
    for(;counter_state > 9999;counter_state -= 10000)            // Zehntausenderstelle
    {s1++;}
    for(;counter_state > 999;counter_state -= 1000)                     // Tausenderstelle
    {s2++;}
    for(;counter_state > 99;counter_state -= 100)                       // Hunderterstelle
    {s3++;}
    for(;counter_state > 9;counter_state -= 10)                         // Zehnerstelle
    {s4++;}
    for(;counter_state > 0;counter_state -= 1)                         // Einerstelle
    {s5++;}
                                      // Konvertierung
                                      // "Verschiebung" nach ASCII-Tabelle um Zeichen aus Zahlen zu erhalten
  ch1 = s1 + 0x30;                                            // Zehntausenderstelle
    ch2 = s2 + 0x30;                                             // Tausenderstelle 
    ch3 = s3 + 0x30;                                                    // Hunderterstelle
    ch4 = s4 + 0x30;                                                  // Zehnerstelle 
    ch5 = s5 + 0x30;                                              // Einerstelle 
  return ch0, ch1, ch2, ch3, ch4, ch5;                  // Rückgabe der zu sendenden Zeichen
}
void main(void)                                                  
{
  PICConfig();                              // Konfiguration der Ports
  InitUsart();                              // Start der USART-Schnittstelle
  Delay10KTCYx(10);                            // Wartezeit
  printf("#CONNECTED#\n");                        // Anfangsintialiserung senden
  while(1)                                  // Hauptschleife
  {
    INTCONbits.TMR0IE = 0;                        // Interrupts ausschalten für Variablenübergabe
    counter_state1 = counter/4;                      // Vergleichsvariable1 zwischenspeichern und Auflösung reduzieren 5000 Schritte pro Umdrehung
    INTCONbits.TMR0IE = 1;                        // Interrupts wieder aktivieren
    InttoChar(counter_state1);                      // Zähler in chars zum Senden umwandeln
    printf("%c%c%c%c%c%c\n",ch0, ch1, ch2, ch3, ch4, ch5);        // Senden des Counters  
  }                                    // Ende Endlosschleife
 }                                      /* Ende Mainfunktion */

Autor: Gangs Sripoche (gangsripoche)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
push;-)

Autor: geb (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: gangsripoche (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: geb (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.