Forum: Mikrocontroller und Digitale Elektronik Atmega8 Encoder + USB HID spinnt manchmal


von Kilo K. (kilo81)


Lesenswert?

Aloha,

ich habe mal eine Frage:
Ich spiele die Flugsimulation Falcon 4.0 BMS, dort werden im Cockpit 
verschiedene Potis bewegt bezüglich Lautstärke Funk, etc.

Für meine Kippschalter benutze ich folgende Schaltung:
https://www.obdev.at/products/vusb/hidkeys-de.html

Nun habe ich für einen lautstärkeregler im Cockpit einen Encoder an 
diese USB HID Schaltung angeschlossen mit folgendem Code:
http://www.leniwiec.org/en/2014/04/28/rotary-shaft-encoder-how-to-connect-it-and-handle-it-with-avr-atmega8-16-32-168-328/

Letztes Beispiel, OHNE Verwendung von Timern!

So und jetzt mein problem. Bei einem Encoder klappt das wunderbar!! man 
sieht im Cockpit sogar das Poti bewegen.
habe ich 2 Encoder an meiner Schaltung, dann bewegt sich der eine Knopf 
im Cockpit nur manchmal und betätige ich den 2. Encoder, dann bewegt 
sich sogar ein anderer Knopf im Cockpit, obwohl die tastenkombination 
eine völlig andere ist. Bei 3 Encodern noch extremer...

Warum kommen die sich in die Quere? Encoder 1 sendet die Tastenkombi 
SHIFT+ALT+Ü und Encoder 2: SHIFT+ALT+CTRL+Ü. Also eine ganz andere, 
dennoch bewegt sich der eine Knopf manchmal mit.

Gruß
Dominik

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Dominik Kristen schrieb:
> mit folgendem Code:
> http://www.leniwiec.org/...
> Letztes Beispiel
Dieser Code ist wegen des delay_ms() bestenfalls ein krudes 
Funktionsmodell. Zudem kann ich dort nicht sehen, wie du damit 3 Encoder 
einlesen willst.

Und nachdem der Code dort sogar funktionieren würde, dein Code aber 
Probleme macht, wäre es sinnvoll, wenn du den problembehafteten Code 
zeigen würdest...

von Dominik (Gast)


Lesenswert?

ich habe einfach jeden Encoder für sich selber definert:
1
uint8_t read_gray_code_from_encoder_1(void )
2
{ 
3
 uint8_t val1=0; 
4
5
  if(!bit_is_clear(PIND, PD2))
6
  val1 |= (1<<1); 
7
8
  if(!bit_is_clear(PIND, PD3))
9
  val1 |= (1<<0); 
10
11
  return val1;
12
} 
13
14
uint8_t read_gray_code_from_encoder_2(void )
15
{ 
16
 uint8_t val2=0; 
17
18
  if(!bit_is_clear(PIND, PD4))
19
  val2 |= (1<<1); 
20
21
  if(!bit_is_clear(PIND, PD5))
22
  val2 |= (1<<0); 
23
24
  return val2;
25
}

und so weiter...

