'*******************************************************************************
'* Wörteruhr (c)2012 by DiLi-Soft - www.liebl-net.de/hard/qlock2/qlock2.php    *
'*                                                                             *
'* Nachempfunden der "QLOCKTWO" von Biegert: www.qlocktwo.com                  *
'*                                                                             *
'* Dieses Programm ist urheberrechtlich geschützt und darf ausschließlich      *
'* für nichtkommerzielle Zwecke verwendet werden.                              *
'*                                                                             *
'* Gewerbliche Nutzung ist ausgeschlossen!!                                    *
'*                                                                             *
'* Weitergabe des Quelltextes ist erwünscht - aber nur komplett, unverändert   *
'* und mit diesen Copyright-Anmerkungen !!!                                    *
'*                                                                             *
'* V1.1 vom 31.8.2012, erstellt unter BASCOM-AVR 2.0.7.5 (www.mcselec.com)     *
'*                                                                             *
'*******************************************************************************


'$prog &HFF , &HFF , &HD1 , &H00                   'ext. Quarz, JTAG off
                                                  'Lock- u. Fusebits
$regfile = "m16def.dat"
$crystal = 4194304                                'Quarz/65536 = 64 !

$hwstack = 80                                     'viel zu viel ...
$swstack = 80                                     'aber ist ja Platz genug
$framesize = 80

Config Dcf77 = Pind., Timer = 1 , Check = 2 , Update = 0 , Gosub = Sectic
                               'ständige Synchronisation + voller Validitätstest

'(
$baud = 9600                                      'UART-Baudrate: 9600 Baud
On Urxc Onrxd                                     'Interrupt-Routine setzen
Enable Urxc                                       'Nur für Tests !
')

Dim Index As Byte , Zeile As Byte , Zeilen_hell As Byte , Spalte As Byte
Dim 8bits As Byte , 3bits As Byte
Dim Ontime As Word , Offtime As Word

Dim Std_canvas As Byte , Std_incr As Byte
Dim Min_canvas As Byte , Old_min As Byte

Dim Sek_no_sync As Byte , Min_no_sync As Byte , Std_no_sync As Byte

Dim Ldr_val As Byte


Dim Canvas(22) As Byte                            '"Bildschirm-Speicher"
                                                  '11 Ausgabe-Zeilen *2 (wg. 11 Bit)


Dim Min_pat(66) As Byte                           '11 Pattern-Def. für Minuten *2*3
                                   '[(Minuten\5 -1) *6 +1]...[(Minuten\5 -1) *6 +6]


'Bit-Belegung der Pattern-Definitionen der Minuten:
'| xxxxxxxx | xxxzyyyy |
'     [n]       [n+1]
'x: Spalte-LEDs (je 1 Bit)
'y: in Zeile (4 Bit)
'z: Std.-Inkrement

Min_pat(01) = &B00000001                          '"FÜNF NACH"
Min_pat(02) = &B11100001
Min_pat(03) = &B00000001
Min_pat(04) = &B11100100
Min_pat(05) = 0
Min_pat(06) = 0

Min_pat(07) = &B11110000                          '"ZEHN NACH"
Min_pat(08) = &B00000010
Min_pat(09) = &B00000001
Min_pat(10) = &B11100100
Min_pat(11) = 0
Min_pat(12) = 0

Min_pat(13) = &B00001111                          '"VIERTEL NACH"
Min_pat(14) = &B11100011
Min_pat(15) = &B00000001
Min_pat(16) = &B11100100
Min_pat(17) = 0
Min_pat(18) = 0

'(
Min_pat(13) = &B00001111                          '"VIERTEL"
Min_pat(14) = &B11110011
Min_pat(15) = 0
Min_pat(16) = 0
Min_pat(17) = 0
Min_pat(18) = 0
')

Min_pat(19) = &B00001111                          '"ZWANZIG NACH"
Min_pat(20) = &B11100010
Min_pat(21) = &B00000001
Min_pat(22) = &B11100100
Min_pat(23) = 0
Min_pat(24) = 0

Min_pat(25) = &B00000001                          '"FÜNF VOR HALB"
Min_pat(26) = &B11110001
Min_pat(27) = &B11100000
Min_pat(28) = &B00010100
Min_pat(29) = &B11110000
Min_pat(30) = &B00010101

