$crystal = 8000000                                          'Interner RC Oszillator

Config Portc.3 = Output
Config Portb.0 = Output                                     'RTC -
Config Portb.3 = Output                                     'PWM Ausgang
Config Portd.0 = Input
Config Portd.1 = Input
Config Portd.2 = Input
Config Portd.3 = Input
Config Portd.7 = Output                                     'RTC +

Portb.0 = 0
Portd.0 = 1
Portd.1 = 1
Portd.2 = 1
Portd.3 = 1
Portd.7 = 1


'Flash ROM Enable
Flash_enable Alias Portc.3

Ds_power Alias Portd.7

Tccr2 = &B01101001                                          'Timer2 fast PWM

'I2C Kommunikation
Config Scl = Portd.5
Config Sda = Portd.6

'Variablen
Dim Frames As Integer
Dim Framecounter As Integer
Dim B As Byte
Dim R As Byte
Dim Dat As Integer

Dim Startadr As Dword
Dim Adr_low As Word At Startadr Overlay
Dim Adr_mid As Word At Startadr + 1 Overlay
Dim Adr_high As Word At Startadr + 2 Overlay
Dim Endadr As Dword

Dim M As Integer
Dim H As Integer
Dim S As Integer

Dim Mm As Integer
Dim Hh As Integer
Dim Ss As Integer

Dim Swait As Integer

Dim S_lookup As Integer

Const Ds3231w = &B11010000
Const Ds3231r = &B11010001

'SPI Kommunikation mit Flash ROM
Config Spi = Soft , Mode = 1 , Data_order = Msb , Din = Pinc.2 , Dout = Portc.4 , Ss = None , Clock = Portc.5

Flash_enable = 1

Dim Spi_data(4) As Byte

Spiinit

Waitms 100                                                  ' Verzögerung war bei mir nötig, damit das ROM initialisiert!

'Timer1 erzeugt den Takt, mit dem die Wiedergabe erfolgt (8 KHz).
Ocr1a = 1000
Config Timer1 = Timer , Prescale = 1 , Clear Timer = 1 , Compare A = Disconnect
On Oc1a Int_t1
Enable Oc1a

Disable Interrupts

Flash_enable = 1

On Int0 Wakeup
Config Int0 = Low Level

On Int1 Zeit_ansagen
Config Int1 = Low Level

'32khz Ausgang des DS3231 ausschalten
I2cstart
I2cwbyte Ds3231w
I2cwbyte &H0F
I2cwbyte &B10000000
I2cstop

Waitms 100

Do
   Ds_power = 0                                             'DS3231 abschalten
   Enable Int0
   Enable Int1

   Enable Interrupts

   Config Powermode = Powerdown                             'Mikrocontroller in Standby
Loop

Wakeup:
   Disable Int0                                             'Keinen weiteren Interrupt zulassen.
   Ds_power = 1                                             'DS3231 mit Strom versorgen
   Waitms 10                                                'Entprellen + Starten DS3231

   While Pind.1 = 0 Or Pind.2 = 0 Or Pind.0 = 0             'Was soll getan werden?

      If Pind.1 = 0 And Pind.2 = 0 Then
         Gosub Minuten_stellen
      Elseif Pind.0 = 0 And Pind.2 = 0 Then
         Gosub Minuten_zurueckstellen
      Elseif Pind.2 = 0 Then
         Gosub Stunden_stellen
      End If
   Wend

Return


Stunden_stellen:
   R = 2
   Gosub I2c_lesen
   Dat = Makedec(dat)
   Incr Dat
   If Dat > 23 Then
      Dat = 0
   End If
   Dat = Makebcd(dat)
   Gosub I2c_stellen

'Stundenansage aufrufen
   Gosub I2c_lesen
   H = Makedec(dat)
   Gosub Stunden

Return

Minuten_zurueckstellen:
   R = 1
   Gosub I2c_lesen
   Dat = Makedec(dat)
   Decr Dat
   If Dat < 0 Then
      Dat = 59
   End If
   Dat = Makebcd(dat)
   Gosub I2c_stellen

'Sekunden auf 0
   Dat = 0
   R = 0
   Gosub I2c_stellen

'Minutenansage aufrufen
   R = 1
   Gosub I2c_lesen
   M = Makedec(dat)
   Gosub Minuten

Return

Minuten_stellen:
   R = 1
   Gosub I2c_lesen
   Dat = Makedec(dat)
   Incr Dat
   If Dat > 59 Then
      Dat = 0
   End If
   Dat = Makebcd(dat)
   Gosub I2c_stellen

'Sekunden auf 0
   Dat = 0
   R = 0
   Gosub I2c_stellen

'Minutenansage aufrufen
   R = 1
   Gosub I2c_lesen
   M = Makedec(dat)
   Gosub Minuten

Return



Zeit_ansagen:

   Disable Int1                                             'Keinen weiteren Interrupt zulassen
   Ds_power = 1                                             'DS3231 mit Strom versorgen
   Waitms 10                                                'Entprellen + Starten DS3231

'Stunden
   R = 2
   Gosub I2c_lesen
   Hh = Makedec(dat)

'Minuten
   R = 1
   Gosub I2c_lesen
   Mm = Makedec(dat)

