Kann mir jemand ein einfaches Beispiel geben wie man in C mit einem ATMEGA8 einen Encoder auswertet? Das Beispiel hier: http://www.mikrocontroller.net/articles/Drehgeber ist zwar recht nett, bezieht sich aber leider auf einen ATMEGA16. Und dort sind ja wieder andere Interrupts vorhanden.
Manfred W. schrieb: > Reicht es dann wenn ich einfach die bezeichnungen umdefiniere? Du solltest schon verstehen, was Du tust. Setze einfach mal selber einen 1ms-Timerinterrupt auf, zähle darin bis 1000 und lasse damit ne LED blinken. Peter
Ich habe die Datenbläter der beiden AVR's jetzt mal verglichen und den Code dementsprechend angepasst. Wenn ich es richtig verstanden habe sollte das so korrekt sein:
1 | #define XTAL 8e6 // 8MHz
|
2 | |
3 | #define PHASE_A (PINC & 1<<PC0)
|
4 | #define PHASE_B (PINC & 1<<PC1)
|
5 | |
6 | #define LEDS_DDR DDRD
|
7 | #define LEDS PORTD // LEDs against VCC
|
8 | |
9 | |
10 | volatile int8_t enc_delta; // -128 ... 127 |
11 | static int8_t last; |
12 | |
13 | |
14 | void encode_init( void ) |
15 | {
|
16 | int8_t new; |
17 | |
18 | new = 0; |
19 | if( PHASE_A ) |
20 | new = 3; |
21 | if( PHASE_B ) |
22 | new ^= 1; // convert gray to binary |
23 | last = new; // power on state |
24 | enc_delta = 0; |
25 | TCCR2 = 1<<WGM20^1<<CS01^1<<CS00; // CTC, XTAL / 64 |
26 | OCR2 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5); // 1ms |
27 | TIMSK |= 1<<OCIE2; |
28 | }
|
Wäre das korrekt? PINC & 1<<PC0 sagt aus das am Eingang PC0 ein HI erwartet wird? Ich muß den Encoder also mit VCC ansteuern, richtig?
Kann mir denn niemand helfen das ganze für einen ATMEGA8 anzupassen? Das problem liegt ja vermutlich nur daran die passenden Interrupts zu finden.
> Wäre das korrekt?
Fast OK, aber nicht komplett zu Ende gedacht.
Atmega16 Atmega8
================================================================
Timer0 8-Bit Timer0 8-Bit
XTAL 8 MHz XTAL 8 MHz möglich (extern und intern)
Modus WGM01 (CTC) Modus WGM01 (CTC) nicht möglich
deshalb z.B. Wechsel auf Timer2 8-Bit
TIMER0_COMP_vect TIMER2_COMP_vect
OCIE0 OCIE2
OCR0 OCR2
TCCR0 TCCR2 = 1<<WGM21^1<<CS22; // CTC2, Prescaler
64
D.h. die Änderungen sind auf vier Zeilen begrenzt
1 | ISR(TIMER2_COMP_vect) |
2 | {
|
3 | ...
|
4 | }
|
5 | |
6 | ...
|
7 | TCCR2 = 1<<WGM21^1<<CS22; // CTC2, Prescaler 64 |
8 | OCR2 = (uint8_t)(XTAL / 64.0 * 1e-3 - 0.5); // 1ms |
9 | TIMSK |= 1<<OCIE2; |
10 | }
|
Statt WGM21 hast du WGM20 bei TCCR2 gesetzt, das passt nicht. Bei den Signalleitungen kannst du mit der Atmega16 Belegung arbeiten Atmega16 Atmega8 ================================================================ #define PHASE_A (PINC & 1<<PC0) PC0 vorhanden #define PHASE_B (PINC & 1<<PC1) PC1 vorhanden #define LEDS_DDR DDRD #define LEDS PORTD PDx vorhanden
Danke erstmal! Ich bräuchte jedoch nochmal eure Hilfe. An den ATMEGA8 hängt nun ein LCD-Display drann das mir den Wert des Encoders ausgeben soll. Der Encoder ist ein ALPS STEC11B03 (15 Pulse/30 Rasterungen) Dieser hängt an PC0 und PC1. Die LCD-Ausgabe erfolgt mit der Lib von Peter Fleury. (Diese ist nur schnell mal zusammengeschustert) Leider tut sich beim drehen so gut wie gar nichts. Nur hin und wieder zuckt ein neuer Wert übers LCD daher.
1 | /************************************************************************/
|
2 | /* */
|
3 | /* Reading rotary encoder */
|
4 | /* one, two and four step encoders supported */
|
5 | /* */
|
6 | /* Author: Peter Dannegger */
|
7 | /* */
|
8 | /************************************************************************/
|
9 | #include <stdlib.h> |
10 | #include <avr/io.h> |
11 | #include "lcd.h" |
12 | #include <avr/interrupt.h> |
13 | |
14 | // target: ATmega8
|
15 | //------------------------------------------------------------------------
|
16 | |
17 | #define XTAL2 8e6 // 8MHz
|
18 | |
19 | #define PHASE_A (PINC & 1<<PC0)
|
20 | #define PHASE_B (PINC & 1<<PC1)
|
21 | |
22 | volatile int8_t enc_delta; // -128 ... 127 |
23 | static int8_t last; |
24 | |
25 | void encode_init(void) |
26 | {
|
27 | int8_t new; |
28 | |
29 | new = 0; |
30 | if(PHASE_A) |
31 | new = 3; |
32 | if(PHASE_B) |
33 | new ^= 1; // convert gray to binary |
34 | last = new; // power on state |
35 | enc_delta = 0; |
36 | TCCR2 = 1<<WGM21^1<<CS22; // CTC2, Prescaler 64 |
37 | OCR2 = (uint8_t)(XTAL2 / 64.0 * 1e-3 - 0.5); // 1ms |
38 | TIMSK |= 1<<OCIE2; |
39 | ;
|
40 | }
|
41 | |
42 | ISR(TIMER2_COMP_vect) // 1ms for manual movement |
43 | {
|
44 | int8_t new, diff; |
45 | |
46 | new = 0; |
47 | if(PHASE_A) |
48 | new = 3; |
49 | if(PHASE_B) |
50 | new ^= 1; // convert gray to binary |
51 | diff = last - new; // difference last - new |
52 | if(diff & 1) |
53 | { // bit 0 = value (1) |
54 | last = new; // store new as next last |
55 | enc_delta += (diff & 2) - 1; // bit 1 = direction (+/-) |
56 | }
|
57 | }
|
58 | |
59 | int8_t encode_read(void) // read single step encoders |
60 | {
|
61 | int8_t val; |
62 | |
63 | cli(); |
64 | val = enc_delta; |
65 | enc_delta = 0; |
66 | sei(); |
67 | return val; // counts since last call |
68 | |
69 | }
|
70 | |
71 | int main(void) |
72 | {
|
73 | int32_t val = 0; |
74 | char buffer[7]; |
75 | |
76 | encode_init(); |
77 | sei(); |
78 | lcd_init(LCD_DISP_ON); |
79 | |
80 | for(;;) |
81 | {
|
82 | val += encode_read(); |
83 | itoa(val, buffer, 10); // convert interger into string |
84 | lcd_clrscr(); // clear display home cursor |
85 | lcd_puts(buffer); // put converted string to display |
86 | }
|
87 | }
|
Manfred W. schrieb: > for(;;) > { > val += encode_read(); > itoa(val, buffer, 10); // convert interger into string > lcd_clrscr(); // clear display home cursor > lcd_puts(buffer); // put converted string to display > } Ich würde da noch den buffer mit \0 terminieren und _delay_ms() aufrufen.
Das delay hat da leider nicht viel bewirkt. Ich bekomme maximal ein -1 aufs LCD. Wo kommt das \0 hin?
Tom M. schrieb: > Ich würde da noch den buffer mit \0 terminieren Manfred W. schrieb: > Wo kommt das \0 hin? Wie das Wort "terminieren" schon sagt: An's Ende (hinter die in ASCII konvertierte Zahl)
Willi W. schrieb: > Tom M. schrieb: >> Ich würde da noch den buffer mit \0 terminieren > > Manfred W. schrieb: >> Wo kommt das \0 hin? > > Wie das Wort "terminieren" schon sagt: An's Ende (hinter die in ASCII > konvertierte Zahl) Wobei es das aber nicht sein kann. itoa macht das schon richtig.
Der AVR wird intern mit 8MHz getaktet. Wird der Encoder nun eigentlich gegen masse oder VCC geschalten?
> Ich bekomme maximal ein -1 aufs LCD. itoa bei einem int32_t val ist nix. Nimm ltoa http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga1d4c7b84110553544081a69a0fc49c52 > char buffer[7]; Ist für eine long (int32_t) Zahl potentiell zu klein und das Damoklesschwert Bufferoverflow schwebt über dem Programm. Der Puffer sollte 12 Zeichen groß sein. Ich würde die LCD Ausgabe puffern, d.h. nur Löschen und Ausgeben, wenn sich der Anzeigewert geändert hat.
1 | val = 0; |
2 | for(;;) |
3 | {
|
4 | static int32_t last_val = 256; |
5 | val += encode_read(); |
6 | if ( val != last_val ) { |
7 | last_val = val; |
8 | itoa(val, buffer, 10); // convert interger into string |
9 | lcd_clrscr(); // clear display home cursor |
10 | lcd_puts(buffer); // put converted string to display |
11 | }
|
12 | }
|
Es ist zum verzweifeln. Das Ding läuft immer noch nicht. Im Prinzip hat sich zu dem davor nicht viel geändert. Die Variable "val" zuckt von selbst sporadisch zwischen 0 und 1 oder -1 herum. Das drehen am Encoder bewirkt gar nichts. Liegts vielleicht daran das der Encoder bei mir auf beide ADC hängt? Oder macht das keinen Unterschied? Er reagiert einfach nicht.
Die I/O-Pins PC1 und PC0 arbeiten laut Programm oben nicht als ADC Pins, sondern als normale digitale Eingangspins. Wie hast du den Encoder ALPS STEC11B03 (Conrad 700697) angeschlossen, so?
1 | Vcc |
2 | | |
3 | +--+--+ |
4 | | | |
5 | # 5K | |
6 | | | |
7 | PC0 o----------o | |
8 | A | |
9 | # 5K |
10 | | |
11 | PC1 o----------------o |
12 | B |
13 | |
14 | GND o-----o |
15 | C |
16 | |
17 | ARef o |
18 | | |
19 | === 100 nF |
20 | | |
21 | AGND o----o GND |
22 | | |
23 | === 100 nF |
24 | | |
25 | AVcc o----o Vcc |
Kannst du mit dem Multimeter beim langsamen Drehen des Encoders und PLUS des Multimeters an den Pins PC0 oder PC1 und MINUS des Multimeters an GND eine mit der Drehung alternierende Spannung (<1.5V bzw. >3.5V) messen?
Oh Mann! Bis auf die beiden Widerstände am Encoder hatte ich die Schaltung genau so aufgebaut. Mit den beiden Widerständen funktioniert das Teil jetzt wunderbar. Warum wirkt sich das so gravierend auf die Funktion aus? Ich war der Meinung das diese Entprellung im Programm schon stattfindet.
Hi
>Ich war der Meinung das diese Entprellung im Programm schon stattfindet.
Die haben nichts mit Entprellung zu tun. Ein Schalter, der mit einer
Seite auf Masse liegt, kann nun mal keinen H-Pegel erzeugen.
MfG Spess
Hm, könnte man das nicht programmtechnisch lösen? Ohne das man die Widerstände benötigt?
@Manfred W. (derschatten) >Hm, könnte man das nicht programmtechnisch lösen? Ohne das man die >Widerstände benötigt? Sicher, interne einschalten.
1 | PORTC |= (1<<PC0) | (1<<PC1); |
http://www.mikrocontroller.net/articles/AVR-Tutorial:_IO-Grundlagen#Pullup-Widerstand MFG Falk
Hi
>Sicher, interne einschalten.
Für ernsthafte Anwendungen etwas schwach auf der Brust. Zum Spielen ja.
MfG Spess
Super, danke. Noch eine Frage zu dem ursprünglichen Beispiel: http://www.mikrocontroller.net/articles/Drehgeber Hier werden mittels dem Encoder LED's angesteuert, die gegen VCC geschalten sind. Muß man, damit das funktioniert den val-Wert nicht in einen Integer umwandeln? Und warum schaltet man die LED's überhaupt im Ruhezustand gegen VCC?
> Muß man, damit das funktioniert den val-Wert nicht in einen Integer > umwandeln? val ist doch ein Integer. Allerdings 32 Bit breit (int32_t). Wenn auf die 8 LEDs von PortD ausgegeben wird, findet in der Zeile LEDS = val; ein impliziter Cast (=> C Grundlagen) statt indem int32_t nach int8_t gewandelt wird. Es werden nur die unteren 8 Bits (0 bis 7) zugewiesen und die restlichen Bits (8 bis 31) werden verworfen. > Und warum schaltet man die LED's überhaupt im Ruhezustand gegen VCC? Ist hier eher Geschmackssache ob man eine active high oder eine active low (hier) Schaltung der LEDs macht. Die Varianten sind im AVR Tutorial erklärt. Historisch gibt es µC (ICs) die besser als Stromsenke (Sink) arbeiten statt als Stromquelle (Source). Bei denen ist die Schaltung gegen Vcc sinnvoller.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.