'------------------------------------------------------------------
' DS3231 Library for PIC18 Basic
' BY COS, 2025/11/16, Pic18 Oshonsoft v5.92
' Description: This library handles communication with the
'              DS3231 Real-Time Clock (RTC) module using PIC18.
'              It contains functions for writing, reading, and
'              configuring the clock.
'-------------------------------------------------------------------

' Pin definition for I2C communication with the DS3231
' DS3231_SDA = TRISC.4
' DS3231_SCL = TRISC.3

' I2C protocol constants
' Const ACK = 0                    ' Acknowledge (confirmation)
' Const NACK = 1                   ' No Acknowledge (no confirmation)

' I2C address of the DS3231 (0xD0 is the base address).
Const DS3231_ADDR = 0xD0

'---------------------------------------------------------------
' Proc:        DS3231_Init
' Description: Initializes the DS3231 module, checks and restarts the oscillator.
' Arguments:   None.
' Returns:     None.
'---------------------------------------------------------------
Proc DS3231_Init()
    ' Initializes the DS3231.
    ' - Ensures the oscillator is enabled (EOSC = 0).
    ' - Disables alarms and interrupts.
    ' - Clears the oscillator stop flag (OSF) and disables the 32kHz output by default.
    Dim ctrl As Byte
    Dim _status As Byte

    ' Read and configure the control register (0x0E).
    ctrl = DS3231_Read(0x0E)
    ctrl.7 = 0           ' EOSC = 0 → oscillator enabled also in battery mode.
    ctrl.2 = 1           ' INTCN = 1 → INT/SQW works as alarm output (SQW off by default).
    ctrl.1 = 0           ' A2IE = 0 → alarm 2 disabled.
    ctrl.0 = 0           ' A1IE = 0 → alarm 1 disabled.
    Call DS3231_Write(0x0E, ctrl)

    ' Read and configure the status register (0x0F).
    _status = DS3231_Read(0x0F)
    _status.7 = 0         ' OSF = 0 → clears the oscillator stop flag.
    _status.3 = 0         ' EN32kHz = 0 → disables 32kHz output initially.
    _status.1 = 0         ' A2F = 0 → clears alarm 2 flag.
    _status.0 = 0         ' A1F = 0 → clears alarm 1 flag.
    Call DS3231_Write(0x0F, _status)

    WaitMs 10
End Proc
'---------------------------------------------------------------
' Proc:        DS3231_Set_24h
' Description: Configures the DS3231 for 24-hour mode.
' Arguments:   None.
' Returns:     None.
'---------------------------------------------------------------
Proc DS3231_Set_24h()
    Dim hour_register As Byte
    Dim hour_bcd As Byte
    Dim hour As Byte

    hour_register = DS3231_Read(0x02)  ' Read the current hour register.

    ' Only convert if the device is currently in 12-hour mode.
    If hour_register.6 = 1 Then
        ' In 12-hour mode, keep only the BCD hour bits.
        ' Bit 6 = 12/24 mode, bit 5 = AM/PM.
        hour_bcd = hour_register And 0x1F

        ' Convert the BCD hour value to decimal.
        hour = Get_BcdToDEC(hour_bcd)

        ' Convert from 12-hour format to 24-hour format.
        If hour_register.5 = 1 Then       ' PM
            If hour < 12 Then hour = hour + 12
        Else                               ' AM
            If hour = 12 Then hour = 0
        Endif

        ' Write the hour back in 24-hour format.
        Call DS3231_Write(0x02, Get_DecToBCD(hour))
    Endif
End Proc

''---------------------------------------------------------------
'' Proc:        DS3231_Set_24h
'' Description: Configures the DS3231 for 24-hour mode.
'' Arguments:   None.
'' Returns:     None.
''---------------------------------------------------------------
'Proc DS3231_Set_24h()
    'Dim hour_register As Byte
    'Dim hour_bcd As Byte
    'Dim hour As Byte

    'hour_register = DS3231_Read(0x02)  ' Read the current hour register.

    '' Only convert if the device is currently in 12-hour mode.
    'If hour_register.6 = 1 Then

        '' In 12-hour mode, mask hour bits only (remove 12/24 and AM/PM bits).
        'hour_bcd = hour_register And 0x1F ' 00011111

        '' Convert the BCD hour value to decimal.
        'hour = Get_BcdToDEC(hour_bcd)

        '' Convert from 12h to 24h.
        'If hour_register.5 = 1 Then       ' PM
            'If hour < 12 Then hour = hour + 12
        'Else                               ' AM
            'If hour = 12 Then hour = 0
        'Endif

        '' Write back in 24-hour format.
        'Call DS3231_Write(0x02, Get_DecToBCD(hour))
    'Endif
