mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Frequenzmessung


Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

mein Code soll mit 2Mhz Quarz und ATmega8 eine Frequenz von 1-100 Hz 
Messen.
Dabei wird bei ankommen einer positiven Flanke an INT0 ein timer mit 
1024erPS gestartet.
Bei der nächsten Flanke wird dann der Timer gestoppt.
Das ergebnis wird jeweils bei aufruf der ISR auf Port B gegeben und ich 
schaus mir mit LEDs an.

Leider funktioniert das ganze nicht.
Hier mal der Code:
.include "m8def.inc"

.def temp = r16
.def temp1=r17
.def temp3=r18
.org 0x0000
        rjmp    main
.org INT0addr
        rjmp int0_handler
.org OVF0addr
        reti
main:

     ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp
        ldi temp, 0b00000011  ; INT0, rising edge
        out MCUCR, temp

        ldi     temp, 0 << TOIE0
        out     TIMSK, temp
        ldi     temp, 0b00000000
        out     TCCR0, temp

    ldi temp, 0x00
    out TCNT0, temp


ldi temp,0xFF      ;Ausgang konfigurieren
out DDRB, temp

ldi temp,0x00   ; Eingang konfigurieren
out DDRD, temp


ldi temp, 0b01000000  ; INT0 aktivieren!
out GICR, temp


ldi temp, 0b00000000 ;Hier Temp mit 1024erPS laden
ldi temp3,0b00000101 ;Hier einmal festgelegt!



sei ;Generelle Interruptfreigabe!
loop:

rjmp loop

int0_handler:   ldi temp1, TCNT0
             out PORTB, temp1

               eor temp, temp3
         out TCCR0, temp
   reti



Eigentlich ein einfaches Programm.

Danke!

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und keiner ne Ahnung?

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Doch, sicherlich. Nur wie Du es machen willst, wird es keinem hier 
gefallenen.
Einfach und elegant ist es, T1 mit hoher Taktfrequenz laufen zu lassen 
und die Periodendauer des Eingangssignals mit dem InputCaptureRegister 
ICR1 zu messen. Dazu muß Dein Signal an ICP1 anliegen, und Überläufe von 
T1 müssen mit ausgewertet werden.
Das Thema ist hier schon öfter ausführlich besprochen worden: suchen und 
finden.

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm naja, ich will aber nicht mehr als 100Hz messen....Dafür so ein 
Aufwand?

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gerade weil Du nur 100Hz messen willst, ist ICP1 ideal.
Dein Programm ist sicherlich sehr kurz, aber es funktioniert ja auch 
nicht ;-)

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja das stimmt ;)


Aber ich dachte mir, mit 2Mhz Quarz währe es dann ideal mit 1024er PS 
sowas zu machen....


Es ist ja nichts großartiges.
Positive Flanke, Zählstart, positive Flanke, Zählstopp.

Das wars ja schon ;)

Autor: ASM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>ldi temp, 0b00000000 ;Hier Temp mit 1024erPS laden
>ldi temp3,0b00000101 ;Hier einmal festgelegt!

Was soll das? Du musst es schon noch in ein Register schreiben, damit es 
eine Auswirkung hat

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
? Ja habe ich doch auch gemacht!

In der ISR des INT0 wird jeweils 0x00 oder ebenhalt 0x05 gesschrieben.
Damit wird dann der timer jeweils gestartet und gestoppt!!

Autor: ASM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Starten und stoppen allein bringt afaik nichts, du musst ihn auch mal 
auf null setzten oder die Differenz zwischen neuem und altem Timerwert 
ausgeben,

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also meinst du:

Timer Starten, Stoppen.

Timer Wert auslesen.
Timer Wert ausgeben.
Timer auf 0 Setzen.

Von Vorn.


?

Autor: ASM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, ich wuerde den Timer nie stoppen sondern so (wenn du es ohne ICP 
machen willst):

Hauptprogramm:
   Timer mit prescaler 1024 starten
   sei()

   Endlosschleife()

*******

Interrupt:
   temp = Timer
   Timer = 0
   PortB = temp
reti

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm jo...


Also meine Lösung kann ja nur "datenmüll" ausgeben...
Denn wenn immer in Endlosschleife der Timer Wert ausgegeben wert, 
welcher sich ständig und schnell ändert, kanns nicht funktionieren ;)

Also erst Zeit messen und ENDWERT, nach Stoppen des Timers, ausgeben.

Autor: ASM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie gesagt, den Timer musst du nicht stoppen, sondern nur auf 0 setzen, 
dann sollte es klappen. Ist mein "Programm" oben nicht verstaendlich?

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja doch schon.
Bis auf die Tatsache, dass der Timer Wert nur jedes zweite mal 
reinkopiert werden soll.
Denn sonst hat man einmal 0 und einmal den Wert.