So, hier ist der code:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <avr/pgmspace.h>
4
#include <avr/wdt.h>
5
#include <util/delay.h>
6
7
#include "usbdrv.h"
8
#include "oddebug.h"
9
10
uint8_t val1=0, val_tmp1 =0;
11
uint8_t val2=0, val_tmp2 =0;
12
uint8_t val3=0, val_tmp3 =0;
13
14
uint8_t read_gray_code_from_encoder_1(void )
15
{ 
16
 uint8_t val1=0; 
17
18
  if(!bit_is_clear(PIND, PC0))
19
  val1 |= (1<<1); 
20
21
  if(!bit_is_clear(PIND, PC1))
22
  val1 |= (1<<0); 
23
24
  return val1;
25
} 
26
uint8_t read_gray_code_from_encoder_2(void )
27
{ 
28
 uint8_t val2=0; 
29
30
  if(!bit_is_clear(PIND, PC2))
31
  val2 |= (1<<1); 
32
33
  if(!bit_is_clear(PIND, PC3))
34
  val2 |= (1<<0); 
35
36
  return val2;
37
} 
38
uint8_t read_gray_code_from_encoder_3(void )
39
{ 
40
 uint8_t val3=0; 
41
42
  if(!bit_is_clear(PIND, PC4))
43
  val3 |= (1<<1); 
44
45
  if(!bit_is_clear(PIND, PC5))
46
  val3 |= (1<<0); 
47
48
  return val3;
49
} 
50
51
52
53
54
55
56
57
58
static void hardwareInit(void)
59
{
60
uchar  i, j;
61
62
    PORTB = 0xff;   /* activate all pull-ups */
63
    DDRB = 0;       /* all pins input */
64
    PORTC = 0xff;   /* activate all pull-ups */
65
    DDRC = 0;       /* all pins input */
66
    PORTD = 0xfa;   /* 1111 1010 bin: activate pull-ups except on USB lines */
67
    DDRD = 0x07;    /* 0000 0111 bin: all pins input except USB (-> USB reset) */
68
  j = 0;
69
  while(--j){     /* USB Reset by device only required on Watchdog Reset */
70
    i = 0;
71
    while(--i); /* delay >10ms for USB reset */
72
  }
73
    DDRD = 0x02;    /* 0000 0010 bin: remove USB reset condition */
74
    /* configure timer 0 for a rate of 12M/(1024 * 256) = 45.78 Hz (~22ms) */
75
    TCCR0 = 5;      /* timer 0 prescaler: 1024 */
76
  DDRD  = 0b00000000;
77
  DDRD |= _BV(PD7);
78
}
79
80
/* ------------------------------------------------------------------------- */
81
82
#define NUM_KEYS    17
83
84
/* The following function returns an index for the first key pressed. It
85
 * returns 0 if no key is pressed.
86
 */
