www.mikrocontroller.net

Forum: www.mikrocontroller.net AVR-Tutorial: Uhr


Autor: Christian Berger (casandro) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist das denn nicht eine sehr umständliche Methode, wie man eine Uhr 
realisieren kann?

Ich meine, da benötigt man ja einen Timer, den man sonst für nichts 
verwenden kann.

Meine Idee wäre da, meiner Meinung nach, eleganter:

Man hat eine relativ breite Zahl im Speicher. Bei jedem Taktzyklus 
erhöht man sie um einen bestimmten Wert. Ist diese Zahl größer als ein 
Schwellenwert, so ist eine Sekunde abgelaufen und die Zahl wird um den 
Schwellenwert reduziert.

Wenn man den Schwellenwert geschickt wählt, so kann man einfach das 
Carry-Flag abfragen.

Was passiert nun da: Man kann sich das so vorstellen wie ein Zeiger, der 
in einem Kreis rotiert. Bei jedem Taktzyklus geht der Zeiger um einen 
bestimmten  Winkel weiter. Wenn man den Winkel jedes Schrittes 
einstellt, so kann man einstellen, wie oft der Zeiger pro Zeiteinheit 
rumläuft.

Nehmen wir mal ein Beispiel. Die ISR wird mit hypothetischen 100 kHz 
aufgerufen. Die Zählervariable hat 32 Bit und der Schwellenwert ist 
somit 2^32. Wenn man nun bei jedem ISR nur um 1 weiterzählt so ergibt 
sich der Überlauf mit einer Frequenz von 100 
kHz/(2^32)=.000023283064365386962890625Hz auf. Zählt man um n weiter, so 
kann man n mal diese Frequenz erzielen. Man kann somit seine 1Hz in 
23uHz-Schritten einstellen, das sind sehr kleine Schritte.

Natürlich kann man die Schrittweite in einer weiteren Variable speichern 
und sie somit anpassen. Stellt euch vor, Ihr habt gelegentlich 
DCF77-Empfang. Man muss sich dann nur die Zeit seit dem letzten Update 
merken und die neue Zeitdifferenz berechnen. Daraus kann man den Fehler 
der Frequenz berechnen und korrigieren. Beim nächsten Verbindungsabbruch 
sollte die Uhr dann kalibriert laufen. So kann man noch viel genauere 
Uhren bauen.

Wie man mit der selben Technik Töne beliebiger Frequenz erzeugen kann, 
kann sich ja jeder denken.

Servus
  Casandro

Autor: Rahul Der trollige (rahul)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Ich meine, da benötigt man ja einen Timer, den man sonst für nichts
>verwenden kann.

Ich habe mir das Tutorium nicht angeguckt, aber die Uhr ist bei meinen 
Timeranwendungen eher ein Nebenprodukt.

>Was passiert nun da: Man kann sich das so vorstellen wie ein Zeiger, der
>in einem Kreis rotiert. Bei jedem Taktzyklus geht der Zeiger um einen
>bestimmten  Winkel weiter. Wenn man den Winkel jedes Schrittes
>einstellt, so kann man einstellen, wie oft der Zeiger pro Zeiteinheit
>rumläuft.

Dafür benutzt man doch den CTC-Modus: Man stellt den "Winkel" so ein, 
dass man ihn auch noch für andere Sachen wie Tastenentprellung, PWM oder 
Multiplexing benutzt.

>Die Zählervariable hat 32 Bit und der Schwellenwert ist somit 2^32.

Welch Speicherverschwendung.

>das sind sehr kleine Schritte.
Wer's braucht...

Ich rechne in der Regel eher in Sekunden (und Teilen davon) bei 
Controllern. Das macht einfach mehr sinn für mich, da ich dann nicht 
alles 1/x rechnen muß.

µHz sind 10^6 Sekunden...

Wie wäre es, wenn du sowas mal programmieren würdest?

Autor: Christian Berger (casandro) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rahul Der trollige wrote:

> Wie wäre es, wenn du sowas mal programmieren würdest?

