Forum: Mikrocontroller und Digitale Elektronik Externe Interrupts zur Zeitmessung, LCD


von swedisch (Gast)


Lesenswert?

Liebe Gemeinde,
stehe Gerade vor folgendem Problem:

Ich werte mit 2 Tiny2313 bei 20 MHz jeweils ein Signal aus. Bei 
positiver Auswertung setzt der Tiny einen Ausgangspin High. Nun möchte 
ich die Laufzeit von einem High Signal zum nächsten messen. minimale 
Laufzeit beträgt ca. 6 us, maximale über eine us. Natürlich will ich das 
ganze so genau wie möglich machen ;)

Bis jetzt  hab ich das ganze folgendermaßen gelöst: 2 Tinys sind für das 
detektieren meiner 2 Signale zuständig und lösen jeweils eine High 
Flanke aus, wenn sie das entsprechende Signal detektiert haben. Die 
beiden Ausgänge der Tinys hab ich an nen 3ten Tiny gehängt, auf int0 und 
int1. In den ISR start ich beim ersten interrupt (0) den 16Bit Timer 
ohne Prescaler (1) und stoppe ihn beim zweiten interrupt (1) wieder. nun 
lese ich die beiden register aus und schiebe sie einmal nach rechts 
(divison durch2, damit hab ich 100ns schritte bei 20 MHz takt). zu guter 
letzt gebe ich das ganze auf einem LCD aus.

Nun zu meinem Problem: Das starten und Stoppen des Timers funktioniert, 
nur bin ich mir nicht ganz sicher wann ich die ausgabe ans LCD 
eingliedern soll. während der ausgabe sollen keine neuen messungen 
gemacht werden, also  interrupst disabled. Dewegen hab ich die komplette 
Umrechnung und Ausgabe in meine ISR von INT1 gelegt. Hierbei komm ich 
aber in teufels Küche und krieg nicht das Ergebnis angezeigt das kommen 
müsste. Wie kann ich das Teilen durch 2 und das Ausgeben aufs Display so 
legen, dass es in der Main Schleife passiert und dabei gleichzeitig die 
Interrupts abgeschalten werden, danach aber wieder an? Ich verwende die 
im tutorial angegebene Routine zur Ausgabe von 16 bit zahlen. Ich hab in 
der ISR nen ganz schönen Hund drinnen, nur ich find ihn nicht.

Danke für die Hilfe,
Max
1
;*********************************************************************
2
3
;          Timer einstellen
4
5
  ldi timer_start, 0b00000001    ; Startwerte für die Timer initialisieren/8->1Count=400ns
6
  ldi timer_stop,  0b00000000    ; Stopwert initialisieren
7
  out TCNT1H,timer_stop        ; COUNTER1  auf 00 initialisieren
8
  out TCNT1L,timer_stop
9
  
10
;**********************************************************************    
11
  
12
;          EXT Interrupts einstellen  
13
      
14
  ldi temp, 0b00001111        ; INT0 und INT1 konfigurieren
15
    out MCUCR, temp
16
  ldi temp, 0b11000000        ; INT0 und INT1 aktivieren
17
    out GIMSK, temp
18
;**********************************************************************
19
;          Endlosschleife
20
  sei
21
loop:
22
  
23
  rjmp loop
24
25
;**********************************************************************
26
;          Interrupt Handler:
27
28
int0_handler:
29
  out TCCR1B, timer_start      ; Timer 1 starten zur Zeitmessung
30
  reti              ; und zurück in die Schleife
31
  
32
   
33
int1_handler:
34
    
35
  out TCCR1B, timer_stop      ; Timer anhalten
36
    in ergebnisL,  TCNT1L      ; Wert des Timers ins Register schreiben
37
  in ergebnisH,  TCNT1H
38
39
  
40
  ldi temp1, 0b11000000      ; Disply 2te Zeile, 1. Zeichen
41
  rcall lcd_command
42
  
43
44
  lsr ergebnisH            ;Timerwert durch 2 Teilen, so dass 100ns schritte entstehen
45
  ror ergebnisL            ; --> Ergebnis in timer ist ein vielfaches von 100ns
46
    
47
  rcall lcd_number16        ; Dezimalzahl bis 32... ausgeben 
48
  ldi ZL, LOW(text1*2)          ; " "us" ausgeben
49
    ldi ZH, HIGH(text1*2)          ; 
50
    rcall lcd_flash_string        ; 
51
  
52
  out TCNT1L, timer_stop      ; Timer 1 auf 0 Rücksetzen
53
  out TCNT1H, timer_stop
54
  
55
  
56
  reti
57
58
59
;********************************************************************************************
60
;          Hier stehen die Konstanten+Includes
61
62
text:  .db "Verzoegerung",0
63
64
text1:  .db " us",0
65
66
67
.include "lcd-routines.asm"

von swedisch (Gast)


Lesenswert?

natürlich ist der längste zu messende Abstand nicht 1 us sondern über 
eine ms.
des weiteren sind die variablen in der ausgaberoutine angepasst an 
ergebnisL, ergebnisH.
MfG Max