87
static uchar    keyPressed(void)
88
{
89
90
val_tmp1 = read_gray_code_from_encoder_1(); 
91
val_tmp2 = read_gray_code_from_encoder_2(); 
92
val_tmp3 = read_gray_code_from_encoder_3(); 
93
94
     if(val1 != val_tmp1)
95
     { 
96
       if( /*(val==2 && val_tmp==3) ||*/
97
         (val1==3 && val_tmp1==1) || 
98
         /*(val==1 && val_tmp==0) ||*/
99
         (val1==0 && val_tmp1==2) 
100
       ) 
101
       { 
102
        return 1;
103
       } 
104
       else if( /*(val==3 && val_tmp==2) ||*/
105
         (val1==2 && val_tmp1==0) || 
106
         /*(val==0 && val_tmp==1) ||*/
107
         (val1==1 && val_tmp1==3) 
108
       ) 
109
       { 
110
        return 2;
111
       } 
112
113
       val1 = val_tmp1; 
114
     } 
115
116
     if(val2 != val_tmp2)
117
     { 
118
       if( /*(val==2 && val_tmp==3) ||*/
119
         (val2==3 && val_tmp2==1) || 
120
         /*(val==1 && val_tmp==0) ||*/
121
         (val2==0 && val_tmp2==2) 
122
       ) 
123
       { 
124
        return 3;
125
       } 
126
       else if( /*(val==3 && val_tmp==2) ||*/
127
         (val2==2 && val_tmp2==0) || 
128
         /*(val==0 && val_tmp==1) ||*/
129
         (val2==1 && val_tmp2==3) 
130
       ) 
131
       { 
132
        return 4;
133
       } 
134
135
       val2 = val_tmp2; 
136
     } 
137
138
     if(val3 != val_tmp3)
139
     { 
140
       if( /*(val==2 && val_tmp==3) ||*/
141
         (val3==3 && val_tmp3==1) || 
142
         /*(val==1 && val_tmp==0) ||*/
143
         (val3==0 && val_tmp3==2) 
144
       ) 
145
       { 
146
        return 5;
147
       } 
148
       else if( /*(val==3 && val_tmp==2) ||*/
149
         (val3==2 && val_tmp3==0) || 
150
         /*(val==0 && val_tmp==1) ||*/
151
         (val3==1 && val_tmp3==3) 
152
       ) 
153
       { 
154
        return 6;
155
       } 
156
157
       val3 = val_tmp3; 
158
     } 
159
160
_delay_ms(1); 
161
 
162
163
164
165
    
166
}
167
168
/* ------------------------------------------------------------------------- */
169
/* ----------------------------- USB interface ----------------------------- */
170
/* ------------------------------------------------------------------------- */
171
172
static uchar    reportBuffer[2];    /* buffer for HID reports */
173
static uchar    idleRate;           /* in 4 ms units */
174
175
const PROGMEM char usbHidReportDescriptor[35] = {   /* USB report descriptor */
176
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
177
    0x09, 0x06,                    // USAGE (Keyboard)
178
    0xa1, 0x01,                    // COLLECTION (Application)
179
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
180
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
181
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
182
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
183
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
184
    0x75, 0x01,                    //   REPORT_SIZE (1)
185
    0x95, 0x08,                    //   REPORT_COUNT (8)
186
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
187
    0x95, 0x01,                    //   REPORT_COUNT (1)
188
    0x75, 0x08,                    //   REPORT_SIZE (8)
189
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
190
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
191
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
192
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
193
    0xc0                           // END_COLLECTION
194
};
195
196
#define MOD_CONTROL_LEFT    (1<<0)
197
#define MOD_SHIFT_LEFT      (1<<1)
198
#define MOD_ALT_LEFT        (1<<2)
199
#define MOD_GUI_LEFT        (1<<3)
200
#define MOD_CONTROL_RIGHT   (1<<4)
201
#define MOD_SHIFT_RIGHT     (1<<5)
202
#define MOD_ALT_RIGHT       (1<<6)
203
#define MOD_GUI_RIGHT       (1<<7)
204
205
#define KEY_A       4
206
#define KEY_B       5
207
#define KEY_C       6
208
#define KEY_D       7
209
#define KEY_E       8
210
#define KEY_F       9
211
#define KEY_G       10
212
#define KEY_H       11
213
#define KEY_I       12
214
#define KEY_J       13
215
#define KEY_K       14
216
#define KEY_L       15
217
#define KEY_M       16
218
#define KEY_N       17
219
#define KEY_O       18
220
#define KEY_P       19
221
#define KEY_Q       20
222
#define KEY_R       21
223
#define KEY_S       22
224
#define KEY_T       23
225
#define KEY_U       24
226
#define KEY_V       25
227
#define KEY_W       26
228
#define KEY_X       27
229
#define KEY_Y       28
230
#define KEY_Z       29
231
#define KEY_1       30
232
#define KEY_2       31
233
#define KEY_3       32
234
#define KEY_4       33
235
#define KEY_5       34
236
#define KEY_6       35
237
#define KEY_7       36
238
#define KEY_8       37
239
#define KEY_9       38
240
#define KEY_0       39
241
242
#define KEY_UE      47
243
#define KEY_PLUS    48
244
245
#define KEY_F1      58
246
#define KEY_F2      59
247
#define KEY_F3      60
248
#define KEY_F4      61
249
#define KEY_F5      62
250
#define KEY_F6      63
251
#define KEY_F7      64
252
#define KEY_F8      65
253
#define KEY_F9      66
254
#define KEY_F10     67
255
#define KEY_F11     68
256
#define KEY_F12     69
257
258
static const uchar  keyReport[NUM_KEYS + 1][2] PROGMEM = {
259
/* none */  {0, 0},                     /* no key pressed */
260
/*  1 */    {MOD_CONTROL_LEFT | MOD_SHIFT_LEFT, KEY_UE},  
261
/*  2 */    {MOD_CONTROL_LEFT | MOD_SHIFT_LEFT, KEY_PLUS},  
262
/*  3 */    {MOD_ALT_LEFT | MOD_CONTROL_LEFT | MOD_SHIFT_LEFT, KEY_UE},             
263
/*  4 */    {MOD_ALT_LEFT | MOD_CONTROL_LEFT | MOD_SHIFT_LEFT, KEY_PLUS},          
264
/*  5 */    {MOD_ALT_LEFT | MOD_CONTROL_LEFT, KEY_UE},
265
/*  6 */    {MOD_ALT_LEFT | MOD_CONTROL_LEFT, KEY_PLUS},
266
/*  7 */    {0, KEY_G},
267
/*  8 */    {0, KEY_H},
268
/*  9 */    {0, KEY_I},
269
/* 10 */    {0, KEY_J},
270
/* 11 */    {0, KEY_K},
271
/* 12 */    {0, KEY_L},
272
/* 13 */    {0, KEY_M},
273
/* 14 */    {0, KEY_N},
274
/* 15 */    {0, KEY_O},
275
/* 16 */    {0, KEY_P},
276
/* 17 */    {0, KEY_Q},
277
};
278
279
static void buildReport(uchar key)
280
{
281
/* This (not so elegant) cast saves us 10 bytes of program memory */
282
    *(int *)reportBuffer = pgm_read_word(keyReport[key]);
283
  
284
}
285
286
uchar  usbFunctionSetup(uchar data[8])
287
{
288
usbRequest_t    *rq = (void *)data;
289
290
    usbMsgPtr = reportBuffer;
291
    if((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS){    /* class request type */
292
        if(rq->bRequest == USBRQ_HID_GET_REPORT){  /* wValue: ReportType (highbyte), ReportID (lowbyte) */
293
            /* we only have one report type, so don't look at wValue */
294
            buildReport(keyPressed());
295
            return sizeof(reportBuffer);
296
        }else if(rq->bRequest == USBRQ_HID_GET_IDLE){
297
            usbMsgPtr = &idleRate;
298
            return 1;
299
        }else if(rq->bRequest == USBRQ_HID_SET_IDLE){
300
            idleRate = rq->wValue.bytes[1];
301
        }
302
    }else{
303
        /* no vendor specific requests implemented */
304
    }
305
  return 0;
306
}
307
308
/* ------------------------------------------------------------------------- */
309
310
int  main(void)
311
{
312
313
 
314
315
  
316
317
318
val1 = read_gray_code_from_encoder_1(); 
319
val2 = read_gray_code_from_encoder_2(); 
320
val3 = read_gray_code_from_encoder_3(); 
321
322
323
324
    uchar   key, lastKey = 0, keyDidChange = 0;
325
326
    hardwareInit();
327
  usbInit();
328
  sei();
329
330
  for(;;)
331
  {
332
333
334
335
336
    usbPoll();
337
        key = keyPressed();
338
  
339
        if(lastKey != key){
340
            lastKey = key;
341
            keyDidChange = 1;
342
        }
343
344
        if((keyDidChange == 1) && usbInterruptIsReady()){
345
            keyDidChange = 2;
346
            buildReport(lastKey);
347
            usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
348
        }
349
350
    if((keyDidChange == 2) && usbInterruptIsReady())
351
    {
352
      keyDidChange = 0;
353
            buildReport(0);
354
            usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
355
    }
356
357
  }
358
  return 0;
359
}
360
361
/* ------------------------------------------------------------------------- */

Wie gesagt: Im prinzip funktioniert es. Nur kommen sich die Encoder 
manchmal in die Quere. Der eine sendet sogar zusätzlich ein 
tastenkürzen, welches ich gar nicht definiert habe.

von Stefan F. (Gast)


Lesenswert?

Bei der Schaltung würde ich nicht lange suchen.

Zum einen hast du da zwei Dioden in der Spannungsversorgung, die einen 
beträchtlichen Innenwiderstand haben. Zweitens sind die I/O Pins von AVR 
Mikrocontrollern nicht USB konform.

Das diese Schaltung unzuverlässig arbeitet, wundert mich gar nicht. Die 
ganzen billigen ISP Programmer, die auf dem selben Prinzip beruhen, sind 
ja auch unzuverlässig.

von Dominik (Gast)


Lesenswert?

Aber warum funktioniert es mit Kippschaltern, Tastern und so weiter?
Die Encoder werden ja auch in der Schleife ständig abgefragt und sind 
unabhängig von einem Timer.

Hat also bei 3 Encodern die Übertragung per USB etwas damit zu tun?

von Vusb (Gast)


Lesenswert?

Ich tippe eher auf ein Optimierungsproblem. VUSB ist empfindlich wenn 
das timing nicht stimmt und man sollte auf keinen fall delays benutzen. 
Das bringt die Usb-Verbindung durcheinander weil die hart an der Grenze 
ist für AVRs (ohne HW-Usb).

Änder dein code so dass er ohne delays auskommt oder wechsel auf ein Avr 
mit Usb. Mega32u4 zum beispiel. Gibts ein Arduino Micro oder Nano oder 
so der den hat. Damit ist ein Eigenbaucockpit kinderleicht gemacht...

von Dominik (Gast)


Lesenswert?

Ich habe mir auch schon fast gedacht, dass es am Soft-USB liegen könnte.

ich werde mal die Encoder nicht in der Flugsim testen sondern mit dem 
Editor. Dann sehe ich wann, welche zeichen gesendet werden.
das _delay nehme ich danach mal raus.

von Kilo K. (kilo81)


Lesenswert?

Achso: AVR wechseln wäre jetzt nicht meine 1. Priorität... :)

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Vusb schrieb:
> Ich tippe eher auf ein Optimierungsproblem.

