From 834f965c9586ec2cbe2e4610fcb50e9a002b44bb Mon Sep 17 00:00:00 2001 From: Antonio Lignan Date: Fri, 11 Dec 2015 12:38:36 +0100 Subject: [PATCH] Added support for the RE-Mote on-board Real Time Clock Calendar (RTCC) --- examples/zolertia/zoul/rtcc/Makefile | 12 + examples/zolertia/zoul/rtcc/project-conf.h | 45 ++ examples/zolertia/zoul/rtcc/test-rtcc.c | 181 +++++ platform/zoul/README.md | 3 +- platform/zoul/remote/Makefile.remote | 2 +- platform/zoul/remote/README.md | 4 +- platform/zoul/remote/rtcc-config.h | 103 +++ platform/zoul/remote/rtcc.c | 868 +++++++++++++++++++++ platform/zoul/remote/rtcc.h | 380 +++++++++ 9 files changed, 1594 insertions(+), 4 deletions(-) create mode 100644 examples/zolertia/zoul/rtcc/Makefile create mode 100644 examples/zolertia/zoul/rtcc/project-conf.h create mode 100644 examples/zolertia/zoul/rtcc/test-rtcc.c create mode 100644 platform/zoul/remote/rtcc-config.h create mode 100644 platform/zoul/remote/rtcc.c create mode 100644 platform/zoul/remote/rtcc.h diff --git a/examples/zolertia/zoul/rtcc/Makefile b/examples/zolertia/zoul/rtcc/Makefile new file mode 100644 index 000000000..25dee77cb --- /dev/null +++ b/examples/zolertia/zoul/rtcc/Makefile @@ -0,0 +1,12 @@ +DEFINES+=PROJECT_CONF_H=\"project-conf.h\" +CONTIKI_PROJECT = test-rtcc + +TARGET = zoul + +# Works in Linux and probably on OSX too (RTCC example) +CFLAGS = -DDATE="\"`date +"%02u %02d %02m %02y %02H %02M %02S"`\"" + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/zolertia/zoul/rtcc/project-conf.h b/examples/zolertia/zoul/rtcc/project-conf.h new file mode 100644 index 000000000..9ff57c953 --- /dev/null +++ b/examples/zolertia/zoul/rtcc/project-conf.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012, Texas Instruments Incorporated - http://www.ti.com/ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \addtogroup remote-examples + * @{ + * + * \file + * Project specific configuration defines for the basic RE-Mote examples + */ +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ + +#define NETSTACK_CONF_RDC nullrdc_driver + +#endif /* PROJECT_CONF_H_ */ + +/** @} */ diff --git a/examples/zolertia/zoul/rtcc/test-rtcc.c b/examples/zolertia/zoul/rtcc/test-rtcc.c new file mode 100644 index 000000000..cb14d5fa5 --- /dev/null +++ b/examples/zolertia/zoul/rtcc/test-rtcc.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015, Zolertia - http://www.zolertia.com + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup remote-examples + * @{ + + * \defgroup remote-rtcc-test RE-Mote on-board RTCC test application + * + * Example project to show the on-board RTCC configuration and operation + * Retrieves the current time and date from the system, then sets an alarm to + * trigger every TEST_ALARM_SECOND match, generating an interrupt event and + * printing the current time/date, toggling also the LEDs + * + * @{ + * + * \file + * RE-Mote on-board RTCC test application + * + * \author + * + * Antonio Lignan + * Aitor Mejias + * Toni Lozano + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "rtcc.h" +#include "dev/i2c.h" +#include "dev/leds.h" +#include +#include +/*---------------------------------------------------------------------------*/ +#ifndef DATE +#define DATE "Unknown" +#endif +/*---------------------------------------------------------------------------*/ +#define LOOP_PERIOD 60L +#define LOOP_INTERVAL (CLOCK_SECOND * LOOP_PERIOD) +#define TEST_ALARM_SECOND 30 +/*---------------------------------------------------------------------------*/ +PROCESS(test_remote_rtcc_process, "Test RTC driver process"); +AUTOSTART_PROCESSES(&test_remote_rtcc_process); +/*---------------------------------------------------------------------------*/ +static uint8_t rtc_buffer[sizeof(simple_td_map)]; +static simple_td_map *simple_td = (simple_td_map *)rtc_buffer; +/*---------------------------------------------------------------------------*/ +static struct etimer et; +/*---------------------------------------------------------------------------*/ +void +rtcc_interrupt_callback(uint8_t value) +{ + printf("A RTCC interrupt just happened! time/date: "); + rtcc_print(RTCC_PRINT_DATE_DEC); + leds_toggle(LEDS_PURPLE); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(test_remote_rtcc_process, ev, data) +{ + static char *next; + + PROCESS_BEGIN(); + + /* Alternatively for test only, undefine DATE and define on your own as + * #define DATE "07 06 12 15 16 00 00" + * Also note that if you restart the node at a given time, it will use the + * already defined DATE, so if you want to update the device date/time you + * need to reflash the node + */ + + /* Get the system date in the following format: wd dd mm yy hh mm ss */ + printf("RE-Mote RTC test, system date: %s\n", DATE); + + /* Sanity check */ + if(strcmp("Unknown", DATE) == 0) { + printf("Fail: could not retrieve date from system\n"); + PROCESS_EXIT(); + } + + /* Configure RTC and return structure with all parameters */ + rtcc_init(); + + /* Map interrupt callback handler */ + RTCC_REGISTER_INT1(rtcc_interrupt_callback); + + /* Configure the RTC with the current values */ + simple_td->weekdays = (uint8_t)strtol(DATE, &next, 10); + simple_td->day = (uint8_t)strtol(next, &next, 10); + simple_td->months = (uint8_t)strtol(next, &next, 10); + simple_td->years = (uint8_t)strtol(next, &next, 10); + simple_td->hours = (uint8_t)strtol(next, &next, 10); + simple_td->minutes = (uint8_t)strtol(next, &next, 10); + simple_td->seconds = (uint8_t)strtol(next, NULL, 10); + + /* Don't care about the milliseconds... */ + simple_td->miliseconds = 0; + + /* This example relies on 24h mode */ + simple_td->mode = RTCC_24H_MODE; + + /* And to simplify the configuration, it relies it will be executed in the + * present century + */ + simple_td->century = RTCC_CENTURY_20XX; + + /* Set the time and date */ + if(rtcc_set_time_date(simple_td) == AB08_ERROR) { + printf("Fail: Time and date not configured\n"); + PROCESS_EXIT(); + } + + /* Wait a bit */ + etimer_set(&et, (CLOCK_SECOND * 2)); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + + /* Retrieve the configured time and date, this doesn't overwrites the + * mode and century values + */ + if(rtcc_get_time_date(simple_td) == AB08_ERROR) { + printf("Fail: Couldn't read time and date\n"); + PROCESS_EXIT(); + } + + /* ...or for visualization only, just print the date directly from the RTCC */ + printf("Configured time: "); + rtcc_print(RTCC_PRINT_DATE_DEC); + + /* Configure the RTCC to trigger an alarm every TEST_ALARM_SECOND tick */ + printf("Setting an alarm to tick every minute matching %u seconds\n", + TEST_ALARM_SECOND); + simple_td->seconds = TEST_ALARM_SECOND; + + /* Notice the arguments, we want to trigger the alarm every time the clock + * matches the seconds values, so the alarm would have to be repeated every + * minute. In case we would want to trigger the alarm on a specific time, + * then we would want to set a daily repeat interval + */ + if(rtcc_set_alarm_time_date(simple_td, RTCC_ALARM_ON, + RTCC_REPEAT_MINUTE) == AB08_ERROR) { + printf("Fail: couldn't set the alarm\n"); + PROCESS_EXIT(); + } + + printf("Alarm set to match: "); + rtcc_print(RTCC_PRINT_ALARM_DEC); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ + diff --git a/platform/zoul/README.md b/platform/zoul/README.md index 385fe06f1..ee34a2f56 100644 --- a/platform/zoul/README.md +++ b/platform/zoul/README.md @@ -59,6 +59,7 @@ In terms of hardware support, the following drivers have been implemented for th * Buttons * Built-in core temperature and battery sensor. * CC1200 sub-1GHz radio interface. + * Real Time Clock Calendar (on the RE-Mote platform). There is a Zoul powering the RE-Mote and Firefly platforms, check out its specific README files for more information about on-board features. @@ -218,7 +219,7 @@ LPM is highly related to the operations of the Radio Duty Cycling (RDC) driver o * When NullRDC is in use, the radio will be always on. As a result, the algorithm discussed above will always choose PM0 and will never attempt to drop to PM1/2. ### Shutdown Mode -The RE-Mote has a built-in shutdown mode which effectively reduces the power consumption down to 300nA. Check its specific README file for more information. +The RE-Mote has a built-in shutdown mode which effectively reduces the power consumption down to 150nA. Check its specific README file for more information. Build headless nodes -------------------- diff --git a/platform/zoul/remote/Makefile.remote b/platform/zoul/remote/Makefile.remote index 92801099e..b7c469840 100644 --- a/platform/zoul/remote/Makefile.remote +++ b/platform/zoul/remote/Makefile.remote @@ -1,2 +1,2 @@ MOTELIST_ZOLERTIA = remote -BOARD_SOURCEFILES += board.c antenna-sw.c +BOARD_SOURCEFILES += board.c antenna-sw.c rtcc.c diff --git a/platform/zoul/remote/README.md b/platform/zoul/remote/README.md index e33a6b207..c1f57a1fd 100644 --- a/platform/zoul/remote/README.md +++ b/platform/zoul/remote/README.md @@ -23,13 +23,13 @@ The RE-Mote features a Zoul as its core module and it is bundled with the follow * Power input with wide range 3.7-26VDC. * On-board micro USB connector for USB 2.0 applications. * RGB LED to allow more than 7 colour combinations. -* On-board nano-watt RTC. +* On-board nano-watt Real Time Clock Calendar (RTCC). * User and Reset buttons. * On-board Micro-SD for external storage. * On-board external Watchdog Timer (WDT) for resilient operation. * Small form-factor of 73x40 mm. -The most prominent feature of the RE-Mote is its ultra low-power implementation, allowing a flexible and time/date-aware control of the platform operation modes by introducing a real-time clock (RTC), nanowatt external timer, ultra-low power PIC governing the battery manager, etc. +The most prominent feature of the RE-Mote is its ultra low-power implementation, allowing a flexible and time/date-aware control of the platform operation modes by introducing a real-time clock (RTCC), nanowatt external timer, ultra-low power PIC governing the battery manager, etc. The RE-Mote features an optional custom-made enclosure to fit most scenarios, allowing to easily connect sensors, actuators and rechargeable LiPo batteries. Its on-board RP-SMA antenna eliminates the need to mechanize an external antenna, allowing to alternatively use either a sub-1GHz or 2.4GHz antenna, or a multiband one. diff --git a/platform/zoul/remote/rtcc-config.h b/platform/zoul/remote/rtcc-config.h new file mode 100644 index 000000000..de39d5ec8 --- /dev/null +++ b/platform/zoul/remote/rtcc-config.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2015, Zolertia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ +/* -------------------------------------------------------------------------- */ +/** + * \addtogroup remote-rtcc + * @{ + * + * Driver for the RE-Mote RTCC (Real Time Clock Calendar) + * @{ + * + * \file + * RTCC configuration file + * + * \author + * + * Antonio Lignan + * Aitor Mejias + * Toni Lozano + */ +/* -------------------------------------------------------------------------- */ +#ifndef RTCC_CONFIG_H_ +#define RTCC_CONFIG_H_ +/* -------------------------------------------------------------------------- */ +#include "rtcc.h" +/* -------------------------------------------------------------------------- */ +/** + * \name RTCC configuration macros + * @{ + */ +#define RTCC_SET_DEFAULT_CONFIG 1 +#define RTCC_CLEAR_INT_MANUALLY 1 +#define RTCC_SET_AUTOCAL 1 +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name RTCC default configuration (if enabled by RTCC_SET_DEFAULT_CONFIG) + * @{ + */ +/* Reset values from the Application Manual */ +#define RTCC_DEFAULT_STATUS 0x00 +#define RTCC_DEFAULT_CTRL1 0x11 +#define RTCC_DEFAULT_CTRL2 0x00 +#define RTCC_DEFAULT_INTMASK 0xE0 +#define RTCC_DEFAULT_SQW 0x26 +#define RTCC_DEFAULT_TIMER_CTRL 0x23 +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name RTCC default configuration structure + * @{ + */ +typedef struct ab080x_register_config { + uint8_t reg; + uint8_t val; +} ab080x_register_config_t; +/* -------------------------------------------------------------------------- */ +static const ab080x_register_config_t ab080x_default_setting[] = +{ + { (CONFIG_MAP_OFFSET + STATUS_ADDR), RTCC_DEFAULT_STATUS }, + { (CONFIG_MAP_OFFSET + CTRL_1_ADDR), RTCC_DEFAULT_CTRL1 }, + { (CONFIG_MAP_OFFSET + CTRL_2_ADDR), RTCC_DEFAULT_CTRL2 }, + { (CONFIG_MAP_OFFSET + INT_MASK_ADDR), RTCC_DEFAULT_INTMASK }, + { (CONFIG_MAP_OFFSET + SQW_ADDR), RTCC_DEFAULT_SQW }, + { (CONFIG_MAP_OFFSET + TIMER_CONTROL_ADDR), RTCC_DEFAULT_TIMER_CTRL }, +}; +/** @} */ +/* -------------------------------------------------------------------------- */ +#endif /* ifndef RTCC_CONFIG_H_ */ +/* -------------------------------------------------------------------------- */ +/** + * @} + * @} + */ + diff --git a/platform/zoul/remote/rtcc.c b/platform/zoul/remote/rtcc.c new file mode 100644 index 000000000..e27bdc555 --- /dev/null +++ b/platform/zoul/remote/rtcc.c @@ -0,0 +1,868 @@ +/* + * Copyright (c) 2015, Zolertia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup remote-rtcc + * @{ + * + * Driver for the RE-Mote RTCC (Real Time Clock Calendar) + * @{ + * + * \file + * Driver for the RE-Mote RF Real Time Clock Calendar (RTCC) + * + * \author + * + * Antonio Lignan + * Aitor Mejias + * Toni Lozano + */ +/*---------------------------------------------------------------------------*/ +#include "contiki.h" +#include "dev/gpio.h" +#include "dev/i2c.h" +#include "rtcc.h" +#include "rtcc-config.h" +#include "dev/leds.h" +#include +/*---------------------------------------------------------------------------*/ +#define DEBUG 0 +#if DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif +/*---------------------------------------------------------------------------*/ +#define RTC_INT1_PORT_BASE GPIO_PORT_TO_BASE(RTC_INT1_PORT) +#define RTC_INT1_PIN_MASK GPIO_PIN_MASK(RTC_INT1_PIN) +/*---------------------------------------------------------------------------*/ +/* Callback pointers when interrupt occurs */ +void (*rtcc_int1_callback)(uint8_t value); +/* -------------------------------------------------------------------------- */ +static const char *ab080x_td_register_name[] = +{ + "Mseconds", + "Seconds", + "Minutes", + "Hours", + "Days", + "Months", + "Years", + "Weekdays", +}; +/* -------------------------------------------------------------------------- */ +static const char *ab080x_config_register_name[] = +{ + "STATUS", + "CTRL1", + "CTRL2", + "INTMASK", + "SQW", + "CAL_XT", + "CAL_RCU", + "CAL_RCL", + "INTPOL", + "TIMER_CTRL", + "TIMER_CDOWN", + "TIMER_INIT", + "WDT", + "OSC_CTRL", + "OSC_STAT", + "CONF_KEY", + "TRICKLE", + "BREF", +}; +/*---------------------------------------------------------------------------*/ +static uint8_t +bcd_to_dec(uint8_t val) +{ + return (uint8_t)(((val >> 4) * 10) + (val % 16)); +} +/*---------------------------------------------------------------------------*/ +static uint8_t +dec_to_bcd(uint8_t val) +{ + return (uint8_t)(((val / 10) << 4) + (val % 10)); +} +/*---------------------------------------------------------------------------*/ +static uint8_t +check_leap_year(uint8_t val) +{ + return ((val % 4) && (val % 100)) || (val % 400); +} +/*---------------------------------------------------------------------------*/ +static uint16_t +ab08_read_reg(uint8_t reg, uint8_t *buf, uint8_t regnum) +{ + i2c_master_enable(); + if(i2c_single_send(AB08XX_ADDR, reg) == I2C_MASTER_ERR_NONE) { + if(i2c_burst_receive(AB08XX_ADDR, buf, regnum) == I2C_MASTER_ERR_NONE) { + return AB08_SUCCESS; + } + } + return AB08_ERROR; +} +/*---------------------------------------------------------------------------*/ +static int8_t +ab08_write_reg(uint8_t reg, uint8_t *buf, uint8_t regnum) +{ + uint8_t i, buff[INT_BUFF_SIZE]; + + if(regnum > (INT_BUFF_SIZE - 1)) { + return AB08_ERROR; + } + + /* FIXME: Replace by single_send/burst_send */ + + buff[0] = reg; + for(i = 0; i < regnum; i++) { + buff[(i + 1)] = buf[i]; + } + + i2c_master_enable(); + if(i2c_burst_send(AB08XX_ADDR, buff, (regnum + 1)) == I2C_MASTER_ERR_NONE) { + return AB08_SUCCESS; + } + + return AB08_ERROR; +} +/*---------------------------------------------------------------------------*/ +static void +write_default_config(void) +{ + const ab080x_register_config_t *settings; + settings = ab080x_default_setting; + uint8_t i, len = (sizeof(ab080x_default_setting) / sizeof(ab080x_register_config_t)); + + for(i = 0; i < len; i++) { + ab08_write_reg(settings[i].reg, (uint8_t *)&settings[i].val, 1); + } +} +/*---------------------------------------------------------------------------*/ +static int8_t +ab08_key_reg(uint8_t unlock) +{ + if((unlock != RTCC_CONFKEY_OSCONTROL) && (unlock != RTCC_CONFKEY_SWRESET) && + (unlock != RTCC_CONFKEY_DEFREGS)) { + PRINTF("RTC: invalid confkey values\n"); + return AB08_ERROR; + } + + if(ab08_write_reg((CONFIG_MAP_OFFSET + CONF_KEY_ADDR), &unlock, 1)) { + PRINTF("RTC: failed to write to confkey register\n"); + return AB08_ERROR; + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +static int8_t +ab08_read_status(uint8_t *buf) +{ + return ab08_read_reg((STATUS_ADDR + CONFIG_MAP_OFFSET), buf, 1); +} +/*---------------------------------------------------------------------------*/ +static int8_t +ab08_ctrl1_config(uint8_t cmd) +{ + uint8_t ctrl1 = 0; + + if(cmd >= RTCC_CMD_MAX) { + return AB08_ERROR; + } + + if(ab08_read_reg((CONFIG_MAP_OFFSET + CTRL_1_ADDR), &ctrl1, 1)) { + PRINTF("RTC: failed to retrieve CTRL1 register\n"); + return AB08_ERROR; + } + + switch(cmd) { + case RTCC_CMD_LOCK: + ctrl1 &= ~CTRL1_WRTC; + break; + case RTCC_CMD_UNLOCK: + ctrl1 |= CTRL1_WRTC; + break; + case RTCC_CMD_ENABLE: + ctrl1 &= ~CTRL1_STOP; + break; + case RTCC_CMD_STOP: + ctrl1 |= CTRL1_STOP; + break; + default: + return AB08_ERROR; + } + + if(ab08_write_reg((CONFIG_MAP_OFFSET + CTRL_1_ADDR), + &ctrl1, 1) == AB08_ERROR) { + PRINTF("RTC: failed to write to the CTRL1 register\n"); + return AB08_ERROR; + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +static int8_t +ab08_check_td_format(simple_td_map *data, uint8_t alarm_state) +{ + /* Using fixed values as these are self-indicative of the variable */ + if((data->seconds > 59) || (data->minutes > 59) || (data->hours > 23)) { + return AB08_ERROR; + } + + if((data->months > 12) || (data->weekdays > 7) || (data->day > 31)) { + return AB08_ERROR; + } + + /* Fixed condition for February (month 2) */ + if(data->months == 2) { + if(check_leap_year(data->years)) { + if(data->day > 29) { + return AB08_ERROR; + } + } else { + if(data->day > 28) { + return AB08_ERROR; + } + } + } + + /* Alarm doesn't care about year */ + if(!alarm_state) { + /* AB08X5 Real-Time Clock Family, page 55 (year up to 2199) */ + if(data->years > 199) { + return AB08_ERROR; + } + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_set_time_date(simple_td_map *data) +{ + uint8_t aux = 0; + uint8_t rtc_buffer[RTCC_TD_MAP_SIZE]; + + if(ab08_check_td_format(data, 0) == AB08_ERROR) { + PRINTF("RTC: Invalid time/date values\n"); + return AB08_ERROR; + } + + if(ab08_read_reg((CTRL_1_ADDR + CONFIG_MAP_OFFSET), + &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve CONTROL1 register\n"); + return AB08_ERROR; + } + + rtc_buffer[WEEKDAYLS_ADDR] = dec_to_bcd(data->weekdays); + rtc_buffer[YEAR_ADDR] = dec_to_bcd(data->years); + rtc_buffer[MONTHS_ADDR] = dec_to_bcd(data->months); + rtc_buffer[DAY_ADDR] = dec_to_bcd(data->day); + rtc_buffer[HOUR_ADDR] = dec_to_bcd(data->hours); + rtc_buffer[MIN_ADDR] = dec_to_bcd(data->minutes); + rtc_buffer[SEC_ADDR] = dec_to_bcd(data->seconds); + rtc_buffer[CENTHS_ADDR] = dec_to_bcd(data->miliseconds); + + /* Check if we are to set the time in 12h/24h format */ + if(data->mode == RTCC_24H_MODE) { + aux &= ~CTRL1_1224; + } else { + if((data->hours == 0) || (data->hours > 12)) { + PRINTF("RTC: Invalid hour configuration (12h mode selected)\n"); + return AB08_ERROR; + } + aux |= CTRL1_1224; + if(data->mode == RTCC_12H_MODE_PM) { + /* Toggle bit for PM */ + rtc_buffer[HOUR_ADDR] |= RTCC_TOGGLE_PM_BIT; + } else { + PRINTF("RTC: Invalid time mode selected\n"); + return AB08_ERROR; + } + } + + /* Write the 12h/24h config */ + if(ab08_write_reg((CTRL_1_ADDR + CONFIG_MAP_OFFSET), + &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to write 12h/24h configuration\n"); + return AB08_ERROR; + } + + /* Reading the STATUS register with the CONTROL1.ARST set will clear the + * interrupt flags, we write directly to the register without caring its + * actual status and let the interrupt handler take care of any pending flag + */ + + if(ab08_read_reg((STATUS_ADDR + CONFIG_MAP_OFFSET), &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve STATUS register\n"); + return AB08_ERROR; + } + + if(data->century == RTCC_CENTURY_20XX) { + aux |= STATUS_CB; + } else if(data->century == RTCC_CENTURY_19XX_21XX) { + aux |= ~STATUS_CB; + } else { + PRINTF("RTC: invalid century value\n"); + return AB08_ERROR; + } + + PRINTF("RTC: current STATUS value 0x%02X\n", aux); + + if(ab08_write_reg((STATUS_ADDR + CONFIG_MAP_OFFSET), &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to write century to STATUS register\n"); + return AB08_ERROR; + } + + /* Set the WRTC bit to enable writting to the counters */ + if(ab08_ctrl1_config(RTCC_CMD_UNLOCK) == AB08_ERROR) { + return AB08_ERROR; + } + + /* Write the buffers but the mode and century fields (used only for config) */ + if(ab08_write_reg(CENTHS_ADDR, rtc_buffer, + RTCC_TD_MAP_SIZE) == AB08_ERROR) { + PRINTF("RTC: failed to write date configuration\n"); + return AB08_ERROR; + } + + /* Lock the RTCC and return */ + if(ab08_ctrl1_config(RTCC_CMD_LOCK) == AB08_ERROR) { + return AB08_ERROR; + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_get_time_date(simple_td_map *data) +{ + uint8_t rtc_buffer[RTCC_TD_MAP_SIZE]; + + if(ab08_read_reg(CENTHS_ADDR, rtc_buffer, + RTCC_TD_MAP_SIZE) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve date and time values\n"); + return AB08_ERROR; + } + + data->weekdays = bcd_to_dec(rtc_buffer[WEEKDAYLS_ADDR]); + data->years = bcd_to_dec(rtc_buffer[YEAR_ADDR]); + data->months = bcd_to_dec(rtc_buffer[MONTHS_ADDR]); + data->day = bcd_to_dec(rtc_buffer[DAY_ADDR]); + data->hours = bcd_to_dec(rtc_buffer[HOUR_ADDR]); + data->minutes = bcd_to_dec(rtc_buffer[MIN_ADDR]); + data->seconds = bcd_to_dec(rtc_buffer[SEC_ADDR]); + data->miliseconds = bcd_to_dec(rtc_buffer[CENTHS_ADDR]); + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_set_alarm_time_date(simple_td_map *data, uint8_t state, uint8_t repeat) +{ + uint8_t aux[4], buf[RTCC_ALARM_MAP_SIZE]; + + if(state == RTCC_ALARM_OFF) { + if(ab08_read_reg((INT_MASK_ADDR + CONFIG_MAP_OFFSET), + &aux[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve INTMASK register\n"); + return AB08_ERROR; + } + + aux[0] &= ~INTMASK_AIE; + + if(ab08_write_reg((INT_MASK_ADDR + CONFIG_MAP_OFFSET), + &aux[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the alarm config\n"); + return AB08_ERROR; + } + return AB08_SUCCESS; + } + + if((data == NULL) || (ab08_check_td_format(data, 1) == AB08_ERROR)) { + PRINTF("RTC: invalid alarm values\n"); + return AB08_ERROR; + } + + if((state >= RTCC_ALARM_MAX) || (repeat >= RTCC_REPEAT_100THS)) { + PRINTF("RTC: invalid alarm config type or state\n"); + return AB08_ERROR; + } + + /* Stop the RTCC */ + ab08_ctrl1_config(RTCC_CMD_STOP); + + buf[WEEKDAYS_ALARM_ADDR] = dec_to_bcd(data->weekdays); + buf[MONTHS_ALARM_ADDR] = dec_to_bcd(data->months); + buf[DAY_ALARMS_ADDR] = dec_to_bcd(data->day); + buf[HOURS_ALARM_ADDR] = dec_to_bcd(data->hours); + buf[MINUTES_ALARM_ADDR] = dec_to_bcd(data->minutes); + buf[SECONDS_ALARM_ADDR] = dec_to_bcd(data->seconds); + buf[HUNDREDTHS_ALARM_ADDR] = dec_to_bcd(data->miliseconds); + + /* Check if the 12h/24h match the current configuration */ + if(ab08_read_reg((CTRL_1_ADDR + CONFIG_MAP_OFFSET), + &aux[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve CONTROL1 register\n"); + return AB08_ERROR; + } + + if(((aux[0] & CTRL1_1224) && (data->mode == RTCC_24H_MODE)) || + (!(aux[0] & CTRL1_1224) && ((data->mode == RTCC_12H_MODE_AM) || + (data->mode == RTCC_12H_MODE_PM)))) { + PRINTF("RTC: 12/24h mode and present date config mismatch\n"); + return AB08_ERROR; + } + + if(data->mode != RTCC_24H_MODE) { + if((data->hours == 0) || (data->hours > 12)) { + PRINTF("RTC: Invalid hour configuration (12h mode selected)\n"); + return AB08_ERROR; + } + + /* Toggle the PM bit */ + if(data->mode == RTCC_12H_MODE_PM) { + buf[HOURS_ALARM_ADDR] |= RTCC_TOGGLE_PM_BIT; + } + } + + /* Clear the RPT field */ + if(ab08_read_reg((TIMER_CONTROL_ADDR + CONFIG_MAP_OFFSET), + &aux[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve TIMER CTRL register\n"); + return AB08_ERROR; + } + + aux[0] &= ~COUNTDOWN_TIMER_RPT_SECOND; + + /* AB08XX application manual, table 76 */ + if(repeat == RTCC_REPEAT_10THS) { + buf[HUNDREDTHS_ALARM_ADDR] |= RTCC_FIX_10THS_HUNDRETHS; + repeat = RTCC_REPEAT_SECOND; + } else if(repeat == RTCC_REPEAT_100THS) { + buf[HUNDREDTHS_ALARM_ADDR] |= RTCC_FIX_100THS_HUNDRETHS; + repeat = RTCC_REPEAT_SECOND; + } + + if(repeat != RTCC_REPEAT_NONE) { + aux[0] |= (repeat << COUNTDOWN_TIMER_RPT_SHIFT); + } + + /* We are using as default the level interrupt instead of pulses */ + /* FIXME: make this selectable */ + aux[0] |= COUNTDOWN_TIMER_TM; + aux[0] &= ~COUNTDOWN_TIMER_TRPT; + + if(ab08_write_reg((TIMER_CONTROL_ADDR + CONFIG_MAP_OFFSET), + &aux[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the alarm config\n"); + return AB08_ERROR; + } + + if(ab08_read_reg((STATUS_ADDR + CONFIG_MAP_OFFSET), + aux, 4) == AB08_ERROR) { + PRINTF("RTC: failed to read configuration registers\n"); + return AB08_ERROR; + } + + /* Clear ALM field if any */ + aux[STATUS_ADDR] &= ~STATUS_ALM; + +#if RTCC_CLEAR_INT_MANUALLY + aux[CTRL_1_ADDR] &= ~CTRL1_ARST; +#endif + + /* Clear the AIE alarm bit */ + aux[INT_MASK_ADDR] &= ~INTMASK_AIE; + + /* Configure Interrupt parameters for Alarm Interrupt Mode in nIRQ pin, + * and fixed level until interrupt flag is cleared + */ + + /* Enable nIRQ if at least one interrupt is enabled */ + aux[CTRL_2_ADDR] |= CTRL2_OUT1S_NIRQ_NAIRQ_OUT; + + if(repeat != RTCC_REPEAT_NONE) { + aux[INT_MASK_ADDR] &= ~INTMASK_IM_LOW; + } else { + aux[INT_MASK_ADDR] |= INTMASK_IM_LOW; + } + + if(ab08_write_reg((STATUS_ADDR + CONFIG_MAP_OFFSET), aux, 4) == AB08_ERROR) { + PRINTF("RTC: failed to clear alarm config\n"); + return AB08_ERROR; + } + + /* Enable interrupts */ + GPIO_ENABLE_INTERRUPT(RTC_INT1_PORT_BASE, RTC_INT1_PIN_MASK); + ioc_set_over(RTC_INT1_PORT, RTC_INT1_PIN, IOC_OVERRIDE_PUE); + nvic_interrupt_enable(RTC_INT1_VECTOR); + + /* Write to the alarm counters */ + if(ab08_write_reg((HUNDREDTHS_ALARM_ADDR + ALARM_MAP_OFFSET), buf, + RTCC_ALARM_MAP_SIZE) == AB08_ERROR) { + PRINTF("RTC: failed to set the alarm\n"); + return AB08_ERROR; + } + + /* And finally enable the AIE bit */ + aux[INT_MASK_ADDR] |= INTMASK_AIE; + if(ab08_write_reg((INT_MASK_ADDR + CONFIG_MAP_OFFSET), + &aux[INT_MASK_ADDR], 1) == AB08_ERROR) { + PRINTF("RTC: failed to enable the alarm\n"); + return AB08_ERROR; + } + + /* Enable back the RTCC */ + ab08_ctrl1_config(RTCC_CMD_ENABLE); + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +PROCESS(rtcc_int_process, "RTCC interruption process handler"); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(rtcc_int_process, ev, data) +{ + static uint8_t buf; + PROCESS_EXITHANDLER(); + PROCESS_BEGIN(); + while(1) { + PROCESS_YIELD_UNTIL(ev == PROCESS_EVENT_POLL); + + if(ab08_read_status(&buf) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve ARST value\n"); + PROCESS_EXIT(); + } + + /* We only handle the AIE (alarm interrupt) only */ + if((buf & STATUS_ALM) && (rtcc_int1_callback != NULL)) { +#if RTCC_CLEAR_INT_MANUALLY + buf &= ~STATUS_ALM; + if(ab08_write_reg((STATUS_ADDR + CONFIG_MAP_OFFSET), + &buf, 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the alarm\n"); + return AB08_ERROR; + } +#endif + rtcc_int1_callback(0); + } + } + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_print(uint8_t value) +{ + uint8_t i, len, reg; + char **name; + uint8_t rtc_buffer[RTCC_CONFIG_MAP_SIZE]; + + if(value >= RTCC_PRINT_MAX) { + return AB08_ERROR; + } + + switch(value) { + case RTCC_PRINT_CONFIG: + len = (RTCC_CONFIG_MAP_SIZE - 1); + reg = STATUS_ADDR + CONFIG_MAP_OFFSET; + name = (char **)ab080x_config_register_name; + break; + case RTCC_PRINT_ALARM: + case RTCC_PRINT_ALARM_DEC: + len = RTCC_ALARM_MAP_SIZE; + reg = HUNDREDTHS_ALARM_ADDR + ALARM_MAP_OFFSET; + name = (char **)ab080x_td_register_name; + break; + case RTCC_PRINT_DATE: + case RTCC_PRINT_DATE_DEC: + len = RTCC_TD_MAP_SIZE; + reg = CENTHS_ADDR; + name = (char **)ab080x_td_register_name; + break; + default: + return AB08_ERROR; + } + + if(ab08_read_reg(reg, rtc_buffer, len) == AB08_ERROR) { + PRINTF("RTC: failed to retrieve values to print\n"); + return AB08_ERROR; + } + + if(value == RTCC_PRINT_ALARM_DEC) { + printf("%02u/%02u (%02u) %02u:%02u:%02u/%02u\n", + bcd_to_dec(rtc_buffer[MONTHS_ALARM_ADDR]), + bcd_to_dec(rtc_buffer[DAY_ALARMS_ADDR]), + bcd_to_dec(rtc_buffer[WEEKDAYS_ALARM_ADDR]), + bcd_to_dec(rtc_buffer[HOURS_ALARM_ADDR]), + bcd_to_dec(rtc_buffer[MINUTES_ALARM_ADDR]), + bcd_to_dec(rtc_buffer[SECONDS_ALARM_ADDR]), + bcd_to_dec(rtc_buffer[HUNDREDTHS_ALARM_ADDR])); + return AB08_SUCCESS; + } + + if(value == RTCC_PRINT_DATE_DEC) { + printf("%02u/%02u/%02u (%02u) %02u:%02u:%02u/%02u\n", + bcd_to_dec(rtc_buffer[YEAR_ADDR]), + bcd_to_dec(rtc_buffer[MONTHS_ADDR]), + bcd_to_dec(rtc_buffer[DAY_ADDR]), + bcd_to_dec(rtc_buffer[WEEKDAYLS_ADDR]), + bcd_to_dec(rtc_buffer[HOUR_ADDR]), + bcd_to_dec(rtc_buffer[MIN_ADDR]), + bcd_to_dec(rtc_buffer[SEC_ADDR]), + bcd_to_dec(rtc_buffer[CENTHS_ADDR])); + return AB08_SUCCESS; + } + + for(i = 0; i < len; i++) { + printf("0x%02X <- %s\n", rtc_buffer[i], name[i]); + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +static void +rtcc_interrupt_handler(uint8_t port, uint8_t pin) +{ + process_poll(&rtcc_int_process); +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_set_autocalibration(uint8_t period) +{ + uint8_t aux; + + if(period > RTCC_AUTOCAL_9_MIN) { + PRINTF("RTC: invalid autocal value\n"); + return AB08_ERROR; + } + + if(ab08_read_reg((OSC_CONTROL_ADDR + CONFIG_MAP_OFFSET), + &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to read oscillator registers\n"); + return AB08_ERROR; + } + + /* Clear ACAL */ + aux &= ~OSCONTROL_ACAL_9_MIN; + + /* Unlock the key register */ + ab08_key_reg(RTCC_CONFKEY_OSCONTROL); + + switch(period) { + case RTCC_AUTOCAL_DISABLE: + break; + case RTCC_AUTOCAL_ONCE: + case RTCC_AUTOCAL_17_MIN: + aux |= OSCONTROL_ACAL_17_MIN; + break; + case RTCC_AUTOCAL_9_MIN: + aux |= OSCONTROL_ACAL_9_MIN; + break; + default: + return AB08_ERROR; + } + + if(ab08_write_reg((OSC_CONTROL_ADDR + CONFIG_MAP_OFFSET), + &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the autocalibration\n"); + return AB08_ERROR; + } + + if(period == RTCC_AUTOCAL_ONCE) { + clock_delay_usec(10000); + ab08_key_reg(RTCC_CONFKEY_OSCONTROL); + aux &= ~OSCONTROL_ACAL_9_MIN; + if(ab08_write_reg((OSC_CONTROL_ADDR + CONFIG_MAP_OFFSET), + &aux, 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the autocalibration\n"); + return AB08_ERROR; + } + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_set_calibration(uint8_t mode, int32_t adjust) +{ + int32_t adjint; + uint8_t adjreg[2]; + uint8_t xtcal; + + if(mode > RTCC_CAL_RC_OSC) { + PRINTF("RTC: invalid calibration mode\n"); + return AB08_ERROR; + } + + /* Fixed values dependant on the oscillator source (Application Manual) */ + if((mode == RTCC_CAL_XT_OSC) && ((adjust <= -610) || (adjust >= 242))) { + PRINTF("RTC: invalid adjust value for XT oscillator\n"); + return AB08_ERROR; + } + + if((mode == RTCC_CAL_RC_OSC) && ((adjust <= -65536) || (adjust >= 65520))) { + PRINTF("RTC: invalid adjust value for XT oscillator\n"); + return AB08_ERROR; + } + + /* Calibration routine taken from the Application manual */ + if(adjust < 0) { + adjint = ((adjust) * 1000 - 953); + } else { + adjint = ((adjust) * 1000 + 953); + } + + adjint = adjint / 1907; + + if(mode == RTCC_CAL_XT_OSC) { + if(adjint > 63) { + xtcal = 0; + /* CMDX = 1 */ + adjreg[0] = ((adjint >> 1) & 0x3F) | 0x80; + } else if(adjint > -65) { + xtcal = 0; + adjreg[0] = (adjint & 0x7F); + } else if(adjint > -129) { + xtcal = 1; + adjreg[0] = ((adjint + 64) & 0x7F); + } else if(adjint > -193) { + xtcal = 2; + adjreg[0] = ((adjint + 128) & 0x7F); + } else if(adjint > -257) { + xtcal = 3; + adjreg[0] = ((adjint + 192) & 0x7F); + } else { + xtcal = 3; + adjreg[0] = ((adjint + 192) >> 1) & 0xFF; + } + + if(ab08_write_reg((CAL_XT_ADDR + CONFIG_MAP_OFFSET), + &adjreg[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the autocalibration\n"); + return AB08_ERROR; + } + + if(ab08_read_reg((OSC_STATUS_ADDR + CONFIG_MAP_OFFSET), + &adjreg[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to read oscillator registers\n"); + return AB08_ERROR; + } + + /* Clear XTCAL and write new value */ + adjreg[0] &= 0x3F; + adjreg[0] |= (xtcal << 6); + + if(ab08_write_reg((OSC_STATUS_ADDR + CONFIG_MAP_OFFSET), + &adjreg[0], 1) == AB08_ERROR) { + PRINTF("RTC: failed to clear the autocalibration\n"); + return AB08_ERROR; + } + } else if(mode == RTCC_CAL_RC_OSC) { + if(adjint > 32767) { + adjreg[1] = ((adjint >> 3) & 0xFF); + adjreg[0] = ((adjint >> 11) | 0xC0); + } else if(adjint > 16383) { + adjreg[1] = ((adjint >> 2) & 0xFF); + adjreg[0] = ((adjint >> 10) | 0x80); + } else if(adjint > 8191) { + adjreg[1] = ((adjint >> 1) & 0xFF); + adjreg[0] = ((adjint >> 9) | 0x40); + } else if(adjint >= 0) { + adjreg[1] = ((adjint) & 0xFF); + adjreg[0] = (adjint >> 8); + } else if(adjint > -8193) { + adjreg[1] = ((adjint) & 0xFF); + adjreg[0] = (adjint >> 8) & 0x3F; + } else if(adjint > -16385) { + adjreg[1] = ((adjint >> 1) & 0xFF); + adjreg[0] = (adjint >> 9) & 0x7F; + } else if(adjint > -32769) { + adjreg[1] = ((adjint >> 2) & 0xFF); + adjreg[0] = (adjint >> 10) & 0xBF; + } else { + adjreg[1] = ((adjint >> 3) & 0xFF); + adjreg[0] = (adjint >> 11) & 0xFF; + } + + if(ab08_write_reg((CAL_RC_HI_ADDR + CONFIG_MAP_OFFSET), + adjreg, 2) == AB08_ERROR) { + PRINTF("RTC: failed to set the RC calibration\n"); + return AB08_ERROR; + } + + /* This should not happen */ + } else { + return AB08_ERROR; + } + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +int8_t +rtcc_init(void) +{ + i2c_init(I2C_SDA_PORT, I2C_SDA_PIN, I2C_SCL_PORT, I2C_SCL_PIN, + I2C_SCL_NORMAL_BUS_SPEED); + +#if RTCC_SET_DEFAULT_CONFIG + write_default_config(); +#endif + +#if RTCC_SET_AUTOCAL + rtcc_set_autocalibration(RTCC_AUTOCAL_17_MIN); +#endif + + /* Initialize interrupts handlers */ + rtcc_int1_callback = NULL; + + /* Configure the interrupts pins */ + GPIO_SOFTWARE_CONTROL(RTC_INT1_PORT_BASE, RTC_INT1_PIN_MASK); + GPIO_SET_INPUT(RTC_INT1_PORT_BASE, RTC_INT1_PIN_MASK); + + /* Pull-up resistor, detect falling edge */ + GPIO_DETECT_EDGE(RTC_INT1_PORT_BASE, RTC_INT1_PIN_MASK); + GPIO_TRIGGER_SINGLE_EDGE(RTC_INT1_PORT_BASE, RTC_INT1_PIN_MASK); + GPIO_DETECT_FALLING(RTC_INT1_PORT_BASE, RTC_INT1_PIN_MASK); + gpio_register_callback(rtcc_interrupt_handler, RTC_INT1_PORT, RTC_INT1_PIN); + + /* Spin process until an interrupt is received */ + process_start(&rtcc_int_process, NULL); + + return AB08_SUCCESS; +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ + diff --git a/platform/zoul/remote/rtcc.h b/platform/zoul/remote/rtcc.h new file mode 100644 index 000000000..4bbcbfe4f --- /dev/null +++ b/platform/zoul/remote/rtcc.h @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2015, Zolertia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + * + */ +/* -------------------------------------------------------------------------- */ +/** + * \addtogroup remote + * @{ + * + * \defgroup remote-rtcc RE-Mote Real Time Clock Calendar + * + * Driver for the RE-Mote on-board ultra-low power RTCC (Real Time Clock + * Calendar) + * @{ + * + * \file + * Header file for the RE-Mote RF antenna switch + */ +/* -------------------------------------------------------------------------- */ +#ifndef RTCC_H_ +#define RTCC_H_ +/* -------------------------------------------------------------------------- */ +#include +#include "i2c.h" +/* -------------------------------------------------------------------------- */ +/** + * \name Callback function to handle the RTCC alarm interrupt and macro + * @{ + */ +#define RTCC_REGISTER_INT1(ptr) rtcc_int1_callback = ptr; +extern void (*rtcc_int1_callback)(uint8_t value); +/** @} */ +/* -------------------------------------------------------------------------- */ +/** \name AB08XX Address registers + * @{ + */ +/* -------------------------------------------------------------------------- */ +/* Time/date registers (no offset) */ +#define CENTHS_ADDR 0x00 +#define SEC_ADDR 0x01 +#define MIN_ADDR 0x02 +#define HOUR_ADDR 0x03 +#define DAY_ADDR 0x04 +#define MONTHS_ADDR 0x05 +#define YEAR_ADDR 0x06 +#define WEEKDAYLS_ADDR 0x07 + +/* Alarm registers */ +#define ALARM_MAP_OFFSET 0x08 +#define HUNDREDTHS_ALARM_ADDR 0x00 +#define SECONDS_ALARM_ADDR 0x01 +#define MINUTES_ALARM_ADDR 0x02 +#define HOURS_ALARM_ADDR 0x03 +#define DAY_ALARMS_ADDR 0x04 +#define MONTHS_ALARM_ADDR 0x05 +#define WEEKDAYS_ALARM_ADDR 0x06 + +/* Configuration registers */ +#define CONFIG_MAP_OFFSET 0x0F +#define STATUS_ADDR 0x00 +#define CTRL_1_ADDR 0x01 +#define CTRL_2_ADDR 0x02 +#define INT_MASK_ADDR 0x03 +#define SQW_ADDR 0x04 +#define CAL_XT_ADDR 0x05 +#define CAL_RC_HI_ADDR 0x06 +#define CAL_RC_LO_ADDR 0x07 +#define INT_POL_ADDR 0x08 +#define TIMER_CONTROL_ADDR 0x09 +#define TIMER_COUNTDOWN_ADDR 0x0A +#define TIMER_INITIAL_ADDR 0x0B +#define WDT_ADDR 0x0C +#define OSC_CONTROL_ADDR 0x0D +#define OSC_STATUS_ADDR 0x0E +#define CONF_KEY_ADDR 0x10 +#define TRICKLE_ADDR 0x11 +#define BREF_CTRL_ADDR 0x12 +#define AF_CTRL_ADDR 0x17 +#define BAT_MODE_IO_ADDR 0x18 +#define ASTAT_ADDR 0x20 +#define OCTRL_ADDR 0x21 +#define EXT_ADDR 0x30 +/* 256b. The upper 2 bits are taken from XADS field */ +#define RAM_1_ADDR (CONFIG_MAP_OFFSET + 0x31) +/* 256b. The upper 2 bits are taken from XADA field */ +#define RAM_2_ADDR (CONFIG_MAP_OFFSET + 0x71) +/** @} */ +/* -------------------------------------------------------------------------- */ +/** \name RTCC Bitmasks and shifts + * @{ + */ +#define STATUS_CB 0x80 +#define STATUS_BAT 0x40 +#define STATUS_WDT 0x20 +#define STATUS_BL 0x10 +#define STATUS_TIM 0x08 +#define STATUS_ALM 0x04 +#define STATUS_EX2 0x02 +#define STATUS_EX1 0x01 + +#define CTRL1_WRTC 0x01 +#define CTRL1_ARST 0x04 +#define CTRL1_OUT 0x10 +#define CTRL1_OUTB 0x20 +#define CTRL1_1224 0x40 +#define CTRL1_STOP 0x80 + +/* Defines the nIRQ pin control */ +#define CTRL2_OUT1S_NIRQ_OUT 0x00 +#define CTRL2_OUT1S_NIRQ_SQW_OUT 0x01 +#define CTRL2_OUT1S_NIRQ_SQW_NIRQ 0x02 +#define CTRL2_OUT1S_NIRQ_NAIRQ_OUT 0x03 + +/* Defines the nIRQ2 pin control */ +#define CTRL2_OUT2S_SQW_OUT 0x04 +#define CTRL2_OUT2S_NAIRQ_OUTB 0x0C +#define CTRL2_OUT2S_TIRQ_OUTB 0x10 +#define CTRL2_OUT2S_NTIRQ_OUTB 0x14 +#define CTRL2_OUT2S_OUTB 0x1C + +/* Interrupt Mask */ +#define INTMASK_EX1E 0x01 +#define INTMASK_EX2E 0x02 +#define INTMASK_AIE 0x04 +#define INTMASK_TIE 0x08 +#define INTMASK_BLIE 0x10 +#define INTMASK_IM_HIGH 0x20 +#define INTMASK_IM_MED 0x40 +#define INTMASK_IM_LOW 0x60 +#define INTMASK_CEB 0x80 + +/* Timer countdown control */ +#define COUNTDOWN_TIMER_TE 0x80 +#define COUNTDOWN_TIMER_TM 0x40 +#define COUNTDOWN_TIMER_TRPT 0x20 +#define COUNTDOWN_TIMER_RPT_SECOND 0x1C +#define COUNTDOWN_TIMER_RPT_MINUTE 0x18 +#define COUNTDOWN_TIMER_RPT_HOUR 0x24 +#define COUNTDOWN_TIMER_RPT_DAY 0x10 +#define COUNTDOWN_TIMER_RPT_WEEK 0x0C +#define COUNTDOWN_TIMER_RPT_MONTH 0x08 +#define COUNTDOWN_TIMER_RPT_YEAR 0x04 +#define COUNTDOWN_TIMER_RPT_SHIFT 0x02 +#define COUNTDOWN_TIMER_TFS_ONE 0x01 +#define COUNTDOWN_TIMER_TFS_TWO 0x02 +#define COUNTDOWN_TIMER_TFS_THREE 0x03 + +/* Oscillator control */ +#define OSCONTROL_ACIE 0x01 +#define OSCONTROL_OFIE 0x02 +#define OSCONTROL_FOS 0x08 +#define OSCONTROL_AOS 0x10 +#define OSCONTROL_ACAL_NO_CAL 0x00 +#define OSCONTROL_ACAL_17_MIN 0x40 +#define OSCONTROL_ACAL_9_MIN 0x60 +#define OSCONTROL_OSEL 0x80 + +/** @} */ +/* -------------------------------------------------------------------------- */ +/** \name RTCC operational values + * @{ + */ +/* I2C address (7-bits) */ +#define AB08XX_ADDR 0x69 +#define INT_BUFF_SIZE 20L +#define TCS_DIODE_3K (TCS_ENABLE + 0x05) +#define TCS_DIODE_6K (TCS_ENABLE + 0x06) +#define TCS_DIODE_11K (TCS_ENABLE + 0x07) +#define RTCC_TOGGLE_PM_BIT 0x20 +#define RTCC_FIX_10THS_HUNDRETHS 0xF0 +#define RTCC_FIX_100THS_HUNDRETHS 0xFF +#define RTCC_TD_MAP_SIZE (WEEKDAYLS_ADDR + 1) +#define RTCC_ALARM_MAP_SIZE (WEEKDAYS_ALARM_ADDR + 1) +#define RTCC_CONFIG_MAP_SIZE (BREF_CTRL_ADDR + 1) +/** @} */ +/* -------------------------------------------------------------------------- */ +/** \name RTCC error values + * @{ + */ +#define AB08_ERROR (-1) +#define AB08_SUCCESS 0x00 +/** @} */ +/* -------------------------------------------------------------------------- */ +/** \name RTCC enumeration and options + * @{ + */ +enum { + RTCC_PRINT_DATE = 0, + RTCC_PRINT_CONFIG, + RTCC_PRINT_ALARM, + RTCC_PRINT_ALARM_DEC, + RTCC_PRINT_DATE_DEC, + RTCC_PRINT_MAX, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_ALARM_OFF = 0, + RTCC_ALARM_ON, + RTCC_ALARM_MAX, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_CMD_UNLOCK = 0, + RTCC_CMD_LOCK, + RTCC_CMD_ENABLE, + RTCC_CMD_STOP, + RTCC_CMD_MAX, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_24H_MODE = 0, + RTCC_12H_MODE_AM, + RTCC_12H_MODE_PM, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_CENTURY_19XX_21XX = 1, + RTCC_CENTURY_20XX, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_REPEAT_NONE = 0, + RTCC_REPEAT_YEAR, + RTCC_REPEAT_MONTH, + RTCC_REPEAT_WEEK, + RTCC_REPEAT_DAY, + RTCC_REPEAT_HOUR, + RTCC_REPEAT_MINUTE, + RTCC_REPEAT_SECOND, + RTCC_REPEAT_10THS, + RTCC_REPEAT_100THS, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_CONFKEY_OSCONTROL = 0xA1, + RTCC_CONFKEY_SWRESET = 0x3C, + RTCC_CONFKEY_DEFREGS = 0x9D, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_CAL_XT_OSC = 0, + RTCC_CAL_RC_OSC, +}; +/* -------------------------------------------------------------------------- */ +enum { + RTCC_AUTOCAL_DISABLE = 0, + RTCC_AUTOCAL_ONCE, + RTCC_AUTOCAL_17_MIN, + RTCC_AUTOCAL_9_MIN, +}; +/** @} */ +/* -------------------------------------------------------------------------- */ +/** \name Readable Date and time memory map implementation + * + * This simplified structure allows the user to set date/alarms with a + * reduced structure, without the bit-defined restrictions of the memory map, + * using decimal values + * + * @{ + */ +typedef struct ab0805_struct_simple_td_reg { + uint8_t miliseconds; + uint8_t seconds; + uint8_t minutes; + uint8_t hours; + uint8_t day; + uint8_t months; + uint8_t years; + uint8_t weekdays; + uint8_t mode; + uint8_t century; +} __attribute__ ((packed)) simple_td_map; +/** @} */ +/* -------------------------------------------------------------------------- */ +/** + * \name RTCC User functions + * @{ + */ + +/** + * \brief Set the time and date + * \param *data Time and date value (decimal format) + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_set_time_date(simple_td_map *data); + +/** + * \brief Get the current time and date + * \param *data buffer to store the results + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_get_time_date(simple_td_map *data); + +/** + * \brief Print data from the RTCC module, either from the memory + * map (values in BCD) or actual readable data (decimal). + * \param value value to print, see RTCC_PRINT_* options available + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_print(uint8_t value); + +/** + * \brief Configure the RTCC to match an alarm counter + * \param data date and time values (in decimal) to match against + * \param state set on/off the alarm interruption + * \param repeat set the frequency of the alarm (minute, hourly, daily, etc.) + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_set_alarm_time_date(simple_td_map *data, uint8_t state, + uint8_t repeat); + +/** + * \brief Manually calibrate the RTCC + * \param mode oscillator to calibrate + * \param adjust value (in ppm) to adjust the oscillator + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_set_calibration(uint8_t mode, int32_t adjust); + +/** + * \brief Set the autocallibration period + * \param period autocalibration configuration + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_set_autocalibration(uint8_t period); + +/** + * \brief Initialize the RTCC, configures the I2C bus, interrupts and registers + * \return + * \ AB08_SUCCESS date/time set + * \ AB08_ERROR failed to set time/date (enable DEBUG for more info) + */ +int8_t rtcc_init(void); +/** @} */ +/* -------------------------------------------------------------------------- */ +#endif /* ifndef RTCC_H_ */ +/* -------------------------------------------------------------------------- */ +/** + * @} + * @} + */