'End Proc


''---------------------------------------------------------------
'' Proc:        DS3231_Set_24h
'' Description: Configures the DS3231 for 24-hour mode.
'' Arguments:   None.
'' Returns:     None.
''---------------------------------------------------------------
'Proc DS3231_Set_24h()
    'Dim hour_register As Byte
    'Dim hour As Byte

    'hour_register = DS3231_Read(0x02)  ' Read the current hour register.
    'hour = hour_register 'Temporary variable for the hour.
    ''Clears the control bits. In the DS3231, bit 6 indicates 12/24-hour mode.
    'hour.6 = 0 ' 24-hour mode.
    'hour.5 = 0 ' Clear the AM/PM bit in 12h mode or the most significant bit in 24h mode.
    'hour = Get_BcdToDEC(hour) 'Convert the hour from BCD to decimal.
    'If hour_register.6 = 1 Then 'If it is in 12-hour mode
        'If hour_register.5 = 1 Then 'If it is PM
            'If hour < 12 Then hour = hour + 12 'Convert the hour from 12h to 24h.
        'Else
            'If hour = 12 Then hour = 0 '0h (24h).
        'Endif
        'Call DS3231_Write(0x02, Get_DecToBCD(hour)) 'Convert the hour from decimal to BCD and update the DS3231.
    'Endif
'End Proc


'---------------------------------------------------------------
' Proc:        DS3231_Set_12h
' Description: Configures the DS3231 for 12-hour mode.
' Arguments:   None.
' Returns:     None.
'---------------------------------------------------------------
Proc DS3231_Set_12h()
    Dim hour_register As Byte
    Dim hour_bcd As Byte
    Dim hour As Byte

    hour_register = DS3231_Read(0x02)  ' Read the current hour register.

    ' Only convert if the device is currently in 24-hour mode.
    If hour_register.6 = 0 Then
        ' In 24-hour mode, keep only the valid BCD hour bits.
        hour_bcd = hour_register And 0x3F

        ' Convert the BCD hour value to decimal.
        hour = Get_BcdToDEC(hour_bcd)

        ' Convert from 24-hour format to 12-hour format.
        If hour >= 12 Then
            ' PM
            If hour > 12 Then hour = hour - 12
            hour_bcd = Get_DecToBCD(hour)
            hour_bcd.6 = 1   ' 12-hour mode
            hour_bcd.5 = 1   ' PM
        Else
            ' AM
            If hour = 0 Then hour = 12
            hour_bcd = Get_DecToBCD(hour)
            hour_bcd.6 = 1   ' 12-hour mode
            hour_bcd.5 = 0   ' AM
        Endif

        ' Write the hour back in 12-hour format.
        Call DS3231_Write(0x02, hour_bcd)
    Endif
End Proc
''---------------------------------------------------------------
'' Proc:        DS3231_Set_12h
'' Description: Configures the DS3231 for 12-hour mode.
'' Arguments:   None.
'' Returns:     None.
''---------------------------------------------------------------
'Proc DS3231_Set_12h()
    'Dim hour_register As Byte
    'Dim hour As Byte
    'hour_register = DS3231_Read(0x02)  ' Read the current hour register.
    'hour = hour_register
    'hour = Get_BcdToDEC(hour_register) 'Convert the hour register from BCD to decimal.
    'hour.6 = 0 '24-hour mode (bit 6 of the DS3231).
    'hour.5 = 0 'Clear the AM/PM bit in 12h mode or the most significant bit in 24h mode.
    ''From 24h to 12h; if it is already in 12h mode, nothing is done.
    'If hour_register.6 = 0 Then '24-hour mode
        'If hour >= 12 Then 'If the hour is between 12h (PM) and 23h.
            ''Convert to 12h PM mode.
            ''If hour = 12, it is not modified.
            'If hour > 12 Then hour = hour - 12 'To 12h
            'hour = Get_DecToBCD(hour) 'Convert the hour from decimal to BCD.
            'hour.6 = 1 'Insert the bit that indicates 12-hour mode (bit 6).
            'hour.5 = 1 'Set PM mode.
            'Call DS3231_Write(0x02, hour) 'Write the hour register.
        'Else
            'If hour = 0 Then hour = 12 '00h to 12h AM
            'hour = Get_DecToBCD(hour) 'Convert the hour from decimal to BCD.
            'hour.6 = 1 'Insert the bit that indicates 12-hour mode (bit 6).
            'hour.5 = 0 'AM mode.
            'Call DS3231_Write(0x02, hour) 'Update the hour register.
        'Endif
    'Endif