Kann gut sein! Das hier zum Beispiel wird sehr wahrscheinlich 
wegoptimiert.
1
 j = 0;
2
  while(--j){     /* USB Reset by device only required on Watchdog Reset */
3
    i = 0;
4
    while(--i); /* delay >10ms for USB reset */
5
  }
Wenn ich mich recht erinnere, habe dieses Stück Code gleich 
rausgeworfen, als ich den USBasp für den guloprog (ATtiny85) angepasst 
habe.

> VUSB ist empfindlich wenn
> das timing nicht stimmt und man sollte auf keinen fall delays benutzen.
> Das bringt die Usb-Verbindung durcheinander weil die hart an der Grenze
> ist für AVRs (ohne HW-Usb).

Das sehe ich eher anders, weil VUSB mit Interrupts arbeitet. delay_ms() 
läuft meines Wissens im Vordergrund und wartet aktiv, kommt also mit den 
Interrupts nicht ins Gehege – was im Fall von VUSB eher ein Vorteil ist.

> Änder dein code so dass er ohne delays auskommt oder wechsel auf ein Avr
> mit Usb. Mega32u4 zum beispiel. Gibts ein Arduino Micro oder Nano oder
> so der den hat. Damit ist ein Eigenbaucockpit kinderleicht gemacht...