Min_pat(31) = &B11110000                          '"HALB"
Min_pat(32) = &B00010101
Min_pat(33) = 0
Min_pat(34) = 0
Min_pat(35) = 0
Min_pat(36) = 0

Min_pat(37) = &B00000001                          '"FÜNF NACH HALB"
Min_pat(38) = &B11110001
Min_pat(39) = &B00000001
Min_pat(40) = &B11110100
Min_pat(41) = &B11110000
Min_pat(42) = &B00010101

Min_pat(43) = &B00001111                          '"ZWANZIG VOR"
Min_pat(44) = &B11110010
Min_pat(45) = &B11100000
Min_pat(46) = &B00010100
Min_pat(47) = 0
Min_pat(48) = 0

Min_pat(49) = &B00001111                          '"VIERTEL VOR"
Min_pat(50) = &B11110011
Min_pat(51) = &B11100000
Min_pat(52) = &B00010100
Min_pat(53) = 0
Min_pat(54) = 0

'(
Min_pat(49) = &B11111111                          '"DREIVIERTEL"
Min_pat(50) = &B11110011
Min_pat(51) = 0
Min_pat(52) = 0
Min_pat(53) = 0
Min_pat(54) = 0
')

Min_pat(55) = &B11110000                          '"ZEHN VOR"
Min_pat(56) = &B00010010
Min_pat(57) = &B11100000
Min_pat(58) = &B00010100
Min_pat(59) = 0
Min_pat(60) = 0

Min_pat(61) = &B00000001                          '"FÜNF VOR"
Min_pat(62) = &B11110001
Min_pat(63) = &B11100000
Min_pat(64) = &B00010100
Min_pat(65) = 0
Min_pat(66) = 0



Dim Std_pat(26) As Byte                           '13 Pattern-Def. für Stunden *2
                                 'bei Stunden >1: [Stunden*2 +1] u. [Stunden*2 +2]

'Bit-Belegung der Pattern-Definitionen der Stunden:
'| xxxxxxxx | xxx-yyyy |
'     [n]       [n+1]
'x: Spalte-LEDs (je 1 Bit)
'y: in Zeile (4 Bit)
'-: unbenutzt

Std_pat(1) = &B11100000                           '"EIN"
Std_pat(2) = &B00000110

Std_pat(3) = &B11110000                           '"EINS"
Std_pat(4) = &B00000110

Std_pat(5) = &B00000001                           '"ZWEI"
Std_pat(6) = &B11100110

Std_pat(7) = &B11110000                           '"DREI"
Std_pat(8) = &B00000111

Std_pat(09) = &B00000001                          '"VIER"
Std_pat(10) = &B11100111

Std_pat(11) = &B00000001                          '"FÜNF"
Std_pat(12) = &B11100101

Std_pat(13) = &B11111000                          '"SECHS"
Std_pat(14) = &B00001000

Std_pat(15) = &B11111100                          '"SIEBEN"
Std_pat(16) = &B00001001

Std_pat(17) = &B00000001                          '"ACHT"
Std_pat(18) = &B11101000

Std_pat(19) = &B00011110                          '"NEUN"
Std_pat(20) = &B00001010

Std_pat(21) = &B11110000                          '"ZEHN"
Std_pat(22) = &B00001010

Std_pat(23) = &B00000111                          '"ELF"
Std_pat(24) = &B00000101

Std_pat(25) = &B00000011                          '"ZWÖLF"
Std_pat(26) = &B11101001



'Zuordnung der Zeilen und Spalten:
Zeile1 Alias Portb.7
Zeile2 Alias Portb.6
Zeile3 Alias Portb.5
Zeile4 Alias Portb.4
Zeile5 Alias Portb.3
Zeile6 Alias Portb.2
Zeile7 Alias Portb.1
Zeile8 Alias Portb.0
Zeile9 Alias Porta.7
Zeile10 Alias Porta.6
Zeile11 Alias Porta.5

