PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Problem mit Alarm bei Selbstbau-Uhr



joho63
02.11.2013, 14:14
Hallo!
Im Rahmen eines größeren Bastelprojekts (Langzeitbetrieb einer Kamera mit PC über Jahre) habe ich ständig Probleme mit der (eingebauten) Wochenzeitschaltuhr.
Diese ist ja leider nicht mit DCF77 ausgerüstet (hab ich noch nie gesehen) und darum muß ich ständig auf der Lauer sein.
Jeden Monat einmal die Zeit korrigieren und jedes Halbjahr die Umstellung Sommer/Winterzeit. Und bei Stromausfall korrigieren.
Das wurde mir jetzt zuviel und deshalb muß jetzt eine "richtige" Uhr her.
Das Programm für die Uhr selbst ist schon fertig und läuft tadellos. Ich habe sie - als Anfänger - aus verschiedenen Codeschnipseln
und etwas eigenem dazu zusammengesetzt. Ein I2C-RTC-Modul steuert die BASCOM-Softclock und wird von einem ELV-DFC77-Modul
genau gehalten.
Ohne Schaltausgang nützt sie mir allerdings garnichts, denn die Uhr soll ja täglich einmal den PC aufwecken, nach 5 Minuten ein
Programm starten und den PC wieder schlafen legen.
Also hab ich versucht, das mal selbst zu entwickeln. Der jetzige Stand ist:
Die Umschaltung Stellen - Alarm EIN funktioniert.
Beim Stellen kann ich auch Stunde und Minute erhöhen.
Beim Zurückschalten auf Alarm Ein geht aber die Anzeige wieder auf Null.
Das sagt mir: die Zahlen im LCD bleiben nicht erhalten. Warum? Keine Ahnung...
Hier noch der Code:



Locate 4 , 1 : Lcd "Alarm"

'--------------- Taster - Schleife --------------------------------
Do
If Ta_stellen = 0 Then
Waitms 10
While Ta_stellen = 0
NOP ' Debouncing
Nop
Wend
Goto Set_alarm
End If
Locate 4 , 13 : Lcd " Ein "
Loop

'-------------- Stellen - Schleife ------------------------------------------
Set_alarm:
Do
If Ta_min = 0 Then
Al_min = Al_min + 1 'Minute stellen
If Al_min = 60 Then Al_min = 0
Waitms 200
End If
If Ta_std = 0 Then
Al_std = Al_std + 1 'Stunde stellen
If Al_std = 24 Then Al_std = 0
Waitms 200
End If
'-----------------------------------------------------------------------------------------
If Ta_stellen = 0 Then Exit Do
'-----------------------------------------------------------------------------------------
Locate 4 , 13 : Lcd "Stellen"
Locate 4 , 7 : Lcd Al_std ; ":" ; Al_min
Loop

Return

End

Wie schon ober geschrieben, ist das nur ein Teil einer ganzen Uhrsteuerung.
Es sollte möglichst keinen Timer oder Interrupt brauchen.
Das muß doch gehen!
Den Rest, also den Uhrzeit-Vergleich und den Ausgang zum Pc kann ich wieder selbst.

Vielleicht ist es nur eine Kleinigkeit, die ich mangels Erfahrung übersehe.
Joachim

for_ro
02.11.2013, 17:09
Hallo Joachim,
nur mit diesem Codeschnipsel kann man dir schlecht raten, was du ändern solltest.
Du hast dort ein Goto Set_alarm drin, also ein Sprung zu einer anderen Stelle im Programm.
Bei dem Label wird die Alarmzeit gestellt, soweit sollte es funktionieren.
Nach der Schleife dort kommt ein Return. Wenn sich der ganze Codeteil innerhalb einer Subroutine befindet, dann würde er jetzt hinter dem entsprechenden Aufruf weitermachen. Wenn nicht, wird der Controller wahrscheinlich resetten und wieder von vorne beginnen.

joho63
02.11.2013, 18:04
Danke für die schnelle Rückmeldung!
Dieser "Codeschnipsel" ist eigentlich schon das ganze Programm bzw. Teilprogramm für den Alarm.
Vom Uhrenprogramm wird nur die aktuelle Zeitinformation übernommen (_hour, _min) und mit der eingestellten Zeit verglichen.
Das ist das Ganze:


$regfile = "m8def.dat"
$crystal = 9830400
$hwstack = 60
$swstack = 32
$framesize = 64
$include "INIT_LCD20x4.bas" 'LCD an Port D
Pinb = &B11000000 'B 0,1 = Enc; 2,3,4,5 = Taster
Portb = &B00111111
Dim Al_min As Byte , Al_std As Byte
Enc_a Alias Pinb.0
Enc_b Alias Pinb.1
Ta_std Alias Pinb.2
Ta_min Alias Pinb.3
Ta_stellen Alias Pinb.4
Ta_enc Alias Pinb.5

Locate 4 , 1 : Lcd "Alarm"

'--------------- Taster - Schleife --------------------------------
Do
If Ta_stellen = 0 Then
Waitms 10
While Ta_stellen = 0
NOP ' Debouncing
Nop
Wend
Goto Set_alarm
End If
Locate 4 , 13 : Lcd " Ein "
Loop