Hier, realtime_s gibt an wie groß der Schritt ist, und realtime_t gibt 
an, wie viele Schritte eine Sekunde braucht. Damit kann 
count_realtime_clock_50Hz mit einer fast beliebigen Frequenz aufgerufen 
werden, durch die beiden Konstanten kann man das immer ausgleichen.
Das geht alles nebenbei, ohne, dass man einen Timer speziell dafür 
abrichten muss. Mein Timer läuft beispielsweise mit 62,5 KHz über und 
irgendwo im Programm habe ich etwa 50 Hz. Diese 50 Hz kann ich 
ausnutzen. Leider sind das jedoch 50,08 Hz, was man jedoch durch die 
beiden Konstanten ausgleichen könnte.

Der Algorithmuss hat aber noch weitere Vorteile. Stell Dir vor, Du 
möchtest einige LEDs mit unterschiedlicher Geschwindigkeit blinken 
lassen. Du nimmst einfach für jede LED etwas Speicher und zählst den bei 
jedem IRQ um x hoch. Die LED steuerst Du einfach mit dem hochwertigsten 
Bit an. Sie wird dann mit einer Frequenz blinken, die genau proportional 
zu X ist. Das macht auch die Erzeugung von Tönen deutlich einfacher.

.dseg

realtime_hsek: .byte 1
realtime_sek: .byte 1
realtime_min: .byte 1
realtime_std: .byte 1

.cseg

.equ realtime_s=-1
.equ realtime_t=50

count_realtime_clock_50Hz:
    PUSH r17
        LDS r17,realtime_hsek
        SUBI r17,realtime_s
        STS realtime_hsek,r17
        CPI r17,realtime_t
        BRGE count_realtime_clock_50Hz_1s
   count_realtime_clock_50Hz_1s_return:
    POP r17
    ret

count_realtime_clock_50Hz_1s:
    SUBI r17,realtime_t
    STS realtime_hsek,r17
    LDS r17,realtime_sek ;Sekunden
    SUBI r17,-1
    STS realtime_sek,r17
    CPI r17,60
    BRGE count_realtime_clock_50Hz_1m
    rjmp count_realtime_clock_50Hz_1s_return

count_realtime_clock_50Hz_1m:
    LDI r17,0
    STS realtime_sek,r17
    LDS r17,realtime_min ;Minuten
    SUBI r17,-1
    STS realtime_min,r17
    CPI r17,60
    BRGE count_realtime_clock_50Hz_1h
    rjmp count_realtime_clock_50Hz_1s_return

count_realtime_clock_50Hz_1h:
    LDI r17,0
    STS realtime_min,r17
    LDS r17,realtime_std ;Stunden
    SUBI r17,-1
    STS realtime_std,r17
    CPI r17,24
    BRGE count_realtime_clock_50Hz_1d
    rjmp count_realtime_clock_50Hz_1s_return

count_realtime_clock_50Hz_1d:
    LDI r17,0
    STS realtime_std,r17
    rjmp count_realtime_clock_50Hz_1s_return


init_realtime_clock: ;Falls die Uhr eine seltsame Zeit hat, einfach mit 
00:00:00 belegen
    LDS r17,realtime_std
    CPI r17,24
    BRGE init_realtime_clock_init_needed
    LDS r17,realtime_min
    CPI r17,60
    BRGE init_realtime_clock_init_needed
    LDS r17,realtime_sek
    CPI r17,60
    BRGE init_realtime_clock_init_needed
    ret
  init_realtime_clock_init_needed:
    LDI r17,0
    STS realtime_std,r17
    STS realtime_min,r17
    STS realtime_sek,r17
    ret

====8<=======8<=========
Tonerzeugung, Phase Distortion Synthesis
(fast gleiches Prinzip)
.dseg



.cseg

.def omega_1rl=r2
.def omega_1rh=r3
.def phi_1rl=r4
.def phi_1rh=r5
.def volume_1r=r6
.def modf_1r=r7
.def modg_1r=r8
.def t1=r17

.def mul_h=r1
.def mul_l=r0