'End Proc
'---------------------------------------------------------------
' Proc:        DS3231_Set_AM_PM
' Description: Configures the DS3231 in AM/PM mode.
' Arguments:   mode (short) - 0 for AM, 1 for PM.
' Returns:     None
'---------------------------------------------------------------
Const AM_ON = 0 'AM
Const AM_OFF = 1 'PM
Const PM_ON = 1 'PM
Const PM_OFF = 0 'AM
Proc DS3231_Set_AM_PM(mode As Short)
    Dim hour_register As Byte
    Call DS3231_Set_12h()   ' Switch to 12-hour mode
    hour_register = DS3231_Read(0x02)   ' Read the current hour register
    hour_register.5 = mode.0  ' Set the AM/PM bit
    Call DS3231_Write(0x02, hour_register) ' Write the updated hour register
End Proc
'---------------------------------------------------------------
' Function:    DS3231_Get_AM_PM_24h
' Description: Retrieves the AM/PM/24-hour mode from the DS3231.
' Arguments:   None
' Returns:     A Short integer (-1 for 24-hour mode, 0 for AM, 1 for PM)
'---------------------------------------------------------------
Function DS3231_Get_AM_PM_24h() As Short
    Dim hour_register As Byte
    Dim AmPm24h As Short
    AmPm24h = -1   ' Default to 24-hour mode
    hour_register = DS3231_Read(0x02)   ' Read the current hour register
    If hour_register.6 = 1 Then   ' Check if the DS3231 is in 12-hour mode
        If hour_register.5 = 0 Then  ' If bit 5 is 0, it is AM
            AmPm24h = 0  ' Return AM
        Else
            AmPm24h = 1  ' Return PM
        Endif
    Endif
    ReturnValue AmPm24h   ' Return the current mode (AM/PM/24h)
End Function
'---------------------------------------------------------------
' Proc:        DS3231_Write
' Description: Sends a command and a data byte to the DS3231.
' Arguments:   address (Byte) - The address of the register.
'              data (Byte) - The data to be written.
' Returns:     None
'---------------------------------------------------------------
Proc DS3231_Write(address As Byte, data As Byte)
    START_I2C()            	 	' Start I2C communication
    WRITE_I2C(DS3231_ADDR)		' DS3231 I2C write address
    WRITE_I2C(address)      	' Write register address
    WRITE_I2C(data)         	' Write the data to the register
    STOP_I2C()              	' End I2C communication
End Proc
'---------------------------------------------------------------
' Function:    DS3231_Read
' Description: Reads a byte from the DS3231.
' Arguments:   address (Byte) - The address of the register to read from.
' Returns:     A Byte of data read from the DS3231.
'---------------------------------------------------------------
Function DS3231_Read(address As Byte) As Byte
    Dim data As Byte
    START_I2C()             	' Start I2C communication
    WRITE_I2C(DS3231_ADDR)      ' DS3231 I2C write address
    WRITE_I2C(address)      	' Send the register address to read from
    START_I2C()             	' Repeated start for reading
    WRITE_I2C(DS3231_ADDR +1)   ' DS3231 I2C read address
    data = READ_I2C(NACK)   	' Read the data from the DS3231
    STOP_I2C()              	' End I2C communication
    ReturnValue data        	' Return the read data