Autor: ASM (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, du kannst bei jeder steigenden Flanke den Timerwert auslesen, nach 
dem auslesen wieder auf 0 setzten. Es gibt so genau einen falschen Wert, 
und der entsteht dann, wenn der Interrupt zum ersten Mal ausgefuehrt 
wird nach einem Reset. Danach stimmt es mit dieser Methode immer. Nix 
mit Timer anhalten...!

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oki,

bin gerade nicht daheim und kanns nich probieren.

Nachher schau ich mal ob es funzt..

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm Funktioniert leider nicht :(

Ich starte den Timer.

Der uC Bleibt dann in einer endlosschleife und mach nichts.
Bei Positiver Flanke wird in der ISR der Timer Wert gesichert, der Timer 
auf null gesetzt und der timer wert aufs port geschrieben.


Lustigerweise kommt bei mir dann immer raus:
0bxxx10011 (Also das, was aufs port c geschrieben wird).
Das verändert sich auch nicht :(

Autor: Vorname Nachname (logout-name)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zeig nochmal das Programm

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So jetzt läufts, erst nicht aber nun doch ;)

Merke:
TCNT0 wir nicht mit ldi in ein arbeitsregister kopiert sondern mit in!

hinterdieohrenschreib

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So funktioniert es glaube ich ;)

.include "m8def.inc"

.def temp = r16
.def temp1=r17
.def temp3=r18


.org 0x0000
        rjmp    main

.org INT0addr
        rjmp int0_handler
.org OVF0addr
        reti

main:

     ldi     temp, LOW(RAMEND)     ; Stackpointer initialisieren
        out     SPL, temp
        ldi     temp, HIGH(RAMEND)
        out     SPH, temp

;******************************
; INTERRUPTs  konfigurieren


      ldi temp, 0b00000011  ; INT0, rising edge generates interrupt 
request
        out MCUCR, temp

    ldi     temp, 0 << TOIE0     ; Bei überlauf von Timer0 KEIN 
Interrupt
        out     TIMSK, temp

;******************************
;    T I M E R 0 konfigurieren

      ldi     temp, 0b00000000     ; Timer für den Anfang ausschalten!
        out     TCCR0, temp

    ldi temp, 0x00
    out TCNT0, temp
;*****************************
;Ports
ldi temp,0xFF      ;Ausgang konfigurieren
out DDRC, temp

ldi temp,0x00   ; Eingang konfigurieren
out DDRD, temp

ldi temp, 0xFF
out PORTD, temp
ldi temp, 0b01000000  ; INT0 aktivieren!
out GICR, temp
ldi temp, 0b00000000
ldi temp3,0b00000101 ;Hier einmal festgelegt!
sei ;Generelle Interruptfreigabe!
 out TCCR0, temp3 ;Timer Starten!


loop:
rjmp loop

int0_handler:
in temp1, TCNT0
out PORTC, temp1
  ldi temp, 0x00
out TCNT0, temp
         reti

Autor: Kachel-Heinz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
- Im ext. Interrupt eine Zählvariable hochzählen.

- Mit Timer einen Sekundentakt erzeugen. Darin die Zählvariable zur
  Ausgabe kopieren und danach löschen.

Oder

- Einen Timer/Counter als asynchronen Zähler über den externen
  Zähleingang (T0 oder T1) benutzen und damit die Impulse zählen.

- Mit anderem Timer einen Sekundentakt erzeugen, in dem den ersten
  Timer/counter auslesen und löschen.

Oder (wenn nicht die Frequenz, sondern die Periodendauer gewünscht wird)

- Timer1 frei laufen lassen.

- Signal an ICP1-Eingang legen.

- Bei ICP-Interrupt ICR1H:ICR1L auslesen und Differenz zum Wert der
  letzten Messung bilden, dann den neuen Wert für die nächste Messung
  merken. Timer dabei nicht löschen. Vorteil: Man kann nebenher noch
  die beiden Compare-Interrupts des Timer1 benutzen, um z.B. eine
  7-Segment-Anzeige zu multiplexen.

- Messwert (Differenz neu minus alt) entspricht der Periodendauer. Soll
  die Frequenz gemessen werden, dann ist er umzurechnen (f=1/t). Da der
  AVR aber kein Hardware-Div hat, wird das eine kleine Fleißarbeit.

KH

Autor: Asm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So um verwirrungen zu vermeiden habe ich jetzt mal nen mega32...
Denn beim mega8 ist das port c ja "abgehackt".

Klappt sehr gut und ich habe eine frequenzabweichung von "nur" einem 
Herz.
Wohlgemerkt mit selbstgebautem Funktionsgenerator, der noch ein etwas zu 
schnelles quarz drinne hat...


Danke nochmal für alle tipps.
Ich werde mich, zwecks genauigkeit, noch mit der ICP Methode und 16bit 
arithmetik befassen...

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.