'-------------- Stellen - Schleife ------------------------------------------
Set_alarm:
Do
If Ta_min = 0 Then
Al_min = Al_min + 1 'Minute stellen
If Al_min = 60 Then Al_min = 0
Waitms 200
End If
If Ta_std = 0 Then
Al_std = Al_std + 1 'Stunde stellen
If Al_std = 24 Then Al_std = 0
Waitms 200
End If
'-----------------------------------------------------------------------------------------
If Ta_stellen = 0 Then Exit Do
'-----------------------------------------------------------------------------------------
Locate 4 , 13 : Lcd "Stellen"
Locate 4 , 7 : Lcd Al_std ; ":" ; Al_min
Loop

'Return

End

'(
'-------------- LCD rechtsbündige Anzeige ------------------------------------
Locate 4 , 7 ' Anzeige Al_Std
If Al_Std < 10 Then Lcd " " ; Al_Std Else Lcd Al_Std ; " "
Locate 4 , 9 ' Anzeige Al_Min
If Al_Min < 10 Then Lcd ":0" ; Al_Min Else Lcd ":" ; Al_Min
Waitms 50
')

Ich hab den RETURN mal rausgenommen, dann bleibt er wie vorhersehbar bei "Stellen" stehen.
Die Frage ist für mich also: Wie komme ich aus der Stellroutine, ohne den Inhalt von Al_std und Al_min
zu verlieren?

joho63
03.11.2013, 11:08
Ich hab jetzt mal versucht, die eingestellten Werte als Temp1 und 2 zu speichern, kein Erfolg, steht nur Null drin....
Es muß doch möglich sein, die engestellten Daten vor dem Verlassen der Schleife woanders zu speichern?
Ich hab auch schon die Stellen-Schleife als SUB deklariert. Dann kann er sie nicht mehr verlassen bzw. springt gleich
nach Mode-Taste wieder rein. So sieht das jetzt aus:


$regfile = "m8def.dat"
$crystal = 9830400
$hwstack = 40
$swstack = 16
$framesize = 32
$include "INIT_LCD20x4.bas" 'LCD an Port D
Pinb = &B11000000 'B 2,3,4 = Taster
Portb = &B00111100
Dim Al_min As Byte , Al_std As Byte , Temp1 As Byte , Temp2 As Byte
Ta_std Alias Pinb.2
Ta_min Alias Pinb.3
Ta_mode Alias Pinb.4
Locate 1 , 1 : Lcd "Alarm "
Locate 2 , 2 : Lcd Temp1
Locate 3 , 2 : Lcd Temp2
'---------------Mode - Taster - Schleife --------------------------------
Do
Locate 1 , 13 : Lcd "Ein "
Locate 1 , 7 : Lcd Al_std ; ":" ; Al_min
If Ta_mode = 0 Then
Waitms 10
While Ta_mode = 0
NOP ' Debouncing
Nop
Wend
Goto Set_alarm
End If
Loop
'-------------- Stellen - Schleife ------------------------------------------
Set_alarm:
Do
If Ta_min = 0 Then
Al_min = Al_min + 1 'Minute stellen
If Al_min = 60 Then Al_min = 0
Waitms 200
End If
If Ta_std = 0 Then
Al_std = Al_std + 1 'Stunde stellen
If Al_std = 24 Then Al_std = 0
Waitms 200
End If
'-----------------------------------------------------------------------------------------
If Ta_mode = 0 Then Exit Do
'-----------------------------------------------------------------------------------------
Locate 1 , 13 : Lcd "Stellen"
Locate 1 , 7 : Lcd Al_std ; ":" ; Al_min
Temp1 = Al_std 'geht auch nicht
Temp2 = Al_min ' nur Nullen drin
Loop

Return

End

')

Ich weiß nicht mehr weiter, mit meinem Anfängerwissen bin ich am Ende...

for_ro
03.11.2013, 11:52
Hallo Joachim,
dein Programm läuft jetzt nach dem Stellen wieder auf das Return. Das funktioniert so nicht.
Du könntest aus dem Goto Set_alarm mal ein Gosub Set_alarm machen.
Nach dem Ende des Stellens würde er aufgrund des Return zurück hinter den Gosub Befehl springen.
Habe ich mir jetzt aber noch nicht angesehen, was dann passieren würde.

Edit: Willst du eigentlich die Taste TA_mode drücken um in den Stellmodus zu gelangen und auch wieder, um den zu verlassen (If Ta_mode = 0 Then Exit Do)? Dann wäre die Taste immer noch gedrückt, wenn das Programm nach dem Return zum Befehl hinter Gosub ... zurückkehrt. Nach der Ausgabe aufs LCD springt er dann sofort wieder in die Set_alarm Routine. Dass müsstest du dir dann noch einmal ansehen.

HeXPloreR
03.11.2013, 12:03
Hallo,