End Function
'---------------------------------------------------------------
' Proc:        RTC_Set_DateTime
' Description: Sets the date and time in the DS3231.
' Arguments:   day, mth, year, dow, hr, min (all of type Byte)
' Returns:     None
'---------------------------------------------------------------
'Before using, first check whether we are in 12h or 24h mode.
'If we store the hour in 12h format while in 24h mode, the AM/PM may not be correct.
Proc DS3231_Set_DateTime(day As Byte, mth As Byte, year As Byte, dow As Byte, hr As Byte, min As Byte)
	Dim hour_register As Byte
	'Read the hour register to insert the previous configuration.
	hour_register = DS3231_Read(0x02) 'Read the hour register (configuration + BCD hour).
	hr = Get_DecToBCD(hr) 'Convert the hour to BCD.
	'Insert the previous configuration bits into the hour.
	If hour_register.6 = 1 Then 'If we are in 12h format.
		hr.5 = hour_register.5 'Preserve the previous AM/PM mode.
		hr.6 = 1 '12h mode.
	Endif
	'Write the input data to the DS3231 in BCD format.
    Call DS3231_Write(0x04, Get_DecToBCD(day))
    Call DS3231_Write(0x05, Get_DecToBCD(mth))
    Call DS3231_Write(0x06, Get_DecToBCD(year))
    Call DS3231_Write(0x03, Get_DecToBCD(dow))
    Call DS3231_Write(0x02, hr) 'We already converted the hour to BCD earlier.
    Call DS3231_Write(0x01, Get_DecToBCD(min))
    Call DS3231_Write(0x00, Get_DecToBCD(0))  ' Seconds set to zero.
End Proc
'---------------------------------------------------------------
' Proc:        RTC_Get_Date
' Description: Reads the date from the DS3231.
' Arguments:   day, mth, year, dow (all of type Byte by reference (ByRef))
' Returns:     None
'---------------------------------------------------------------
Proc DS3231_Get_Date(ByRef day As Byte, ByRef mth As Byte, ByRef year As Byte, ByRef dow As Byte)
    day = Get_BcdToDEC(DS3231_Read(0x04))  ' Read and convert the day to decimal
    mth = Get_BcdToDEC(DS3231_Read(0x05))  ' Read and convert the month to decimal
    year = Get_BcdToDEC(DS3231_Read(0x06)) ' Read and convert the year to decimal
    dow = Get_BcdToDEC(DS3231_Read(0x03))  ' Read and convert the day of the week to decimal
End Proc
'---------------------------------------------------------------
' Proc:        RTC_Get_Time
' Description: Reads the current time from the DS3231.
' Arguments:   hr, min, sec (all of type Byte by ByRef)
' Returns:     None
'---------------------------------------------------------------
Proc DS3231_Get_Time(ByRef hr As Byte, ByRef min As Byte, ByRef sec As Byte)
	'Read the hour register and clear the configuration bits.
    hr = DS3231_Read(0x02) 'Read hour register (configuration + hour in BCD format).
	If hr.6 = 1 Then hr.5 = 0 'If 12h format, clear the AM/PM bit
	hr.6 = 0 'Clear 12h format (not necessary in 24h mode).
	hr = Get_BcdToDEC(hr) 'Convert from BCD to decimal
    min = Get_BcdToDEC(DS3231_Read(0x01))  ' Read and convert the minutes.
    sec = Get_BcdToDEC(DS3231_Read(0x00))  ' Read and convert the seconds.
End Proc
'---------------------------------------------------------------
' Proc:        RTC_Set_Hour
' Description: Sets the time in the DS3231.
' Arguments:   hout (Byte)
' Returns:     None
'---------------------------------------------------------------
'Before using, first check whether we are in 12h or 24h mode.
'If we store the hour in 12h format while in 24h mode, the AM/PM may not be correct.
Proc DS3231_Set_Hour(hr As Byte)
	Dim hour_register As Byte
	'Read the hour register to insert the previous configuration.
	hour_register = DS3231_Read(0x02) 'Read the hour register (configuration + BCD hour).
	hr = Get_DecToBCD(hr) 'Convert the hour to BCD.
	'Insert the previous configuration bits into the hour.
	If hour_register.6 = 1 Then 'If we are in 12h format.
		hr.5 = hour_register.5 'Preserve the previous AM/PM mode.
		hr.6 = 1 '12h mode.
	Endif
	'Write the input data to the DS3231 in BCD format.
    Call DS3231_Write(0x02, hr) 'We already converted the hour to BCD earlier.
