PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Timer, Zähler, zwei Frequenzen



wodka
03.03.2009, 18:03
Irgendwie stelle ich mir immer noch die Frage, wie realisiere ich z.B. zwei verschiedene Zeiten/Frequenzen bzw. zwei LEDs sollen unterschiedlich schnell blinken mit nur einem Timer. (Mit wait Zeiten im Programm wäre es ja einfach, hält ja aber alles nur auf.)

Nehmen wir das Beispiel.

LED 1 im 0,5Hz Takt = 2sec Takt
LED 2 im 1 Hz Takt = 1sec Takt

Vom Ansatz würde ich mir nun überlegen. Ich programmiere einen Timer auf 0,5 Hz, lass in der ISR eine Variable X zählen. Von 1 - 2, bei wert 2 fängt er wieder bei 1 an.
In der Hauptschleife frage ich die Variable X mit einem If ab!

Ist der Ansatz so richtig, oder löst man sowas anders ?

Besserwessi
03.03.2009, 18:19
Im Prinzip schon, nur gehen die wenigsten Timer so langsam.

PicNick
03.03.2009, 18:20
Umgekehrt. Timer immer die kleinste Einheit.
Die zählst 1 Sekunde --> LED2
jedes zweite mal ---> LED1

Da 1 Sekunde für einen Timer ganzschön lang ist, ist es besser, z.B. 1mS einzustellen, also bei 1000->LED2 bei 2000 -> LED1 uns dann wieder von vorn.

Durch das Frequenz-Verhältnis von 1:2 gibt es noch mehr Möglichkeiten, aber sei's drum

wodka
03.03.2009, 19:23
Supi, danke... Also war mein Ansatz ja nicht verkehrt ;-) Macht ja auch sinn dann mit 1ms (1khz).. Vielen Dank

Bob13
03.03.2009, 19:44
etwas langsamere Frequenzen bekommst du mit nem Uhrenquarz, allerdings schwingt der immer noch relativ schnell.

wodka
05.03.2009, 12:05
Ich habe nun mal ein Code, der funktioniert. Aber selbst persönlich finde ich die Lösung nicht so gut. Vll habt ihr ja andere bessere Ideen:
z.B man viel nun nicht 0,5sek sondern auf 0,2sec Ein/AUS... Dann müsste man mit meinm Prog alles wieder anpassen.



$eeprom
$eepromhex
$regfile = "m32def.dat" ' Prozessortyp ATmega32
$crystal = 16000000 ' Taktrate
$hwstack = 32
$swstack = 10
$framesize = 40
$baud = 4800


Config Portb.2 = Output
Led1 Alias Portb.2
Config Portb.3 = Output
Led2 Alias Portb.3


Config Lcd = 20 * 2
Config Lcdpin = Pin , Db4 = Porta.0 , Db5 = Porta.1 , Db6 = Porta.2 , Db7 = Porta.3 , E = Porta.5 , Rs = Porta.4
Cls
Cursor Off


Config Timer0 = Timer , Prescale = 64
On Timer0 Timer_irq
Const Timervorgabe = 6
Enable Timer0
Enable Interrupts



Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc

Dim W As Word
Dim Taktzaehler As Integer
Dim 05takt As Bit
Dim 10takt As Bit
Dim 20takt As Bit
Dim Merker1 As Bit
Dim Merker2 As Bit




Do

W = Getadc(7)

Locate 2 , 15
Lcd W ; " "
Waitms 200

If W >= 400 Then
Merker1 = 1
Else
Merker1 = 0
End If

If W <= 400 Then
Merker2 = 1
Else
Merker2 = 0
End If





Loop

Timer_irq:
Timer0 = Timervorgabe

If Taktzaehler >= 4000 Then
Taktzaehler = 0
End If

Taktzaehler = Taktzaehler + 1


If Taktzaehler >= 0 And Taktzaehler <= 500 Then
05takt = 1
10takt = 1
End If

If Taktzaehler >= 500 And Taktzaehler <= 1000 Then
05takt = 0
10takt = 1
End If

If Taktzaehler >= 1000 And Taktzaehler <= 1500 Then
05takt = 1
10takt = 0
End If

If Taktzaehler >= 1500 And Taktzaehler <= 2000 Then
05takt = 0
10takt = 0
End If


If 05takt = 1 And Merker1 = 1 Then
Led2 = 0
Else
Led2 = 1
End If

If 10takt = 1 And Merker2 = 1 Then
Led1 = 0
Else
Led1 = 1
End If

Return
End

Sauerbruch
05.03.2009, 14:31
Aber selbst persönlich finde ich die Lösung nicht so gut.

Zwar führen viele Wege nach Rom, und Schönheit liegt ja bekanntlich im Auge des Betrachters ( O:) ), aber eine grundsätzliche Regel sollte man konsequent befolgen:
In Interrupt-Routinen sollte so wenig wie möglich Code stehen, damit die Hauptschleife nicht so lange unterbrochen wird. Während eines Interrupts sind nämlich die anderen Interrupts erstmal blockiert, und daraus können unübersichtliche Verschachtelungen entstehen. Am besten setzt man in der ISR nur ein Flag-Bit, das in der Hauptschleife abgefragt wird. Ist es 1, wird der dazugehörige Code ausgeführt und es anschließend wieder gelöscht - fertig.

In Deinem speziellen Fall würde ich (...) den Timer z.B. alle 0,1 Sekunden überlaufen und in der ISR ein Flag setzen lassen.

In der Hauptschleife wird dann das Flag abgefragt. Wenn es 1 ist, inkrementierst Du 2 Zähl-Variablen. Wenn die eine 2 bzw. die andere 5 erreicht hat (und Deine "Merker" gesetzt sind), wird der Status der dazugehörigen LED einfach getoggelt und die Zählvariable auf 0 zurückgesetzt.