ich meine das mit dem "return" was for_ro anspricht ist schon mal ein ganz heißer Tipp. Du solltest vielleicht Gosub's aus Deinen Einstellungsteilen machen und da muss dann mit einem "return" beendet werden - hier ist das "return" falsch, da vorher kein Aufruf erfolgt ist.

Das Goto Set_Alarm benötigst Du nicht wenn Du den Teil auf den sich das bezieht in den ersten mit integrierst. Weil Du gehst ja sowieso dahin, wenn Du drin bist.

Das "debouncing" ist nutzlos wenn nach dem zweiten testen nicht die Reaktion des Tasters ausgeführt wird.

joho63
03.11.2013, 13:04
@ for_ro:
Genau wie Du schreibst, ist es!
Nochmal kurz erklärt, was ich eigentlich will:
Der "Mode"-Taster soll zwischen "Anzeigen-und Alarm-Ein" Mode" und " Stellen" Mode" umschalten.
Es könnte auch so gehen, das ich gleichzeitig Mode- und Stelltaster zum stellen drücken muß.
Ich glaube, das geht wohl am Einfachsten.
Das Debouncing stammt noch vom alten Codeschnipsel, das wird nicht nötig sein.

- - - Aktualisiert - - -

Ich hab das jetzt mal so gemacht:


$regfile = "m8def.dat"
$crystal = 9830400
$hwstack = 40
$swstack = 16
$framesize = 32
$include "INIT_LCD20x4.bas" 'LCD an Port D
Pinb = &B11000000 'B 2,3,4 = Taster
Portb = &B00111100
Dim Al_min As Byte , Al_std As Byte
Ta_std Alias Pinb.2
Ta_min Alias Pinb.3
Ta_mode Alias Pinb.4
Locate 1 , 1 : Lcd "Alarm "
Do
If Ta_mode = 0 Then
Locate 1 , 13 : Lcd "Stellen"
Else
Locate 1 , 13 : Lcd "Ein "
End If
If Ta_min = 0 And Ta_mode = 0 Then
Al_min = Al_min + 1 'Minute stellen
If Al_min = 60 Then Al_min = 0
Waitms 200
End If
If Ta_std = 0 And Ta_mode = 0 Then
Al_std = Al_std + 1 'Stunde stellen
If Al_std = 24 Then Al_std = 0
Waitms 200
End If
'-------------- LCD rechtsbündige Anzeige ------------------------------------
Locate 1 , 7
If Al_Std < 10 Then Lcd " " ; Al_Std Else Lcd Al_Std ; " "
Locate 1 , 9
If Al_Min < 10 Then Lcd ":0" ; Al_Min Else Lcd ":" ; Al_Min
Waitms 50
Loop
End



und das geht so wie gewollt! Die zwei-Taster-Bedienung ist garnicht mal schlecht.

Jetzt brauch ich nur noch den Uhrzeit-Vergleich und die Ausgabe austüfteln.

Ich danke Euch für die hilfreichen Tipps, hab ich doch wieder was gelernt!

Joachim

joho63
13.11.2013, 15:37
Hallo!
Ich hänge mich jetzt mal an meinen Thread selbst dran, weil ich noch eine Frage zur selben Sache habe.

Erstmal kann ich mitteilen, daß dank eurer Hilfe die Uhr nun soweit läuft.

Zum Einstellen der Alarmzeit bin ich aber von den Tastern abgegangen, weil mid das doch zu aufwendig war, die werden
ja nur ein-zweimal im Jahr gebraucht.
Dazu kommt noch der Nachteil, das ich damit nur vorwärts stellen könnte.

Also hab ich mir bei ebay diese Drehencoder besorgt:

26726

Die gabs 15 Stück für 8 €, haben einen versenkbaren Drehknopf und einen Kontakt beim rausdrücken. Nach vielen Versuchen fand
ich auch einen Programmschnipsel, der für diese Type sehr gut läuft:


$regfile = "m8def.dat"
$crystal = 9830400
$hwstack = 40
$swstack = 16
$framesize = 32
$include "D:\PROG\INIT_LCD20x4.bas"
Deflcdchar 1 , 2 , 6 , 14 , 31 , 14 , 6 , 2 , 32 'Pfeil nach links
Portc = &B111 'Pullup
Enc_a Alias Pinc.1
Enc_b Alias Pinc.0
Ta_alarm Alias Pinc.2
Dim Al_min As Integer 'INT wegen Wert kleiner Null !
Dim Al_std As Integer
Dim Zw As Byte 'Zustandswechsel z.B.: &b0011_0010
Config Timer0 = Timer , Prescale = 1024 '= ~10MHz= ~0,1µs * 1024= 0,1ms
On Timer0 Enc_int 'Interrupt
Timer0 = 246 'Timerstartwert:10; ~0,1ms * 10= 1ms
Enable Timer0
Enable Interrupts
Locate 1 , 2 : Lcd "Encoder mit Timer0"
Locate 4 , 1 : Lcd "Alarmzeit: " '11 Zeichen
If Ta_alarm = 0 Then
Do
Locate 4 , 12
If Al_std < 10 Then Lcd " " ; Al_std Else Lcd Al_std ' ; " "
Locate 4 , 14
If Al_min < 10 Then Lcd ":0" ; Al_min Else Lcd ":" ; Al_min ; " "
Locate 4 , 17 : Lcd " " ; Chr(1) ; " "
If Al_min < 0 Then
Al_min = 59
Al_std = Al_std - 1
End If
If Al_std < 0 Then Al_std = 23
If Al_min > 59 Then
Al_min = 0
Al_std = Al_std + 1
End If
If Al_std > 23 Then Al_std = 0
'-------Ab hier die Alarmzeit mit GOSUB in RTC speichern !-------------
If Ta_alarm = 1 Then Exit Do
Loop
End If