;In der ISR habe ich keine Zeit fuer Speicherzugriffe
.macro audio_isr_prepare
    PUSH omega_1rl
    LDS omega_1rl,omega_1
    PUSH omega_1rh
    LDS omega_1rh,omega_1+1

    PUSH phi_1rl
    LDS phi_1rl,phi_1
    PUSH phi_1rh
    LDS phi_1rh,phi_1+1

    PUSH volume_1r
    LDS volume_1r,volume_1

    PUSH modf_1r
    LDS modf_1r,modf_1

    PUSH modg_1r
    LDS modg_1r,modg_1

    PUSH r1
    PUSH r0

.endmacro

.macro audio_isr_finish
    POP r0
    POP r1

    POP modg_1r

    POP modf_1r

    POP volume_1r

    STS phi_1,phi_1rl
    POP phi_1rl
    STS phi_1+1,phi_1rh
    POP phi_1rh

    POP omega_1rl
    POP omega_1rh

.endmacro

;Wird ziemlich genau mit 15625 Hz aufgerufen, braucht genau 19TZ
.macro audio_pm_15625Hz
    LDI zh,high(sintab*2)       ;1

    ADD phi_1rl,omega_1rl       ;1
    ADC phi_1rh,omega_1rh       ;1

    MUL phi_1rh,modf_1r         ;2

    MOV zl,mul_l                ;1

    LPM t1,Z                    ;3
    MUL t1,modg_1r              ;2
    MOV zl,phi_1rh              ;1
    ADD zl,mul_h                ;1
    LPM t1,Z                    ;3
    MUL t1,volume_1r            ;2
    OUT OCR0,mul_h                      ;1
;                          ==========
;                               19 TZ
.endmacro


sound_init:
   LDI r17,0
   STS volume_1,r17
   ret

sound_playsound: ; r17 ton; r18 dauer; r19 Decay; r20 Klangfarbe
;r17 Bits 4-7 Oktave
;    Bits 0-3 Ton
;r18 Dauer in 20ms Schritten
;r19 Nachhall in 20 ms Schritten
    PUSH r21
    PUSH r22
    PUSH zl
    PUSH zh
        ;Klangfarbe

        MOV r21,r20
        ANDI r21,$f0 ;Modulationsfrequenz
        SWAP r21
        STS modf_1,r21
        MOV r21,r20
        ANDI r21,$0f ;Modulationsgrad
        SWAP r21 ; damit grad staerker wird
        STS modg_1,r21

        ;Tonhoehe
        LDI zh,high(sound_scale*2)
        LDI zl,low(sound_scale*2)

        MOV r21,r17 ;Ton holen
        ANDI r21,$0f ;maskieren
        ;SWAP r21 ;verschieben

        ADD zl,r21
        ADD zl,r21
        LPM r21,Z+ ;low Byte
        LPM r22,Z  ;high Byte
        MOV zh,r17 ;Register recyclen
        SWAP zh
        ANDI zh,$0f
   sound_playsound_octave_loop:
        LSR r22
        ROR r21
        INC zh
        CPI zh,$0f
       BRLT sound_playsound_octave_loop
    ;frequenz steht in r21:r20 drin
        ;LDI r21,0
        ;LDI r22,20
        STS omega_1,r21
        STS omega_1+1,r22
        ;wenn Ton kuerzer als 4*20=80 ms, dann nicht anschwingen
        CPI r18,4
        BRLT sound_playsound_hold
     ;Anschwingen 80 ms
        LDI r21,7
        LDI r22,0
    sound_playsound_attack_loop:
        SUBI r22,-63
        STS volume_1,r22
        call sleep_frame
        DEC r21
        BRNE sound_playsound_attack_loop
        SUBI r18,4
    sound_playsound_hold: ;Ton halten
        LDI r22,255
        STS volume_1,r22
        call sleep_frame
        DEC r18
        BRNE sound_playsound_hold
    sound_playsound_decay: ;abklingen max 1s
        LDI r18,255
     sound_playsound_decay_loop:
        call sleep_frame
        SUBI r18,5
        BREQ sound_end
        STS volume_1,r18
        DEC r19
        BRNE sound_playsound_decay_loop
      sound_end:
        ;Ton aus
        LDI r18,0
        STS volume_1,r18
    POP zh
    POP zl
    POP r22
    POP r21
