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
>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?
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.
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
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
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