Forum: Mikrocontroller und Digitale Elektronik Timing & Triggern mit 2 Timern in ASM


von Philipp H. (philipp_h)


Angehängte Dateien:

Lesenswert?

Hallo liebe Gemeinde,

ich komme am besten gleich zur Sache im Anhang ist ein Timingdiagramm 
und mein Quellcode in ASM gegeben. Leider funktioniert das nicht ganz so 
wie ich mir das denke.

Meine Vermutung: Timer 1 ist nicht richtig konfiguriert und die 
Interrupts vermurcheln mir mein SP (siehe Timingdiagramm), der CP 
funktioniert einwandfrei und ist an PD5 angeschlossen (PB3<->PD5)

Ich benutze den Atmega168 mit einem 20 MHz- Quarz. In dem Quellcode sind 
meine Gedankengänge im entsprechenden Bereich kommentiert.
Ich hoffe Ihr könnt mir helfen den Fehler in meinen Gedanken oder im 
Code zu finden.
1
; Config Timer 1
2
  ; Output Compare Intterrupts / PS = 1 / Per Interrupt Startpulse erzeugen
3
  ; Beide Vergleichsregister nutzen
4
  ; Theoretisch sollten zwischen den Startpulsen 2600 µs bzw 2,6 ms liegen
5
  ; es sind aber ca 10 ms und die Pulse "springen" in einem bestimmten Bereich
6
  ; hin und her und haben eine undefinierte variable Länge
7
  
8
  ; Dieser Teil funktioniert nicht
9
10
11
  ldi temp, 0b00000000
12
  sts TCCR1A, temp
13
14
  ldi temp, 0b00001110 ; CTC-Mode / fallende Flanke /externer Takt an T1-Pin(PD5)
15
  sts TCCR1B, temp
16
17
  ldi temp, 0b00100110 ; Aktiviere Interrupts für OCR1A & OCR1B
18
  sts TIMSK1, temp
19
20
  ;Vergleichswert in OCR1B soll 2 sein
21
  ldi H_byte,0b00000000
22
  sts OCR1BH,H_byte
23
  ldi L_byte,0b00000010
24
  sts OCR1BL,L_byte
25
26
  ;Vergleichswert in OCR1A soll 2600 sein
27
  ldi H_byte,0b00101000
28
  sts OCR1AH,H_byte
29
  ldi L_byte,0b00001010
30
  sts OCR1AL,L_byte
31
  
32
  sei; Interrupt enable
33
34
  loop:
35
  rjmp loop
36
37
;Interrupt Routinen für Timer/Counter 1
38
  
39
TIM1_COMPA:      ;Bei Comparematch A
40
  sbi PORTD,PD0  ;Bit wird gesetzt PD0
41
42
/*  
43
  ;So laufen die Startpulse nicht stark auseinander bzw definierter
44
  ;Counterwert manuell zurück setzen
45
  cli        ;Interrupt disable
46
  ldi H_byte,0b00000000
47
  sts TCNT1H,H_byte
48
  ldi L_byte,0b00000000
49
  sts TCNT1L,L_byte
50
  sei        ;Interrupt enable
51
*/
52
  reti
53
TIM1_COMPB:      ;Bei Comparematch B
54
  cbi PORTD,PD0  ;Bit wird gelöscht PD0
55
  reti

MFG Phil

von Zimbo (Gast)


Lesenswert?

Philipp H. schrieb:
> ldi H_byte,0b00101000
>   sts OCR1AH,H_byte

Werden Ports nicht mit "Out" beschrieben?
so?
out OCR1AH,H_byte

von Karl H. (kbuchegg)


Lesenswert?

>   ldi temp, 0b00100110 ; Aktiviere Interrupts für OCR1A & OCR1B
>   sts TIMSK1, temp

Da sind 3 Stück 1 Bits in der Maske. Du schreibst aber nur von 2 
Interrupt Service Routinen und hast auch nur 2 definiert.

generell würdest du dir viel leichter tun, wenn du diesen blödsinnigen 
Binärschreibweisen-Fetischismus aufgeben würdest. Ohne Not 
Binärschreibweise zu verwenden oder ohne dafür etwas zu kriegen ist 
nicht cool, sondern fehleranfällig.

: Bearbeitet durch User
von Philipp H. (philipp_h)


Lesenswert?

Laut Datenblatt ja auch die Beispielcodes sind so aufgebaut ich bekomme 
dann aber beim Compelieren einen Fehler und bei Timer 2 funktioniert das 
ja auch.