Der 32U4 ist sicher eine feine Sache, da läuft HW-USB sogar ohne Quarz. 
Für viele kleine Anwendungen ist er aber vielleicht doch eine Nummer zu 
groß.

von Vusb (Gast)


Lesenswert?

Na bei 4-6€ für ein Pro-Micro Breakout Board ist mir der overkill nicht 
so wichtig. Ok, dauert Wochen aus China, aber ich hatte die eh mal im 
10er Pack bestellt, dann noch günstiger. Das schöne ist wenn man 
wirklich nur ein original F16-Stick aus den 80ern (Gameport und aus 
Metall und so!) wiederbeleben will dann ist das damit und mit der 
Arduino-IDE ein Nobrainer und man kann sich auf die mechanische 
Umsetzung konzentrieren.

Genauso einfach geht auch fast alles andere USB-HID-inputmässig.

Aber ich will niemand davon abbringen ein vorhandenes Vusb zum laufen zu 
bringen! Wollte nur eine mögliche Alternative aufzeigen die ich für 
etwas Ähnliches benutzt hab. Vusb ist toll, nicht ohne Grung hab ich 
damals für meine Bastelei von denen eine Lizenz mit meinen eigenen 
Vid/Pid geholt...

Mit an der Grenze meinte ich die Geschwindigkeit der 
USB-Lowspeed-Kommunikation, die den AVR im augenblich in dem über USB 
kommuniziert wird fast voll auslastet um die Signale zu samplen bzw. zu 
senden. Wenn da was dazwisvhen kommt oder man zu viel in die 
Hauptschleife packt (das hatte ich mal) dann kann Kauderwelsch 
rauskommen. Ich hab dann delays weggelassen und statt dessen eine 
statemachine gebaut die mit einem freizählenden Timer vergleicht um 
Zeiten zu bekommen.