Enc_int:
Timer0 = 246 'Timerstartwert
Zw.0 = Enc_a 'Zustandswechsel
Zw.1 = Enc_b
Select Case Zw
Case &H0_2 : Incr Al_min
Case &H0_1 : Decr Al_min
End Select
Zw.4 = Zw.0
Zw.5 = Zw.1
Return

Damit stelle ich die Minuten, die Stunden folgen selbst. Vorwärts und rückwärts. Das kenne ich so von meinem Herd, ist ganz praktisch.

Aber jetzt kommt die Frage!
Bei Übereinstimmung von Uhrzeit und Alarmzeit will ich ja ein Signal bekommen, und zwar in der Zeit einstellbar im Bereich eine
bis ca. 5 Sekunden. Das kann ich ja nicht in die Main-Schleife einbauen, weil die würde ja dann stehenbleiben!
Bei einer SUB passiert dasselbe, bis zum RETURN bleibt alles stehen.
Wie kann ich zu diesem "Unterprogramm" springen, ohne den normalen Ablauf zu unterbrechen?
Also zur gegebenen Zeit die 5 Sekunden nebenher ablaufen lassen, ohne weitere Aktionen.
Das kann ich nicht rausfinden, trotz intensiver Hilfesuche.
Vielleicht kann mir da jemand auf die Sprünge helfen! Ich hänge hier noch das ganze Uhrenprogramm dran, das könnte
Ja hilfreich sein:

$regfile = "m8def.dat"
$crystal = 9830400
$hwstack = 64
$swstack = 32
$framesize = 64
$include "D:\PROG\INIT_LCD20x4.bas" 'LCD an Port D
'------------------------------------------ 'für RTC ------------------------------------------------------------
$lib "I2C_TWI.lbx"
Config Sda = Portc.4
Config Scl = Portc.5
Config Twi = 100000
I2cinit
Dim Ds_weekday As Byte , Ds_day As Byte , Ds_month As Byte , Ds_year As Byte
Dim Ds_sec As Byte , Ds_min As Byte , Ds_hour As Byte
'-------------------------------------------------- 'für DCF ---------------------------------------------------
Config Dcf77 = Pinc.3 , Timer = 1
Config Date = Dmy , Separator = .
Config Pinb.3 = Output 'DCF - Status - LED
Config Pinb.4 = Output ' DCF - Blink - LED
'---------------------------------------für ENC ---------------------------------------------------------------
Portc = &B111 ' Pullups
Enc_a Alias Pinc.1
Enc_b Alias Pinc.0
Ta_alarm Alias Pinc.2 'Taste Alarmzeit stellen EIN - AUS
Deflcdchar 1 , 2 , 6 , 14 , 31 , 14 , 6 , 2 , 32 'Pfeil nach links
Dim Al_min As Integer , Al_std As Integer 'nötig wegen <Null !
Dim V_al_min As Byte , V_al_std As Byte 'für Anzeige
Dim Zw As Byte 'Zustandswechsel z.B.: &b0011_0010
Config Timer0 = Timer , Prescale = 1024 '= ~10MHz= ~0,1µs * 1024= 0,1ms
On Timer0 Enc_int 'Interrupt
Timer0 = 246 'Timerstartwert:10; ~0,1ms * 10= 1ms
Enable Timer0
Stop Timer0
Enable Interrupts
'---------------------------------------- für Ausgang --------------------------------------------------------
Config Pinb.0 = Input 'Schalter Alarm EIN - AUS
Portb.0 = 1 'Pullup
Config Pinb.1 = Output 'grüne LED für Ausgang EIN