Wenn Du ein paar Zeilen Code brauchst, sag Bescheid. Aber selber schreiben ist viel schöner
=P~

Gruß & Co

wodka
05.03.2009, 17:00
Hi, danke für die Antwort. Die hat schon viel geholfen :-) Habe das Programm noch nicht testen können, aber ich denke so meinst du das? Meine ISR läuft nur alle 1ms über und somit auch 500ms, 200ms in der Zählervariable.

Aber wenn ich nun z.B. das Display nur alle Sek bzw hier halbe Sekunde aktualisieren möchte, ist das doch in meinem Code falsch, dann würden die LED nicht mehr richtig blinken wg der Wartezeit oder?
Wie um gehe ich nun sowas?
Ich hatte mal gelesen, kontinuerliche LCD Routinen sind nicht vorteilhaft (wg Zykluszeit??)!



$eeprom
$eepromhex
$regfile = "m32def.dat" ' Prozessortyp ATmega32
$crystal = 16000000 ' Taktrate
$hwstack = 32
$swstack = 10
$framesize = 40
$baud = 4800


Config Portb.2 = Output
Led1 Alias Portb.2
Config Portb.3 = Output
Led2 Alias Portb.3


Config Lcd = 20 * 2
Config Lcdpin = Pin , Db4 = Porta.0 , Db5 = Porta.1 , Db6 = Porta.2 , Db7 = Porta.3 , E = Porta.5 , Rs = Porta.4
Cls
Cursor Off


Config Timer0 = Timer , Prescale = 64
On Timer0 Timer_irq
Const Timervorgabe = 6
Enable Timer0
Enable Interrupts



Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc

Dim W As Word
Dim Taktzaehler02 As Integer
Dim Taktzaehler05 As Integer
Dim Timer0_flag As Bit
Timer0_flag = 0


Do

W = Getadc(7)

Locate 2 , 15
Lcd W ; " "
Waitms 500 'Was ist wenn hier die Zeit nun auf 500ms stehen würde?


If Timer0_flag = 1 Then
Taktzaehler02 = Taktzaehler02 + 1
Taktzaehler05 = Taktzaehler05 + 1
Timer0_flag = 0
End If

If Taktzaehler02 = 200 Then
Toggle Led1
Taktzaehler02 = 0
End If

If Taktzaehler05 = 500 Then
Toggle Led2
Taktzaehler05 = 0
End If


Loop

Timer_irq:
Timer0 = Timervorgabe

Timer0_flag = 1

Return
End

wodka
05.03.2009, 17:55
Letztendlich sollte das doch auch so gehen mit der Wartezeit für das LCD ohne die main loop aufzuhalten ?



$eeprom
$eepromhex
$regfile = "m32def.dat" ' Prozessortyp ATmega32
$crystal = 16000000 ' Taktrate
$hwstack = 32
$swstack = 10
$framesize = 40
$baud = 4800


Config Portb.2 = Output
Led1 Alias Portb.2
Config Portb.3 = Output
Led2 Alias Portb.3


Config Lcd = 20 * 2
Config Lcdpin = Pin , Db4 = Porta.0 , Db5 = Porta.1 , Db6 = Porta.2 , Db7 = Porta.3 , E = Porta.5 , Rs = Porta.4
Cls
Cursor Off


Config Timer0 = Timer , Prescale = 64
On Timer0 Timer_irq
Const Timervorgabe = 6
Enable Timer0
Enable Interrupts


Config Adc = Single , Prescaler = Auto , Reference = Avcc
Start Adc

Dim W As Word
Dim Taktzaehler02 As Integer
Dim Taktzaehler05 As Integer
Dim Taktzaehler1000 As Integer


Do


If Timer0_flag = 1 Then
Taktzaehler02 = Taktzaehler02 + 1
Taktzaehler05 = Taktzaehler05 + 1
Taktzaehler1000 = Taktzaehler1000 + 1
Timer0_flag = 0
End If

If Taktzaehler02 = 200 Then
Toggle Led1
Taktzaehler02 = 0
End If

If Taktzaehler05 = 500 Then
Toggle Led2
Taktzaehler05 = 0
End If

If Taktzaehler1000 = 1000 Then
W = Getadc(7)
Locate 2 , 15
Lcd W ; " "
Taktzaehler1000 = 0
End If

Loop


Timer_irq:
Timer0 = Timervorgabe

Timer0_flag = 1

Return
End

Sauerbruch
05.03.2009, 20:45
Letztendlich sollte das doch auch so gehen mit der Wartezeit für das LCD ohne die main loop aufzuhalten ?

Ganz genau! So ist es viel eleganter als mit waitms500, die das Programm halt für diese Zeit total lahmgelegt hätten. Und Du bekommst genauso alle 1 Sek. den aktuellen ADC-Wert angezeigt.

Eigentlich müsste es mit diesem Code super laufen!

Gruß,

Daniel

wodka
05.03.2009, 20:46
Vielen Dank :-) Ich werde es morgen mal ausprobieren. Im Simulator läuft es schon ganz gut

stefan_Z
06.03.2009, 01:46
Es gibt so ne kleine .exe die dir die Timer-Settings ganz genau ausrechnet.
AVRcalc: http://clsql.med-info.com/

Das Progi spuckt halt die besten annäherungen für die Timer-Preloads und Überläufe aus.
Mit 16-Bit Timern schafft man per passenden Prescaler auch Zeiten im Sekundenbereich.

wodka
06.03.2009, 06:24
Ich wollte gerne den Timer0 nehmen und mit einem 1ms Takt kann man ja auch gut zählen.

Aber danke für den Link, dass ist echt ein sehr guter Rechner.

Dankeschön