'Sekunden
   R = 0
   Gosub I2c_lesen
   Ss = Makedec(dat)

'hh/mm/ss=Uhrzeit aus RTC, h/m/s=Uhrzeit für Ansage
   M = Mm
   H = Hh
   S = Ss

'Die nächsten vollen 10 Sekunden ermitteln ...

   Select Case S
      Case Is > 52 : Gosub Ueberlauf : S_lookup = 86 : Swait = 10
      Case Is > 42 : Gosub Ueberlauf : S_lookup = 85 : Swait = 0
      Case Is > 32 : S_lookup = 90 : Swait = 50
      Case Is > 22 : S_lookup = 89 : Swait = 40
      Case Is > 12 : S_lookup = 88 : Swait = 30
      Case Is > 2 : S_lookup = 87 : Swait = 20
      Case Is >= 0 : S_lookup = 86 : Swait = 10
   End Select

'Ansage los ...
'Beim nächsten Ton ist es ...
   Startadr = 0
   Frames = 13555
   Gosub Ausgabe

   Waitms 250

'... Stunden ansagen
   Gosub Stunden

   Waitms 250

'... Minuten ansagen
   Gosub Minuten

   Waitms 250

'... Sekunden ansagen
   Startadr = Lookup(s_lookup , Rom_adr)
   Incr S_lookup
   Endadr = Lookup(s_lookup , Rom_adr)
   Frames = Endadr - Startadr
   Gosub Ausgabe


'Warteschleife bis 10 Sek.voll sind
   Ss = 99
   R = 0
   While Swait <> Ss
      Gosub I2c_lesen
      Ss = Makedec(dat)
      Waitms 10
   Wend

'Beep
   Startadr = 1026941
   Frames = 1502
   Gosub Ausgabe

Return

'Stunden + Minutenansage in eigenem Unterprogramm, da diese auch zum Stellen aufgerufen werden!
Stunden:
   H = H + 1
   Startadr = Lookup(h , Rom_adr)
   Incr H
   Endadr = Lookup(h , Rom_adr)
   Frames = Endadr - Startadr
   Gosub Ausgabe
Return

Minuten:
   M = M + 25
   Startadr = Lookup(m , Rom_adr)
   Incr M
   Endadr = Lookup(m , Rom_adr)
   Frames = Endadr - Startadr
   Gosub Ausgabe
Return


Ueberlauf:
   Incr M
   If M > 59 Then
      M = 0
      Incr H
   End If

   If H > 23 Then
      H = 0
   End If
Return


Ausgabe:
   Spi_data(1) = &H03                                       'Kommando zum Lesen des Speichers
   Spi_data(2) = Adr_high                                   '24 Adressbits
   Spi_data(3) = Adr_mid
   Spi_data(4) = Adr_low

   Flash_enable = 0                                         'Enable ROM
   Spiout Spi_data(1) , 4                                   'Kommando "03" + Adresse zum ROM senden
   Framecounter = 0
   Enable Interrupts                                        'Jetzt wird bei jedem Timer1 Interrupt ein Byte aus dem ROM gelesen.
   While Framecounter < Frames                              'Ist die gewünschte Anzahl von Frames abgespielt, werden Interrupts deaktiviert und das Unterprogramm beendet.
   Wend
   Disable Interrupts
   Flash_enable = 1                                         'Abschalten des Flash ROM
Return


Int_t1:
   Incr Framecounter
   Spiin B , 1                                              'Byte aus Flash lesen
   Ocr2 = B                                                 'Byte an das PWM Register des Timer0 übergeben.
Return

I2c_lesen:
   I2cstart
   I2cwbyte Ds3231w
   I2cwbyte R
   I2cstart
   I2cwbyte Ds3231r
   I2crbyte Dat , Nack
   I2cstop
Return

I2c_stellen:
   I2cstart
   I2cwbyte Ds3231w
   I2cwbyte R
   I2cwbyte Dat
   I2cstop
Return

End

'Die Startadressen der Sprachsampels
Rom_adr:
   Data 0& , 13574& , 21157& , 28961& , 38339& , 46145& , 53983& , 62043& , 69278& , 77155& , 84702& , 92322& , 100492& , 108992& , 117784& , 127127& , 136615& , 145921& , 156252& , 165301& , 173068&
   Data 181897& , 190140& , 199336& , 208898& , 220328& , 230367& , 239452& , 250003& , 259858& , 270776& , 280814& , 291768& , 301990& , 311368& , 322323& , 332910& , 343718& , 355002& , 366615&
   Data 378376& , 390649& , 403068& , 415964& , 427138& , 438312& , 449449& , 462235& , 475168& , 487770& , 500263& , 514221& , 527923& , 541149& , 554045& , 567563& , 578774& , 590754& , 603906&
   Data 615923& , 629038& , 641604& , 653914& , 666993& , 679816& , 692199& , 703336& , 714913& , 727662& , 739679& , 752538& , 765361& , 777853& , 790200& , 802473& , 814306& , 825883& , 837973&
   Data 850356& , 863471& , 876880& , 890362& , 902782& , 914872& , 927658& , 941836& , 954951& , 969386& , 984077& , 998658& , 1012909& , 1026886& , 1028241&