'--------------------------MAIN - SCHLEIFE----------------------------------------------------------------
Do
If Pinc.3 = 1 Then Portb.4 = 1 Else Portb.4 = 0 ' LED für DCF-Impuls
If Dcf_status.7 = 1 Then Portb.3 = 1 Else Portb.3 = 0 'LED für DCF-Status
Gosub Show_clock ' Anzeige vom RCT - Speicher holen
Gosub Show_alarm ' Anzeige vom RCT - Speicher holen
If Ta_alarm = 0 Then Gosub Al_stellen
Loop
'------------------------------------------------------------------------------------------------------------------
Al_stellen:
Do
Start Timer0
Locate 3 , 12
If Al_std < 10 Then Lcd "0" ; Al_std Else Lcd Al_std ' ; " "
Locate 3 , 14
If Al_min < 10 Then Lcd ":0" ; Al_min Else Lcd ":" ; Al_min ; " "
Locate 3 , 17 : Lcd " " ; Chr(1) ; " "
If Al_min < 0 Then
Al_min = 59
Al_std = Al_std - 1
End If
If Al_std < 0 Then Al_std = 23
If Al_min > 59 Then
Al_min = 0
Al_std = Al_std + 1
End If
If Al_std > 23 Then Al_std = 0
Gosub Alarm_speichern
If Ta_alarm = 1 Then
Stop Timer0
Exit Do
End If
Loop
'----------TIMER 0 Interrupt für ENC -------------------------------------------------
Enc_int:
Timer0 = 246 'Timerstartwert für 1ms
Zw.0 = Enc_a 'Zustandswechsel
Zw.1 = Enc_b
Select Case Zw
Case &H0_2 : Incr Al_min 'bei jedem zweiten Tick
Case &H0_1 : Decr Al_min 'dito
End Select
Zw.4 = Zw.0
Zw.5 = Zw.1
Return

'----------RTC Uhrzeit und Datum auslesen und anzeigen----------------------
Show_clock:
I2cstart
I2cwbyte 208
I2cwbyte 0
I2cstop
I2cstart
I2cwbyte 209
I2crbyte Ds_sec , Ack
I2crbyte Ds_min , Ack
I2crbyte Ds_hour , Ack
I2crbyte Ds_weekday , Ack
I2crbyte Ds_day , Ack
I2crbyte Ds_month , Ack
I2crbyte Ds_year , Nack
I2cstop
Gosub Rtc_stellen
'------------------ANZEIGE-----------------------------------------------------
Locate 1 , 1 : Lcd "Uhrzeit: " ; Bcd(ds_hour) ; ":" ; Bcd(ds_min) ; ":" ; Bcd(ds_sec)
Locate 2 , 1 : Lcd "Datum: " ; Lookupstr(ds_weekday , Wochentag) ; ", "
Locate 2 , 12 : Lcd Bcd(ds_day) ; "." ; Bcd(ds_month) ; "." ; Bcd(ds_year)
'------------------ANZEIGE-----------------------------------------------------
Return
'-------------RTC Alarmzeit auslesen und anzeigen--------------------------
Show_alarm:
I2cstart
I2cwbyte 208
I2cwbyte 10
I2cstart
I2cwbyte 209
I2crbyte V_al_std , Ack
I2crbyte V_al_min , Nack
I2cstop
Locate 3 , 1 : Lcd "Alarmzeit: " ; Bcd(v_al_std) ; ":" ; Bcd(v_al_min) ; " "
If Pinb.0 = 0 Then
Locate 3 , 18 : Lcd "Aus"
Else
Locate 3 , 18 : Lcd "Ein"
End If
Return
'----------------- Uhrzeit in RTC speichern
Rtc_speichern:
Ds_sec = Makebcd(ds_sec)
Ds_min = Makebcd(ds_min)
Ds_hour = Makebcd(ds_hour)
Ds_day = Makebcd(ds_day)
Ds_month = Makebcd(ds_month)
Ds_year = Makebcd(ds_year)
Ds_weekday = Makebcd(ds_weekday)
I2cstart
I2cwbyte 208
I2cwbyte 0
I2cwbyte Ds_sec
I2cwbyte Ds_min
I2cwbyte Ds_hour
I2cwbyte Ds_weekday
I2cwbyte Ds_day
I2cwbyte Ds_month
I2cwbyte Ds_year
I2cstop
Return
'-------------------Alarmzeit in RTC speichern
Alarm_speichern:
V_al_min = Makebcd(al_min)
V_al_std = Makebcd(al_std)
I2cstart
I2cwbyte 208
I2cwbyte 10
I2cwbyte V_al_std
I2cwbyte V_al_min
I2cstop
Return
'----------------- RTC nach DCF77 stellen
Rtc_stellen:
If Dcf_status.7 = 1 And Ds_sec <> _sec Then
Ds_hour = _hour
Ds_min = _min
Ds_sec = _sec
Ds_weekday = _weekday
Ds_day = _day
Ds_month = _month
Ds_year = _year
Gosub Rtc_speichern
End If
Return
'-----------------AUSGANG - BAUSTELLE ! ---------------------------------------------

If Ta_alarm = 1 And Pinb.0 = 1 Then Gosub Ausgang

Ausgang:

If V_al_std = Ds_hour And V_al_min = Ds_min Then Portb.1 = 1
Wait 5
Portb = 1


Wochentag:
Data " " , "Mo" , "Di" , "Mi" , "Do" , "Fr" , "Sa" , "So"
Joachim

peterfido
13.11.2013, 16:11
Meine Methode ist da wesentlich anders. Ich stelle die RTC, wenn die Zeit per NTP gestellt wurde (In Deinem Fall per DCF-Signal empfangen und für gültig erklärt wurde) und laufe die restliche Zeit mit der internen Uhr von Bascom. Einmal die Stunde wird wieder aktualisiert. Für DCF: Wenn tagsüber schlechter Empfang herrscht (z.B. Schaltnetzteil in der Nähe), dann die RTC nachts nachstellen. Ist der DCF-Empfang tagsüber schlecht, dann die Zeit mal von der RTC holen.

