/*
 * i2c_ds3231.h
 *
 *  Created on: May 3, 2024
 *      Author: tm
 */

#ifndef I2C_DS3231_H_
#define I2C_DS3231_H_

#include <stdint.h>
#include <stdbool.h>

typedef enum {
    DS3231_REG_SEC        = 0x00U,
    DS3231_REG_min        = 0x01U,
    DS3231_REG_HR         = 0x02U,
    DS3231_REG_DAY        = 0x03U,
    DS3231_REG_DATE       = 0x04U,
    DS3231_REG_MONTH      = 0x05U,
    DS3231_REG_YEAR       = 0x06U,
    DS3231_REG_ALARM1_SEC = 0x07U,
    DS3231_REG_ALARM1_MIN = 0x08U,
    DS3231_REG_ALARM1_HR  = 0x09U,
    DS3231_REG_ALARM1_DAY = 0x0AU,
    DS3231_REG_ALARM2_MIN = 0x0BU,
    DS3231_REG_ALARM2_HR  = 0x0CU,
    DS3231_REG_ALARM2_DAY = 0x0DU,
    DS3231_REG_CTRL1      = 0x0EU,
    DS3231_REG_CTRL2      = 0x0FU,
    DS3231_REG_AGING      = 0x10U,
    DS3231_REG_TEMPH      = 0x11U,
    DS3231_REG_TEMPL      = 0x12U
} DS3231_REG;

typedef enum {
    DS3231_ALARM1_EACH_SEC,
    DS3231_ALARM1_MATCH_SEC,
    DS3231_ALARM1_MATCH_SEC_MIN,
    DS3231_ALARM1_MATCH_SEC_MIN_HR,
    DS3231_ALARM1_MATCH_SEC_MIN_HR_DATE,
    DS3231_ALARM1_MATCH_SEC_MIN_HR_DAY
} DS3231_ALARM1_MATCH;

typedef enum {
    DS3231_ALARM2_EACH_MIN,
    DS3231_ALARM2_MATCH_MIN,
    DS3231_ALARM2_MATCH_MIN_HR,
    DS3231_ALARM2_MATCH_MIN_HR_DATE,
    DS3231_ALARM2_MATCH_MIN_HR_DAY
} DS3231_ALARM2_MATCH;

typedef enum {
    DS3231_DOW_SUNDAY    = 1,
    DS3231_DOW_MONDAY    = 2,
    DS3231_DOW_TUESDAY   = 3,
    DS3231_DOW_WEDNESDAY = 4,
    DS3231_DOW_THURSDAY  = 5,
    DS3231_DOW_FRIDAY    = 6,
    DS3231_DOW_SATURDAY  = 7
} DS3231_DAY_OF_WEEK;

typedef struct
{
    uint8_t  seconds;      // 0 - 59
    uint8_t  minutes;      // 0 - 59
    uint8_t  hours;        // 0 - 23
    uint8_t  day_of_week;  // 1 - 7
    uint8_t  day_of_month; // 1 - 31
    uint8_t  month;        // 1 - 12
    uint16_t year;         // 2000 - 2199
} DS3231_DateTime;

// For alarm2, the seconds field is not used
// If a match is not enabled the value in that reg is Don't Care, I recommend to zero fill
typedef struct
{
    uint8_t  seconds;      // 0 - 59
    uint8_t  minutes;      // 0 - 59
    uint8_t  hours;        // 0 - 23
    uint8_t  day_of_week;  // 1 - 7
    uint8_t  day_of_month; // 1 - 31
} DS3231_Alarm;

// Used to sign extend in ds3231_temp_reg_to_degC
typedef struct
{
    int reg : 10;
} DS3231_Temp;

char const * DS3231_DAY_OF_WEEK_to_string(DS3231_DAY_OF_WEEK dow);

// init ds3231
// if out_osc_stopped is non-null, indicate if oscillator stopped since last init, this probably means the battery failed
// en_alarm1/en_alarm2 configure alarms
// any active alarm will be cleared
// return 1 on success, <=0 on failure
int ds3231_init(bool en_alarm1, bool en_alarm2, bool* out_osc_stopped);

// return DS3231_REG_CTRL2
// return 1 on success, <=0 on failure
int ds3231_get_status(uint8_t* out_status);

// set clock on ds3231
// return 1 on success, <=0 on failure
int ds3231_set_time_test(DS3231_DateTime const * const time);

void ds3231_set_time(void);

// get clock on ds3231
// return 1 on success, <=0 on failure
int ds3231_get_time(DS3231_DateTime* out_time);
void cmd_ds3231_time_get(void);

// get temperature on ds3231
// return 1 on success, <=0 on failure
int ds3231_get_temp(uint16_t* out_temp);

float ds3231_temp_reg_to_degC(uint16_t temp_reg);

// set alarm1 on ds3231
// return 1 on success, <=0 on failure
int ds3231_set_alarm1(DS3231_Alarm const * const alarm, bool day_ndate, DS3231_ALARM1_MATCH match);

// set alarm2 on ds3231
// return 1 on success, <=0 on failure
int ds3231_set_alarm2(DS3231_Alarm const * const alarm, bool day_ndate, DS3231_ALARM2_MATCH match);

// set alarm enables and clear alarm flags for ds3231
// return 1 on success, <=0 on failure
int ds3231_enable_alarm(bool en1, bool en2);

uint8_t decToBcd(uint8_t val);
uint8_t bcdToDec(uint8_t val);

#endif /* I2C_DS3231_H_ */