End Proc
'---------------------------------------------------------------
' Proc:        RTC_Set_Minutes
' Description: Sets the Minutes in the DS3231.
' Arguments:   minutes (type Byte)
' Returns:     None
'---------------------------------------------------------------
Proc DS3231_Set_Minutes(min As Byte)
	'Write the input data to the DS3231 in BCD format.
    Call DS3231_Write(0x01, Get_DecToBCD(min))
End Proc'---------------------------------------------------------------
' Proc:        RTC_Set_Seconds
' Description: Sets the seconds in the DS3231.
' Arguments:   seconds (type Byte)
' Returns:     None
'---------------------------------------------------------------
Proc DS3231_Set_Seconds(sec As Byte)
	'Write the input data to the DS3231 in BCD format.
    Call DS3231_Write(0x00, Get_DecToBCD(sec))
End Proc
' Proc:        DS3231_Set_DayOfWeek
' Description: Sets the day of week in the DS3231.
' Arguments:   dow (day of week) (type Byte, value 1 to 7)
' Returns:     None
'---------------------------------------------------------------
Proc DS3231_Set_DayOfWeek(dow As Byte)
	'Write the input data to the DS1302 in BCD format.
	Call DS3231_Write(0x03, Get_DecToBCD(dow))
End Proc
' Proc:        DS3231_Get_DayOfWeek
' Description: Reads the day of week in the DS1302.
' Arguments:   none
' Returns:     day of week (type byte, value 1 to 7)
'---------------------------------------------------------------
Function DS3231_Get_DayOfWeek() As Byte
	'Read the data to the DS1302 in BCD format.
    ReturnValue Get_BcdToDEC(DS3231_Read(0x03))  ' Read and convert the day of the week to decimal.
End Function
'---------------------------------------------------------------
' Proc:        DS3231_SquareWave
' Description: Enables the square wave output on the DS3231.
'              This function configures the control register to
'              activate the square wave signal on the SQW/OUT pin.
' Arguments:   freq (Byte) - The frequency of the square wave:
'				  0: Disable
'                 1: 1Hz
'				  2: 1.024Khz
'                 3: 4.096kHz
'                 4: 8.192kHz
' Returns:     None
'---------------------------------------------------------------
' Configures the INT/SQW output of the DS3231 as a square wave.
' Frequency parameter:
Const DS3231_1hz =		 		1 '   1 = 1 Hz
Const DS3231_1024Khz =			2 '   2 = 1.024 kHz
Const DS3231_4096Khz = 			3 '   3 = 4.096 kHz
Const DS3231_8192Khz =			4 '   4 = 8.192 kHz
Const DS3231_Freq_DISABLE = 	0 '   0 = SQW disabled (INT/SQW used only for alarms).
Proc DS3231_SquareWave(freq As Byte)
     Dim ctrl As Byte

    ' Start from a safe base value: oscillator ON, alarms disabled.
    ctrl = 0
    ctrl.7 = 0    ' EOSC = 0 → oscillator enabled.
    ctrl.6 = 0    ' BBSQW = 0.
    ctrl.5 = 0    ' CONV = 0.
    ctrl.1 = 0    ' A2IE = 0.
    ctrl.0 = 0    ' A1IE = 0.

    Select Case freq
        Case 0
            ' SQW disabled: INTCN = 1 → INT/SQW only for alarms.
            ctrl.2 = 1               ' INTCN = 1
        Case 1
            ' 1 Hz: RS2=0, RS1=0, INTCN=0.
            ctrl.4 = 0               ' RS2
            ctrl.3 = 0               ' RS1
            ctrl.2 = 0               ' INTCN = 0 → SQW output
        Case 2
            ' 1.024 kHz: RS2=0, RS1=1
            ctrl.4 = 0
            ctrl.3 = 1
            ctrl.2 = 0
        Case 3
            ' 4.096 kHz: RS2=1, RS1=0
            ctrl.4 = 1
            ctrl.3 = 0
            ctrl.2 = 0
        Case Else
            ' 8.192 kHz (default value for any code >=4): RS2=1, RS1=1
            ctrl.4 = 1
            ctrl.3 = 1
            ctrl.2 = 0
    EndSelect

    Call DS3231_Write(0x0E, ctrl)
End Proc