von swedisch (Gast)


Lesenswert?

so, nun habe ich das programm ein wenig umgeschrieben.
jetzt werden die interrupts gegenseiteig gesperrt, sprich, es wird 
zuerst nur der erste aktiviert, gewartet bis der auslöst, dann in der 
isr der erste abgeschaltet und der 2te zugeschaltet, gewartet bis der 
auslöst, beide gesperrt und dann den wert auf dem lcd ausgegeben. nach 
ausgabe auf dem lcd wieder int0 freigegeben. int0 kommt immer zuerst und 
ist meine referenz, der nullzeitpunkt. leider krieg ich immer das 
gleiche ergebnis auf dem display und langsam verzweifel ich.

auf der anzeige erscheint immer 6 oder 7, was 12 oder 14 takten 
entspricht. diese braucht er genau, um von einer isr in die andere zu 
springen. das würde aber bedeuten, dass der 2te interrupt direkt nach 
dem ersten ausgelöst wird, und das kann nicht sein. auf dem oszi sind 
die beiden signale sauber und mit 20 us verzögerung dargestellt.

hier nochmal der neue code. langsam verzweifel ich und weiß nicht mehr 
weiter.
Irgendwo mache ich einen großen Denkfehler nur find ich den nicht. Kann 
mir irgendjemand helfen?
1
.include "tn2313def.inc"
2
3
4
.def temp  = r16
5
.def temp1 = r17
6
.def temp2 = r18
7
.def temp3 = r19
8
.def timer_stop = r20
9
.def timer_start = r21
10
.def ergebnisL = r22
11
.def ergebnisH = r23
12
.org 0x000
13
         rjmp main                ; Reset Handler
14
.org INT0addr
15
         rjmp int0_handler        ; IRQ0 Handler
16
.org INT1addr
17
         rjmp int1_handler        ; IRQ1 Handler
18
19
20
;********************************************************************
21
;          Hier beginnt die Main Routine
22
23
main:
24
  ldi temp, LOW(RAMEND)
25
    out SPL, temp
26
      
27
  ldi temp, 0xFF                ; Port B auf Ausgang
28
    out DDRB, temp
29
  ldi  temp, 0b11110000      ; Port D auf Ausgang
30
  out DDRD, temp          ; Interruptpins auf Eingang
31
  
32
  
33
;********************************************************************
34
;                   Display einstellen
35
36
  rcall lcd_init            ; Display initialisieren
37
  rcall lcd_clear
38
  rcall lcd_home
39
  
40
  ldi ZL, LOW(text*2)            ; Adresse des Strings in den
41
    ldi ZH, HIGH(text*2)          ; Z-Pointer laden
42
    rcall lcd_flash_string        ; Text ausgeben
43
  
44
  ldi temp1, 0b11001000      ; 2te Zeile, 1. Zeichen
45
  rcall lcd_command
46
47
  ldi ZL, LOW(text1*2)          ; " "us" ausgeben
48
    ldi ZH, HIGH(text1*2)          ; 
49
    rcall lcd_flash_string        ; 
50
  
51
;*********************************************************************
52
53
;          Timer einstellen
54
55
  ldi timer_start, 0b00000001    ; Startwerte für die Timer initialisieren/8->1Count=400ns
56
  ldi timer_stop,  0b00000000    ; Stopwert initialisieren
57
  out TCNT1H,timer_stop        ; COUNTER1  auf 00 initialisieren
58
  out TCNT1L,timer_stop
59
  ldi ergebnisL, 0x00        ; Ergebnis auf 0 Initiolisieren
60
;**********************************************************************    
61
  
62
;          EXT Interrupts einstellen  
63
      
64
  ldi temp, 0b00001111        ; INT0 und INT1 konfigurieren, steigende flanke
65
    out MCUCR, temp
66
  ldi temp, 0b01000000        ; INT0  aktivieren
67
    out GIMSK, temp
68
;**********************************************************************
69
;          Endlosschleife
70
  sei                  ; interupts aktivieren
71
loop:
72
  tst ergebnisL
73
  breq loop              ; falls kein messwert vorliegt, schleife....
74
75
  cli                  ; wenn was im ergebnis steht, interupts aus, lcd ist an der 
76
                    ; reihe
77
  lsr ergebnisH            ; Timerwert durch 2 Teilen, so dass 100ns schritte entstehen
78
  ror ergebnisL            ; --> Ergebnis in timer ist ein vielfaches von 100ns
79
  
80
  ldi temp1, 0b11000000        ; Disply 2te Zeile, 1. Zeichen
81
  rcall lcd_command  
82
  
83
  mov temp2, ergebnisL
84
  mov temp3, ergebnisH
85
    
86
  rcall lcd_number16          ; Dezimalzahl bis 32... ausgeben 
87
  
88
  ldi ergebnisL, 0x00          ; Ergebnis zurücksetzen
89
  ldi ergebnisH, 0x00
90
  
91
  out TCNT1H,timer_stop          ; COUNTER1  auf 00 setzen