Die RTC würde ich nie kontinuierlich auslesen. Die DS1307 würde dann sogar evtl. arg nachgehen. Zeitfunktionen kommen bei mir ins Sectic. Werden also maximal einmal pro Sekunde ausgeführt. In dem Sub Sectic werden auch Timer abgearbeitet.
Vereinfachtes Prinzip mal eben schnell aus dem Kopf:


Wenn Minutealt <> Minute dann ' So umgeht man, dass man die Sekunde 0 wegen evtl. Zeiteinstellung verpasst...
Minutealt=Minute
Wenn Weckeraktiv>0 dann Weckeraktiv = Weckeraktiv -1
Wenn Weckzeit=Uhrzeit, dann Variable für Weckeraktiv auf 60 Minuten stellen (habe ein Internetradio gebaut).
Ist Weckeraktiv>0 dann Radio einschalten, wenn es noch nicht an ist.
Ist Weckeraktiv=0 dann Radio ausschalten
Das geht auch mit Sekunden.

joho63
23.11.2013, 10:45
Bezüglich der Nachstellung hab ich eigentlich keine Probleme. Du hast ja NTP, da ist das etwas einfacher, aber es geht auch so.
Die RTC wird ja maximal jede Minute aktualisiert (wenn das DCF77-Statusbit einen kompletten Satz Daten empfangen hat).
Und da der Speicher der RTC SRAM ist, gibt es eigentlich kein Problem.

Aber das Problem des Alarmausgangs habe ich inzwischen gelöst. Mit Deiner Methode kam ich nicht zurecht, es wollte nicht laufen.
Nun ist der Ausgang in einer Sub, die von der Hauptschleife abgerufen wird:


Do ' MAIN - SCHLEIFE
Locate 1 , 16
If Dcf_pin = 1 Then Lcd "+" Else Lcd " " 'DCF-Impuls
Locate 1 , 6
If Dcf_status.7 = 1 Then Lcd "*" Else Lcd " " 'DCF-Status
Gosub Show_clock ' Anzeige vom RCT - Speicher holen
Gosub Show_alarm ' Anzeige vom RCT - Speicher holen
Gosub Hbl_pwm 'Hintergrundbeleuchtung
If Ta_alarm = 1 And Sw_alarm = 1 Then A = 1 Else A = 0
If V_al_std = Ds_hour And V_al_min = Ds_min Then B = 1 Else B = 0
If A = 1 And B = 1 Then C = 1 Else C = 0
If C = 1 And Ds_sec = 5 Then Gosub Ausgang
If Ta_alarm = 0 Then Gosub Al_stellen
Loop
Ausgang: ' AUSGANG
Al_out = 1
Sound Buzz , 200 , 150 'Buzzer Länge, Tonhöhe
Wait 1 'Alarm - EIN - Zeit
Al_out = 0
Return
Die Anzeige bleibt zwar für die Alarmzeit (1 bis 2 Sekunden) stehen, aber die Uhr läuft weiter.

Da nun alles zur Zufriedenheit lief und der Mega8 nichtmal halb ausgelastet ist, habe ich als kleines Schmankerl noch eine
Steuerung der Hintergrundbeleuchtung über das Umgebungslicht draufgesetzt.
Zunächst wollte ich das kontinuierlich regeln, habe aber davon Abstand genommen, weil ich ein flackern der Helligkeit einfach nicht
wegbekam. So habe ich den ADC- Ausgang auf 5 Bit runtergesetzt und schalte damit nur noch Hell oder Dunkel. Das reicht mir völlig
aus. Die Steuerung macht der Timer 2 mit PWM, dahinter ist noch ein Transistor, weil die HBL ca. 40mA braucht. Der Code dafür ist
recht einfach:


Hbl_pwm: 'Hintergrundbeleuchtung
Ldr = Getadc(2)
Shift Ldr , Right , 5 'Auflösung 5 Bit
If Ldr < 8 Then Pwm_ = 50
If Ldr > 15 Then Pwm_ = 255
Compare2 = Pwm_
Return
und ist auch als Sub in der Hauptschleife. Das ganze sieht nun so aus:


