osd-contiki/platform/zoul/remote/rtcc.c

869 lines
25 KiB
C

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