von Karl H. (kbuchegg)


Lesenswert?

Philipp H. schrieb:
> Laut Datenblatt ja auch die Beispielcodes sind so aufgebaut ich bekomme
> dann aber beim Compelieren einen Fehler und bei Timer 2 funktioniert das
> ja auch.

Ist das die Antwort auf die STS <-> OUT Frage?

Das heißt nichts, wenn es beim Timer 2 auch funktioniert. Die einzige 
Autorität ist das Datenblatt und dort wiederrum die Tabelle, in der die 
Adresslage der einzelnen Register aufgeführt ist. Wenn dort zu entnehmen 
ist, dass ein spezifisches Register nur mit STS zu erreichen ist, dann 
muss STS benutzt werden. Unabhängig davon, ob andere Register deselben 
oder eines anderen Timers mittels OUT zu erreichen wären oder nicht.

von spess53 (Gast)


Lesenswert?

Hi

STS passt schon. Die Register der Timer liegen alle oberhalb von 
$3F/$5F.

>ldi temp, 0b00100110 ; Aktiviere Interrupts für OCR1A & OCR1B

So etwas schreibst du besser

ldi tmp,1<<ICIE1|1<<OCIE1B|1<<OCIE1A

Wobei mir allerdings unklar ist, was du mit dem Input Compare Interrupt 
willst. Zumal, wie KHB schon bemerkt hat, du keine Interruptroutine 
hast.

MfG spess

von Philipp H. (philipp_h)


Lesenswert?

Ja danke das war es, ja vlt hat sich der Compare Interrupt durch meine 
unpassende schreibweise eingeschlichen, aber genau der hat eben dieses 
zappeln ausgelöst. Ich werde mir das versuchen so anzugewöhnen ;) nur 
die Zeit zwischen den SP-Impulsen passt noch nicht da hab ich wohl OCR1A 
noch falsch beschrieben, habt ihr da vlt auch ein Tipp wie das besser 
geht?

Danke nochmals

von Karl H. (kbuchegg)


Lesenswert?

Philipp H. schrieb:
> Ja danke das war es, ja vlt

nicht vielleicht.
Sondern sogar sicher!

> die Zeit zwischen den SP-Impulsen passt noch nicht da hab ich wohl OCR1A
> noch falsch beschrieben, habt ihr da vlt auch ein Tipp wie das besser
> geht?

warum schreibst du es nicht so
1
  ldi   H_byte, HIGH( 2600 )
2
  sts   OCR1AH, H_byte
3
  ldi   L_byte, LOW( 2600 )
4
  sts   OCR1AL, L_byte

beim Stack-Pointer beschreiben hast du ja auch die HIGH und LOW Makros 
benutzt.
Warum diese Schreibweise besser ist, brauch ich wohl nicht extra 
anführen.

von Zimbo (Gast)


Lesenswert?

spess53 schrieb:
> STS passt schon. Die Register der Timer liegen alle oberhalb von
> $3F/$5F.

Alles klar, danke. Ich war so dämlich nicht ins Datenblatt zu schauen, 
den M168 hatte ich noch nie beim Wickel. Sorry.

Was wäre zu tun? Den Interrupt nicht zu aktivieren, oder erst mal ein 
Reti an die angesprungene Adresse setzen. So kreise ich meine Fehler 
ein, wenn Oszi-Debugging nicht mehr hilft.

von Philipp H. (philipp_h)


Lesenswert?

noch einmal danke Karl Heinz ich programmiere noch nicht lange in ASM 
aber dieses mal ist es notwendig. Man lernt halt nie aus ^^ und 
Ratschläge nehme ich gerne an.

Ich werd den Code wohl noch einmal überarbeiten :D

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Philipp H. schrieb:
>> Ja danke das war es, ja vlt
>
> nicht vielleicht.
> Sondern sogar sicher!
>
>> die Zeit zwischen den SP-Impulsen passt noch nicht da hab ich wohl OCR1A
>> noch falsch beschrieben, habt ihr da vlt auch ein Tipp wie das besser
>> geht?
>
> warum schreibst du es nicht so
>
1
>   ldi   H_byte, HIGH( 2600 )
2
>   sts   OCR1AH, H_byte
3
>   ldi   L_byte, LOW( 2600 )
4
>   sts   OCR1AL, L_byte
5
>
>
> beim Stack-Pointer beschreiben hast du ja auch die HIGH und LOW Makros
> benutzt.
> Warum diese Schreibweise besser ist, brauch ich wohl nicht extra
> anführen.
1
  ldi H_byte,0b00101000