'Ausgänge = B.1 = Alarm , B.2 = Buzzer , B.3 = HBL(PWM)
'C.4 und C.5 = TWI/I2C , Port D 2 bis D.7 für LCD
'Eingänge = , D.0 = Schalter Alarm Ein , D.1 = Taster Alarm stellen
'C.0 und C1 = ENC , C.2 = LDR für HBL , B.0 = DCF77
$regfile = "m8def.dat"
$crystal = 2000000
$hwstack = 64
$swstack = 32
$framesize = 64
$include "D:\PROG\INIT_LCD16x2.bas" 'LCD an Port D
$lib "I2C_TWI.lbx"
Config Sda = Portc.4
Config Scl = Portc.5
Config Twi = 100000
Ddrb = &B11001110
Portc = &B11 ' Pullups
Portd = &B11 'Pullups
Enc_a Alias Pinc.1
Enc_b Alias Pinc.0
Dcf_pin Alias Pinb.0
Al_out Alias Portb.1
Buzz Alias Portb.2
Hbl_out Alias Portb.3 'Beleuchtung Ausgang (PWM)
Sw_alarm Alias Pind.0 'Schalter Alarm EIN - AUS
Ta_alarm Alias Pind.1 'Taste Alarmzeit stellen EIN - AUS
Dim Al_zeit As Byte , A As Byte , B As Byte , C As Byte , Zw As Byte
Dim Ds_day As Byte , Ds_month As Byte , Ds_year As Byte
Dim Ds_sec As Byte , Ds_min As Byte , Ds_hour As Byte
Dim Al_min As Integer , Al_std As Integer , V_al_min As Byte , V_al_std As Byte
Dim Ldr As Word , Pwm_ As Byte 'LDR = Fotowiderstand
Al_min = 30 : Al_std = 20 'Startwert
Deflcdchar 1 , 2 , 6 , 14 , 31 , 14 , 6 , 2 , 32 'Pfeil nach links
'*********** TIMER 1 für DCF77****************
Config Dcf77 = Pinb.0 , Timer = 1
Config Date = Dmy , Separator = .
'*********** TIMER 0 für Drehencoder*********
Config Timer0 = Timer , Prescale = 64 '= ~2Mhz = ~0,5µs * 64 = ~ 32µs
Timer0 = 225 'Startwert = 31 x ~ 32µs = ~1ms
On Timer0 Enc_int
Enable Timer0
'*********** TIMER 2 und ADC 2 für Hintergrundbeleuchtung*********
Config Timer2 = Pwm , Prescale = 64 , Compare Pwm = Clear Up
Enable Timer2
Config Adc = Single , Prescaler = Auto , Reference = Avcc

Enable Interrupts
Do ' MAIN - SCHLEIFE
Locate 1 , 16
If Dcf_pin = 1 Then Lcd "+" Else Lcd " " 'DCF-Impuls
Locate 1 , 6
If Dcf_status.7 = 1 Then Lcd "*" Else Lcd " " 'DCF-Status
Gosub Show_clock ' Anzeige vom RCT - Speicher holen
Gosub Show_alarm ' Anzeige vom RCT - Speicher holen
Gosub Hbl_pwm 'Hintergrundbeleuchtung
If Ta_alarm = 1 And Sw_alarm = 1 Then A = 1 Else A = 0
If V_al_std = Ds_hour And V_al_min = Ds_min Then B = 1 Else B = 0
If A = 1 And B = 1 Then C = 1 Else C = 0
If C = 1 And Ds_sec = 5 Then Gosub Ausgang
If Ta_alarm = 0 Then Gosub Al_stellen
Loop
Ausgang: ' AUSGANG
Al_out = 1
Sound Buzz , 200 , 150 'Buzzer Länge, Tonhöhe
Wait 1 'Alarm - EIN - Zeit
Al_out = 0
Return
Al_stellen: ' STELLEN
Do
Locate 2 , 8
If Al_std < 10 Then Lcd "0" ; Al_std Else Lcd Al_std
Locate 2 , 10
If Al_min < 10 Then Lcd ":0" ; Al_min Else Lcd ":" ; Al_min ; " "
Locate 2 , 14 : Lcd " " ; Chr(1) ; " "
If Al_min < 0 Then
Al_min = 59
Al_std = Al_std - 1
End If
If Al_std < 0 Then Al_std = 23
If Al_min > 59 Then
Al_min = 0
Al_std = Al_std + 1
End If
If Al_std > 23 Then Al_std = 0
Gosub Alarm_speichern
If Ta_alarm = 1 Then Exit Do
Loop
Enc_int: 'TIMER 0 Interrupt für Drehencoder
Timer0 = 225 'Timer Startwert
Zw.0 = Enc_a 'Zustandswechsel
Zw.1 = Enc_b
Select Case Zw
Case &H0_2 : Incr Al_min 'bei jedem zweiten Tick
Case &H0_1 : Decr Al_min
End Select
Zw.4 = Zw.0
Zw.5 = Zw.1
Return
Show_clock: ' RTC Uhrzeit und Datum auslesen und anzeigen
I2cstart
I2cwbyte 208
I2cwbyte 0
I2cstart
I2cwbyte 209
I2crbyte Ds_sec , Ack
I2crbyte Ds_min , Ack
I2crbyte Ds_hour , Ack
I2crbyte Ds_day , Ack
I2crbyte Ds_month , Ack
I2crbyte Ds_year , Nack
I2cstop
Gosub Rtc_stellen
Locate 1 , 1 : Lcd "Zeit:"
Locate 1 , 8 : Lcd Bcd(ds_hour) ; ":" ; Bcd(ds_min) ; ":" ; Bcd(ds_sec)
Return
Show_alarm: ' RTC Alarmzeit auslesen und anzeigen
I2cstart
I2cwbyte 208
I2cwbyte 10
I2cstart
I2cwbyte 209
I2crbyte V_al_std , Ack
I2crbyte V_al_min , Nack
I2cstop
Locate 2 , 1 : Lcd "Alarm: " ; Bcd(v_al_std) ; ":" ; Bcd(v_al_min)
Locate 2 , 14
If Sw_alarm = 0 Then Lcd "Aus" Else Lcd "Ein"
Return
Rtc_speichern: ' Uhrzeit in RTC speichern
Ds_sec = Makebcd(ds_sec)
Ds_min = Makebcd(ds_min)
Ds_hour = Makebcd(ds_hour)
Ds_day = Makebcd(ds_day)
Ds_month = Makebcd(ds_month)
Ds_year = Makebcd(ds_year)
I2cstart
I2cwbyte 208
I2cwbyte 0
I2cwbyte Ds_sec
I2cwbyte Ds_min
I2cwbyte Ds_hour
I2cwbyte Ds_day
I2cwbyte Ds_month
I2cwbyte Ds_year
I2cstop
Return
Alarm_speichern: ' Alarmzeit in RTC speichern
V_al_min = Makebcd(al_min)
V_al_std = Makebcd(al_std)
I2cstart
I2cwbyte 208
I2cwbyte 10
I2cwbyte V_al_std
I2cwbyte V_al_min
I2cstop
Return
Rtc_stellen: ' RTC nach DCF77 stellen
If Dcf_status.7 = 1 And Ds_sec <> _sec Then
Ds_sec = _sec
Ds_hour = _hour
Ds_min = _min
Ds_day = _day
Ds_month = _month
Ds_year = _year
Gosub Rtc_speichern
End If
Reset Dcf_status.7
Return
Hbl_pwm: 'Hintergrundbeleuchtung
Ldr = Getadc(2)
Shift Ldr , Right , 5 'Auflösung 5 Bit
If Ldr < 8 Then Pwm_ = 50
If Ldr > 15 Then Pwm_ = 255
Compare2 = Pwm_
Return
Hier noch ein Foto der gesamten Technik, aufgebaut mit myAVR-Light Board:

