/* * pcf8523.c * * Created on: Mar 25, 2023 * Author: Daniel Peter Chokola */ /* Includes */ #include "pcf8523.h" #include "i2c.h" #include "time.h" /* Definitions */ /* I2C addresses */ #define PCF_I2C_W (0xd0) #define PCF_I2C_R (0xd1) /* Registers */ #define PCF_REG_CONTROL_1 (0x00) #define PCF_REG_CONTROL_2 (0x01) #define PCF_REG_CONTROL_3 (0x02) #define PCF_REG_SECONDS (0x03) #define PCF_REG_MINUTES (0x04) #define PCF_REG_HOURS (0x05) #define PCF_REG_DAYS (0x06) #define PCF_REG_WEEKDAYS (0x07) #define PCF_REG_MONTHS (0x08) #define PCF_REG_YEARS (0x09) #define PCF_REG_MINUTE_ALARM (0x0A) #define PCF_REG_HOUR_ALARM (0x0B) #define PCF_REG_DAY_ALARM (0x0C) #define PCF_REG_WEEKDAY_ALARM (0x0D) #define PCF_REG_OFFSET (0x0E) #define PCF_REG_TMR_CLKOUT_CTRL (0x0F) #define PCF_REG_TMR_A_FREQ_CTRL (0x10) #define PCF_REG_TMR_A_REG (0x11) #define PCF_REG_TMR_B_FREQ_CTRL (0x12) #define PCF_REG_TMR_B_REG (0x13) /* Bitmasks */ #define PCF_MASK_CTL1_CAP_SEL (0x80) /* internal oscillator capacitor selection for quartz crystals with a corresponding load capacitance */ #define PCF_MASK_CTL1_STOP (0x20) /* 0 RTC time circuits running; 1 RTC time circuits frozen */ #define PCF_MASK_CTL1_SR (0x10) /* 0 no software reset; 1 initiate software reset */ #define PCF_MASK_CTL1_12_24 (0x08) /* 0 24 hour mode is selected; 1 12 hour mode is selected */ #define PCF_MASK_CTL1_SIE (0x04) /* 0 second interrupt disabled; 1 second interrupt enabled */ #define PCF_MASK_CTL1_AIE (0x02) /* 0 alarm interrupt disabled; 1 alarm interrupt enabled */ #define PCF_MASK_CTL1_CIE (0x01) /* 0 no correction interrupt generated; 1 interrupt pulses are generated at every correction cycle */ #define PCF_MASK_CTL2_WTAF (0x80) /* 0 no watchdog timer A interrupt generated; 1 flag set when watchdog timer A interrupt generated */ #define PCF_MASK_CTL2_CTAF (0x40) /* 0 no countdown timer A interrupt generated; 1 flag set when countdown timer A interrupt generated */ #define PCF_MASK_CTL2_CTBF (0x20) /* 0 no countdown timer B interrupt generated; 1 flag set when countdown timer B interrupt generated */ #define PCF_MASK_CTL2_SF (0x10) /* 0 no second interrupt generated; 1 flag set when second interrupt generated */ #define PCF_MASK_CTL2_AF (0x08) /* 0 no alarm interrupt generated; 1 flag set when alarm triggered */ #define PCF_MASK_CTL2_WTAIE (0x04) /* 0 watchdog timer A interrupt is disabled; 1 watchdog timer A interrupt is enabled */ #define PCF_MASK_CTL2_CTAIE (0x02) /* 0 countdown timer A interrupt is disabled; 1 countdown timer A interrupt is enabled */ #define PCF_MASK_CTL2_CTBIE (0x01) /* 0 countdown timer B interrupt is disabled; 1 countdown timer B interrupt is enabled */ #define PCF_MASK_CTL3_PM (0xe0) /* battery switch-over and battery low detection control */ #define PCF_MASK_CTL3_BSF (0x08) /* 0 no battery switch-over interrupt generated; 1 flag set when battery switch-over occurs */ #define PCF_MASK_CTL3_BLF (0x04) /* 0 battery status ok; 1 battery status low; flag is read-only */ #define PCF_MASK_CTL3_BSIE (0x02) /* 0 no interrupt generated from battery switch-over flag, BSF; 1 interrupt generated when BSF is set */ #define PCF_MASK_CTL3_BLIE (0x01) /* 0 no interrupt generated from battery low flag, BLF; 1 interrupt generated when BLF is set */ #define PCF_CMD_CTL1_RESET (0x58) /* software reset */ /* Slick Macros */ #define bin2bcd(x) ((x / 10) * 6 + x) #define bcd2bin(x) (x - ((x >> 4) * 6)) #define return_if_fail(cond) \ if(!(cond)) { return; } #define return_val_if_fail(cond, val) \ if(!(cond)) { return (val); } #define return_null_if_fail(cond) return_val_if_fail((cond), NULL) /* Private Variables */ /* Function Prototypes */ static void pcf8253_write_byte(pcf8523_t *pcf8523, uint8_t reg, uint8_t data); static void pcf8253_read_byte(pcf8523_t *pcf8523, uint8_t reg, uint8_t *data); static void pcf8253_write(pcf8523_t *pcf8523, uint8_t reg, uint8_t *data, uint16_t len); static void pcf8253_read(pcf8523_t *pcf8523, uint8_t reg, uint8_t *data, uint16_t len); /* Function Definitions */ int32_t pcf8523_init(pcf8523_t *pcf8523, I2C_HandleTypeDef *hi2c) { uint8_t tmp; return_val_if_fail(pcf8523, -1); return_val_if_fail(hi2c, -1); pcf8523->hi2c = hi2c; /* initiate reset */ pcf8253_write_byte(pcf8523, PCF_REG_CONTROL_1, PCF_CMD_CTL1_RESET); /* check if the device is initialized */ pcf8253_read_byte(pcf8523, PCF_REG_CONTROL_3, &tmp); if((tmp & PCF_MASK_CTL3_PM) == PCF_MASK_CTL3_PM) { /* the RTC has not been initialized -- initialize it */ /* FIXME: I don't know how to initialize it. */ /* turn on battery switchover mode */ pcf8253_write_byte(pcf8523, PCF_REG_CONTROL_3, 0x00); } return 0; } time_t pcf8523_now(pcf8523_t *pcf8523) { struct tm t = { 0 }; time_t time; uint8_t buf[7]; return_val_if_fail(pcf8523, -1); /* read current time */ pcf8253_read(pcf8523, PCF_REG_SECONDS, buf, 7); t.tm_sec = bcd2bin(buf[0]); /* Seconds [0, 60] */ t.tm_min = bcd2bin(buf[1]); /* Minutes [0, 59] */ t.tm_hour = bcd2bin(buf[2]); /* Hour [0, 23] */ t.tm_mday = bcd2bin(buf[3]); /* Day of the month [1, 31] */ t.tm_wday = bcd2bin(buf[4]); /* Month [0, 11] (January = 0) */ t.tm_yday = 0; /* Day of the year [0, 365] (Jan/01 = 0) */ t.tm_mon = bcd2bin(buf[5]); /* Month [0, 11] (January = 0) */ t.tm_year = bcd2bin(buf[6]) + 100; /* Year minus 1900 */ /* tm_wday and tm_yday are ignored by mktime() */ time = mktime(&t); return time; } int32_t pcf8523_set_time_t(pcf8523_t *pcf8523, time_t *time) { struct tm *t; return_val_if_fail(pcf8523, -1); return_val_if_fail(time, -1); t = gmtime(time); return pcf8523_set_time(pcf8523, t); } int32_t pcf8523_set_time(pcf8523_t *pcf8523, struct tm *t) { uint8_t buf[7]; return_val_if_fail(pcf8523, -1); return_val_if_fail(t, -1); buf[0] = bin2bcd(t->tm_sec); buf[1] = bin2bcd(t->tm_min); buf[2] = bin2bcd(t->tm_hour); buf[3] = bin2bcd(t->tm_mday); buf[4] = bin2bcd(t->tm_wday); buf[5] = bin2bcd(t->tm_mon); buf[6] = bin2bcd(t->tm_year % 100); pcf8253_write(pcf8523, PCF_REG_SECONDS, buf, 7); return 0; } static void pcf8253_write_byte(pcf8523_t *pcf8523, uint8_t reg, uint8_t data) { HAL_I2C_Mem_Write(pcf8523->hi2c, PCF_I2C_W, reg, I2C_MEMADD_SIZE_8BIT, &data, 1, 10); } static void pcf8253_read_byte(pcf8523_t *pcf8523, uint8_t reg, uint8_t *data) { HAL_I2C_Mem_Read(pcf8523->hi2c, PCF_I2C_R, reg, I2C_MEMADD_SIZE_8BIT, data, 1, 10); } static void pcf8253_write(pcf8523_t *pcf8523, uint8_t reg, uint8_t *data, uint16_t len) { HAL_I2C_Mem_Write(pcf8523->hi2c, PCF_I2C_W, reg, I2C_MEMADD_SIZE_8BIT, data, len, 10); } static void pcf8253_read(pcf8523_t *pcf8523, uint8_t reg, uint8_t *data, uint16_t len) { HAL_I2C_Mem_Read(pcf8523->hi2c, PCF_I2C_R, reg, I2C_MEMADD_SIZE_8BIT, data, len, 10); }