'---------------------------------------------------------------
' Proc:        DS3231_ClearOSF
' Description: Clears the OSF (Oscillator Stop Flag) in the status
'              register. Useful to later check whether the clock
'              has lost the time.
'---------------------------------------------------------------
Proc DS3231_ClearOSF()
    Dim _status As Byte
    STATUS = DS3231_Read(0x0F)   ' Status register
    STATUS.7 = 0                 ' OSF = 0
    Call DS3231_Write(0x0F, _status)
End Proc

'---------------------------------------------------------------
' Proc:        DS3231_32Khz
' Description: Enables or disables the 32kHz output on the 32K pin.
' Arguments:   enable (Byte)
'                0 = disables 32.768 kHz output
'                1 = enables 32.768 kHz output
'---------------------------------------------------------------
Proc DS3231_32Khz(_enable As Bit)
    Dim _status As Byte
    STATUS = DS3231_Read(0x0F)   ' Status register
    If _enable = 0 Then
        _status.3 = 0             ' EN32kHz = 0
    Else
        _status.3 = 1             ' EN32kHz = 1
    Endif
    Call DS3231_Write(0x0F, _status)
End Proc

'---------------------------------------------------------------
' Function:    DS3231_ReadTemperature
' Description: Reads the internal temperature of the DS3231.
' Returns:     Temperature in quarter-degrees (°C * 4).
'              Example: value 85 → 21.25 °C.
'---------------------------------------------------------------
Function DS3231_ReadTemperature() As Single
    Dim msb As Byte
    Dim lsb As Byte
    Dim tempX4 As Single

    msb = DS3231_Read(0x11)      ' Temperature MSB (signed)
    lsb = DS3231_Read(0x12)      ' LSB, only bits 7 and 6 are valid

    ' The value is 10-bit two's complement with 0.25 °C resolution.
    ' tempX4 = temperature * 4
    tempX4 = CSingle msb * 4        ' Integer part * 4
    tempX4 = tempX4 + (lsb >> 6)  ' Add the two 0.25 °C bits

    ReturnValue tempX4/4 ' °C
End Function

'---------------------------------------------------------------
' Proc:        DS3231_SetAlarm1
' Description: Configures alarm 1 to trigger when seconds,
'              minutes, hours, and day/date match.
' Arguments:
'   second  (Byte) - seconds (0–59)
'   minute  (Byte) - minutes (0–59)
'   hour    (Byte) - hours in 24-hour format (0–23)
'   day     (Byte) - day of month (1–31) or day of week (1–7)
'   useDOW  (Byte) - 0 = use day of month, 1 = use day of week
'---------------------------------------------------------------
Proc DS3231_SetAlarm1(second As Byte, minute As Byte, hour As Byte, day As Byte, useDOW As Byte)
    Dim a1sec, a1min, a1hr, a1day As Byte

    a1sec = Get_DecToBCD(second)  ' A1M1=0 → bit7 = 0
    a1min = Get_DecToBCD(minute)  ' A1M2=0
    a1hr  = Get_DecToBCD(hour)    ' A1M3=0, 24h format
    a1day = Get_DecToBCD(day)     ' A1M4=0, DY/DT according to parameter

    a1sec.7 = 0                   ' A1M1 = 0 → seconds must match
    a1min.7 = 0                   ' A1M2 = 0 → minutes must match
    a1hr.7  = 0                   ' A1M3 = 0 → hours must match
    a1day.7 = 0                   ' A1M4 = 0 → day/date must match

    If useDOW = 0 Then
        a1day.6 = 0               ' DY/DT = 0 → use date (day of month)
    Else
        a1day.6 = 1               ' DY/DT = 1 → use day of week
    Endif

    Call DS3231_Write(0x07, a1sec)
    Call DS3231_Write(0x08, a1min)
    Call DS3231_Write(0x09, a1hr)
    Call DS3231_Write(0x0A, a1day)
End Proc

'---------------------------------------------------------------
' Proc:        DS3231_EnableAlarm1
' Description: Enables or disables alarm 1 interrupt output
'              on the INT/SQW pin.
' Arguments:   enable (Byte)
'                0 = disable alarm 1
'                1 = enable alarm 1
'---------------------------------------------------------------
Proc DS3231_EnableAlarm1(_enable As Byte)
    Dim ctrl As Byte

    ctrl = DS3231_Read(0x0E)

    ' Interrupt mode: INTCN = 1
    ctrl.2 = 1

    If _enable = 0 Then
        ctrl.0 = 0                ' A1IE = 0
    Else
        ctrl.0 = 1                ' A1IE = 1
    Endif

    Call DS3231_Write(0x0E, ctrl)
