Projet

Général

Profil

Publication de fichiers » ds1305.c

Redmine Admin, 2013-02-16 17:28

 
/*
*
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
*/
(1-1/3)