diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 3fbe4fb3..fb5cc0c0 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -33,3 +33,4 @@ add_subdirectory(servo) add_subdirectory(encoder) add_subdirectory(motor) add_subdirectory(vl53l5cx) +add_subdirectory(pcf85063a) diff --git a/drivers/pcf85063a/CMakeLists.txt b/drivers/pcf85063a/CMakeLists.txt new file mode 100644 index 00000000..57240f29 --- /dev/null +++ b/drivers/pcf85063a/CMakeLists.txt @@ -0,0 +1 @@ +include(pcf85063a.cmake) diff --git a/drivers/pcf85063a/pcf85063a.cmake b/drivers/pcf85063a/pcf85063a.cmake new file mode 100644 index 00000000..817e6161 --- /dev/null +++ b/drivers/pcf85063a/pcf85063a.cmake @@ -0,0 +1,10 @@ +set(DRIVER_NAME pcf85063a) +add_library(${DRIVER_NAME} INTERFACE) + +target_sources(${DRIVER_NAME} INTERFACE + ${CMAKE_CURRENT_LIST_DIR}/${DRIVER_NAME}.cpp) + +target_include_directories(${DRIVER_NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}) + +# Pull in pico libraries that we need +target_link_libraries(${DRIVER_NAME} INTERFACE pico_stdlib hardware_i2c hardware_rtc) diff --git a/drivers/pcf85063a/pcf85063a.cpp b/drivers/pcf85063a/pcf85063a.cpp new file mode 100644 index 00000000..64e72940 --- /dev/null +++ b/drivers/pcf85063a/pcf85063a.cpp @@ -0,0 +1,146 @@ +#include "pcf85063a.hpp" + +#include + +#include "hardware/i2c.h" +#include "hardware/rtc.h" + +namespace pimoroni { + + const uint8_t REG_CONTROL_1 = 0x00; + const uint8_t REG_CONTROL_2 = 0x01; + const uint8_t REG_OFFSET = 0x02; + const uint8_t REG_RAM_BYTE = 0x03; + + const uint8_t REG_SECONDS = 0x04; + const uint8_t REG_MINUTES = 0x05; + const uint8_t REG_HOURS = 0x06; + const uint8_t REG_DAYS = 0x07; + const uint8_t REG_WEEKDAYS = 0x08; + const uint8_t REG_MONTHS = 0x09; + const uint8_t REG_YEARS = 0x0a; + + const uint8_t REG_SECOND_ALARM = 0x0b; + const uint8_t REG_MINUTE_ALARM = 0x0c; + const uint8_t REG_HOUR_ALARM = 0x0d; + const uint8_t REG_DAY_ALARM = 0x0e; + const uint8_t REG_WEEKDAY_ALARM = 0x0f; + + const uint8_t REG_TIMER_VALUE = 0x10; + const uint8_t REG_TIMER_MODE = 0x11; + + + void PCF85063A::init() { + // configure i2c interface and pins + i2c_init(i2c, i2c_baud); + + gpio_set_function(sda, GPIO_FUNC_I2C); + gpio_set_function(scl, GPIO_FUNC_I2C); + + 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::configure(uint8_t flags) { + static uint8_t command[2] = {REG_CONTROL_2, flags}; + i2c_write_blocking(i2c, I2C_ADDRESS, command, 2, false); + } + + void PCF85063A::reset() { + static uint8_t command[2] = {REG_CONTROL_1, COMMAND_RESET}; + i2c_write_blocking(i2c, I2C_ADDRESS, command, 2, false); + } + + uint8_t bcd_encode(uint v) { + uint v10 = v / 10; + uint v1 = v - (v10 * 10); + return v1 | (v10 << 4); + } + + int8_t bcd_decode(uint v) { + uint v10 = (v >> 4) & 0x0f; + uint v1 = v & 0x0f; + return v1 + (v10 * 10); + } + + bool PCF85063A::set_datetime(datetime_t *t) { + /*if (!valid_datetime(t)) { + return false; + } */ + + static uint8_t command[8] = {0}; + + command[0] = REG_SECONDS; + command[1] = bcd_encode((uint)t->sec); + command[2] = bcd_encode((uint)t->min); + command[3] = bcd_encode((uint)t->hour); + command[4] = bcd_encode((uint)t->dotw); + command[5] = bcd_encode((uint)t->day); + command[6] = bcd_encode((uint)t->month); + command[7] = bcd_encode((uint)t->year - 2000); + + i2c_write_blocking(i2c, I2C_ADDRESS, command, 8, false); + + return true; + } + + datetime_t PCF85063A::get_datetime() { + static uint8_t result[7] = {0}; + + i2c_write_blocking(i2c, I2C_ADDRESS, ®_SECONDS, 1, false); + i2c_read_blocking(i2c, I2C_ADDRESS, result, 7, false); + + datetime_t dt = { + .year = (int16_t)(bcd_decode(result[6]) + 2000), + .month = (int8_t)bcd_decode(result[5]), + .day = (int8_t)bcd_decode(result[4]), + .dotw = (int8_t)bcd_decode(result[3]), + .hour = (int8_t)bcd_decode(result[2]), + .min = (int8_t)bcd_decode(result[1]), + .sec = (int8_t)bcd_decode(result[0] & 0x7f) + }; + + return dt; + } + + void PCF85063A::set_second_alarm(uint sec) { + uint8_t se = bcd_encode((uint)sec); + se |= 0x80; // enable alarm bit + static uint8_t command[2] = {REG_SECOND_ALARM, se}; + i2c_write_blocking(i2c, I2C_ADDRESS, command, 2, false); + } + + void PCF85063A::reset_timer() { + static uint8_t command[2] = {REG_TIMER_MODE, 0b00000000}; + i2c_write_blocking(i2c, I2C_ADDRESS, command, 2, false); + } + + void PCF85063A::set_seconds_timer(uint8_t sec) { + reset_timer(); + static uint8_t command1[2] = {REG_TIMER_VALUE, sec}; + i2c_write_blocking(i2c, I2C_ADDRESS, command1, 2, false); + + static uint8_t command2[2] = {REG_TIMER_MODE, 0b00010110}; + i2c_write_blocking(i2c, I2C_ADDRESS, command2, 2, false); + } + + i2c_inst_t* PCF85063A::get_i2c() const { + return i2c; + } + + int PCF85063A::get_sda() const { + return sda; + } + + int PCF85063A::get_scl() const { + return scl; + } + + int PCF85063A::get_interrupt() const { + return interrupt; + } + +} \ No newline at end of file diff --git a/drivers/pcf85063a/pcf85063a.hpp b/drivers/pcf85063a/pcf85063a.hpp new file mode 100644 index 00000000..a23661cb --- /dev/null +++ b/drivers/pcf85063a/pcf85063a.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include "hardware/i2c.h" +#include "../../common/pimoroni_common.hpp" + +namespace pimoroni { + + class PCF85063A { + //-------------------------------------------------- + // Constants + //-------------------------------------------------- + public: + static const uint DEFAULT_SDA_PIN = 4; + static const uint DEFAULT_SCL_PIN = 5; + static const uint8_t I2C_ADDRESS = 0x51; + + static const uint8_t COMMAND_RESET = 0b01011000; + + static const uint8_t ALARM_INTERRUPT = 0b10000000; + static const uint8_t CLEAR_ALARM_FLAG = 0b01000000; + static const uint8_t MINUTE_INTERRUPT = 0b00100000; + static const uint8_t HALF_MINUTE_INTERRUPT = 0b00010000; + static const uint8_t TIMER_FLAG = 0b00001000; + + static const uint8_t CLOCK_OUT_32768HZ = 0b00000000; + static const uint8_t CLOCK_OUT_16384HZ = 0b00000001; + static const uint8_t CLOCK_OUT_8192HZ = 0b00000010; + static const uint8_t CLOCK_OUT_4096HZ = 0b00000011; + static const uint8_t CLOCK_OUT_2048HZ = 0b00000100; + static const uint8_t CLOCK_OUT_1024HZ = 0b00000101; + static const uint8_t CLOCK_OUT_1HZ = 0b00000110; + static const uint8_t CLOCK_OUT_OFF = 0b00000111; + + static const uint8_t RTC_STOP = 0b00100000; + static const uint8_t MODE_12_HOUR = 0b00000010; + static const uint8_t MODE_24_HOUR = 0b00000000; + static const uint8_t CAP_7PF = 0b00000000; + static const uint8_t CAP_12_5PF = 0b00000001; + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + private: + // ???? + + public: + // ???? + + private: + i2c_inst_t *i2c = i2c0; + + // interface pins with our standard defaults where appropriate + uint sda = DEFAULT_SDA_PIN; + uint scl = DEFAULT_SCL_PIN; + uint interrupt = PIN_UNUSED; + + uint32_t i2c_baud = 400000; + + //-------------------------------------------------- + // Constructors/Destructor + //-------------------------------------------------- + public: + PCF85063A() {} + + PCF85063A(i2c_inst_t *i2c, uint sda, uint scl, uint interrupt = PIN_UNUSED) : + i2c(i2c), sda(sda), scl(scl), interrupt(interrupt) {} + + //-------------------------------------------------- + // Methods + //-------------------------------------------------- + public: + void init(); + void reset(); + + bool set_datetime(datetime_t *t); + datetime_t get_datetime(); + + void set_second_alarm(uint sec); + void configure(uint8_t flags); + + void reset_timer(); + void set_seconds_timer(uint8_t sec); + + i2c_inst_t* get_i2c() const; + int get_sda() const; + int get_scl() const; + int get_interrupt() const; + }; + +} \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 2fc73111..ad727ad7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory(pico_explorer_encoder) add_subdirectory(pico_motor_shim) add_subdirectory(pico_rgb_keypad) add_subdirectory(pico_rtc_display) +add_subdirectory(pico_rtc) add_subdirectory(pico_tof_display) add_subdirectory(pico_trackball_display) add_subdirectory(pico_audio) diff --git a/examples/pico_rtc/CMakeLists.txt b/examples/pico_rtc/CMakeLists.txt new file mode 100644 index 00000000..8747fdbb --- /dev/null +++ b/examples/pico_rtc/CMakeLists.txt @@ -0,0 +1,14 @@ +set(OUTPUT_NAME rtc) + +add_executable( + ${OUTPUT_NAME} + demo.cpp +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib pcf85063a) + +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/pico_rtc/demo.cpp b/examples/pico_rtc/demo.cpp new file mode 100644 index 00000000..0cf35dfc --- /dev/null +++ b/examples/pico_rtc/demo.cpp @@ -0,0 +1,69 @@ +#include + +#include +#include "pico/stdlib.h" + +#include "pcf85063a.hpp" + +using namespace pimoroni; + +PCF85063A rtc; + +int main() { + // keep the pico awake by holding vsys_en high + gpio_set_function(2, GPIO_FUNC_SIO); + gpio_set_dir(2, GPIO_OUT); + gpio_put(2, true); + + stdio_init_all(); + + printf("woken up!\n"); + + // turn on led to show we're awake + gpio_set_function(6, GPIO_FUNC_SIO); + gpio_set_dir(6, GPIO_OUT); + gpio_put(6, true); + + + rtc.init(); + rtc.configure(PCF85063A::CLEAR_ALARM_FLAG); + // rtc.setup(false); + + + + // Make sure we have 24-hour time (smaller display!) +// if(rtc.is_12_hour()) + //rtc.set_24_hour(); + datetime_t now = { + .year = 2022, .month = 5, .day = 27, + .hour = 12, .min = 29, .sec = 14 + }; + rtc.set_datetime(&now); + + // stay awake for 1 second + sleep_ms(1000); + + // shuold wake up 4 seconds after going to sleep + rtc.set_seconds_timer(3); + + + // printf("going to sleep!\n"); + + // go to sleep by driving vsys_en low + //gpio_put(2, false); + gpio_set_dir(2, GPIO_IN); + +/* + while(true) { + datetime_t dt = rtc.get_datetime(); + printf( + "%04d-%02d-%02d %02d:%02d:%02d\n", + dt.year, dt.month, dt.day, + dt.hour, dt.min, dt.sec + ); + + sleep_ms(1000); + } +*/ + return 0; +}