2
  sts OCR1AH,H_byte
3
  ldi L_byte,0b00001010

du hast dich beim Abschreiben der Binärzahl aus dem 
Windows-Taschenrechner in der Reihenfolge der Bytes vertan.
Dezimal 2600 ist binär 00001010 00101000

Schreibs vernünftig, dann passiert dir so ein Leichtsinnsfehler nicht.
Lass den Assembler für dich arbeiten!
Was interessiert mich die Binärform. HIGH(2600) ist das Highbyte davon, 
LOW(2600) ist das Low-Byte davon. Mehr muss ich nicht wissen und dann 
ist auch die Chance kleiner, dass ich da beim Umrechnen und 
auseinanderdividieren auf Binär einen Fehler mache.

Generell: Binärschreibweise ist fast immer die schlechteste Form, die du 
finden kannst. Für fast alles gibt es bessere Schreibweisen, die dir als 
Mensch entgegen kommen und deine Chance Fehler zu machen verkleinern. 
Zum Teil massiv verkleinern.

: Bearbeitet durch User
von der alte Hanns (Gast)


Lesenswert?

> Ratschläge nehme ich gerne an.

Das 'cli' zu Beginn der ISR ist unnötig, und das 'sei' am Ende wird auch 
durch das 'reti' übernommen.

von der alte Hanns (Gast)


Lesenswert?

Als Anregung, in der Hoffnung, Ihre Absicht richtig verstanden zu haben:
1
.include "m48def.inc"
2
3
#define  fkHz           20000       ; Clock-Frequenz in kHz
4
#define  fOCR2A         1000        ; 1 MHz: 1 us  auf phiCP == OC2A
5
#define  c_OCR2A        (fkHz/fOCR2A /2) -1
6
#define  phiSP_high      1
7
#define  phiSP_cnt      2600
8
#define  c_OCR1A        phiSP_cnt -1
9
#define  c_OCR1B        phiSP_cnt - phiSP_high -1
10
11
.equ  DDR_phiCP       = DDRB
12
.equ  phiCP           = 3          ; OC2A  --> PD5 == T1
13
.equ  DDR_phiSP       = DDRD
14
.equ  PORT_phiSP      = PORTD
15
.equ  phiSP           = 0
16
17
.def  tmp0            = r16
18
19
.org $0000
20
  rjmp  reset
21
.org OC1Aaddr
22
  rjmp  OC1Aaddr_
23
.org OC1Baddr
24
  rjmp  OC1Baddr_
25
26
OC1Aaddr_:
27
  cbi    PORT_phiSP,phiSP
28
  reti
29
30
OC1Baddr_:
31
  sbi    PORT_phiSP,phiSP
32
  reti
33
34
reset:
35
  sbi    DDR_phiCP,phiCP
36
  sbi    DDR_phiSP,phiSP
37
38
  ldi    tmp0,c_OCR2A
39
  sts    OCR2A,tmp0
40
  ldi    tmp0,(1<<COM2A0)+(1<<WGM21)      ; toggle OC2A, CTC per OCR2A
41
  sts    TCCR2A,tmp0
42
  ldi    tmp0,(1<<CS20)                   ; /1
43
  sts    TCCR2B,tmp0
44
45
  ldi    tmp0,high(c_OCR1A)
46
  sts    OCR1AH,tmp0
47
  ldi    tmp0,low(c_OCR1A)
48
  sts    OCR1AL,tmp0
49
  ldi    tmp0,high(c_OCR1B)
50
  sts    OCR1BH,tmp0
51
  ldi    tmp0,low(c_OCR1B)
52
  sts    OCR1BL,tmp0
53
  ldi    tmp0,(1<<WGM12)+(1<<CS12)+(1<<CS11) ; CTC per OCR1A, ext. T1 fallend
54
  sts    TCCR1B,tmp0
55
  ldi    tmp0,(1<<OCIE1B)+(1<<OCIE1A)
56
  sts    TIMSK1,tmp0
57
  sei
58
main_loop:
59
  rjmp  main_loop

von Philipp H. (philipp_h)


Lesenswert?

Wenn auch etwas spät möchte ich mich bei dir bedanken @der alte Hanns 
dein Code funktioniert prächtig. Ich denke ich habe jetzt verstanden was 
Ihr an meiner Binärschreibweise kritisiert habt ;). Ich habe für mich 
selber deinen Code noch etwas detalierter kommentiert und wollte diesen 
nun noch um eine Funktion ergänzen. Leider klappt das nicht so ganz, ich 
denke das hat was mit der Verzweigung im Interrupt zu tun. Ich habe 
bereits probiert mit JMP und CPSE zu arbeiten und dann mittels JMP in 
eine weiter Subroutine zu springen. Ohne Erfolg. Was ich vor habe, ist 
bei jedem Flankenwechsel an PD2 die Frequenz von CP zu wechseln --> 
OCR2A ändern. Das klappt auch, aber nur einmal. Also von 1MHz auf 500 
kHz. Könntet Ihr mir dabei vlt. noch einmal helfen ?

Mit freundlichem Gruß
Phil
1
#define  fkHz           20000       ; Clock-Frequenz in kHz
2
#define  fOCR2A         1000        ; 1 MHz: 1 us  auf phiCP == OC2A
3
#define  f1OCR2A        500         ; 0,5 MHz: 2 us  auf phiCP == OC2A
4
#define  c_OCR2A        (fkHz/fOCR2A /2) -1 ; Comparewert für OCR2A => 9
5
#define  c1_OCR2A        (fkHz/f1OCR2A /2) -1 ; Comparewert1 für OCR2A => 19
6
#define  phiSP_high      1 ; SP soll 1 Takt von CP High sein
7
#define  phiSP_cnt      3000 ; nach 3000 Takten von CP soll neuer SP kommen
8
#define  c_OCR1A        phiSP_cnt -1 ; 1 Takt wird benötigt um SP zu HIGH zu schalten
9
#define  c_OCR1B        phiSP_cnt - phiSP_high -1 ; 1 Takt wird benötigt um SP wieder auf LOW zu setzen
10
11
.equ  DDR_phiCP       = DDRB  ; Datadirectionregister von CP 
12
.equ  phiCP           = 3       ; (PIN PB3 = CP) = Ausgang
13
.equ  DDR_phiSP       = DDRD  ; Datadirectionregister von SP
14
.equ  phiSP           = 0    ; (PIN PD0 = SP) = Ausgang
15
.equ  PORT_phiSP      = PORTD  ; Definiert den PORTD für SP um dort Werte zu setzen
16
17
.def  tmp0            = r16    ; Tmp0 zum arbeiten definieren
18
.def  tmp1            = r17    ; Tmp0 zum arbeiten definieren
19
20
;----------Interrupttable--------
21
22
.org $0000
23
  rjmp  reset
24
.org PCI0addr
25
  rjmp PCI0addr_
26
.org OC1Aaddr
27
  rjmp  OC1Aaddr_
28
.org OC1Baddr
29
  rjmp  OC1Baddr_
30
31
32
;--------------------------------
33
34
;+++++++++++++++++Interrupthandler++++++++++++++++++++++
35
36
;----------SP- Erzeugung---------
37
38
; Bei erreichen des Vergleichswertes c_OCR1A wird folgender
39
; Interrupt ausgelöst:
40
OC1Aaddr_:  
41
  cbi    PORT_phiSP,phiSP ; Bit in PORTD0 wird gelöscht
42
  reti
43
44
; Bei erreichen des Vergleichswertes c_OCR1B wird folgender
45
; Interrupt ausgelöst:
46
OC1Baddr_:
47
  sbi    PORT_phiSP,phiSP ; Bit in PORTD0 wird gesetzt
48
  reti
49
;--------------------------------
50
51
;-------Frequenzwechslung--------
52
; Bei jedem Flankenwechsel an PD2 soll die 
53
; CP-Frequenz gewechselt werden
54
PCI0addr_:
55
  push tmp1             ; Das SREG in tmp1 sichern. Vorher
56
  in   tmp1, SREG       ; muss natürlich tmp1 gesichert werden
57
  
58
  ldi  tmp0,OCR2A
59
  ldi  tmp1,c1_OCR2A
60
  CP  tmp0,tmp1    ; Vergleiche Konstante mit Vergleichsregister
61
  BRNE  ungleich    ; Wenn Ergebniss gleich überspringe das hier
62
  BREQ  istgleich    ; Ergebniss war gleich
63
64
istgleich:
65
    ldi tmp0,c_OCR2A    ; Lade c1_OCR2A in tmp0
66
    sts OCR2A,tmp0      ; speichere tmp0 in Vergleichsregister OCR2A
67
    out SREG, tmp1        ; Die Register SREG und tmp1 wieder
68
    pop tmp1              ; herstellen
69
    reti  
70
    
71
ungleich:
72
    ldi tmp0,c1_OCR2A    ; Lade c1_OCR2A in tmp0
73
    sts OCR2A,tmp0      ; speichere tmp0 in Vergleichsregister OCR2A
74
    out SREG, tmp1        ; Die Register SREG und tmp1 wieder
75
    pop tmp1              ; herstellen
76
    reti  
77
      
78
79
;-------------------------------
80
81
82
;-------Bei Reset/Start µC------
83
84
reset:
85
  ldi tmp0, LOW(RAMEND)
86
  out SPL, tmp0
87
  ldi tmp0, HIGH(RAMEND)
88
  out SPH, tmp0
89
90
91
  sbi    DDR_phiCP,phiCP  ; Setze Bit phiCP in DDR_phiCP DDRB=0b00001000
92
  sbi    DDR_phiSP,phiSP  ; Setze Bit phiCP in DDR_phiCP DDRD=0b00000001
93
  
94
  ldi  tmp0,(1<<ISC00)    ; Jeder Flankenwechsel löst INT aus [tmp0 = 0b00000001]
95
  sts  EICRA,tmp0      ; speichere tmp0 ins InterruptControlRegister EICRA
96
  ldi   tmp0,(1<<INT0)    ; PD2 ist für Interrupt verantwortlich [tmp0 = 0b00000001]
97
  out  EIMSK,tmp0      ; speichere tmp0 ins InterruptMaskRegister EIMSK
98
  
99
100
  ;    8-Bit-Timer-2 (CP-Erzeugung)
101
  ldi    tmp0,c_OCR2A        ; Lade c_OCR2A in tmp0
102
  sts    OCR2A,tmp0          ; speichere tmp0 in Vergleichsregister OCR2A
103
  ldi    tmp0,(1<<COM2A0)+(1<<WGM21) ; toggle OC2A, CTC per OCR2A [tmp0 = 0b01000010]
104
  sts    TCCR2A,tmp0        ; speichere tmp0 ins Controlregister TCCR2A
105
  ldi    tmp0,(1<<CS20)             ; Prescaler = 1 [tmp0 = 0b00000001]
106
  sts    TCCR2B,tmp0        ; speichere tmp0 ins Controlregister TCCR2B
107
108
  ;    16-Bit-Timer-1 (SP-Erzeugung)
109
  ldi    tmp0,high(c_OCR1A)      ; Lade Highanteil c_OCR1A in tmp0
110
  sts    OCR1AH,tmp0        ; speichere tmp0 in Vergleichsregister OCR1AH
111
  ldi    tmp0,low(c_OCR1A)      ; Lade Lowanteil c_OCR1A in tmp0
112
  sts    OCR1AL,tmp0        ; speichere tmp0 in Vergleichsregister OCR1AH
113
  ldi    tmp0,high(c_OCR1B)      ; Lade Highanteil c_OCR1B in tmp0
114
  sts    OCR1BH,tmp0        ; speichere tmp0 in Vergleichsregister OCR1BH
115
  ldi    tmp0,low(c_OCR1B)      ; Lade Lowanteil c_OCR1B in tmp0
116
  sts    OCR1BL,tmp0        ; speichere tmp0 in Vergleichsregister OCR1BH
117
  ldi    tmp0,(1<<WGM12)+(1<<CS12)+(1<<CS11) ; CTC per OCR1A, ext. T1 fallend
118
                      ; [tmp0 = 0b00001110]
119
  sts    TCCR1B,tmp0          ; speichere tmp0 ins Controlregister TCCR1B
120
  ldi    tmp0,(1<<OCIE1B)+(1<<OCIE1A)  ; Aktiviere Interrupts für Comparematch bei
121
                    ; OCR1A & OCR1B [tmp0 = 0b00000110]
122
  sts    TIMSK1,tmp0        ; speichere tmp0 ins InterruptMaskRegister TIMSK1
123
  sei                ; Aktiviere Globale Interrupts
124
125
;----------------------------
126
127
;++++++++++++++++++++++++++++++++++++++++++++++++++++++
128
129
;-------Hauptschleife--------
130
main_loop:
131
  rjmp  main_loop

von Philipp H. (philipp_h)


Lesenswert?

Kann geschlossen werden,

statt ldi  tmp0,OCR2A
muss lds tmp0,OCR2A

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.