|
/*
|
|
*
|
|
Disclaimer
|
|
Copyright (c) 2010, 2011. 2012 Patrice Nadeau
|
|
@n All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
- Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
- 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.
|
|
- Neither the name of Patrice Nadeau 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 Patrice Nadeau 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.
|
|
*/
|
|
|
|
/**
|
|
* @internal
|
|
* @file ds1305.c
|
|
* @brief Maxim Timer DS1305 functions for Atmel AVR
|
|
* @author Patrice Nadeau <patricen@telwarwick.net>
|
|
*/
|
|
|
|
#include <util/atomic.h>
|
|
#include "ds1305/ds1305.h"
|
|
|
|
/**
|
|
* @brief Offset from read to write registers
|
|
*/
|
|
#define _REG_WRITE_OFFSET 0x80
|
|
|
|
/**
|
|
* @name Bases and offsets for the alarms
|
|
* @{
|
|
*/
|
|
#define _REG_ALARM0_BASE 0x07
|
|
#define _REG_ALARM1_BASE 0x0b
|
|
#define _REG_MINUTES_OFFSET 0x01
|
|
#define _REG_HOURS_OFFSET 0x02
|
|
#define _REG_DAY_OFFSET 0x03
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Main calendar registers
|
|
* @{
|
|
*/
|
|
#define _REG_SECONDS 0x00
|
|
#define _REG_MINUTES 0x01
|
|
#define _REG_HOURS 0x02
|
|
#define _REG_DAY 0x03
|
|
#define _REG_DATE 0x04
|
|
#define _REG_MONTH 0x05
|
|
#define _REG_YEAR 0x06
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Alarm0 registers
|
|
* @{
|
|
*/
|
|
#define _REG_SECONDS0 0x07
|
|
#define _REG_MINUTES0 0x08
|
|
#define _REG_HOURS0 0x09
|
|
#define _REG_DAY0 0x0a
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Alarm1 registers
|
|
* @{
|
|
*/
|
|
#define _REG_SECONDS1 0x0b
|
|
#define _REG_MINUTES1 0x0c
|
|
#define _REG_HOURS1 0x0d
|
|
#define _REG_DAY1 0x0e
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Other registers
|
|
* @{
|
|
*/
|
|
/** @brief Control Register */
|
|
#define _REG_CR 0x0f
|
|
/** @brief Status Register */
|
|
#define _REG_SR 0x10
|
|
/** @brief Trickle Charger Register */
|
|
#define _REG_TCR 0x11
|
|
/** @brief RAM register */
|
|
#define _REG_RAM 0x20
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Control Register bits
|
|
* @{
|
|
*/
|
|
/** @brief Interrupt 0 enable @note Active high */
|
|
#define _BIT_AIE0 0
|
|
/** @brief Interrupt 1 enable @note Active high */
|
|
#define _BIT_AIE1 1
|
|
/** @brief Enable int0 pin for both interrupts */
|
|
#define _BIT_INTCN 2
|
|
/** @brief Write protect bit */
|
|
#define _BIT_WP 6
|
|
/** @brief Enable oscillator bit */
|
|
#define _BIT_EOSC 7
|
|
/** @brief Alarm bit */
|
|
#define _BIT_ALARM 7
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Status Register bits
|
|
* @{
|
|
*/
|
|
/** @brief Indicate interrupt 0 */
|
|
#define _BIT_IRQF0 0
|
|
/** @brief Indicate interrupt 1 */
|
|
#define _BIT_IRQF1 1
|
|
/** @} */
|
|
|
|
|
|
/**
|
|
* @brief Enable / disable the CE pin
|
|
* @param[in] status
|
|
* - @n enable
|
|
* - @n disable
|
|
* @note DS1305 is active high, AVR is active low */
|
|
static void _ChipEnable(uint8_t status)
|
|
{
|
|
if (status == true)
|
|
spi_SlaveSelect(false);
|
|
else
|
|
spi_SlaveSelect(true);
|
|
}
|
|
|
|
/**
|
|
* @brief Write @a temp into the Control Register
|
|
* @param[in] temp Value to write
|
|
* @pre Write protect must be disabled before calling this function
|
|
* */
|
|
static void _WriteControlRegister (uint8_t temp)
|
|
{
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_CR + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(temp);
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/**
|
|
* @brief Return the value of the control register
|
|
* @return Value of Control Register
|
|
* */
|
|
static uint8_t _ReadControlRegister(void)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_CR);
|
|
/* read the currents CR register */
|
|
temp = spi_MasterTransmit(0x00);
|
|
_ChipEnable(false);
|
|
return (temp);
|
|
}
|
|
|
|
/**
|
|
* @brief Return the status of the bit write-protect
|
|
* @return Status
|
|
* @retval true WriteProtect is enable
|
|
* @retval false WriteProtect is disable
|
|
*/
|
|
static bool _IsWriteProtect(void)
|
|
{
|
|
return (_ReadControlRegister() && (1 < _BIT_WP));
|
|
}
|
|
|
|
/**
|
|
* @brief Return the content of the status register
|
|
* @return Contents of the SR register
|
|
*/
|
|
static uint8_t _ReadStatusRegister(void)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_SR);
|
|
temp = spi_MasterTransmit(0x00);
|
|
_ChipEnable(false);
|
|
return (temp);
|
|
}
|
|
|
|
/**
|
|
* @brief _WriteTrickleChargeRegister
|
|
* @param temp
|
|
* @todo
|
|
static void _WriteTrickleChargeRegister (uint8_t temp)
|
|
{
|
|
|
|
}
|
|
**/
|
|
|
|
/**
|
|
* @brief _ReadTrickleChargeRegister
|
|
* @return
|
|
* @todo
|
|
static uint8_t _ReadTrickleChargeRegister (void)
|
|
{
|
|
return 0;
|
|
}
|
|
**/
|
|
|
|
/**
|
|
* @brief Change the status of the write-protect
|
|
* @param[in] state Enable/disable
|
|
* - @n enable
|
|
* - @n disable
|
|
* @note Needed for clock, interrupts and RAM operations
|
|
* */
|
|
static void _WriteProtect(bool state)
|
|
{
|
|
uint8_t temp;
|
|
temp = _ReadControlRegister();
|
|
if (state == true)
|
|
/* WP enable, active high */
|
|
temp |= (1 << _BIT_WP);
|
|
else
|
|
/* WP disable */
|
|
temp &= ~(1 << _BIT_WP);
|
|
/* write back the changed ds1305_BIT_WP bit */
|
|
_WriteControlRegister(temp);
|
|
}
|
|
|
|
/**
|
|
* @brief Change the EOSC bit
|
|
* @param[in] state Enable/disable
|
|
* - @n enable
|
|
* - @n disable
|
|
* @pre _WriteProtect(false);
|
|
* */
|
|
static void _EnableOSC(bool state)
|
|
{
|
|
uint8_t temp;
|
|
temp = _ReadControlRegister();
|
|
if (state == true)
|
|
/* EOSC enable, active low */
|
|
temp &= ~(1 << _BIT_EOSC);
|
|
else
|
|
/* EOSC disable */
|
|
temp |= (1 << _BIT_EOSC);
|
|
_WriteControlRegister(temp);
|
|
}
|
|
|
|
/**
|
|
* @post Write protect is enable
|
|
* @post OSC is enable
|
|
* @note SPI : MSB, Mode3, clock/16, interrupt disable
|
|
* @note DS1305 max clock is : 0.6MHz @ Vcc=2V, 2MHz @ Vcc=5V,
|
|
* @note Using spi_CLOCK_16 should permit an AVR speed up to 20MHz
|
|
* @warning The SERMODE pin must be connected to Vcc to enable SPI mode
|
|
*/
|
|
void ds1305_SPIInit(void)
|
|
{
|
|
spi_MasterInit (spi_MSB, spi_MODE3, spi_CLOCK_16, false);
|
|
_EnableOSC(true);
|
|
_WriteProtect(false);
|
|
}
|
|
|
|
/**
|
|
* @brief Write seconds from decimal to BCD format
|
|
* @param[in] seconds Range : 0-59
|
|
* @param[in] mode Mode
|
|
* - @n ds1305_TIMER
|
|
* - @n ds1305_ALARM0
|
|
* - @n ds1305_ALARM1
|
|
* @note Used by calendar and alarms
|
|
*/
|
|
static void _WriteSeconds (uint8_t seconds, uint8_t mode)
|
|
{
|
|
uint8_t reg, tmp;
|
|
/* select the register */
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
reg = _REG_SECONDS0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
reg = _REG_SECONDS1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
reg = _REG_SECONDS;
|
|
break;
|
|
}
|
|
reg = reg + _REG_WRITE_OFFSET;
|
|
tmp = binary_DecToBcd(seconds);
|
|
/* an interrupt could be generated when seconds is 0 */
|
|
/* that was a problem after an upload to the avr even with a H/W reset */
|
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(reg);
|
|
spi_MasterTransmit(tmp);
|
|
_ChipEnable(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Return in decimal format the seconds
|
|
* @param[in] mode Mode
|
|
* - @n _ds1305_TIMER
|
|
* - @n _ds1305_ALARM0
|
|
* - @n _ds1305_ALARM1
|
|
* @return Second in decimal
|
|
* @retval Seconds 00 - 59
|
|
* @note Used by calendar and alarms
|
|
* */
|
|
static uint8_t _ReadSeconds(uint8_t mode)
|
|
{
|
|
uint8_t tmp;
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_SECONDS0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_SECONDS1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_SECONDS;
|
|
break;
|
|
}
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp);
|
|
tmp = binary_BcdToDec(spi_MasterTransmit(0x00));
|
|
_ChipEnable(false);
|
|
return tmp;
|
|
}
|
|
|
|
/**
|
|
* @brief Write minutes from decimal to BCD format
|
|
* @param[in] minutes Range : 0-59
|
|
* @param[in] mode Mode
|
|
* - @n ds1305_TIMER
|
|
* - @n ds1305_ALARM0
|
|
* - @n ds1305_ALARM1
|
|
* @note Used by calendar and alarms
|
|
* */
|
|
static void _WriteMinutes (uint8_t minutes, uint8_t mode)
|
|
{
|
|
uint8_t tmp;
|
|
/* select the register */
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_MINUTES0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_MINUTES1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_MINUTES;
|
|
break;
|
|
}
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(binary_DecToBcd(minutes));
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/** @brief Return the minutes in decimal
|
|
* @param[in] mode Mode
|
|
* - @n _REG_MINUTES
|
|
* - @n _REG_MINUTES0
|
|
* - @n _REG_MINUTES1
|
|
* @return Minutes in decimal
|
|
* @retval 0-59 A minute
|
|
* @note Used by calendar and alarms
|
|
* */
|
|
static uint8_t _ReadMinutes (uint8_t mode)
|
|
{
|
|
uint8_t tmp;
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_MINUTES0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_MINUTES1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_MINUTES;
|
|
break;
|
|
}
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp);
|
|
tmp = binary_BcdToDec(spi_MasterTransmit(0x00));
|
|
_ChipEnable(false);
|
|
return tmp;
|
|
}
|
|
|
|
/**
|
|
* @brief Write @a hours from decimal to BCD
|
|
* @param[in] hours Range : 0-23
|
|
* @param[in] mode Mode
|
|
* - @n ds1305_TIMER
|
|
* - @n ds1305_ALARM0
|
|
* - @n ds1305_ALARM1
|
|
* @note Used by calendar and alarms
|
|
* */
|
|
static void _WriteHours (uint8_t hours, uint8_t mode)
|
|
{
|
|
uint8_t tmp;
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_HOURS0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_HOURS1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_HOURS;
|
|
break;
|
|
}
|
|
/* 24 hours format */
|
|
tmp &= ~(1 << 6);
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(binary_DecToBcd(hours));
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/**
|
|
* @brief Get the hours in decimal
|
|
* @param[in] mode Mode
|
|
* - @n ds1305_TIMER
|
|
* - @n ds1305_ALARM0
|
|
* - @n ds1305_ALARM1
|
|
* @return The hours
|
|
* @retval 00-23 An hour
|
|
* @note Used by calendar and alarms
|
|
* */
|
|
static uint8_t _ReadHours (uint8_t mode)
|
|
{
|
|
uint8_t tmp, tmp2;
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_HOURS0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_HOURS1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_HOURS;
|
|
break;
|
|
}
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp);
|
|
/* dummy read */
|
|
tmp = spi_MasterTransmit(0x00);
|
|
_ChipEnable(false);
|
|
/* 10hours */
|
|
tmp2 = ((tmp & ascii_NUMERIC_OFFSET) >> 4) * 10;
|
|
/* hours */
|
|
tmp2 += (tmp & 0x0f);
|
|
return tmp2;
|
|
}
|
|
|
|
/**
|
|
* @brief Write a date from decimal to BCD format
|
|
* @param[in] date Day of the month
|
|
* - @n 1-31
|
|
* */
|
|
static void _WriteDate (uint8_t date)
|
|
{
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_DATE + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(binary_DecToBcd(date));
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/** @brief Get the date in decimal
|
|
* @return The date
|
|
* @retval 1-31 A day of the month
|
|
* */
|
|
static uint8_t _ReadDate (void)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_DATE);
|
|
/* dummy read */
|
|
temp = binary_BcdToDec(spi_MasterTransmit(0x00));
|
|
_ChipEnable(false);
|
|
return temp;
|
|
}
|
|
|
|
/** @brief Write a day from decimal to BCD format
|
|
* @param[in] day Day of the week
|
|
* - @n 1-7
|
|
* @param[in] mode Mode
|
|
* - @n ds1305_TIMER
|
|
* - @n ds1305_ALARM0
|
|
* - @n ds1305_ALARM1
|
|
* @note Used by calendar and alarms
|
|
*/
|
|
static void _WriteDay (uint8_t day, uint8_t mode)
|
|
{
|
|
uint8_t tmp;
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_DAY0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_DAY1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_DAY;
|
|
break;
|
|
}
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(binary_DecToBcd(day));
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/** @brief Get the day in decimal
|
|
* @param[in] mode Mode
|
|
* - @n ds1305_TIMER
|
|
* - @n ds1305_ALARM0
|
|
* - @n ds1305_ALARM1
|
|
* @return Range : 1-7
|
|
* @note Used by both calendar and alarms
|
|
* */
|
|
static uint8_t _ReadDay (uint8_t mode)
|
|
{
|
|
int8_t tmp;
|
|
switch (mode) {
|
|
case ds1305_ALARM0 :
|
|
tmp = _REG_DAY0;
|
|
break;
|
|
case ds1305_ALARM1 :
|
|
tmp = _REG_DAY1;
|
|
break;
|
|
default :
|
|
/* or ds1305_TIMER */
|
|
tmp = _REG_DAY;
|
|
break;
|
|
}
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(tmp);
|
|
/* dummy read */
|
|
tmp = binary_BcdToDec(spi_MasterTransmit(0x00));
|
|
_ChipEnable(false);
|
|
return tmp;
|
|
}
|
|
|
|
/** @brief Write the month from decimal to BCD format
|
|
* @param[in] month Month of the year
|
|
* - @n 1-12
|
|
* */
|
|
static void _WriteMonth (uint8_t month)
|
|
{
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_MONTH + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(binary_DecToBcd(month));
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/** @brief Get the month in decimal
|
|
* @return Month of the year
|
|
* @retval 1-12 Month
|
|
* */
|
|
static uint8_t _ReadMonth (void)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_MONTH);
|
|
/* dummy read */
|
|
temp = binary_BcdToDec(spi_MasterTransmit(0x00));
|
|
_ChipEnable(false);
|
|
return temp;
|
|
}
|
|
|
|
/** @brief Write the year from decimal to BCD format
|
|
* @param[in] year Year
|
|
* - @n 0-99
|
|
* */
|
|
static void _WriteYear (uint8_t year)
|
|
{
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_YEAR + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(binary_DecToBcd(year));
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
/** @brief Get the year in decimal
|
|
* @return The year
|
|
* @retval year From 00 to 99 inclusively
|
|
* */
|
|
static uint8_t _ReadYear (void)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_YEAR);
|
|
/* dummy read */
|
|
temp = binary_BcdToDec(spi_MasterTransmit(0x00));
|
|
_ChipEnable(false);
|
|
return temp;
|
|
}
|
|
|
|
void ds1305_ReadDate (struct ds1305_time *date)
|
|
{
|
|
date->year = _ReadYear();
|
|
date->month = _ReadMonth();
|
|
date->date = _ReadDate();
|
|
}
|
|
|
|
void ds1305_ReadTime (struct ds1305_time *time)
|
|
{
|
|
time->hours = _ReadHours(ds1305_TIMER);
|
|
time->minutes = _ReadMinutes(ds1305_TIMER);
|
|
time->seconds = _ReadSeconds(ds1305_TIMER);
|
|
}
|
|
|
|
void ds1305_WriteDate (struct ds1305_time date)
|
|
{
|
|
uint8_t wp;
|
|
/* save the write protect flag */
|
|
wp = _IsWriteProtect();
|
|
_WriteProtect(false);
|
|
_WriteYear(date.year);
|
|
_WriteMonth(date.month);
|
|
_WriteDate(date.date);
|
|
/* restore the write protect flag */
|
|
_WriteProtect(wp);
|
|
}
|
|
|
|
/**
|
|
* @bug see @e _WriteSecond
|
|
*/
|
|
void ds1305_WriteTime (struct ds1305_time time)
|
|
{
|
|
uint8_t wp;
|
|
/* save the write protect flag */
|
|
wp = _IsWriteProtect();
|
|
_WriteProtect(false);
|
|
_WriteHours(time.hours, ds1305_TIMER);
|
|
_WriteMinutes(time.minutes, ds1305_TIMER);
|
|
_WriteSeconds(time.seconds, ds1305_TIMER);
|
|
/* restore the write protect flag */
|
|
_WriteProtect(wp);
|
|
}
|
|
|
|
/**
|
|
* @brief Set alarm bit to enable alarms
|
|
* @param[in] reg Register Register to write
|
|
* - @n ds1305_REG_SECONDSx
|
|
* - @n ds1305_REG_MINUTESx
|
|
* - @n ds1305_REG_HOURSx
|
|
* - @n ds1305_REG_DAYx
|
|
* @note Active high
|
|
* */
|
|
static void _SetAlarmBit(uint8_t reg)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
/* read the current value */
|
|
spi_MasterTransmit(reg + _REG_WRITE_OFFSET);
|
|
temp = spi_MasterTransmit(0x00);
|
|
/* needed to use another register */
|
|
_ChipEnable(false);
|
|
/* set bit 7 */
|
|
temp |= (1 << _BIT_ALARM);
|
|
_ChipEnable(true);
|
|
/* write back with bit 7 enabled */
|
|
spi_MasterTransmit(reg + _REG_WRITE_OFFSET);
|
|
spi_MasterTransmit(temp);
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
void ds1305_WriteAlarm (struct ds1305_time time, uint8_t alarm,
|
|
uint8_t interval)
|
|
{
|
|
uint8_t wp, base;
|
|
wp = _IsWriteProtect();
|
|
_WriteProtect(false);
|
|
/* to which alarm */
|
|
if (alarm == ds1305_ALARM0)
|
|
base = _REG_ALARM0_BASE;
|
|
else
|
|
base = _REG_ALARM1_BASE;
|
|
_WriteSeconds(time.seconds, alarm);
|
|
_WriteMinutes(time.minutes, alarm);
|
|
_WriteHours(time.hours, alarm);
|
|
_WriteDay(time.day, alarm);
|
|
/* set the interval (see table 2 in the datasheet) */
|
|
switch (interval) {
|
|
case ds1305_ALARM_ONCE :
|
|
/* every seconds */
|
|
_SetAlarmBit(base);
|
|
/* no break */
|
|
case ds1305_ALARM_SECOND :
|
|
/* when seconds match */
|
|
_SetAlarmBit(base + _REG_MINUTES_OFFSET);
|
|
/* no break */
|
|
case ds1305_ALARM_MINUTE :
|
|
/* when minutes and seconds match */
|
|
_SetAlarmBit(base + _REG_HOURS_OFFSET);
|
|
/* no break */
|
|
case ds1305_ALARM_HOUR :
|
|
/* when hour, minutes & seconds match */
|
|
_SetAlarmBit(base + _REG_DAY_OFFSET);
|
|
/* no break */
|
|
case ds1305_ALARM_DAY :
|
|
/* when day, hours, minutes & seconds match */
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
/* put back the previous write protect state */
|
|
_WriteProtect(wp);
|
|
}
|
|
|
|
/** @brief Get alarm Bit
|
|
* @param[in] reg Register to read
|
|
* - @n _REG_SECONDSx
|
|
* - @n _REG_MINUTESx
|
|
* - @n _REG_HOURSx
|
|
* - @n _REG_DAYx
|
|
* @return The alarm status
|
|
* @retval enable
|
|
* @retval disable
|
|
* @note Active high
|
|
*/
|
|
static uint8_t _GetAlarmBit(uint8_t reg)
|
|
{
|
|
uint8_t temp;
|
|
_ChipEnable(true);
|
|
/* read the current value */
|
|
spi_MasterTransmit(reg);
|
|
temp = spi_MasterTransmit(0x00);
|
|
_ChipEnable(false);
|
|
/* return the result if bit 7 set */
|
|
return (temp & (1 << _BIT_ALARM));
|
|
}
|
|
|
|
uint8_t ds1305_ReadAlarm (struct ds1305_time *time, uint8_t alarm)
|
|
{
|
|
uint8_t interval, base;
|
|
/* to which alarm */
|
|
if (alarm == ds1305_ALARM0)
|
|
base = _REG_ALARM0_BASE;
|
|
else
|
|
base = _REG_ALARM1_BASE;
|
|
time->day = _ReadDay(alarm);
|
|
time->hours = _ReadHours(alarm);
|
|
time->minutes = _ReadMinutes(alarm);
|
|
time->seconds = _ReadSeconds(alarm);
|
|
interval = 0;
|
|
if (_GetAlarmBit(base))
|
|
interval |= (1 << 3);
|
|
if (_GetAlarmBit(base + _REG_MINUTES_OFFSET))
|
|
interval |= (1 << 2);
|
|
if (_GetAlarmBit(base + _REG_HOURS_OFFSET))
|
|
interval |= (1 << 1);
|
|
if (_GetAlarmBit(base + _REG_DAY_OFFSET))
|
|
interval |= (1 << 0);
|
|
return interval;
|
|
}
|
|
|
|
void ds1305_WriteRAM(struct ds1305_ram ram)
|
|
{
|
|
uint8_t x, wp;
|
|
wp = _IsWriteProtect();
|
|
_WriteProtect(false);
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_RAM + _REG_WRITE_OFFSET);
|
|
for (x = 0; x < 96; x++) {
|
|
spi_MasterTransmit(ram.byte[x]);
|
|
}
|
|
_ChipEnable(false);
|
|
_WriteProtect(wp);
|
|
}
|
|
|
|
void ds1305_ReadRAM(struct ds1305_ram *ram)
|
|
{
|
|
uint8_t x;
|
|
_ChipEnable(true);
|
|
spi_MasterTransmit(_REG_RAM);
|
|
for (x = 0; x < 96; x++) {
|
|
ram->byte[x] = spi_MasterTransmit(0x00);
|
|
}
|
|
_ChipEnable(false);
|
|
}
|
|
|
|
void ds1305_EnableInt (uint8_t one_pin, uint8_t int0, uint8_t int1)
|
|
{
|
|
uint8_t temp, wp;
|
|
temp = _ReadControlRegister();
|
|
if (one_pin == true)
|
|
/* only int0 pin will be used */
|
|
temp &= ~(1 << _BIT_INTCN);
|
|
else
|
|
/* both int0 & int1 will be used */
|
|
temp |= (1 << _BIT_INTCN);
|
|
if (int0 == true)
|
|
/* enable int0 */
|
|
temp |= (1 << _BIT_AIE0);
|
|
else
|
|
/* disable int0 */
|
|
temp &= ~(1 << _BIT_AIE0);
|
|
if (int1 == true)
|
|
/* enable int1 */
|
|
temp |= (1 << _BIT_AIE1);
|
|
else
|
|
/* disable int1 */
|
|
temp &= ~(1 << _BIT_AIE1);
|
|
wp = _IsWriteProtect();
|
|
_WriteProtect(false);
|
|
_WriteControlRegister(temp);
|
|
/* put back the previous state of write protect */
|
|
_WriteProtect(wp);
|
|
}
|
|
|
|
void ds1305_ClearInt (uint8_t nb) {
|
|
uint8_t tmp;
|
|
/* the read is just to clear the int flag */
|
|
tmp = _ReadSeconds(nb);
|
|
}
|
|
|
|
void ds1305_TimeToStr(struct ds1305_time time, char *string, uint8_t format)
|
|
{
|
|
uint8_t ctr;
|
|
ctr = 0;
|
|
if (format == ds1305_ASCII_FMT_TIME_EXT)
|
|
goto time;
|
|
if (format == ds1305_ASCII_FMT_ALARM)
|
|
goto alarm;
|
|
/* 20xx (century) is built in */
|
|
string[ctr++] = '2';
|
|
string[ctr++] = '0';
|
|
/* convert 10year to an ASCII number */
|
|
string[ctr++] = (time.year / 10) + ascii_NUMERIC_OFFSET;
|
|
/* convert year to an ASCII number */
|
|
string[ctr++] = (time.year % 10) + ascii_NUMERIC_OFFSET;
|
|
/* separator */
|
|
string[ctr++] = '-';
|
|
/* convert 10month to an ASCII number */
|
|
string[ctr++] = (time.month / 10) + ascii_NUMERIC_OFFSET;
|
|
/* convert month to an ASCII number */
|
|
string[ctr++] = (time.month % 10) + ascii_NUMERIC_OFFSET;
|
|
/* separator */
|
|
string[ctr++] = '-';
|
|
/* convert 10date to an ASCII number */
|
|
string[ctr++] = (time.date / 10) + ascii_NUMERIC_OFFSET;
|
|
/* convert date to an ASCII number */
|
|
string[ctr++] = (time.date % 10) + ascii_NUMERIC_OFFSET;
|
|
/* separator */
|
|
if (format == ds1305_ASCII_FMT_DATE_EXT)
|
|
goto end;
|
|
string[ctr++] = ' ';
|
|
/* skip the day if not an alarm */
|
|
if (format == ds1305_ASCII_FMT_FULL_EXT)
|
|
goto time;
|
|
alarm:
|
|
/* add the day of the week/alarm */
|
|
string[ctr++] = (time.day + ascii_NUMERIC_OFFSET);
|
|
string[ctr++] = ',';
|
|
string[ctr++] = ' ';
|
|
time:
|
|
/* convert 10hours to an ASCII number */
|
|
string[ctr++] = (time.hours / 10) + ascii_NUMERIC_OFFSET;
|
|
/* convert hours to an ASCII number */
|
|
string[ctr++] = (time.hours % 10) + ascii_NUMERIC_OFFSET;
|
|
/* separator */
|
|
string[ctr++] = ':';
|
|
/* convert 10minutes to an ASCII number */
|
|
string[ctr++] = (time.minutes / 10) + ascii_NUMERIC_OFFSET;
|
|
/* convert minutes to an ASCII number */
|
|
string[ctr++] = (time.minutes % 10) + ascii_NUMERIC_OFFSET;
|
|
/* separator */
|
|
string[ctr++] = ':';
|
|
/* convert 10seconds to an ASCII number */
|
|
string[ctr++] = (time.seconds / 10) + ascii_NUMERIC_OFFSET;
|
|
/* convert seconds to an ASCII number */
|
|
string[ctr++] = (time.seconds % 10) + ascii_NUMERIC_OFFSET;
|
|
end:
|
|
/* end the string */
|
|
string[ctr] = ascii_NULL;
|
|
}
|
|
|
|
void ds1305_ReadDateAscii(char *string)
|
|
{
|
|
struct ds1305_time date;
|
|
ds1305_ReadDate(&date);
|
|
ds1305_TimeToStr(date, string, ds1305_ASCII_FMT_DATE_EXT);
|
|
}
|
|
|
|
void ds1305_ReadTimeAscii(char *string)
|
|
{
|
|
struct ds1305_time time;
|
|
ds1305_ReadTime(&time);
|
|
ds1305_TimeToStr(time, string, ds1305_ASCII_FMT_TIME_EXT);
|
|
}
|
|
|
|
void ds1305_ReadAlarmAscii(char *string)
|
|
{
|
|
struct ds1305_time time;
|
|
ds1305_ReadTime(&time);
|
|
ds1305_TimeToStr(time, string, ds1305_ASCII_FMT_ALARM);
|
|
}
|
|
|
|
/**
|
|
* @brief Validate a date in ASCII format
|
|
* @param[in] str A pointer to the string
|
|
* @return The state of the validation
|
|
* @retval 0 String is OK
|
|
* @retval ds1305_ASCII_ERR_EMPTY String is empty
|
|
* @retval ds1305_ASCII_ERR_SHORT String is too short
|
|
* @retval ds1305_ASCII_ERR_LONG String is too long
|
|
* @retval ds1305_ASCII_ERR_DATE_FORMAT String format is not valid
|
|
* @retval ds1305_ASCII_ERR_DATE_RANGE String range is not valid
|
|
*/
|
|
uint8_t _ValidateAsciiDate (const char *str)
|
|
{
|
|
uint8_t status, ctr;
|
|
uint16_t year;
|
|
uint8_t month;
|
|
uint8_t day;
|
|
char tmp[5];
|
|
status = 0;
|
|
/* Super loop, exit whit break */
|
|
do {
|
|
ctr = strlen(str);
|
|
if (0 == ctr) {
|
|
status = ds1305_ASCII_ERR_EMPTY;
|
|
break;
|
|
}
|
|
if (ctr < ds1305_ASCII_FMT_DATE_EXT) {
|
|
status = ds1305_ASCII_ERR_SHORT;
|
|
break;
|
|
}
|
|
if (ctr > ds1305_ASCII_FMT_DATE_EXT) {
|
|
status = ds1305_ASCII_ERR_LONG;
|
|
break;
|
|
}
|
|
/* Date separator check */
|
|
if ((str[4] != '-') || (str[7] != '-')) {
|
|
status = ds1305_ASCII_ERR_DATE_FORMAT;
|
|
break;
|
|
}
|
|
/* Year check */
|
|
if (utils_IsNumber(str, 4, 0)) {
|
|
utils_substr(tmp, str, 4, 0);
|
|
year = atoi(tmp);
|
|
}
|
|
else {
|
|
status = ds1305_ASCII_ERR_DATE_FORMAT;
|
|
break;
|
|
}
|
|
/* Month check */
|
|
if (utils_IsNumber(str, 2, 5)) {
|
|
utils_substr(tmp, str, 2, 5);
|
|
month = atoi(tmp);
|
|
}
|
|
else {
|
|
status = ds1305_ASCII_ERR_DATE_FORMAT;
|
|
break;
|
|
}
|
|
/* Day check */
|
|
if (utils_IsNumber(str, 2, 8)) {
|
|
utils_substr(tmp, str, 2, 8);
|
|
day = atoi(tmp);
|
|
}
|
|
else {
|
|
status = ds1305_ASCII_ERR_DATE_FORMAT;
|
|
break;
|
|
}
|
|
/* Validate the date */
|
|
if (date_ValidateDate(year, month, day) != date_ERROR_RANGE_OK) {
|
|
status = ds1305_ASCII_ERR_DATE_RANGE;
|
|
break;
|
|
}
|
|
} while(0);
|
|
return (status);
|
|
}
|
|
|
|
/**
|
|
* @brief Validate a time in ASCII format
|
|
* @param[in] str A pointer to the string
|
|
* @return The state of the validation
|
|
* @retval 0 String is OK
|
|
* @retval ds1305_ASCII_ERR_EMPTY String is empty
|
|
* @retval ds1305_ASCII_ERR_SHORT String is too short
|
|
* @retval ds1305_ASCII_ERR_LONG String is too long
|
|
* @retval ds1305_ASCII_ERR_TIME_FORMAT String format is not valid
|
|
* @retval ds1305_ASCII_ERR_TIME_RANGE String range is not valid
|
|
*/
|
|
uint8_t _ValidateAsciiTime (const char *str)
|
|
{
|
|
uint8_t status, ctr;
|
|
uint8_t hour;
|
|
uint8_t minute;
|
|
uint8_t second;
|
|
char tmp[3];
|
|
status = 0;
|
|
/* Super loop, exit with break */
|
|
do {
|
|
ctr = strlen(str);
|
|
if (ctr == 0) {
|
|
status = ds1305_ASCII_ERR_EMPTY;
|
|
break;
|
|
}
|
|
if (ctr < ds1305_ASCII_FMT_TIME_EXT) {
|
|
status = ds1305_ASCII_ERR_SHORT;
|
|
break;
|
|
}
|
|
if (ctr > ds1305_ASCII_FMT_TIME_EXT) {
|
|
status = ds1305_ASCII_ERR_LONG;
|
|
break;
|
|
}
|
|
/* Time separator check */
|
|
if ((str[2] != ':') || (str[5] != ':')) {
|
|
status = ds1305_ASCII_ERR_TIME_FORMAT;
|
|
break;
|
|
}
|
|
/* Hour check */
|
|
if (utils_IsNumber(str, 2 ,0)) {
|
|
utils_substr(tmp, str, 2, 0);
|
|
hour = atoi(tmp);
|
|
}
|
|
else {
|
|
status = ds1305_ASCII_ERR_TIME_FORMAT;
|
|
break;
|
|
}
|
|
/* Minutes check */
|
|
if (utils_IsNumber(str, 2, 3)) {
|
|
utils_substr(tmp, str, 2, 3);
|
|
minute = atoi(tmp);
|
|
}
|
|
else {
|
|
status = ds1305_ASCII_ERR_TIME_FORMAT;
|
|
break;
|
|
}
|
|
/* Seconds check */
|
|
if (utils_IsNumber(str, 2, 6)) {
|
|
utils_substr(tmp, str, 2, 6);
|
|
second = atoi(tmp);
|
|
}
|
|
else {
|
|
status = ds1305_ASCII_ERR_TIME_FORMAT;
|
|
break;
|
|
}
|
|
/* Validate the time */
|
|
if (date_ValidateTime(hour, minute, second) != date_ERROR_RANGE_OK) {
|
|
status = ds1305_ASCII_ERR_TIME_RANGE;
|
|
break;
|
|
}
|
|
} while(0);
|
|
return (status);
|
|
}
|
|
|
|
|
|
/**
|
|
* @warning Need more checking before being ready
|
|
* @todo Add a check for alarms
|
|
**/
|
|
uint8_t ds1305_ValidateAscii(char *string, uint8_t format)
|
|
{
|
|
uint8_t status;
|
|
char str[11];
|
|
/* by default, the string is OK */
|
|
status = 0;
|
|
switch (format) {
|
|
case ds1305_ASCII_FMT_TIME_EXT :
|
|
status = _ValidateAsciiTime(string);
|
|
break;
|
|
case ds1305_ASCII_FMT_DATE_EXT :
|
|
status = _ValidateAsciiDate(string);
|
|
break;
|
|
case ds1305_ASCII_FMT_FULL_EXT :
|
|
// need to check the lenght of str here
|
|
/* Separator beetween the date and time check */
|
|
if (string[10] != ' ') {
|
|
status = ds1305_ASCII_ERR_INVALID;
|
|
break;
|
|
}
|
|
// put the date in str
|
|
status = _ValidateAsciiDate(str);
|
|
if (status != 0)
|
|
break;
|
|
// put the time into str
|
|
status = _ValidateAsciiTime(str);
|
|
break;
|
|
default :
|
|
status = ds1305_ASCII_UNDEFINED;
|
|
break;
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
/**
|
|
* @bug (internal) time.year must be initialized once before being written
|
|
* */
|
|
void ds1305_WriteDateAscii(const char *string)
|
|
{
|
|
char temp[3];
|
|
struct ds1305_time date;
|
|
date.year = 0;
|
|
/* YYxx */
|
|
utils_substr(temp, string, 2, 2);
|
|
date.year = atoi(temp);
|
|
/* YYYY-xx */
|
|
utils_substr(temp, string, 2, 5);
|
|
date.month = atoi(temp);
|
|
/* YYYY-MM-xx */
|
|
utils_substr(temp, string, 2, 8);
|
|
date.date = atoi(temp);
|
|
ds1305_WriteDate(date);
|
|
}
|
|
|
|
/**
|
|
* @bug see @e _WriteSecond
|
|
*/
|
|
void ds1305_WriteTimeAscii(const char *string)
|
|
{
|
|
char temp[3];
|
|
struct ds1305_time time;
|
|
/* Hours */
|
|
utils_substr(temp, string, 2, 0);
|
|
time.hours = atoi(temp);
|
|
/* HH:xx */
|
|
utils_substr(temp, string, 2, 3);
|
|
time.minutes = atoi(temp);
|
|
/* HH:MM:xx */
|
|
utils_substr(temp, string, 2, 6);
|
|
time.seconds = atoi(temp);
|
|
ds1305_WriteTime(time);
|
|
}
|
|
|
|
|
|
/* change log
|
|
* 2010-09-09 Patrice Nadeau
|
|
* First draft
|
|
* 2010-09-26 Patrice Nadeau
|
|
* First release
|
|
* 2010-09-27 Patrice Nadeau
|
|
* added ds1305_EnableInt() & ds1305_ClearInt()
|
|
* removed _WriteStatusRegister() (not used anyway)
|
|
* 2010-10-02 Patrice Nadeau
|
|
* 2011-01-08 Patrice Nadeau
|
|
* Changed comments for Doxygen
|
|
* 2011-01-12 Patrice Nadeau
|
|
* Changed code style to K&R
|
|
* 2011-01-16 Patrice Nadeau
|
|
* Added ds1395_WriteTimeAScii()
|
|
* _WriteSeconds() : was missing "+ _REG_WRITE_OFFSET"
|
|
* 2011-02-12 Patrice Nadeau
|
|
* _WriteProtectIsOn : added
|
|
* ds1305_WriteAlarm : changed the name of the parameters to be more concise,
|
|
rework was not setting bit 7, use of _WriteProtectIsOn
|
|
* _SetAlarmBit : added
|
|
* The write functions are now keeping the current status of write protect
|
|
* 2011-02-13 Patrice Nadeau
|
|
* Read and write functions are now using utils_DecToBcd & utils_BcdToDec
|
|
* Read and write function are now using an uint8_t parameters instead of the
|
|
ds1305_time structure
|
|
* _WriteHours : added parameters format, am_pm
|
|
* ds1305_TimeToStr ; added
|
|
* ds1305_ReadTimeASCII : modified to use ds1305_TimeToStr
|
|
* _ReadStatusRegister : added
|
|
* ds1305_ReadStatusRegister : added
|
|
* ds1305_ReadControlRegister : added
|
|
* 2011-02-19 Patrice Nadeau
|
|
* Added new #define to use an offset for the alarms registers
|
|
* ds1305_WriteAlarm : use the new offset, reduced code
|
|
* ds1305_ReadAlarm : use the new offset, reduced code
|
|
* _GetAlarmBit : renamed from _AlarmStatus
|
|
* 2011-02-26 Patrice Nadeau
|
|
* _IsWriteProtect : renamed from _WriteProtectIsOn
|
|
* 2011-03-03 Patrice Nadeau
|
|
* Moved the #define to ds1305.h, prefixed them with ds1305
|
|
* 2011-04-14 Patrice Nadeau
|
|
* ds1305_WriteTimeAscii : char *string -> const char *string
|
|
* _strncpyx : char *src -> const char *src
|
|
*/
|