ret

sound_demo:
sound_demo2:
    LDI r22,0
    sound_demo2_start:
    LDI zh,high(sound_freude_schoener_goetterfunken*2)
    LDI zl,low(sound_freude_schoener_goetterfunken*2);
   sound_demo2_loop:
    LPM r23,Z+
    LDI r17,'-'
    call out_char_r17
    CPI r23,0
    BREQ sound_demo2_start
    MOV r24,r23
    ANDI r24,$7f
    LDI r17,$A0
    ADD r17,r24
    LDI r18,10
    LDI r19,12
    SBRC r23,7
    LDI r19,22
    MOV r20,r22
    call sound_playsound
    call get_key_r17
    CPI r17,27
    BREQ sound_demo2_ende
    CPI r17,'0'
    BRLT sound_demo2_loop
    CPI r17,'9'+1
    BRGE sound_demo2_loop
    MOV r22,r17
    call out_char_r17
    SUBI r22,'0'
    SWAP r22
    ORI r22,$06
    rjmp sound_demo2_loop
   sound_demo2_ende:
ret


nop
nop
nop
nop

.cseg

sound_scale: ;wird in Intel-Richtung abgespeichert
.dw 
17557,18601,19708,20879,22121,23436,24830,26306,27871,29528,31283,33144

sound_freude_schoener_goetterfunken:
.db $14,$14,$15,$17,$17,$15,$14,$12,$10,$10,$12,$14,$14,$12,$12+$80, 
$14
.db     $14,$15,$17,$17,$15,$14,$12,$10,$10,$12,$14,$12,$10,$10+$80
.db $12+$80,$14,$10,$12,$14,$15,$14,$10,$14,$15,$14,$12,$10,$12
.db $7,$14,$15,$17,$17,$15,$14,$12,$10,$10,$12,$14,$12,$10,$90,0

=8<==========8<============8<=========

Das sind natürlich nur Fragmente aus einem größeren Projekt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du hast schonmal in die Codesammlung geguckt ?

Beitrag "Die genaue Sekunde / RTC"


Du weißt schon, daß es unhöflich ist, die Leute sich totscrollen zu 
lassen ?

Wozu ist denn wohl der Dateianhang da ?



Peter

Autor: Christian Berger (casandro) Flattr this
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:
> Du hast schonmal in die Codesammlung geguckt ?
>
> Beitrag "Die genaue Sekunde / RTC"

Da steht nichts über die Funktion, da ist nur Code in irgendeinem 
seltsamen Packerformat dabei. Ich vermute jedoch, dass das ähnlich 
funktioniert. So etwas ist jedoch, meiner Meinung nach, so grundlegend, 
dass es sich in das Tutorium gehört.

> Du weißt schon, daß es unhöflich ist, die Leute sich totscrollen zu
> lassen ?

Das mag sein, aber wer ließt denn sonst den Code?

> Wozu ist denn wohl der Dateianhang da ?

Für ein komplettes Projekt würde ich den auch hernehmen, aber für kurze 
Fragmente ist das nicht sinnvoll. Wie schon gesagt, den Anhang ließt 
kein Mensch.

> Peter

Servus
  Casandro

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Christian Berger wrote:

> Da steht nichts über die Funktion, da ist nur Code in irgendeinem
> seltsamen Packerformat dabei.

Oooch is der aber drollig.


> Das mag sein, aber wer ließt denn sonst den Code?

Falsch !

Umgekehrt wird ein Schuh draus.

Man öffnet den Anhang in einem neuen Fenster und schon kann man den Code 
lesen und den Beitrag.


> Für ein komplettes Projekt würde ich den auch hernehmen, aber für kurze
> Fragmente ist das nicht sinnvoll.

Kurze Fragmente sind etwa 20 Zeilen, aber kein 318-Zeilen-Monster.
Warscheinlich isses nichmal assemblierbar.


> Wie schon gesagt, den Anhang ließt
> kein Mensch.

Wie schon gesagt, ich habs nicht ansehen können, da mein Scrollfinger 
schmerzt.
Ich konnte nur die "Ende"-Taste drücken und schon wars vorbei.


Peter

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.