Spalte1 Alias Portc.7
Spalte2 Alias Portc.6
Spalte3 Alias Portc.5
Spalte4 Alias Portc.4
Spalte5 Alias Portc.3
Spalte6 Alias Portc.2
Spalte7 Alias Portc.1
Spalte8 Alias Portc.0
Spalte9 Alias Portd.7
Spalte10 Alias Portd.6
Spalte11 Alias Portd.5



'-----------------  Ausgangs- / Eingangs-Pins konfig.:  ------------------------

'Zeilen:
Ddrb = &B11111111                                 'Zeile1 - Zeile8
Ddra = &B11100000                                 'Zeile9 - Zeile11; ADC0 als Input

'Spalten:
Ddrc = &B11111111                                 'Spalte1 - Spalte8
Ddrd = &B11100000                                 'Spalte9 - Spalte11
                                                  'PD2: Input (DCF-Empfänger)
                                                  'PD3: Input für Teststellung

Portb = &B11111111                                'alle Zeilen AUS (low-aktiv!)
Porta = &B11100000

Portc = 0                                         'alle Spalten AUS (high-aktiv):
Portd = 0


'----------------------------  ADC konfig.:  -----------------------------------

Admux.refs0 = 0
Admux.refs1 = 0                                   'Ref ist der AREF-Pin

Admux.adlar = 1                                   '8-bit-Mode reicht

Admux.mux0 = 0
Admux.mux1 = 0
Admux.mux2 = 0
Admux.mux3 = 0
Admux.mux4 = 0                                    'ADC0 ist Eingang

Adcsra.adps0 = 1
Adcsra.adps1 = 1
Adcsra.adps2 = 1                                  'Prescaler=128

Sfior.= 0
Sfior.= 0
Sfior.= 0                                       'ADTS2:0 : free running mode

Adcsra.= 1                                      'Auto Trigger
Adcsra.aden = 1                                   'ADC einschalten
Adcsra.adsc = 1                                   'und Conversion starten



'---------------------------------  main()  ------------------------------------

'----------------------------  LED Testroutine:  -------------------------------

If Pind.= 0 Then                                'Teststellung: PIN 17 auf
   Ontime = &HFFFF                                'Masse: Blinkmuster ausgeben
   Do
      'alle Spalten EIN:
      Portc = &B11111111
      Portd = &B11100000

      'alle Zeilen AUS:
      Portb = &B11111111
      Porta = &B11100000

      For Zeile = 1 To 11                         'alle Zeilen nacheinander EIN
         Select Case Zeile
            Case 01 : Zeile11 = 1
                      Zeile1 = 0
            Case 02 : Zeile1 = 1
                      Zeile2 = 0
            Case 03 : Zeile2 = 1
                      Zeile3 = 0
            Case 04 : Zeile3 = 1
                      Zeile4 = 0
            Case 05 : Zeile4 = 1
                      Zeile5 = 0
            Case 06 : Zeile5 = 1
                      Zeile6 = 0
            Case 07 : Zeile6 = 1
                      Zeile7 = 0
            Case 08 : Zeile7 = 1
                      Zeile8 = 0
            Case 09 : Zeile8 = 1
                      Zeile9 = 0
            Case 10 : Zeile9 = 1
                      Zeile10 = 0
            Case 11 : Zeile10 = 1
                      Zeile11 = 0
         End Select
         Waitus Ontime
      Next

      'alle Spalten AUS:
      Portc = 0
      Portd = 0

      'alle Zeilen EIN:
      Portb = 0
      Porta = 0

      For Spalte = 1 To 11                        'alle Spalten nacheinander EIN
         Select Case Spalte
            Case 01 : Spalte11 = 0
                      Spalte1 = 1
            Case 02 : Spalte1 = 0
                      Spalte2 = 1
            Case 03 : Spalte2 = 0
                      Spalte3 = 1
            Case 04 : Spalte3 = 0
                      Spalte4 = 1
            Case 05 : Spalte4 = 0
                      Spalte5 = 1
            Case 06 : Spalte5 = 0
                      Spalte6 = 1
            Case 07 : Spalte6 = 0
                      Spalte7 = 1
            Case 08 : Spalte7 = 0
                      Spalte8 = 1
            Case 09 : Spalte8 = 0
                      Spalte9 = 1
            Case 10 : Spalte9 = 0
                      Spalte10 = 1
            Case 11 : Spalte10 = 0
                      Spalte11 = 1
         End Select
         Waitus Ontime
      Next
   Loop                                           'endlos weiter
