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


von Udo S. (Firma: allround) (1udo1)


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
1
int8_t encode_read1( void )
2
3
und mit
4
  
5
int8_t encode_read2( void )
6
7
und auch mit 
8
  
9
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.
1
//Steuerung mit Encode, Impuls, Eingabe (ein Motor) v1.doc
2
3
// TIMER0 ENCODER 8 BIT, 1ms TOV, Bremsrampe für Motoren, 
4
// OCR1A wird per countup und countdown in switch geladen
5
// TIMER1 PWM 10 Bit,  WARTESCHLEIFE 16 BIT, kein Teiler, 8,8ms TOV
6
// TIMER2 PWM 8 BIT ENTPRELLEN, 8,8 ms TOV
7
8
// März 2009 o.k.
9
10
// Der Motor läuft langsam bis Maximum, danach bremst er mit derselben Bremstabelle wieder auf Minimum (Fast PWM)
11
12
#include <avr/io.h>
13
#include <avr/interrupt.h>
14
#include <inttypes.h>
15
16
#define  F_CPU  7372800    
17
#include <util/delay.h>
18
#include <avr/pgmspace.h>
19
  
20
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 };
21
 
22
//############################## Globale Variablen ###############################
23
24
volatile uint8_t up1=2, countTimer1 =240;     // ISR TIMER1
25
volatile uint8_t c;          // INTERRUPT 0 und INTERRUPT 1
26
volatile char countbrems0, tmp=0, brems0, a=0; // für Warteschleifen der Motoren und switch pwm in ISR TIMER0-COMP
27
          
28
int32_t val = 0;        // Encoder
29
volatile int8_t enc_delta;      // Encoder
30
static int8_t last;        // Encoder
31
32
#define waitpind PIND
33
#define waitportbit2 PD2      // INT0
34
#define waitportbit3 PD3      // INT1
35
36
#define PHASE_A  (PIND & 1<<PD0)
37
#define PHASE_B  (PIND & 1<<PD1)
38
39
//############################## DEFINE PWM ###################################
40
41
#define OC1A_PIN PD5      // OC1A pin 
42
#define OC1A_DDR DDRD     // OC1A DDR 
43
44
#define IN1A PA0
45
#define IN1B PA1
46
47
// ####################### DEFINE Tastatur-Entprellen ########################################
48
49
// 2 Tasten -----  PD6, PD7 kurz / lang gedrückt    LED an PORTCx  wird ein- und ausgeschaltet
50
51
#define KEY_PIN    PIND
52
#define KEY7      7
53
#define KEY6      6
54
55
#define LED7      7
56
#define LED6      6
57
#define LED5      5
58
#define LED4      4
59
#define LED3      3
60
#define LED2      2
61
#define LED1      1
62
#define LED0      0
63
64
65
typedef unsigned char  u8;
66
typedef signed short  s16;
67
68
volatile u8 key_state;            
69
volatile u8 key_press;            
70
71
//############################# EXTERNER INT0 #######################################
72
ISR(INT0_vect)                   
73
{
74
  PORTC=~c; 
75
  if(waitpind & (1<<waitportbit2));
76
  
77
  c++;
78
}
79
//############################# EXTERNER INT1 #######################################
80
81
ISR(INT1_vect)                   
82
{
83
  PORTC=~c; 
84
  if(waitpind & (1<<waitportbit3));
85
  
86
  c--;
87
}
88
89
//###################### TIMER0_OVF  ENCODER #####################################
90
91
ISR(TIMER0_OVF_vect)        // 350us, da Timer0 bei Match auf Null gesetzt wird
92
                            // OCR0 hat den Ladewert 40, 
93
                            //somit wird nach 8,6us x 40 Takten ca. 350us MATCH ausgelöst gesetzt
94
95
{
96
PORTA ^= (1<<PA7);        // gemessen nix, da er durch Match von OCR0 kein TOP erreicht
97
}
98
99
//###################### TIMER 1 PWM  16 Bit Timer #######################################
100
101
ISR(TIMER1_OVF_vect)         // kein Teiler, alle 8,8ms TOV
102
103
{
104
  PORTA ^= (1<<PA6);       // gemessen 8,5ms
105
  
106
  countTimer1++;
107
108
  if(countTimer1==200)
109
    {
110
    up1++;
111
    }
112
}
113
114
//###################### TIMER2  ENTPRELLEN, WARTEN #####################################
115
116
ISR(TIMER2_OVF_vect)       
117
118
{
119
  PORTA ^= (1<<PA5);      // gemessen 2,2ms
120
  
121
    static u8 ct0, ct1; 
122
      u8 i;
123
        i = key_state ^ ~KEY_PIN;     
124
      ct0 = ~( ct0 & i );        
125
      ct1 = ct0 ^ (ct1 & i);       
126
      i &= ct0 & ct1;          
127
      key_state ^= i;         
128
      key_press |= key_state & i;        
129
}
130
//###################### TIMER0 COMPARE Interrupt ENCODER #######################################
131
  
