Forum: Mikrocontroller und Digitale Elektronik Encoder mit ATMEGA8


von Lokus P. (derschatten)


Lesenswert?

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.

von Arne S. (Gast)


Lesenswert?

Der ATmega8 hat die ganzen Interrupts, die du dafür brauchst aber 
auch...

von Lokus P. (derschatten)


Lesenswert?

Reicht es dann wenn ich einfach die bezeichnungen umdefiniere?

von Peter D. (peda)


Lesenswert?

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

von Lokus P. (derschatten)


Lesenswert?

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?

von Lokus P. (derschatten)


Lesenswert?

Kann mir denn niemand helfen das ganze für einen ATMEGA8 anzupassen?
Das problem liegt ja vermutlich nur daran die passenden Interrupts zu 
finden.

von Helfer (Gast)


Lesenswert?

> 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

von Lokus P. (derschatten)


Lesenswert?

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
}

von Tom M. (tomm) Benutzerseite


Lesenswert?

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.

von Lokus P. (derschatten)


Lesenswert?

Das delay hat da leider nicht viel bewirkt.
Ich bekomme maximal ein -1 aufs LCD.

Wo kommt das \0 hin?

von Willi W. (Gast)


Lesenswert?

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)

von Karl H. (kbuchegg)


Lesenswert?

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.

von Lokus P. (derschatten)


Lesenswert?

Der AVR wird intern mit 8MHz getaktet.
Wird der Encoder nun eigentlich gegen masse oder VCC geschalten?

von Falk B. (falk)


Lesenswert?

Masse

von Helfer (Gast)


Lesenswert?

> 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
  }

von Lokus P. (derschatten)


Lesenswert?

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.

von Helfer (Gast)


Lesenswert?

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?

von Lokus P. (derschatten)


Lesenswert?

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.

von spess53 (Gast)


Lesenswert?

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

von Lokus P. (derschatten)


Lesenswert?

Hm, könnte man das nicht programmtechnisch lösen? Ohne das man die 
Widerstände benötigt?

von Falk B. (falk)


Lesenswert?

@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

von spess53 (Gast)


Lesenswert?

Hi

>Sicher, interne einschalten.

Für ernsthafte Anwendungen etwas schwach auf der Brust. Zum Spielen ja.

MfG Spess

von Lokus P. (derschatten)


Lesenswert?

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?

von Helfer (Gast)


Lesenswert?

> 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
Noch kein Account? Hier anmelden.