ext. Interrupt mit Inkrementalgeber und 8051-Controller

OP #2467644
Lesenswert?

Hallo zusammen,
ich versuch grad mit einem Inkrementalgeber einen Zähler zu verändern 
und den aktuellen Wert des Zählers zur Kontrolle über LEDs wieder 
auszugeben. Als Controller verwende ich den AT89S8253.

Ein Signal des Gebers geht auf Port 3.2 und das andere auf Port 3.3. Der 
Interrupt wird über Port 3.2 ausgelöst. Die LEDs sind an Port 0 
angeschlossen.

Der Geber hat 15 Inkremente und 30 Raststellungen pro Umdrehung. Je 
nachdem in welcher Stelle er sich gerade befindet zeigten die LEDs 
entweder irgend ein Leuchtmuster an oder die LEDs 0 bis 6 leuchten und 
die 7. blinkt. Ich hab im Moment keine Ahnung was ich an dem Programm 
verändern muss und auch m Netz hab ich nichts gefunden.
1
#include <REG8253.H>
2

3
;------------------------------------------------
4

5
; Reservierung des Stack-Bereichs
6

7
?STACK SEGMENT IDATA    
8

9
  RSEG  ?STACK         
10

11
  DS  10H  
12

13
;------------------------------------------------
14

15
; Variablen im int. direkt adressierbaren RAM
16

17
Variablen SEGMENT DATA    ;Variablen im direkt adressierbaren internen RAM
18

19
  RSEG  Variablen       ;Variablen-Segment waehlen
20

21
zahl:  DS  1    ;Reserviere 1Byte
22

23
zeit1:  DS  1
24

25
zeit100:DS  1
26

27
;--------------------------------------------------------------------------
28
; Auf Adresse 0 im Code-Speicher muss ein LJMP zum Programmanfang stehen
29

30
  CSEG    AT  0           ;absolutes Code-Segment bei Adresse 0
31

32
  ljmp    start           ;Springe zur Marke start
33

34
;--------------------------------------------------------------------------
35
; Interrupt Vektoren
36

37
  cseg at 03h
38

39
  ljmp positionierung
40

41
;--------------------------------------------------------------------------
42
; Hier folgt Programmcode (Code-Beginn ueber Linker festgelegt)
43

44
program_code   SEGMENT CODE
45

46
        RSEG   program_code     ;waehle Code-Segment
47

48
        USING   0               ;Zeige Benuetzung von Registerbank 0 fuer den folgenden Programmcode an
49

50
;--------------------------------------------------------------------------
51
;Initialisierung  Stack und I/O-Karte
52

53
start:
54

55
  mov sp,#?stack-1  ;STACK-Definition
56

57
;--------------------------------------------------------------------------
58

59

60
  setb ea      ;Interrupts zulassen
61

62

63

64
  mov zahl,#0    ;Zahl vordefinieren
65

66

67

68
loop:
69

70
  mov a,zahl    ;Zahl ausgeben
71

72
  cpl a
73

74
  mov p0,a
75

76
  setb ex0    ;Fenster für Interrupt
77

78
  nop
79

80
  clr ex0
81

82

83

84
  ljmp loop    ;Hauptschleife
85

86

87

88
;--------------------------------------------------------------------------
89

90

91
positionierung:      ;externer Interrupt 0
92

93
  jb p3.3,invers    ;Drehrichtung
94

95
  inc zahl    ;hochzählen
96

97
  ljmp positionierung2  ;wieder zusammenführen
98

99
invers:
100

101
  dec zahl    ;runterzählen
102

103
positionierung2:
104

105
  lcall t100ms    ;Entprellzeit
106

107
  clr ie0      ;Flag löschen
108

109
  reti
110

111

112

113
;--------------------------------------------------------------------------
114

115

116
t1ms:        ;1ms Wartezeit
117

118
  mov zeit1,#250
119

120
zeitmarke1:
121

122
  nop
123

124
  nop
125

126
  djnz zeit1,zeitmarke1
127

128
  ret
129

130

131

132
t100ms:        ;100ms Wartezeit
133

134
  mov zeit100,#100
135

136
zeitmarke100:
137

138
  djnz zeit100,zeitmarke100
139

140
  ret
141

142

143

144
  END      ;END Of File
OP #2467797
Lesenswert?

