mikrocontroller.net

Forum: Compiler & IDEs Debouncing 8 Keys und rotary encoder von P. Dannegger.


Autor: Udo Scharnitzki (Firma: allround) (1udo1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

(eingesetzter Prozessor: MEGA16)

ich benutze Debouncing 8 Keys und Reading rotary encoder von PETER 
Dannegger innerhalb des untenstehenden Programms. Der Motor wird mit 
FAST PWM angesteuert. Fast alles funktioniert zufriedenstellend mit 
Ausnahme des Einlesens der Encoderscheibe  mit A-B Kanal 
Gabellichtschranke(Robotik-Hardware). Die ist auf der Motorachse 
montiert. Die Anzeige des "val-Wertes" an PORTB flattert. Ich habe es 
schon mit
int8_t encode_read1( void )

und mit
  
int8_t encode_read2( void )

und auch mit 
  
int8_t encode_read4( void )

versucht. Die Anzeige an PORTB hat Aussetzer. Sieht aus, als ob der 
Eingang prellt. Ich habe es mit 1ms und momentan mit ca. 350us 
TIMER_COMPARE probiert. Mit dem ALPS Drehgeber funktioniert der ENCODER 
einwandfrei.

An der ENCODER-Scheibe mit 120 Strichen kann ich keine ausgefranzten 
Striche erkennen. Die wird in Ordnung sein. Der Encoder von Peter muss 
doch so eine Scheibe locker einlesen. Da bin ich sicher.

Ich benutze 3 Timer.

Timer0_COMPARE 350us
TIMER1 8,5 ms und
TIMER2 2,2 ms

Meine 2 Fragen:

1   Ist eine solche ENCODER (Plastik)- Scheibe nur bedingt einsetzbar?

2   Kommen sich die Timer irgendwie in die Quere, vielleicht mit
    Prioritäten.
    Denke daran, dass sich irgendein Timer vorfuscht und Timer0 die Show
    stiehlt, woraufhin er ein paar Takte von der Encoderscheibe nicht
    einlesen kann. Ich glaube nicht, dass das der Fall ist.
    Aber vielleicht kann mir das jemand beantworten, ob jeder Timer
    seinen Job macht, ohne den anderen zu stören.

Ich habe die switch-Schleife in TIMER0_COMPARE in Verdacht. Die hat 
bestimmt zu viel Code,oder? Über den Daumen gerechnet sind das ca. 30 
bis 40us vor der eigentlichen Auswertung der Phasen A und B!!???? Oder 
macht das den Braten nicht fett?

Hier ist mein funktionierendes Programm, mit Ausnahme der richtigen 
Anzeige an PORTB.
//Steuerung mit Encode, Impuls, Eingabe (ein Motor) v1.doc

// TIMER0 ENCODER 8 BIT, 1ms TOV, Bremsrampe für Motoren, 
// OCR1A wird per countup und countdown in switch geladen
// TIMER1 PWM 10 Bit,  WARTESCHLEIFE 16 BIT, kein Teiler, 8,8ms TOV
// TIMER2 PWM 8 BIT ENTPRELLEN, 8,8 ms TOV

// März 2009 o.k.

// Der Motor läuft langsam bis Maximum, danach bremst er mit derselben Bremstabelle wieder auf Minimum (Fast PWM)

#include <avr/io.h>
#include <avr/interrupt.h>
#include <inttypes.h>

#define  F_CPU  7372800    
#include <util/delay.h>
#include <avr/pgmspace.h>
  
uint16_t pwmtable[25]  PROGMEM =   {  80,80,85,90,90,100,115,190, 295, 385, 425, 465,4 85, 495, 505,535, 585,685, 785, 815, 865, 895, 950, 1000,1023 };
 
//############################## Globale Variablen ###############################

volatile uint8_t up1=2, countTimer1 =240;     // ISR TIMER1
volatile uint8_t c;          // INTERRUPT 0 und INTERRUPT 1
volatile char countbrems0, tmp=0, brems0, a=0; // für Warteschleifen der Motoren und switch pwm in ISR TIMER0-COMP
          
int32_t val = 0;        // Encoder
volatile int8_t enc_delta;      // Encoder
static int8_t last;        // Encoder

#define waitpind PIND
#define waitportbit2 PD2      // INT0
#define waitportbit3 PD3      // INT1

#define PHASE_A  (PIND & 1<<PD0)
#define PHASE_B  (PIND & 1<<PD1)

//############################## DEFINE PWM ###################################

#define OC1A_PIN PD5      // OC1A pin 
#define OC1A_DDR DDRD     // OC1A DDR 

#define IN1A PA0
#define IN1B PA1

// ####################### DEFINE Tastatur-Entprellen ########################################

// 2 Tasten -----  PD6, PD7 kurz / lang gedrückt    LED an PORTCx  wird ein- und ausgeschaltet

#define KEY_PIN    PIND
#define KEY7      7
#define KEY6      6

#define LED7      7
#define LED6      6
#define LED5      5
#define LED4      4
#define LED3      3
#define LED2      2
#define LED1      1
#define LED0      0


typedef unsigned char  u8;
typedef signed short  s16;

volatile u8 key_state;            
volatile u8 key_press;            

//############################# EXTERNER INT0 #######################################
ISR(INT0_vect)                   
{
  PORTC=~c; 
  if(waitpind & (1<<waitportbit2));
  
  c++;
}
//############################# EXTERNER INT1 #######################################

ISR(INT1_vect)                   
{
  PORTC=~c; 
  if(waitpind & (1<<waitportbit3));
  
  c--;
}

//###################### TIMER0_OVF  ENCODER #####################################

ISR(TIMER0_OVF_vect)        // 350us, da Timer0 bei Match auf Null gesetzt wird
                            // OCR0 hat den Ladewert 40, 
                            //somit wird nach 8,6us x 40 Takten ca. 350us MATCH ausgelöst gesetzt

{
PORTA ^= (1<<PA7);        // gemessen nix, da er durch Match von OCR0 kein TOP erreicht
}

//###################### TIMER 1 PWM  16 Bit Timer #######################################

ISR(TIMER1_OVF_vect)         // kein Teiler, alle 8,8ms TOV

{
  PORTA ^= (1<<PA6);       // gemessen 8,5ms
  
  countTimer1++;

  if(countTimer1==200)
    {
    up1++;
    }
}

//###################### TIMER2  ENTPRELLEN, WARTEN #####################################

ISR(TIMER2_OVF_vect)       

{
  PORTA ^= (1<<PA5);      // gemessen 2,2ms
  
    static u8 ct0, ct1; 
      u8 i;
        i = key_state ^ ~KEY_PIN;     
      ct0 = ~( ct0 & i );        
      ct1 = ct0 ^ (ct1 & i);       
      i &= ct0 & ct1;          
      key_state ^= i;         
      key_press |= key_state & i;        
}
//###################### TIMER0 COMPARE Interrupt ENCODER #######################################
  
ISR( TIMER0_COMP_vect )      
{
    PORTA ^= (1<<PA4); // gemessen 350us
    
    switch(a)
  {
    case 0:
    PORTB=0xff;
    {
      countbrems0++;      //a=0

      if(countbrems0==255)
      {
        {
        brems0++;      // 2,2ms x 255 = 561 ms ,countbrems0=155
        }
      
        if(brems0==15)
          {
          countbrems0=0;     // 561ms x 10= 5,61 Sekunden,brems0=2
          brems0=0;
           tmp++;
          PORTC=~tmp;
          if(tmp==24)
            {
            a=1;
            }      
            } // Ende if innen
      } //Ende if außen
    } break;  // Ende case 0

    case 1:

    PORTB=0xff;
    {          //a=1
      countbrems0++;

      if(countbrems0==255)
      {
        {         // 2,2ms x 255 = 561 ms ,countbrems0=155
        brems0++;
        }
      
        if(brems0==15)
          {        
            countbrems0=0;   // 561ms x 10= 5,61 Sekunden,brems0=2
            brems0=0;
             tmp--;
            PORTC=~tmp;
            if(tmp==0)
            {
            a=0;
            }      
            } // Ende if innen
      } //Ende if außen
    }break;  // Ende case 1
  } // Ende switch
    

OCR0 = 40;  
    
    int8_t new, diff;
    new = 0;

      if( PHASE_A )      
        new = 3;      
            if( PHASE_B )    
          new ^= 1;      
                  diff = last - new;    
                   if( diff & 1 )       
         {  
           last = new;    
         enc_delta += (diff & 2) - 1;  // bit 1 = direction (+/-)
        }
}

// ############################## TASTEN ENTPRELLEN  ####################################

u8 get_key_press( u8 key_mask )
{
  cli();                  
  key_mask &= key_press;                         
  key_press ^= key_mask;                        
  sei();
  return key_mask;
}

u8 get_key_short( u8 key_mask )
{
  cli();                    
  return get_key_press( ~key_state & key_mask );
}

//###################### ENCODER Übergabe  Zustand der Bits  #######################################

int8_t encode_read1( void )      // read single step encoders
{
  int8_t val;
 
  cli();
  val = enc_delta;
  enc_delta = 0;
  sei();
  return val;          // counts since last call
}


// ############################ TIMER initialisieren ##########################################

//------------------------------------- TIMER0 ---------------------------------------------------
 
void init(void)
{
TIMSK =  (1<<TOIE0)|(1<<TOIE1) |(1<<TOIE2) | 1<<OCIE0 ; 
                                 // TIMER0(ENCODER),TIMER1(WARTEN),
                                 // TIMER2(ENTPRELLEN)
                                 // und OCR0-Register für ENCODER Interrupt

TCCR0 = (1<<WGM01)|  (1<<CS01)|(1<<CS00);  
                    // 1/64 Prescale Encoder
                    // 1ms TOV, da T/64 2,2 ms pro TOV und 8,6us pro Takt
                    // 8,6us x 116 = 1ms
//---------------------------------------------------- TIMER1 PWM ------------------------------------------------------------------------ 

  TCCR1A =  _BV(COM1A1) | _BV(WGM11) | _BV(WGM10);  // non-inverted PWM on OC1A, 10 Bit Fast PWM

  TCCR1B = _BV(CS10) | _BV(CS11) | _BV(WGM12);  // precaler 64 -> ~122 Hz PWM frequency 

//------------------------------------------- TIMER2 ENTPRELLEN ------------------------------------------
  

  TCCR2 = 1<<CS22;        // CTC, Takt / 64, ENTPRELLEN

//---------------------------------------- EXTERNER INTERUPT INITIALISIERUNG -----------------------------------------------------

  GICR = (1<<INT0) | (1<<INT1);      
  MCUCR =(1<<ISC01) | (1<<ISC11);    

   sei();

  DDRD  &=~((1<<PD7) | (1<<PD6) | (1<<PD4) | (1<<PD3) | (1<<PD2) | (1<<PD1) ); // Eingänge, ausser PD5 OC1A PWM 
  
  DDRD |= (1<<PD5);               //LED uses OC1A
  PORTD = 0xff;        // PORTD ist Eingang,ausser PD5
}
//################### Encoder initialisieren und Timer 0 und Timer 2 starten ####################

void encode_init( void )
{
int8_t new;
   new = 0;
    if( PHASE_A )          
        new = 3;
       if( PHASE_B )     
         new ^= 1;    
       last = new;    
       enc_delta = 0;
}
 
int main(void)
{
      
       init();

    DDRA=0xff;
    PORTA = 0xfe;

    DDRB=0xff;
    PORTB = 0xff;

    DDRC=0xff;
    PORTC = 0xff;

  

      while(1) 
    {
      OCR1A = pgm_read_word(pwmtable+tmp);
            
      if( get_key_press( 1<<KEY7 ))
            PORTC ^= (1<<LED7);

      if( get_key_press( 1<<KEY6 ))
      PORTC ^= (1<<LED6);

      val += encode_read1();      

      PORTB = ~val; 

    } // Ende while
  return 0;
} // Ende main


Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei den paar Befehlen in den ISR'S holt der Prozessor zwischendrin noch 
Kaffee und liest Zeitung.

Eventuell ist die Taktfrequenz der Encoderauswertimers zu niedrig. Hast 
du da schonmal höhere Werte probiert?  Mit welcher Drehzahl läuft der 
Motor?

Oliver

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
Übersehen:
>Ich habe es mit 1ms und momentan mit ca. 350us TIMER_COMPARE probiert.

Bei 1kHz ISR-Takt verliert die Auswertung schon mit einem handbedienten 
32-Puls-Geber Takte, wenn man nur etwas forsch am Rad dreht. Ohne jetzt 
im einzelnen Takte gezählt zu haben, dürfte deine ISR auch noch 
problemlos mit 20kHz (und mehr) laufen. Die paar ifs und x++ sind 
schnell gemacht. Probiers aus.

OT:
ISR(INT1_vect)                   
{
  PORTC=~c; 
  if(waitpind & (1<<waitportbit3));
  
  c--;
}

Ist das Semikolon nach dem if so gewollt?

Oliver

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.