Viel Erfolg!

von Kilo K. (kilo81)


Lesenswert?

So, _delay rausgenommen und:
1
j = 0;
2
  while(--j){     /* USB Reset by device only required on Watchdog Reset */
3
    i = 0;
4
    while(--i); /* delay >10ms for USB reset */
5
  }

auskommentiert....
Problem besteht weiterhin. :(
Encoder 3 funzt super!
Encoder 1 nur manchmal
Encoder 2 bewegt die anderen Knöpfe gleich mit :(

von Kilo K. (kilo81)


Lesenswert?

Noch eine Anmerkung.
hab mir mal Buchstaben senden lassen an den Texteditor.
Encoder 2 und 3 senden bei einem "Klick" 2 Impulse. 1 Impuls kommt 
bereits, bevor ein Klick zu hören ist!!! Dreht man also langsam, bevor 
es klick macht, dann funktioniert alles wi es soll. Aber das kann ja 
nicht sein :(


Encoder 1 funktioniert einwandfrei und ist übrigens von eine, anderen 
Hersteller. Weiß aber nicht welcher.
Encoder 2 und 3 sehen aus wie Potis von Alps. Keine Ahnung ob es diese 
Marke ist.

Mir scheint, es liegt also nicht an der Schaltung sondern an den 
Encodern?!

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Deine Encoderabfrage ist auch sehr empfindlich gegen prellende 
Drehgeber. Du solltest dir mal die Encoder Routine von PeDa anschauen:
https://www.mikrocontroller.net/articles/Drehgeber

Die kommt selbst mit völlig abgenudelten Teilen klar, wenn man den Timer 
ein wenig anpasst.

von Kilo K. (kilo81)


Lesenswert?

Hm.. ich probiere das mal!
Muss da nur den Timer1 nehmen, weil der andere ja schon für USB ist...
hatte da anfangs auch Probleme mit dem Timer1 :(

von Kilo K. (kilo81)


Lesenswert?

Also mit dem Code von Peter Dannegger tut sich gar nichts bei mir :(

Was soll denn eigentlich als 'val' rauskommen? 0 und 1?
Kann mir mal einer sagen ob ich den Timer1 falsch initialisiere?
1
void encode_init( void )
2
{
3
  int8_t new;
4
 
5
  new = 0;
6
  if( PHASE_A )
7
    new = 3;
8
  if( PHASE_B )
9
    new ^= 1;                   // convert gray to binary
10
  last = new;                   // power on state
11
  enc_delta = 0;
12
  TCCR1A = (1<<COM1A0);                // CTC mode, toggle OC1A on compare match
13
  TCCR1B = (1<<CS12)|(1<<CS10)         // Start Timer1 with prescaler 1024
14
              |(1<<WGM12);
15
  OCR1A = (uint8_t)(8 / 64.0 * 1e-3 - 0.5);   // 1ms
16
  TIMSK |= 1<<OCIE1A;
17
}
18
 
19
 
20
ISR( TIMER1_COMP_vect )             // 1ms for manual movement
21
{
22
  int8_t new, diff;
23
 
24
  new = 0;
25
  if( PHASE_A )
26
    new = 3;
27
  if( PHASE_B )
28
    new ^= 1;                   // convert gray to binary
29
  diff = last - new;                // difference last - new
30
  if( diff & 1 ){               // bit 0 = value (1)
31
    last = new;                 // store new as next last
32
    enc_delta += (diff & 2) - 1;        // bit 1 = direction (+/-)
33
  }
34
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Du hast die Abfrage des Encoders vergessen:
1
int8_t encode_read2( void )         // read two step encoders
2
{
3
  int8_t val; 
4
  cli();
5
  val = enc_delta;
6
  enc_delta = val & 1;
7
  sei();
8
  return val >> 1;
9
}
10
// Das Beispiel aus dem Artikel:
11
int main( void )
12
{
13
  int32_t val = 0;
14
 
15
  LEDS_DDR = 0xFF;
16
  encode_init();
17
  sei();
18
 
19
  for(;;){
20
    val += encode_read2();          // read a 2 step encoder
21
    LEDS = val;
22
  }
23
}

von Kilo K. (kilo81)


Lesenswert?

ja die hatte ich auch drinnen... mir gings jetzt nur um den Timer1.
Denn mit dem lief gar nichts :(

Ich weiß auch gar nichts was 'val' für einen Wert ausgibt.
Bei meinem Beispiel, konnte ich nämlich auch auswählen, welcher Wert 
zurückgegeben werden soll:
1
if(val1 != val_tmp1)
2
     { 
3
       if( /*(val==2 && val_tmp==3) ||*/
4
         (val1==3 && val_tmp1==1) || 
5
         /*(val==1 && val_tmp==0) ||*/
6
         (val1==0 && val_tmp1==2) 
7
       ) 
8
       { 
9
        return 1;
10
       }
Encoder 1 soll 1 und 2 zurückgeben als zahl
Encoder 2 => 3 und 4
und so weiter...

Das Beispiel von PeDa läuft bei mir nicht.. Also ich kriege das mit dem 
Timer anscheinend nicht hin.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Dominik Kristen schrieb:
> Das Beispiel von PeDa läuft bei mir nicht.. Also ich kriege das mit dem
> Timer anscheinend nicht hin.

Je nach verwendetem AVR löst der CTC Interrupt verschiedene ISR aus. Bei 
manchen (z.B. der Tiny26) ist es der Timer Overflow, bei manchen der OC 
Interrupt. Ich habe schon länger nichts mehr mit dem Mega8 gemacht, 
glaube aber, das es im CTC der OC1 Interrupt ist, wäre also bei dir 
richtig.

> TCCR1A = (1<<COM1A0);                // CTC mode, toggle OC1A on
> compare match

Das solltest du nicht tun, denn du willst ja gar kein Signal am Pin, 
sondern nur den Interrupt. Kann aber zum Testen nützlich sein, wenn der 
Pin frei ist.
Achte auf die Prioritäten: Der USB Interrupt muss höhere Priorität als 
der Timer sein, evtl. musst du die Timer ISR als 'non-blocking' 
erklären:
1
ISR( TIMER1_COMPA_vect,ISR_NOBLOCK )
Beachte ausserdem deinen Schreibfehler - das Dings heisst 
ISR(TIMER1_COMPA_vect), steht so in der avr_libc Referenz.

> Ich weiß auch gar nichts was 'val' für einen Wert ausgibt.

In PeDas Beispiel wird val hoch und runtergezählt, je nachdem, ob du 
rechts oder links drehst. Fängt bei 0 an und ist dann eine 
vorzeichenbehaftete Variable:
1
  int32_t val = 0;

: Bearbeitet durch User
von Kilo K. (kilo81)


Lesenswert?

Hallo Matthias,

danke schön. Ja ich habs nicht so mit Timern! :(
Meinen Schreibfehler hab ich auch schon bemerkt. Vielleicht lag es auch 
daran. Muss ich später nochmal testen.

von Kilo K. (kilo81)


Lesenswert?

Mal ne andere Frage:
Wie könnte man denn ein Poti an den ADC hängen, bzw. es programmieren, 
dass es sich wie ein Encoder verhält?
Sprich, bei einer 8bit Auflösung soll es alle, sagen wir mal, 20 
Schritte eine Variable hochzählen. Bzw. runterzählen wenn ich wieder 
zurückdrehe.
Das müsste doch auch irgendwie gehen oder?

von Karl M. (Gast)


Lesenswert?

Dominik,

wie machst Du das mit dem Anschlägen am Poti ? 360° kann es halt nicht.

Sonst kann man das Poti auch direkt zur Eingabe von Werten verwenden.

von Ulrich F. (Gast)


Lesenswert?

> wie machst Du das mit dem Anschlägen am Poti ? 360° kann es halt nicht.
Es gibt 360° Potis ohne Anschläge

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Dominik Kristen schrieb:
> Sprich, bei einer 8bit Auflösung soll es alle, sagen wir mal, 20
> Schritte eine Variable hochzählen. Bzw. runterzählen wenn ich wieder
> zurückdrehe.

Das macht der ADC alleine, er liefert grössere Werte, wenn mehr Spannung 
am Eingang liegt und kleinere Werte, wenn weniger Spannung da ist.
Die AVR haben im Allgemeinen 10 Bit Wandler, so das eine Spannung nahe 
AREF Werte um die 1023 liefert, null Volt am Eingang bringen etwa Werte 
nahe 0.
Für 8 bit Werte reicht es aus, nur ADCH zu lesen, sofern ADLAR gesetzt 
ist. Steht aber alles im Datenblatt.
Nur mitteln solltest du kräftig, sonst zappelt der Wert. Für einen 
Joystick ist es evtl. auch sinnvoll, erst dann neue Werte zu liefern, 
wenn eine gewisse Schwelle zum vorherigen Wert überschritten wird.

von Kilo K. (kilo81)


Lesenswert?

Ne es muss nicht 360° können.. das ist egal!
Wenn ich das Poti "aufdrehe", will ich alle 20 ADC-Schritte eine 
variable hochzählen.
Genauso umgekehrt, wenn ich wieder zurückdrehe.
ich frage mich nur, wie lege ich die Schwelle fest.

von Kilo K. (kilo81)


Lesenswert?

Matthias Sch. schrieb:
> Dominik Kristen schrieb:
>> Sprich, bei einer 8bit Auflösung soll es alle, sagen wir mal, 20
>> Schritte eine Variable hochzählen. Bzw. runterzählen wenn ich wieder
>> zurückdrehe.
>
> Das macht der ADC alleine, er liefert grössere Werte, wenn mehr Spannung
> am Eingang liegt und kleinere Werte, wenn weniger Spannung da ist.
> Die AVR haben im Allgemeinen 10 Bit Wandler, so das eine Spannung nahe
> AREF Werte um die 1023 liefert, null Volt am Eingang bringen etwa Werte
> nahe 0.
> Für 8 bit Werte reicht es aus, nur ADCH zu lesen, sofern ADLAR gesetzt
> ist. Steht aber alles im Datenblatt.
> Nur mitteln solltest du kräftig, sonst zappelt der Wert. Für einen
> Joystick ist es evtl. auch sinnvoll, erst dann neue Werte zu liefern,
> wenn eine gewisse Schwelle zum vorherigen Wert überschritten wird.

ja das stimmt schon. Aber ich will meine eigenen Werte definieren 
können.
hm... Dann müsste ich den ADC Wert teilen, so dass ich auf zahlen von 
1-20 komme.

Ein Joystick wird es nicht. Es soll weiterhin als tastaturcontroller Key 
commands senden.
Also um das mal zu erklären:

Ein lautstärkepoti im Cockpit der Simulation kann mit '+' aufgedreht 
werden und mit 'ü' wieder leiser gedreht werden.

jetzt könnte man einen Joystick anschließen und das Signal einer 
analogen Achse zuweisen. Das will ich aber aus einigen Gründen nicht!
D.h. ich verwende weiterhin den Tastaturcontroller und schicke mein 'ü' 
und mein '+' per USB raus. Bei einem Encoder ist das ja leicht. Da kann 
ich pro Klick nach rechts die Taste senden.
Bei einem Poti wird es da aber schon schwieriger.

: Bearbeitet durch User
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.