Ok, das hilft schonmal weiter. Die Auswertung nach einer festen Zeit mit 
einem Timer Interrupt werd ich gleich mal ausprobieren. Aber ich hab 
auch nochmal ins Datenblatt meines Gebers geschaut und leider hab ich 
einen, bei dem der Rastpunkt genau auf der Flanke von Signal B liegt. 
Und der AT89S8253 Mikrocontroller besitzt (so wie ich es bisher aus dem 
Datenblatt verstanden hab) nur externe Interrupts für positive Flanken. 
Daher muss ich das Signal wohl noch hardwaremäßig invertieren um ein 
Interrupt mit der negativen Flanke auslösen zu können.
#2467906
Lesenswert?

1
phase_a         equ     p3.2
2
phase_b         equ     p3.3
3
led_port        equ     p0
4

5
        dseg    at 30h
6
enc_delta:      ds 1
7
enc_last:       ds 1
8

9
        cseg
10

11
main:
12
        call    poll_encoder
13
        call    read_encoder2
14
        add     a, led_port
15
        mov     led_port, a
16
        jmp     main
17

18
poll_encoder:
19
        clr     a
20
        mov     c, phase_a              ; input phase_a
21
        rlc     a
22
        jnb     phase_b, _poen1         ; input phase_b
23
        xrl     a, #3                   ; convert gray to binary
24
_poen1:
25
        xch     a, enc_last             ; store new as next last
26
        subb    a, enc_last             ; difference last - new
27
        jnb     acc.0, _poen3           ; bit 0 = value (1)
28
        jnb     acc.1, _poen2           ; bit 1 = direction (+/-)
29
        inc     enc_delta
30
        ret
31
_poen2:
32
        dec     enc_delta
33
_poen3:
34
        ret
35

36
read_encoder2:                          ; read two phase encoder
37
        mov     a, enc_delta            ; read delta counts
38
        anl     enc_delta, #1           ; clear all bits except debouncing bit
39
        mov     c, acc.7                ; copy sign bit
40
        rrc     a                       ; / 2
41
        ret
42

43
end


Peter
Gast #2467910
Lesenswert?

> so wie ich es bisher aus dem Datenblatt verstanden hab) nur externe
> Interrupts für positive Flanken.
Soweit ich weiss besitzt er eher welche für negative Flanken... kann 
mich aber auch täuschen...

Ralf
OP #2468199
Lesenswert?

@ Peter:
Ich hab versucht deinen Code zu verstehen und auf meinem Controller zum 
laufen zu bringen, aber beides ist mir leider nicht gelungen. Was die 
Funktion "poll_encoder" in etwa macht konnte ich noch einigermaßen 
nachvollziehen, aber bei der Funktion "read_encoder2" und bei der 
Ausgabe auf den LEDs klappt das nicht. Im Anhang ist der Code so wie ich 
ihn kompiliert hab.

@ Ralf:
Das wäre auch möglich. Aber zumindest dürfte es nicht beides geben. Von 
daher bleibt das Problem das gleiche.
Angehängte Dateien:
OP #2470502
Lesenswert?

Naja, "klappt nicht" war auch keine Fehlerbeschreibung, sondern nur die 
Aussage, dass ich den Teil des Programms nicht versteh. Aber ich muss 
zugegeben dass ich wirklich noch hätte schreiben sollen was auf dem 
LED-Port passiert: Alle LEDs flackern und wenn ich am Geber drehe ändert 
sich auch nichts.
Wenn ich enc_delta direkt auf die LEDs geb passiert das Gleiche.

Inzwischen hab ich aber ein eigenes Programm zum laufen gebracht. Das 
läuft jetzt über ein Timer-Interrupt, welches alle 10ms auslöst. Dabei 
werden auch nur die Flanken der Spur A und die Zustände der Spur B 
ausgewertet. Das Programm ist vermutlich nicht so effizient wie deines, 
aber zumindest hab ich jetzt ein Programm bei dem ich weiß was es macht.

Übrigens war der hauptsächliche Fehler dass das erste Programm nicht 
funktioniert hat der, dass der externe Interrupt voreingestellt 
zustands- und nicht flankengesteuert arbeitet. Das steht aber nicht im 
Datenblatt, sondern in einem anderen Dokument das allgemein die 
8051-Architektur beschreibt. Dort stand außerdem, dass es wirklich 
fallende Flanken sind, die ein Interrupt auslösen.

Michael
Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren