From 4f7ddd09f826e685359c182cc0617a187d1d67e8 Mon Sep 17 00:00:00 2001 From: "Ruslan V. Uss" Date: Sat, 5 Nov 2016 16:12:16 +0600 Subject: [PATCH] DS1302 RTC driver (#258) --- examples/ds1302_test/Makefile | 4 + examples/ds1302_test/main.c | 48 +++++++ extras/ds1302/component.mk | 9 ++ extras/ds1302/ds1302.c | 232 ++++++++++++++++++++++++++++++++++ extras/ds1302/ds1302.h | 84 ++++++++++++ 5 files changed, 377 insertions(+) create mode 100644 examples/ds1302_test/Makefile create mode 100644 examples/ds1302_test/main.c create mode 100644 extras/ds1302/component.mk create mode 100644 extras/ds1302/ds1302.c create mode 100644 extras/ds1302/ds1302.h diff --git a/examples/ds1302_test/Makefile b/examples/ds1302_test/Makefile new file mode 100644 index 0000000..2ba09a7 --- /dev/null +++ b/examples/ds1302_test/Makefile @@ -0,0 +1,4 @@ +PROGRAM = ds1302_test +EXTRA_COMPONENTS = extras/ds1302 +#ESPBAUD = 460800 +include ../../common.mk diff --git a/examples/ds1302_test/main.c b/examples/ds1302_test/main.c new file mode 100644 index 0000000..57bb848 --- /dev/null +++ b/examples/ds1302_test/main.c @@ -0,0 +1,48 @@ +/* + * Example of using DS1302 RTC driver + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * Pavel Merzlyakov + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include + +#define CE_PIN 5 +#define IO_PIN 4 +#define SCLK_PIN 0 + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct tm time = { + .tm_year = 2016, + .tm_mon = 9, + .tm_mday = 31, + .tm_hour = 21, + .tm_min = 54, + .tm_sec = 10 + }; + + ds1302_init(CE_PIN, IO_PIN, SCLK_PIN); + ds1302_set_write_protect(false); + + ds1302_set_time(&time); + ds1302_start(true); + + while (true) + { + ds1302_get_time(&time); + + printf("%04d-%02d-%02d %02d:%02d:%02d\n", time.tm_year, time.tm_mon + 1, + time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); + + for (uint32_t i = 0; i < 1000; i++) + sdk_os_delay_us(500); + } +} diff --git a/extras/ds1302/component.mk b/extras/ds1302/component.mk new file mode 100644 index 0000000..7e5ea70 --- /dev/null +++ b/extras/ds1302/component.mk @@ -0,0 +1,9 @@ +# Component makefile for extras/ds1302 + +# expected anyone using RTC driver includes it as 'ds1302/ds1302.h' +INC_DIRS += $(ds1302_ROOT).. + +# args for passing into compile rule generation +ds1302_SRC_DIR = $(ds1302_ROOT) + +$(eval $(call component_compile_rules,ds1302)) diff --git a/extras/ds1302/ds1302.c b/extras/ds1302/ds1302.c new file mode 100644 index 0000000..2ff80df --- /dev/null +++ b/extras/ds1302/ds1302.c @@ -0,0 +1,232 @@ +/* + * Driver for DS1302 RTC + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss , + * Pavel Merzlyakov + * BSD Licensed as described in the file LICENSE + */ +#include "ds1302.h" +#include +#include + +#define CH_REG 0x80 +#define WP_REG 0x8e + +#define CH_BIT (1 << 7) +#define WP_BIT (1 << 7) +#define HOUR12_BIT (1 << 7) +#define PM_BIT (1 << 5) + +#define CH_MASK ((uint8_t)(~CH_BIT)) +#define WP_MASK ((uint8_t)(~WP_BIT)) + +#define CLOCK_BURST 0xbe +#define RAM_BURST 0xfe + +#define SECONDS_MASK 0x7f +#define HOUR12_MASK 0x1f +#define HOUR24_MASK 0x3f + +static uint8_t _ce_pin; +static uint8_t _io_pin; +static uint8_t _sclk_pin; +static bool _wp; +static uint8_t _ch; + +static uint8_t bcd2dec(uint8_t val) +{ + return (val >> 4) * 10 + (val & 0x0f); +} + +static uint8_t dec2bcd(uint8_t val) +{ + return ((val / 10) << 4) + (val % 10); +} + +inline static void chip_enable() +{ + gpio_write(_ce_pin, true); + sdk_os_delay_us(4); +} + +inline static void chip_disable() +{ + gpio_write(_ce_pin, false); +} + +inline static void prepare(gpio_direction_t dir) +{ + gpio_enable(_io_pin, dir); + gpio_write(_sclk_pin, false); + chip_enable(); +} + +inline static void toggle_clock() +{ + gpio_write(_sclk_pin, true); + sdk_os_delay_us(1); + gpio_write(_sclk_pin, false); + sdk_os_delay_us(1); +} + +static void write_byte(uint8_t b) +{ + for (uint8_t i = 0; i < 8; i++) + { + gpio_write(_io_pin, (b >> i) & 1); + toggle_clock(); + } +} + +static uint8_t read_byte() +{ + uint8_t b = 0; + for (uint8_t i = 0; i < 8; i++) + { + b |= gpio_read(_io_pin) << i; + toggle_clock(); + } + return b; +} + +static uint8_t read_register(uint8_t reg) +{ + prepare(GPIO_OUTPUT); + write_byte(reg | 0x01); + prepare(GPIO_INPUT); + uint8_t res = read_byte(); + chip_disable(); + return res; +} + +static void write_register(uint8_t reg, uint8_t val) +{ + prepare(GPIO_OUTPUT); + write_byte(reg); + write_byte(val); + chip_disable(); +} + +static void burst_read(uint8_t reg, uint8_t *dst, uint8_t len) +{ + prepare(GPIO_OUTPUT); + write_byte(reg | 0x01); + prepare(GPIO_INPUT); + for (uint8_t i = 0; i < len; i++, dst++) + *dst = read_byte(); + chip_disable(); +} + +static void burst_write(uint8_t reg, uint8_t *src, uint8_t len) +{ + prepare(GPIO_OUTPUT); + write_byte(reg); + for (uint8_t i = 0; i < len; i++, src++) + write_byte(*src); + chip_disable(); +} + +inline static void update_register(uint8_t reg, uint8_t mask, uint8_t val) +{ + write_register(reg, (read_register(reg) & mask) | val); +} + +void ds1302_init(uint8_t ce_pin, uint8_t io_pin, uint8_t sclk_pin) +{ + _ce_pin = ce_pin; + _io_pin = io_pin; + _sclk_pin = sclk_pin; + + gpio_enable(_ce_pin, GPIO_OUTPUT); + gpio_enable(_sclk_pin, GPIO_OUTPUT); + + _wp = ds1302_get_write_protect(); + _ch = read_register(CH_REG) & CH_BIT; +} + +bool ds1302_start(bool start) +{ + if (_wp) return false; + + _ch = start ? 0 : CH_BIT; + update_register(CH_REG, CH_MASK, _ch); + + return true; +} + +bool ds1302_is_running() +{ + return !(read_register(CH_REG) & CH_BIT); +} + +void ds1302_set_write_protect(bool protect) +{ + update_register(WP_REG, WP_MASK, protect ? WP_BIT : 0); + _wp = protect; +} + +bool ds1302_get_write_protect() +{ + return (read_register(WP_REG) & WP_BIT) != 0; +} + +void ds1302_get_time(struct tm *time) +{ + uint8_t buf[7]; + burst_read(CLOCK_BURST, buf, 7); + + time->tm_sec = bcd2dec(buf[0] & SECONDS_MASK); + time->tm_min = bcd2dec(buf[1]); + if (buf[2] & HOUR12_BIT) + { + // RTC in 12-hour mode + time->tm_hour = bcd2dec(buf[2] & HOUR12_MASK) - 1; + if (buf[2] & PM_BIT) + time->tm_hour += 12; + } + else time->tm_hour = bcd2dec(buf[2] & HOUR24_MASK); + time->tm_mday = bcd2dec(buf[3]); + time->tm_mon = bcd2dec(buf[4]) - 1; + time->tm_wday = bcd2dec(buf[5]) - 1; + time->tm_year = bcd2dec(buf[6]) + 2000; +} + +bool ds1302_set_time(const struct tm *time) +{ + if (_wp) return false; + + uint8_t buf[8] = { + dec2bcd(time->tm_sec) | _ch, + dec2bcd(time->tm_min), + dec2bcd(time->tm_hour), + dec2bcd(time->tm_mday), + dec2bcd(time->tm_mon + 1), + dec2bcd(time->tm_wday + 1), + dec2bcd(time->tm_year - 2000), + 0 + }; + burst_write(CLOCK_BURST, buf, 8); + + return true; +} + +bool ds1302_read_sram(uint8_t offset, void *buf, uint8_t len) +{ + if (offset + len > DS1302_RAM_SIZE) + return false; + + burst_read(RAM_BURST, (uint8_t *)buf, len); + + return true; +} + +bool ds1302_write_sram(uint8_t offset, void *buf, uint8_t len) +{ + if (offset + len > DS1302_RAM_SIZE) + return false; + + burst_write(RAM_BURST, (uint8_t *)buf, len); + + return true; +} diff --git a/extras/ds1302/ds1302.h b/extras/ds1302/ds1302.h new file mode 100644 index 0000000..4b3c1ba --- /dev/null +++ b/extras/ds1302/ds1302.h @@ -0,0 +1,84 @@ +/* + * Driver for DS1302 RTC + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss , + * Pavel Merlyakov + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_DS1302_H_ +#define EXTRAS_DS1302_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DS1302_RAM_SIZE 31 + +void ds1302_init(uint8_t ce_pin, uint8_t io_pin, uint8_t sclk_pin); + +/** + * \brief Start/stop clock + * \param start Start clock if true + * \return False if RTC is write-protected + */ +bool ds1302_start(bool start); + +/** + * \brief Get current clock state + * \return true if clock running + */ +bool ds1302_is_running(); + +/** + * \brief Enable/disable write protection + * \param protect Set RTC write-protected if true + */ +void ds1302_set_write_protect(bool protect); + +/** + * \brief Get write protection status + * \return true if RTC write-protected + */ +bool ds1302_get_write_protect(); + +/** + * \brief Get current time + * \param time Pointer to the time struct to fill + */ +void ds1302_get_time(struct tm *time); + +/** + * \brief Set time to RTC + * \param time Pointer to the time struct + * \return False if RTC is write-protected + */ +bool ds1302_set_time(const struct tm *time); + +/** + * \brief Read RAM contents into the buffer + * \param offset Start byte, 0..55 + * \param buf Buffer + * \param len Bytes to read, 1..56 + * \return false if error occured (invalid offset or buffer too big) + */ +bool ds1302_read_sram(uint8_t offset, void *buf, uint8_t len); + +/** + * \brief Write buffer to RTC RAM + * \param offset Start byte, 0..55 + * \param buf Buffer + * \param len Bytes to write, 1..56 + * \return false if error occured (invalid offset or buffer too big) + */ +bool ds1302_write_sram(uint8_t offset, void *buf, uint8_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* EXTRAS_DS1302_H_ */