132
ISR( TIMER0_COMP_vect )      
133
{
134
    PORTA ^= (1<<PA4); // gemessen 350us
135
    
136
    switch(a)
137
  {
138
    case 0:
139
    PORTB=0xff;
140
    {
141
      countbrems0++;      //a=0
142
143
      if(countbrems0==255)
144
      {
145
        {
146
        brems0++;      // 2,2ms x 255 = 561 ms ,countbrems0=155
147
        }
148
      
149
        if(brems0==15)
150
          {
151
          countbrems0=0;     // 561ms x 10= 5,61 Sekunden,brems0=2
152
          brems0=0;
153
           tmp++;
154
          PORTC=~tmp;
155
          if(tmp==24)
156
            {
157
            a=1;
158
            }      
159
            } // Ende if innen
160
      } //Ende if außen
161
    } break;  // Ende case 0
162
163
    case 1:
164
165
    PORTB=0xff;
166
    {          //a=1
167
      countbrems0++;
168
169
      if(countbrems0==255)
170
      {
171
        {         // 2,2ms x 255 = 561 ms ,countbrems0=155
172
        brems0++;
173
        }
174
      
175
        if(brems0==15)
176
          {        
177
            countbrems0=0;   // 561ms x 10= 5,61 Sekunden,brems0=2
178
            brems0=0;
179
             tmp--;
180
            PORTC=~tmp;
181
            if(tmp==0)
182
            {
183
            a=0;
184
            }      
185
            } // Ende if innen
186
      } //Ende if außen
187
    }break;  // Ende case 1
188
  } // Ende switch
189
    
190
191
OCR0 = 40;  
192
    
193
    int8_t new, diff;
194
    new = 0;
195
196
      if( PHASE_A )      
197
        new = 3;      
198
            if( PHASE_B )    
199
          new ^= 1;      
200
                  diff = last - new;    
201
                   if( diff & 1 )       
202
         {  
203
           last = new;    
204
         enc_delta += (diff & 2) - 1;  // bit 1 = direction (+/-)
205
        }
206
}
207
208
// ############################## TASTEN ENTPRELLEN  ####################################
209
210
u8 get_key_press( u8 key_mask )
211
{
212
  cli();                  
213
  key_mask &= key_press;                         
214
  key_press ^= key_mask;                        
215
  sei();
216
  return key_mask;
217
}
218
219
u8 get_key_short( u8 key_mask )
220
{
221
  cli();                    
222
  return get_key_press( ~key_state & key_mask );
223
}
224
225
//###################### ENCODER Übergabe  Zustand der Bits  #######################################
226
227
int8_t encode_read1( void )      // read single step encoders
228
{
229
  int8_t val;
230
 
231
  cli();
232
  val = enc_delta;
233
  enc_delta = 0;
234
  sei();
235
  return val;          // counts since last call
236
}
237
238
239
// ############################ TIMER initialisieren ##########################################
240
241
//------------------------------------- TIMER0 ---------------------------------------------------
242
 
243
void init(void)
244
{
245
TIMSK =  (1<<TOIE0)|(1<<TOIE1) |(1<<TOIE2) | 1<<OCIE0 ; 
246
                                 // TIMER0(ENCODER),TIMER1(WARTEN),
247
                                 // TIMER2(ENTPRELLEN)
248
                                 // und OCR0-Register für ENCODER Interrupt
249
250
TCCR0 = (1<<WGM01)|  (1<<CS01)|(1<<CS00);  
251
                    // 1/64 Prescale Encoder
252
                    // 1ms TOV, da T/64 2,2 ms pro TOV und 8,6us pro Takt
253
                    // 8,6us x 116 = 1ms
254
//---------------------------------------------------- TIMER1 PWM ------------------------------------------------------------------------ 
255
256
  TCCR1A =  _BV(COM1A1) | _BV(WGM11) | _BV(WGM10);  // non-inverted PWM on OC1A, 10 Bit Fast PWM
257
258
  TCCR1B = _BV(CS10) | _BV(CS11) | _BV(WGM12);  // precaler 64 -> ~122 Hz PWM frequency 
259
260
//------------------------------------------- TIMER2 ENTPRELLEN ------------------------------------------
261
  
262
263
  TCCR2 = 1<<CS22;        // CTC, Takt / 64, ENTPRELLEN
264
265
//---------------------------------------- EXTERNER INTERUPT INITIALISIERUNG -----------------------------------------------------
266
267
  GICR = (1<<INT0) | (1<<INT1);      
268
  MCUCR =(1<<ISC01) | (1<<ISC11);    
269
270
   sei();
271
272
  DDRD  &=~((1<<PD7) | (1<<PD6) | (1<<PD4) | (1<<PD3) | (1<<PD2) | (1<<PD1) ); // Eingänge, ausser PD5 OC1A PWM 
273
  
274
  DDRD |= (1<<PD5);               //LED uses OC1A
275
  PORTD = 0xff;        // PORTD ist Eingang,ausser PD5
276
}
277
//################### Encoder initialisieren und Timer 0 und Timer 2 starten ####################
278
279
void encode_init( void )
280
{
281
int8_t new;
282
   new = 0;
283
    if( PHASE_A )          
284
        new = 3;
285
       if( PHASE_B )     
286
         new ^= 1;    
287
       last = new;    
288
       enc_delta = 0;
289
}
290
 
291
int main(void)
292
{
293
      
294
       init();
295
296
    DDRA=0xff;
297
    PORTA = 0xfe;
298
299
    DDRB=0xff;
300
    PORTB = 0xff;
301
302
    DDRC=0xff;
303
    PORTC = 0xff;
304
305
  
306
307
      while(1) 
308
    {
309
      OCR1A = pgm_read_word(pwmtable+tmp);
310
            
311
      if( get_key_press( 1<<KEY7 ))
312
            PORTC ^= (1<<LED7);
313
314
      if( get_key_press( 1<<KEY6 ))
315
      PORTC ^= (1<<LED6);
316
317
      val += encode_read1();      
318
319
      PORTB = ~val; 
320
321
    } // Ende while
322
  return 0;
323
} // Ende main

von Oliver (Gast)


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

von Oliver (Gast)


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:
1
ISR(INT1_vect)                   
2
{
3
  PORTC=~c; 
4
  if(waitpind & (1<<waitportbit3));
5
  
6
  c--;
7
}

Ist das Semikolon nach dem if so gewollt?

Oliver

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.