Added support for the RE-Mote on-board Real Time Clock Calendar (RTCC)

This commit is contained in:
Antonio Lignan 2015-12-11 12:38:36 +01:00
parent a2cae3359b
commit 834f965c95
9 changed files with 1594 additions and 4 deletions

View File

@ -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

View File

@ -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_ */
/** @} */

View File

@ -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 <alinan@zolertia.com>
* Aitor Mejias <amejias@zolertia.com>
* Toni Lozano <tlozano@zolertia.com>
*/
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "rtcc.h"
#include "dev/i2c.h"
#include "dev/leds.h"
#include <stdio.h>
#include <stdlib.h>
/*---------------------------------------------------------------------------*/
#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();
}
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View File

@ -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
--------------------

View File

@ -1,2 +1,2 @@
MOTELIST_ZOLERTIA = remote
BOARD_SOURCEFILES += board.c antenna-sw.c
BOARD_SOURCEFILES += board.c antenna-sw.c rtcc.c

View File

@ -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.

View File

@ -0,0 +1,103 @@
/*
* Copyright (c) 2015, Zolertia <http://www.zolertia.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 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 <alinan@zolertia.com>
* Aitor Mejias <amejias@zolertia.com>
* Toni Lozano <tlozano@zolertia.com>
*/
/* -------------------------------------------------------------------------- */
#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_ */
/* -------------------------------------------------------------------------- */
/**
* @}
* @}
*/

868
platform/zoul/remote/rtcc.c Normal file
View File

@ -0,0 +1,868 @@
/*
* Copyright (c) 2015, Zolertia <http://www.zolertia.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 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 <alinan@zolertia.com>
* Aitor Mejias <amejias@zolertia.com>
* Toni Lozano <tlozano@zolertia.com>
*/
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "dev/gpio.h"
#include "dev/i2c.h"
#include "rtcc.h"
#include "rtcc-config.h"
#include "dev/leds.h"
#include <stdio.h>
/*---------------------------------------------------------------------------*/
#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;
}
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

380
platform/zoul/remote/rtcc.h Normal file
View File

@ -0,0 +1,380 @@
/*
* Copyright (c) 2015, Zolertia <http://www.zolertia.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 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 <stdio.h>
#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_ */
/* -------------------------------------------------------------------------- */
/**
* @}
* @}
*/