www.mikrocontroller.net

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


Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
;*********************************************************************

;          Timer einstellen

  ldi timer_start, 0b00000001    ; Startwerte für die Timer initialisieren/8->1Count=400ns
  ldi timer_stop,  0b00000000    ; Stopwert initialisieren
  out TCNT1H,timer_stop        ; COUNTER1  auf 00 initialisieren
  out TCNT1L,timer_stop
  
;**********************************************************************    
  
;          EXT Interrupts einstellen  
      
  ldi temp, 0b00001111        ; INT0 und INT1 konfigurieren
    out MCUCR, temp
  ldi temp, 0b11000000        ; INT0 und INT1 aktivieren
    out GIMSK, temp
;**********************************************************************
;          Endlosschleife
  sei
loop:
  
  rjmp loop

;**********************************************************************
;          Interrupt Handler:

int0_handler:
  out TCCR1B, timer_start      ; Timer 1 starten zur Zeitmessung
  reti              ; und zurück in die Schleife
  
   
int1_handler:
    
  out TCCR1B, timer_stop      ; Timer anhalten
    in ergebnisL,  TCNT1L      ; Wert des Timers ins Register schreiben
  in ergebnisH,  TCNT1H

  
  ldi temp1, 0b11000000      ; Disply 2te Zeile, 1. Zeichen
  rcall lcd_command
  

  lsr ergebnisH            ;Timerwert durch 2 Teilen, so dass 100ns schritte entstehen
  ror ergebnisL            ; --> Ergebnis in timer ist ein vielfaches von 100ns
    
  rcall lcd_number16        ; Dezimalzahl bis 32... ausgeben 
  ldi ZL, LOW(text1*2)          ; " "us" ausgeben
    ldi ZH, HIGH(text1*2)          ; 
    rcall lcd_flash_string        ; 
  
  out TCNT1L, timer_stop      ; Timer 1 auf 0 Rücksetzen
  out TCNT1H, timer_stop
  
  
  reti


;********************************************************************************************
;          Hier stehen die Konstanten+Includes

text:  .db "Verzoegerung",0

text1:  .db " us",0


.include "lcd-routines.asm" 


Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?
.include "tn2313def.inc"


.def temp  = r16
.def temp1 = r17
.def temp2 = r18
.def temp3 = r19
.def timer_stop = r20
.def timer_start = r21
.def ergebnisL = r22
.def ergebnisH = r23
.org 0x000
         rjmp main                ; Reset Handler
.org INT0addr
         rjmp int0_handler        ; IRQ0 Handler
.org INT1addr
         rjmp int1_handler        ; IRQ1 Handler


;********************************************************************
;          Hier beginnt die Main Routine

main:
  ldi temp, LOW(RAMEND)
    out SPL, temp
      
  ldi temp, 0xFF                ; Port B auf Ausgang
    out DDRB, temp
  ldi  temp, 0b11110000      ; Port D auf Ausgang
  out DDRD, temp          ; Interruptpins auf Eingang
  
  
;********************************************************************
;                   Display einstellen

  rcall lcd_init            ; Display initialisieren
  rcall lcd_clear
  rcall lcd_home
  
  ldi ZL, LOW(text*2)            ; Adresse des Strings in den
    ldi ZH, HIGH(text*2)          ; Z-Pointer laden
    rcall lcd_flash_string        ; Text ausgeben
  
  ldi temp1, 0b11001000      ; 2te Zeile, 1. Zeichen
  rcall lcd_command

  ldi ZL, LOW(text1*2)          ; " "us" ausgeben
    ldi ZH, HIGH(text1*2)          ; 
    rcall lcd_flash_string        ; 
  
;*********************************************************************

;          Timer einstellen

  ldi timer_start, 0b00000001    ; Startwerte für die Timer initialisieren/8->1Count=400ns
  ldi timer_stop,  0b00000000    ; Stopwert initialisieren
  out TCNT1H,timer_stop        ; COUNTER1  auf 00 initialisieren
  out TCNT1L,timer_stop
  ldi ergebnisL, 0x00        ; Ergebnis auf 0 Initiolisieren
;**********************************************************************    
  
;          EXT Interrupts einstellen  
      
  ldi temp, 0b00001111        ; INT0 und INT1 konfigurieren, steigende flanke
    out MCUCR, temp
  ldi temp, 0b01000000        ; INT0  aktivieren
    out GIMSK, temp
;**********************************************************************
;          Endlosschleife
  sei                  ; interupts aktivieren
loop:
  tst ergebnisL
  breq loop              ; falls kein messwert vorliegt, schleife....

  cli                  ; wenn was im ergebnis steht, interupts aus, lcd ist an der 
                    ; reihe
  lsr ergebnisH            ; Timerwert durch 2 Teilen, so dass 100ns schritte entstehen
  ror ergebnisL            ; --> Ergebnis in timer ist ein vielfaches von 100ns
  
  ldi temp1, 0b11000000        ; Disply 2te Zeile, 1. Zeichen
  rcall lcd_command  
  
  mov temp2, ergebnisL
  mov temp3, ergebnisH
    
  rcall lcd_number16          ; Dezimalzahl bis 32... ausgeben 
  
  ldi ergebnisL, 0x00          ; Ergebnis zurücksetzen
  ldi ergebnisH, 0x00
  
  out TCNT1H,timer_stop          ; COUNTER1  auf 00 setzen
  out TCNT1L,timer_stop
  //rcall delay
  ldi temp, 0b01000000          ; INT0  wieder aktivieren
    out GIMSK, temp
  sei
  rjmp loop

;**********************************************************************
;          Interrupt Handler:

int0_handler:
  cli
  out TCCR1B, timer_start      ; Timer 1 starten zur Zeitmessung
  ldi temp, 0b10000000        ; INT0 deaktivieren, int1 aktivieren
    out GIMSK, temp
  reti              ; und zurück in die Schleife
  
   
int1_handler:
  
  out TCCR1B, timer_stop
    in ergebnisL,  TCNT1L      ; Wert des Timers ins Register schreiben
  in ergebnisH,  TCNT1H      
                   
    out GIMSK, timer_stop      ; int1 deaktivieren
  reti


;********************************************************************************************
;          Hier stehen die Konstanten+Includes

text:  .db "Verzoegerung",0

text1:  .db " us",0


.include "lcd-routines.asm" 

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: swedisch (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.