End If                                            'Testmodus

'------------------------------  Stellphase:  ----------------------------------

Sreg.= 1                                        'Interrupts erst hier freigeben !!

Portc = &B00011110                                'Spalten 4-7 EIN
Portd = 0
Porta = &B11100000                                'unbeteiligte Zeilen aus
Do
   If Pind.= 1 Then                             'DCF-Signal: "FUNK" blinkt
      Portb = &B11101111                          'Zeile 4 EIN
   Else
      Portb = &B11111111                          'Zeile 4 AUS
   End If
'die 4 blinkenden LEDs müssen über einen 100 Ohm-Vorwiderstand betrieben werden !!!
Loop Until _day > 0                               'Uhr ist gestellt

'------------------------------  Hauptschleife:  -------------------------------

Old_min = 99
Ontime = 999 : Offtime = 1                        '1 ms per Zeile (max. 5 Zeilen)

Do
   Min_canvas = _min
   If Min_canvas <> Old_min Then                  'wenn neue Minute
      Old_min = Min_canvas                        'also: nur 1x pro Minute!

      '----------  Zwangs-Synchronisation nach 1 Tag:  ----------
      _day = 0                                    'siehe "sectic"-Interrupt !!
      If Std_no_sync > 18 Then                    'lange nicht synchronisiert
         If _hour = 3 Then                        'und es ist nachts 3 Uhr
            If Ldr_val < 20 Then                  'und es ist ziemlich dunkel
               Portb = &B11111111                 'alle Zeilen AUS
               Porta = &B11100000

               Old_min = _min
               _min = 0
               Do                                 'LEDs (und damit ggf.
                  nop                             'Funk-Störungen) abschalten
               Loop Until _day > 0 Or _min > 14   'und max. 15 Min. auf Synchro warten

               Std_no_sync = 0                    'neuer Zählbeginn, so oder so
               Min_no_sync = 0
               Sek_no_sync = 0

               If _day = 0 Then                   'Synchr. nicht erfolgreich:
                  _min = Old_min + 15             'Uhr "restaurieren"
               End If

            End If
         End If
      End If

      '----------  ggf. Stunden-Korrektur:  ----------
      Std_canvas = _hour
      If Std_canvas > 12 Then
         Std_canvas = Std_canvas - 12
      End If
      If Std_canvas = 0 Then Std_canvas = 12

      '----------  Canvas (Wiederholungs-Speicher) füllen:  ----------
      Canvas(1) = &B11011100                      '"ES IST" ausgeben
      For Index = 2 To 22
          Canvas(index) = 0                       'Rest leeren
      Next

      '----------  Canvas mit Minuten füllen:  ----------
      If Min_canvas > 4 Then
         Index = Min_canvas \ 5
         Decr Index
         Index = Index * 6                        'index=(((minuten div 5)-1)*6)+1
         Incr Index                               '5...9 ->1, 10...14 -> 7, ...

         8bits = Min_pat(index)                   'erstes Wort
         Incr Index
         3bits = Min_pat(index)
         Zeile = 3bits And &B00001111
         Shift Zeile , Left                       'index = Zeile *2 -1
         Decr Zeile

         Canvas(zeile) = Canvas(zeile) Or 8bits
         Incr Zeile                               'index = Zeile *2
         Canvas(zeile) = Canvas(zeile) Or 3bits

         Std_incr = 3bits And &B00010000
         If Std_incr > 0 Then
            Incr Std_canvas                       'wg. Stunden-Inkrement
            If Std_canvas > 12 Then
               Std_canvas = Std_canvas - 12
            End If
         End If                                   'z.B. Halb "4" bei 3:30

         Incr Index                               'evtl. 2. Wort
         8bits = Min_pat(index)
         Incr Index
         3bits = Min_pat(index)
         Zeile = 3bits And &B00001111
         If Zeile > 0 Then
            Shift Zeile , Left
            Decr Zeile

            Canvas(zeile) = Canvas(zeile) Or 8bits
            Incr Zeile
            Canvas(zeile) = Canvas(zeile) Or 3bits
         End If

         Incr Index                               'evtl. 3. Wort
         8bits = Min_pat(index)
         Incr Index
         3bits = Min_pat(index)

         Zeile = 3bits And &B00001111
         If Zeile > 0 Then
            Shift Zeile , Left
            Decr Zeile

            Canvas(zeile) = Canvas(zeile) Or 8bits
            Incr Zeile
            Canvas(zeile) = Canvas(zeile) Or 3bits
         End If

      Else                                        'nur bei Minuten <5
         Canvas(20) = Canvas(20) Or &B11100000    '"UHR" anzeigen
      End If                                      'Minuten > 4

      8bits = Min_canvas Mod 5                    'Einzel-Minuten ausgeben
      Select Case 8bits                           'über die 4 Eck-LEDs
         Case 0 : 8bits = 0
         Case 1 : 8bits = &B00001000
         Case 2 : 8bits = &B00001100
         Case 3 : 8bits = &B00001110
         Case 4 : 8bits = &B00001111
      End Select
      Canvas(21) = Canvas(21) Or 8bits

     '----------  Canvas mit Stunden füllen:  ----------
      If Std_canvas < 2 Then                      '"EIN" bzw. "EINS"
         If Min_canvas < 5 Then
            Index = 1                             '"EIN"    ...   UHR
         Else
            Index = 3                             '"EINS"
         End If
      Else                                        '"ZWEI" ... "ZWÖLF"
         Index = Std_canvas
         Shift Index , Left
         Incr Index                               'index = Std *2 +1
      End If                                      'Std <2

      8bits = Std_pat(index)                      'Stunden-Muster holen
      Incr Index
      3bits = Std_pat(index)

      Zeile = 3bits And &B00001111
      Shift Zeile , Left                          'index = Zeile *2 -1
      Decr Zeile
      Canvas(zeile) = Canvas(zeile) Or 8bits      'Stunden ausgeben
      Incr Zeile                                  'index = Zeile *2
      Canvas(zeile) = Canvas(zeile) Or 3bits
   End If                                         'Minuten <> old_min

   '----------  Canvas ausgeben:  ----------
   Zeilen_hell = 0
   For Zeile = 1 To 11                            'Zeilen multiplexen
      Index = Zeile                               'und "Bildschirm"-Speicher
      Shift Index , Left                          'ausgeben
      Decr Index                                  'index = zeile *2 -1
      8bits = Canvas(index)
      Incr Index                                  'index = zeile *2
      3bits = Canvas(index)
      3bits = 3bits And &B11100000                'Zeilen-Nr + Std_incr entfernen