End Proc

'---------------------------------------------------------------
' Proc:        DS3231_SetAlarm2
' Description: Configures alarm 2 to trigger when
'              minutes, hours, and day/date match.
' Arguments:
'   minute  (Byte) - minutes (0–59)
'   hour    (Byte) - hours in 24-hour format (0–23)
'   day     (Byte) - day of month (1–31) or day of week (1–7)
'   useDOW  (Byte) - 0 = use day of month, 1 = use day of week
'---------------------------------------------------------------
Proc DS3231_SetAlarm2(minute As Byte, hour As Byte, day As Byte, useDOW As Byte)
    Dim a2sec, a2min, a2hr, a2day As Byte

    a2min = Get_DecToBCD(minute)  ' A2M2=0
    a2hr  = Get_DecToBCD(hour)    ' A2M3=0, 24h format
    a2day = Get_DecToBCD(day)     ' A2M4=0, DY/DT according to parameter

    a2min.7 = 0                   ' A2M2 = 0 → minutes must match
    a2hr.7  = 0                   ' A2M3 = 0 → hours must match
    a2day.7 = 0                   ' A2M4 = 0 → day/date must match

    If useDOW = 0 Then
        a2day.6 = 0               ' DY/DT = 0 → use date (day of month)
    Else
        a2day.6 = 1               ' DY/DT = 1 → use day of week
    Endif

    Call DS3231_Write(0x0B, a2min)
    Call DS3231_Write(0x0C, a2hr)
    Call DS3231_Write(0x0D, a2day)
End Proc

'---------------------------------------------------------------
' Proc:        DS3231_EnableAlarm2
' Description: Enables or disables alarm 2 interrupt output
'              on the INT/SQW pin.
' Arguments:   enable (Byte)
'                0 = disable alarm 2
'                1 = enable alarm 2
'---------------------------------------------------------------
Proc DS3231_EnableAlarm2(_enable As Byte)
    Dim ctrl As Byte

    ctrl = DS3231_Read(0x0E)

    ' Interrupt mode: INTCN = 1
    ctrl.2 = 1

    If _enable = 0 Then
        ctrl.1 = 0                ' A2IE = 0
    Else
        ctrl.1 = 1                ' A2IE = 1
    Endif

    Call DS3231_Write(0x0E, ctrl)
End Proc

'---------------------------------------------------------------
' Proc:        DS3231_ClearAlarmFlags
' Description: Clears alarm 1 and alarm 2 flags in the status
'              register. Required after servicing an alarm
'              interrupt.
'---------------------------------------------------------------
Proc DS3231_ClearAlarmFlags()
    Dim _status As Byte

    _status = DS3231_Read(0x0F)
    _status.1 = 0                  ' A2F = 0
    _status.0 = 0                  ' A1F = 0
    Call DS3231_Write(0x0F, _status)
End Proc

'---------------------------------------------------------------
' Function:    Get_DecToBCD
' Description: Converts a decimal value to its BCD format.
' Arguments:   data (Byte) - Decimal value to convert.
' Returns:     The value converted to BCD.
'---------------------------------------------------------------
Function Get_DecToBCD(data As Byte) As Byte
    Dim nibh, nibl As Byte
    nibh = data / 10              ' Calculate the tens
    nibl = data - (nibh * 10)     ' Calculate the units
    ReturnValue (nibh << 4) | nibl  ' Return the value in BCD format
End Function
'---------------------------------------------------------------
' Function:    Get_BcdToDEC
' Description: Converts a BCD value to decimal.
' Arguments:   data (Byte) - BCD value to convert.
' Returns:     The value converted to decimal.
'---------------------------------------------------------------
Function Get_BcdToDEC(data As Byte) As Byte
    Dim i As Byte
    i = data
    data = (i >> 4) * 10          ' Extract the tens
    data = data + (i & 0x0F)      ' Extract the units
    ReturnValue data              ' Return the decimal value
End Function