92
  out TCNT1L,timer_stop
93
  //rcall delay
94
  ldi temp, 0b01000000          ; INT0  wieder aktivieren
95
    out GIMSK, temp
96
  sei
97
  rjmp loop
98
99
;**********************************************************************
100
;          Interrupt Handler:
101
102
int0_handler:
103
  cli
104
  out TCCR1B, timer_start      ; Timer 1 starten zur Zeitmessung
105
  ldi temp, 0b10000000        ; INT0 deaktivieren, int1 aktivieren
106
    out GIMSK, temp
107
  reti              ; und zurück in die Schleife
108
  
109
   
110
int1_handler:
111
  
112
  out TCCR1B, timer_stop
113
    in ergebnisL,  TCNT1L      ; Wert des Timers ins Register schreiben
114
  in ergebnisH,  TCNT1H      
115
                   
116
    out GIMSK, timer_stop      ; int1 deaktivieren
117
  reti
118
119
120
;********************************************************************************************
121
;          Hier stehen die Konstanten+Includes
122
123
text:  .db "Verzoegerung",0
124
125
text1:  .db " us",0
126
127
128
.include "lcd-routines.asm"

von Peter D. (peda)


Lesenswert?

swedisch wrote:
> auf der anzeige erscheint immer 6 oder 7, was 12 oder 14 takten
> entspricht. diese braucht er genau, um von einer isr in die andere zu
> springen. das würde aber bedeuten, dass der 2te interrupt direkt nach
> dem ersten ausgelöst wird, und das kann nicht sein.

Doch, genau das wird es sein.

Wer sagt denn, daß das auslösende Ereignis erst nach der Freigabe des 
Interrupts erfolgen darf?

Atmels Datenblatt jedenfalls nicht.
Sonst könnte man ja auf Ereignisse nicht pollen.

Daher immer erst das Ereignisflag löschen direkt vor der 
Interruptfreigabe, wenn man nicht auf uralte Ereignisse triggern will.


Peter

von swedisch (Gast)


Lesenswert?

Danke, das hab ich jetzt mal ausprobiert. In der Tat steht der Interrupt 
schon an, während der erste abgearbeitet wird. wenn ich aber am ende der 
ersten ISR die Flags lösche, so passiert trotzdem das gleiche. hab 
sowohl vor der freigabe vor der hauptschleife das EIFR gelöscht als auch 
nochmal am ende der ISR0 routine. klappt beides mal nicht. die flanken 
kommen aber definitiv mit 20 us laufzeitunterschied. das sagt mir mein 
oszi. irgendwo ist trotzdem der wurm drinnen...

von swedisch (Gast)


Lesenswert?

Hallo,
kann mir vielleicht jemand einen Tipp geben, wie ich mit externen 
Interrupts debuggen kann? wenn ich das ganze im Simulator durchspiel 
läuft es einwandfrei. Da setz ich manuell die Interrupts und das 
Programm tut genau das was ich will. sobald ich aber über den Dragen 
meinen Tiny im Laufenden Betrieb debug ist es logisch, dass beide 
interrupts sofort anliegen, da ich ja nie in dieser kurzen zeit meine 
Steps abarbeiten kann. gibts da ne möglichkeit das doch irgendwie 
realitätsnah hinzukriegen? wie gesagt im simulator mit manuellem 
auslösen der Interrupts geht das einwandfrei. nur nicht aufm tiny. da 
springt er immer sofort in die 2te isr. wie der ausgelöst wird ist mir 
noch sehr unklar, da da kein signal drauf liegt.

MfG, Max

von Peter D. (peda)


Lesenswert?

swedisch wrote:
> Danke, das hab ich jetzt mal ausprobiert. In der Tat steht der Interrupt
> schon an, während der erste abgearbeitet wird. wenn ich aber am ende der
> ersten ISR die Flags lösche


Ne, nur direkt vor der Freigabe.
Und zum Löschen mußt Du setzen !!!
Das hat irgendein Atmel Witzbold so festgelegt.


Peter

von swedisch (Gast)


Lesenswert?

Tausend Dank Peter
genau das war es. Vielen Dank für die schnelle und tolle Hilfe hier. 1a!
Ich war schon kurz vor in die Tonne treten, aber es klappt!
Schönen Mittwoch nachmittag, meiner ist gerettet ,)

Gruß,
Max

von swedisch (Gast)


Lesenswert?

ein komisches phänomen hab ich noch:
sobald ich über 1 wirde debugge, die verbindung zum dragon abzieh und 
den tiny resete, läuft das programm genau so wie es soll. Wenn ich jetzt 
aber übers avr studio den Tiny wieder freigeb für ISP, dann läuft das 
Programm nicht mehr.
zeigt nur noch lauter 0en an. Gibts hierfür eine erklärung? Die Zeit die 
ich im Debug modus ohne debug messe ist exakt die die rauskommen soll, 
bis auf 100 ns genau.
Notfalls lass ich den Tiny einfach im debug modus. nicht schön, aber 
geht.

MfG,
Max

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.