Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

whoops. #477

Merged
merged 1 commit into from
Aug 2, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
376 changes: 187 additions & 189 deletions drivers/pcf85063a/pcf85063a.cpp
Original file line number Diff line number Diff line change
@@ -1,189 +1,187 @@
#include "pcf85063a.hpp"

#include <chrono>
#include <cstdio>

// 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);
}

void PCF85063A::set_byte(uint8_t v) {
i2c->reg_write_uint8(address, Registers::RAM_BYTE, v);
}

uint8_t PCF85063A::get_byte() {
return i2c->reg_read_uint8(address, Registers::RAM_BYTE);
}

}
#include "pcf85063a.hpp"

#include <chrono>
#include <cstdio>

// 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);
}
}

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);
}

void PCF85063A::set_byte(uint8_t v) {
i2c->reg_write_uint8(address, Registers::RAM_BYTE, v);
}

uint8_t PCF85063A::get_byte() {
return i2c->reg_read_uint8(address, Registers::RAM_BYTE);
}

}