#include "pcf85063a.hpp" #include #include // binary coded decimal conversion helper functions uint8_t bcd_encode(uint v) { uint v10 = v / 10, v1 = v - (v10 * 10); return v1 | (v10 << 4); } int8_t bcd_decode(uint v) { uint v10 = (v >> 4) & 0x0f, v1 = v & 0x0f; return v1 + (v10 * 10); } namespace pimoroni { void PCF85063A::init() { if(interrupt != PIN_UNUSED) { gpio_set_function(interrupt, GPIO_FUNC_SIO); gpio_set_dir(interrupt, GPIO_IN); gpio_set_pulls(interrupt, false, true); } reset(); } void PCF85063A::reset() { // magic soft reset command i2c->reg_write_uint8(address, Registers::CONTROL_1, 0x58); // read the oscillator status bit until it is cleared uint8_t status = 0x80; while(status & 0x80) { // attempt to clear oscillator stop flag, then read it back i2c->reg_write_uint8(address, Registers::OSCILLATOR_STATUS, 0x00); status = i2c->reg_read_uint8(address, Registers::OSCILLATOR_STATUS); } } // i2c helper methods i2c_inst_t* PCF85063A::get_i2c() const { return i2c->get_i2c(); } int PCF85063A::get_address() const { return address; } int PCF85063A::get_sda() const { return i2c->get_sda(); } int PCF85063A::get_scl() const { return i2c->get_scl(); } int PCF85063A::get_int() const { return interrupt; } datetime_t PCF85063A::get_datetime() { static uint8_t result[7] = {0}; i2c->read_bytes(address, Registers::SECONDS, result, 7); datetime_t dt = { .year = (int16_t)(bcd_decode(result[6]) + 2000), // offset year .month = ( int8_t) bcd_decode(result[5]), .day = ( int8_t) bcd_decode(result[3]), .dotw = ( int8_t) bcd_decode(result[4]), .hour = ( int8_t) bcd_decode(result[2]), .min = ( int8_t) bcd_decode(result[1]), .sec = ( int8_t) bcd_decode(result[0] & 0x7f) // mask out status bit }; return dt; } void PCF85063A::set_datetime(datetime_t *t) { static uint8_t data[7] = { bcd_encode((uint)t->sec), bcd_encode((uint)t->min), bcd_encode((uint)t->hour), bcd_encode((uint)t->day), bcd_encode((uint)t->dotw), bcd_encode((uint)t->month), bcd_encode((uint)t->year - 2000) // offset year }; i2c->write_bytes(address, Registers::SECONDS, data, 7); } void PCF85063A::set_alarm(int second, int minute, int hour, int day) { uint8_t alarm[5] = { uint8_t(second != PARAM_UNUSED ? bcd_encode(second) : 0x80), uint8_t(minute != PARAM_UNUSED ? bcd_encode(minute) : 0x80), uint8_t(hour != PARAM_UNUSED ? bcd_encode( hour) : 0x80), uint8_t(day != PARAM_UNUSED ? bcd_encode( day) : 0x80), uint8_t(0x80) }; i2c->write_bytes(address, Registers::SECOND_ALARM, alarm, 5); } void PCF85063A::set_weekday_alarm( int second, int minute, int hour, DayOfWeek dotw) { uint8_t alarm[5] = { uint8_t(second != PARAM_UNUSED ? bcd_encode(second) : 0x80), uint8_t(minute != PARAM_UNUSED ? bcd_encode(minute) : 0x80), uint8_t(hour != PARAM_UNUSED ? bcd_encode( hour) : 0x80), uint8_t(0x80), uint8_t(dotw != DayOfWeek::NONE ? bcd_encode( dotw) : 0x80) }; i2c->write_bytes(address, Registers::SECOND_ALARM, alarm, 5); } void PCF85063A::enable_alarm_interrupt(bool enable) { uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2); bits = enable ? (bits | 0x80) : (bits & ~0x80); bits |= 0x40; // ensure alarm flag isn't reset i2c->reg_write_uint8(address, Registers::CONTROL_2, bits); } bool PCF85063A::read_alarm_flag() { uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2); return bits & 0x40; } void PCF85063A::clear_alarm_flag() { uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2); bits &= ~0x40; i2c->reg_write_uint8(address, Registers::CONTROL_2, bits); } void PCF85063A::unset_alarm() { uint8_t dummy[5] = {0}; i2c->write_bytes(address, Registers::SECOND_ALARM, dummy, 5); } void PCF85063A::set_timer(uint8_t ticks, TimerTickPeriod ttp) { uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE); uint8_t timer[2] = { ticks, uint8_t((bits & ~0x18) | (ttp << 3) | 0x04) // mask out current ttp and set new + enable }; i2c->write_bytes(address, Registers::TIMER_VALUE, timer, 2); } void PCF85063A::enable_timer_interrupt(bool enable, bool flag_only) { uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE); bits = (bits & ~0x03) | (enable ? 0x02 : 0x00) | (flag_only ? 0x01 : 0x00); i2c->reg_write_uint8(address, Registers::TIMER_MODE, bits); } bool PCF85063A::read_timer_flag() { uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2); return bits & 0x08; } void PCF85063A::clear_timer_flag() { uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2); bits &= ~0x08; i2c->reg_write_uint8(address, Registers::CONTROL_2, bits); } void PCF85063A::unset_timer() { uint8_t bits = i2c->reg_read_uint8(address, Registers::TIMER_MODE); bits &= ~0x04; i2c->reg_write_uint8(address, Registers::TIMER_MODE, bits); } // set the speed of (or turn off) the clock output void PCF85063A::set_clock_output(ClockOut co) { uint8_t bits = i2c->reg_read_uint8(address, Registers::CONTROL_2); bits = (bits & ~0x07) | uint8_t(co); i2c->reg_write_uint8( address, Registers::CONTROL_2, bits); } }