26782

Links der Fotowiderstand mit Vorwiderständen, unten die ELV-DCF77 und rechts der Drehencoder und der Schalter für die
Alarmzeiteinstellung.
Einen Schaltplan hab ich auch gemacht, er zeigt mir gleich die mechanische Anordnung der Teile. Das LCD kommt über die Platine
zu liegen:

26783

Dieses ganze Projekt hätte ich mir sparen können, wenn es eine einfachere Lösung für das eigentliche Problem gäbe, nämlich:

Eine Möglichkeit festzustellen, ob am PC ein USB- Gerät aktiv ist oder nicht. Denn meine Robot- Kamera schaltet ja bei Dunkelheit
aus bzw. um auf USB- Ausgang. Ich habe aber nicht herausgefunden, wie man das am PC auswerten könnte.
Der bräuchte dann nur noch die gemachten Fotos runterzuladen, mit Datum zu versehen, abzuspeichern und die SD-Karte
der Kamera zu löschen. Das macht er jetzt auch schon, seit über sechs Jahren, es fehlen nur drei Tage wegen Stromausfall
bzw. Computerversagen. Nur die Schaltuhr mußte ich ständig beobachten.
Aber das hat jetzt ein Ende.

Allerdings war die Entwicklung auch sehr interessant und lehrreich, das ist für die nächsten Projekte bestimmt von Nutzen!
Eine Uhr kann man immer gebrauchen ;)
Joachim

peterfido
23.11.2013, 12:14
Mit PWM und zugleich DCF-Modul (von Pollin) an einer Spannungsquelle hatte ich schon schlechte Erfahrung gesammelt. Die Lösung war dann vor dem DCF-Modul noch eine Diode und hinter der Diode die Spannung stabilisieren.

USB Geräte auflisten:

Windows:
USBDeview

Linux:
lsusb

joho63
23.11.2013, 13:15
Das Pollin-Modul hab ich schon modifiziert - eine Schaltstufe am Ausgang, eine Zenerdiode 2,7V mit Vorwiderstand, Ausgang invertiert ( da ist
eine Lötbrücke auf der Platine!), den Elko gegen keramischen Kondensator getauscht. Das war die Version für 3V, es gibt bei Pollin auch welche für
5V. Zur Zeit benutze ich aber das ELV-Modul, ebenfall mit extra Schaltstufe wegen dem beim Original invertierten Ausgang.

USBDeView kenne ich, hilft aber nicht, weil, es müßte ja ständig aktualisiert werden, um einen neuen Teilnehmer zu sehen.

Windows erzeugt ja bei jedem Neuzugang ein Signal. Wenn man das abgreifen könnte und damit das Ladeprogramm starten würde...
Allerdings müßte ja dann der PC ständig laufen - keine Option!
Da fällt mir was ein - könnte das nicht ein µC übernehmen?
Z.B. der Mega 8 hat doch TX/RX-Eingänge. Und ich hab einen USB-UART-Adapter (von myAVR). Ob der µC das pollen kann? Und dann
den Pc starten? Fragen über Fragen....muß ich mal was austesten!

Achim