'(
      If Ontime = 0 Then                          '0 = ganz aus
         Portb = &B11111111                       'alle Zeilen AUS
         Porta = &B11100000
         Goto Dunkel                              'kein Multiplexing !!
      End If                                      'Nur für Tests !
')
      If 8bits = 0 And 3bits = 0 Then Goto Dunkel 'wenn Zeile dunkel

      Select Case Zeile
         Case 01 : Portb = &B01111111             '1 Zeile ansteuern
         Case 02 : Portb = &B10111111             'low-aktiv!
         Case 03 : Portb = &B11011111
         Case 04 : Portb = &B11101111
         Case 05 : Portb = &B11110111
         Case 06 : Portb = &B11111011
         Case 07 : Portb = &B11111101
         Case 08 : Portb = &B11111110
         Case 09 : Porta = &B01100000
         Case 10 : Porta = &B10100000
         Case 11 : Porta = &B11000000
      End Select
      Portc = 8bits                               'danach Spalten ansteuern
      Portd = 3bits
      Incr Zeilen_hell                            'zählt angesteuerte Zeilen
      Waitus Ontime                               'Hellzeit

      Portb = &B11111111                          'danach alle Zeilen AUS
      Porta = &B11100000                          'low-aktiv!
      Portc = 0                                   'und alle Spalten aus
      Portd = 0                                   'high-aktiv
      Waitus Offtime                              'Dunkelzeit

      Dunkel:                                     'keine Zeit( = Helligkeit)
   Next                                           'verschwenden!!

   'sorgt für gleiche Helligkeit hei untersch. Anzahl von hellen Zeilen (2-5)
   For Zeile = Zeilen_hell To 4                   'z.B bei 10:00: nur 2 Zeilen
      Waitus Offtime                              'z.B bei 10:49: 5 Zeilen!!
      Waitus Ontime                               'Wartezeit
   Next
