diff ds1307.c @ 0:3879f487b661

Initial commit of routines I copy and paste. Need work to make them more portable (esp cons)
author darius@Inchoate
date Wed, 11 Mar 2009 16:42:27 +1030
parents
children 15d89caaf516
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ds1307.c	Wed Mar 11 16:42:27 2009 +1030
@@ -0,0 +1,495 @@
+/*
+ * Interface to a DS1307
+ *
+ * Copyright (c) 2008
+ *      Daniel O'Connor <darius@dons.net.au>.  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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR 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 AUTHOR 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <util/twi.h>
+#include <util/delay.h>
+
+#include "ds1307.h"
+
+// #define TWDEBUG
+
+/* 
+ * ds1307_init
+ *
+ * Setup TWI interface
+ *
+ */
+int
+ds1307_init(void) {
+    PRR &= _BV(PRTWI);		/* Power TWI on - note that the
+				 * datasheet says this is already 0 at
+				 * power on.. */
+    TWSR = 0; 			/* TWI Prescaler = 1 */
+#if F_CPU < 3600000UL
+    TWBR = 10;			/* Smallest valid TWBR */
+#else
+    TWBR = (F_CPU / 100000UL - 16) / 2;
+#endif
+
+    TWCR = _BV(TWEN);
+
+    return(0);
+}
+
+/*
+ * iic_read
+ *
+ * Read len bytes of data from address adr in slave sla into
+ * data. Presume that the slave auto-increments the address on
+ * successive reads.
+ *
+ * Returns the number of bytes read, or the following on failure.
+ * IIC_STFAIL	Could generate START condition (broken bus or busy).
+ * IIC_FAILARB	Failed bus arbitration.
+ * IIC_SLNAK	Slave NAK'd.
+ * IIC_NOREPLY	No reply (no such slave?)
+ * IIC_UNKNOWN	Unexpected return from TWI reg.
+ *
+ * Heaviy cribbed from twitest.c by Joerg Wunsch
+ */
+int8_t
+iic_read(uint8_t *data, uint8_t len, uint8_t adr, uint8_t sla) {
+    uint8_t	twst, twcr, cnt;
+
+    /* Generate START */
+    TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
+
+    /* Spin waiting for START to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+    switch (twst = TW_STATUS) {
+	case TW_REP_START:	/* OK but shouldn't happen */
+	case TW_START:
+	    break;
+	    
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+	    break;
+
+	default:
+	    /* Not in start condition, bail */
+	    return IIC_UNKNOWN;
+    }
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent START\r\n"));
+#endif
+    /* Send SLA+W */
+    TWDR = sla | TW_WRITE;
+    TWCR = _BV(TWINT) | _BV(TWEN);
+
+    /* Spin waiting for a response to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent SLA+W\r\n"));
+#endif
+    switch (twst = TW_STATUS) {
+	case TW_MT_SLA_ACK:
+	    break;
+	    
+	case TW_MT_SLA_NACK:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_SLNAK;
+
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+	    break;
+
+	default:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_UNKNOWN;
+    }
+    /* Send address */
+    TWDR = adr;
+    TWCR = _BV(TWINT) | _BV(TWEN);
+
+    /* Spin waiting for a response to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent address\r\n"));
+#endif
+    switch ((twst = TW_STATUS)) {
+	case TW_MT_DATA_ACK:
+	    break;
+
+	case TW_MT_DATA_NACK:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_SLNAK;
+	    
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+
+	default:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_UNKNOWN;
+    }
+    
+    /* Master receive cycle */
+    TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
+    while ((TWCR & _BV(TWINT)) == 0) /* wait for transmission */
+	;
+    
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent START\r\n"));
+#endif    
+    switch ((twst = TW_STATUS)) {
+	case TW_REP_START: /* OK but shouldn't happen */
+	case TW_START:
+	    break;
+	    
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+	    
+	default:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_UNKNOWN;
+    }
+
+    /* send SLA+R */
+    TWDR = sla | TW_READ;
+    TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
+
+    /* Spin waiting for a response to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent SLA+R\r\n"));
+#endif
+    switch ((twst = TW_STATUS)) {
+	case TW_MR_SLA_ACK:
+	    break;
+
+	case TW_MR_SLA_NACK:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_SLNAK;
+	    
+	case TW_MR_ARB_LOST:
+	    return IIC_FAILARB;
+	    
+	default:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_UNKNOWN;
+    }
+
+    cnt = 0;
+    for (twcr = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
+	 len > 0; len--) {
+	/* Send NAK on last byte */
+	if (len == 1)
+	    twcr = _BV(TWINT) | _BV(TWEN); 
+	TWCR = twcr;		/* clear int to start transmission */
+	/* Spin waiting for a response to be generated */
+	while ((TWCR & _BV(TWINT)) == 0)
+	    ;
+#ifdef TWDEBUG
+	printf_P(PSTR("Data request done\r\n"));
+#endif
+	switch ((twst = TW_STATUS)) {
+	    case TW_MR_DATA_NACK:
+		/* Send STOP */
+		TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+		//printf_P(PSTR("NACK on byte %d\r\n"), cnt);
+		return cnt;
+		
+	    case TW_MR_DATA_ACK:
+		*data++ = TWDR;
+		//printf_P(PSTR("ACK on byte %d for 0x%02x\r\n"), cnt, *(data - 1));
+		cnt++;
+		break;
+
+	    default:
+		return IIC_UNKNOWN;
+	}
+    }
+
+    /* Send STOP */
+    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+    return cnt;
+}
+
+/*
+ * iic_write
+ *
+ * Write len bytes of data from address adr in slave sla into
+ * data. Presume that the slave auto-increments the address on
+ * successive writes.
+ *
+ * Returns the number of bytes read, or the following on failure.
+ * IIC_STFAIL	Could generate START condition (broken bus or busy).
+ * IIC_FAILARB	Failed bus arbitration.
+ * IIC_SLNAK	Slave NAK'd.
+ * IIC_NOREPLY	No reply (no such slave?)
+ * IIC_UNKNOWN	Unexpected return from TWI reg.
+ *
+ * Heaviy cribbed from twitest.c by Joerg Wunsch
+ */
+int8_t
+iic_write(uint8_t *data, uint8_t len, uint8_t adr, uint8_t sla) {
+    uint8_t	twst, cnt;
+
+    /* Generate START */
+    TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
+
+    /* Spin waiting for START to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+    switch (twst = TW_STATUS) {
+	case TW_REP_START:	/* OK but shouldn't happen */
+	case TW_START:
+	    break;
+	    
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+	    break;
+
+	default:
+	    /* Not in start condition, bail */
+	    return IIC_UNKNOWN;
+    }
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent START\r\n"));
+#endif
+
+    /* Send SLA+W */
+    TWDR = sla | TW_WRITE;
+    TWCR = _BV(TWINT) | _BV(TWEN);
+
+    /* Spin waiting for a response to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent SLA+W\r\n"));
+#endif
+    switch (twst = TW_STATUS) {
+	case TW_MT_SLA_ACK:
+	    break;
+	    
+	case TW_MT_SLA_NACK:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_SLNAK;
+
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+	    break;
+
+	default:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_UNKNOWN;
+    }
+    /* Send address */
+    TWDR = adr;
+    TWCR = _BV(TWINT) | _BV(TWEN);
+
+    /* Spin waiting for a response to be generated */
+    while ((TWCR & _BV(TWINT)) == 0)
+	;
+#ifdef TWDEBUG
+    printf_P(PSTR("Sent address\r\n"));
+#endif
+    switch ((twst = TW_STATUS)) {
+	case TW_MT_DATA_ACK:
+	    break;
+
+	case TW_MT_DATA_NACK:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_SLNAK;
+	    
+	case TW_MT_ARB_LOST:
+	    return IIC_FAILARB;
+
+	default:
+	    /* Send STOP */
+	    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+	    return IIC_UNKNOWN;
+    }
+    
+    cnt = 0;
+    for (; len > 0; len--) {
+	TWDR = *data++;
+	TWCR = _BV(TWINT) | _BV(TWEN);
+	
+	/* Spin waiting for a response to be generated */
+	while ((TWCR & _BV(TWINT)) == 0)
+	    ;
+#ifdef TWDEBUG
+    printf_P(PSTR("Data sent\r\n"));
+#endif
+    switch ((twst = TW_STATUS)) {
+	    case TW_MT_DATA_NACK:
+		/* Send STOP */
+		TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+		return cnt;
+		
+	    case TW_MT_DATA_ACK:
+		cnt++;
+		break;
+
+	    default:
+		return IIC_UNKNOWN;
+	}
+    }
+
+    /* Send STOP */
+    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
+    return cnt;
+}
+
+/* 
+ * ds1307_gettod
+ *
+ * Read time of day from DS1307 into time
+ *
+ * Note that we canonify to 24hr mode.
+ *
+ */
+int8_t
+ds1307_gettod(ds1307raw_t *time) {
+    int8_t len;
+
+    len = iic_read((uint8_t *)time, sizeof(ds1307raw_t) + 1, 0, DS1307_ADR);
+    if (len < 0) {
+	printf_P(PSTR("iic_read failed - %d\r\n"), len);
+	return(0);
+    }
+
+#if 1
+    if (len != sizeof(ds1307raw_t)) {
+	printf_P(PSTR("Only got %d bytes (vs %d)\r\n"), len, sizeof(ds1307raw_t));
+	return(0);
+    }
+#endif
+
+#ifdef TWDEBUG
+    int i;
+    
+    for (i = 0; i < len; i++)
+	printf_P(PSTR("0x%02x: 0x%02x\r\n"), i, *(((uint8_t *)time) + i));
+#endif
+
+    return(1);
+}
+
+/* 
+ * ds1307_settod
+ *
+ * Set the DS1307 with the supplied time, format like so
+ * sc 2008/10/29 23:45:30
+ *
+ */
+int8_t
+ds1307_settod(char *date) {
+    ds1307raw_t rtime;
+    uint16_t year;
+    uint8_t i,  month, day, hour, min, sec;
+
+    if ((i = sscanf_P(date, PSTR("%hu/%hhd/%hhd %hhd:%hhd:%hhd"), &year, &month, &day, &hour, &min, &sec)) != 6) {
+	printf_P(PSTR("Can't parse date\r\n"));
+	return(0);
+    }
+
+    if (year > 1900)
+	year -= 1900;
+    
+    rtime.split.year10 = year / 10;
+    rtime.split.year = year % 10;
+    rtime.split.month10 = month / 10;
+    rtime.split.month = month % 10;
+    rtime.split.day10 = day / 10;
+    rtime.split.day = day % 10;
+    rtime.split.pmam = ((hour / 10) & 0x02) >> 1;
+    rtime.split.hour10 = (hour / 10) & 0x01;
+    rtime.split.hour = hour % 10;
+    rtime.split.min10 = min / 10;
+    rtime.split.min = min % 10;
+    rtime.split.sec10 = sec / 10;
+    rtime.split.sec = sec % 10;
+
+    rtime.split.ch = 0; // Enable clock
+    rtime.split.s1224 = 0; // 24 hour mode
+    rtime.split.dow = 0; // XXX: unused
+    rtime.split.out = 0; // No clock out
+
+#ifdef TWDEBUG
+    for (i = 0; i < sizeof(ds1307raw_t); i++)
+	printf_P(PSTR("0x%02x: 0x%02x\r\n"), i, *(((uint8_t *)&rtime) + i));
+#endif
+    if ((i = iic_write((uint8_t *)&rtime, sizeof(ds1307raw_t), 0, DS1307_ADR)) != sizeof(ds1307raw_t))
+	printf_P(PSTR("Can't write to RTC, sent %d (vs %d)\r\n"), i, sizeof(ds1307raw_t));
+    
+    return(1);
+}
+
+/* 
+ * ds1307_printtime
+ *
+ * Print the time in rtime with trailer
+ *
+ */
+void
+ds1307_printtime(char *leader, char *trailer) {
+    ds1307raw_t rtime;
+    uint8_t hour;
+
+    if (ds1307_gettod(&rtime) != 1)
+	return;
+    
+    // Handle 12/24 hour time
+    hour = rtime.split.hour10 * 10 + rtime.split.hour;
+    if (rtime.split.s1224) {
+	if (rtime.split.pmam)
+	    hour += 12;
+    } else
+	hour += (rtime.split.pmam << 1) * 10;
+	
+    printf_P(PSTR("%S%04d/%02d/%02d %02d:%02d:%02d%S"), leader,
+	     1900 + rtime.split.year10 * 10 + rtime.split.year,
+	     rtime.split.month10 * 10 + rtime.split.month,
+	     rtime.split.day10 * 10 + rtime.split.day,
+	     hour,
+	     rtime.split.min10 * 10 + rtime.split.min,
+	     rtime.split.sec10 * 10 + rtime.split.sec,
+	     trailer);
+}