Loop                                              'Ende der Hauptschleife


'---------  Timer-Interrupt (jede Sekunde) für Helligkeitsregelung:  -----------

Dim Adc_ret As Integer

Sectic:
   If _hour = 0 And _min = 0 And _sec = 0 Then _day = 0       'wg. Tageswechsel !!!

   If _day > 0 Then                               'DCF-synchronisiert:
      Sek_no_sync = 0                             'no_sync-Zähler neu starten
      Min_no_sync = 0
      Std_no_sync = 0
   End If

   If _day = 0 Then                               '0: Semaphore für "nicht synchronisiert"
      Incr Sek_no_sync                            'wird von der DCF-Uhr überschrieben,
      If Sek_no_sync > 59 Then                    'wenn sie sich synchronisiert
         Sek_no_sync = 0
         Incr Min_no_sync
      End If
      If Min_no_sync > 59 Then
         Min_no_sync = 0
         Incr Std_no_sync                         'zählt die nicht synchr. Zeit
      End If
   End If

'   If Ontime = 0 Then Return                      'Nur für Tests !

   Ldr_val = Adch                                 'Helligkeitswert
   Adc_ret = 5 * Ldr_val
   Adc_ret = Adc_ret - 25                         'Ontime = (5 * Adc_ret) - 25
   If Adc_ret < 2 Then Adc_ret = 2                '= niedrigste Helligkeit
   If Adc_ret > 999 Then Adc_ret = 999            '= höchste Helligkeit
   Ontime = Adc_ret
   Offtime = 1000 - Ontime                        'Ontime + Offtime = 1ms

'  Print Adc_ret ; " : " ; Ontime                  'Nur für Tests

Return


'-------------------------  RS232-Empfangsroutine:  ----------------------------

'nur für Testzwecke !!!

'(
Dim Ser_buf As String * 5
Dim Udr_buf As Byte
Dim Dummy As String * 2

Onrxd:
   Udr_buf = Udr                                  'Byte aus der UART auslesen
   If Udr_buf <> 13 Then
      If Udr_buf <> 8 Then
         If Len(ser_buf) < 5 Then
            Ser_buf = Ser_buf + Chr(udr_buf)
            Udr = Udr_buf                         'Echo auf Konsole
         End If
      End If
   Else                                           'erst nach CR übergeben
      Udr = 13
      Udr = 10                                    'und CRLF zur Konsole

      If Charpos(ser_buf , ":") > 0 Then          ' "08:15" :  Uhrzeit setzen
         Dummy = Left(ser_buf , 2)
         _hour = Val(dummy)
         Dummy = Mid(ser_buf , 4 , 2)
         _min = Val(dummy)
      Else
         If Charpos(ser_buf , ".") > 0 Then       ' "12." : _day setzen
            _day = Val(ser_buf)                   ' "0." : nicht synchr. setzen
         Else
            If Ser_buf = "?" Then                 ' "?" : akt. Zeit abfragen
               Print _day ; "." ; _month ; "." ; _year
               Print _hour ; ":" ; _min ; ":" ; _sec
            Else
               If Ser_buf = "??" Then             ' "??" : no_sync-Zeit + Helligkeit
                  Print Std_no_sync ; ":" ; Min_no_sync ; ":" ; Sek_no_sync
                  Print Ldr_val
               Else
                  If Charpos(ser_buf , "_") > 0 Then ' "20_: Std_no_sync setzen
                     Std_no_sync = Val(ser_buf)
                  Else
                     Ontime = Val(ser_buf)        ' "250" : Helligkeit setzen
                     Offtime = 1000 - Ontime      ' "0" : Multiplexing aus
                  End If
               End If
            End If
         End If
      End If

      Ser_buf = ""
   End If
Return
')

End                                               'end program