Mercurial > ~darius > hgwebdir.cgi > avr-lib
view ds1307.c @ 8:119688bb743f
Don't spin forever waiting for the TWI hardware to do something.
I _think_ this will help the case where I find the micro is hanging but I
haven't seen an error from it yet.
author | darius@dons.net.au |
---|---|
date | Fri, 01 May 2009 15:21:31 +0930 |
parents | 3da232f97e81 |
children | b5e4591b6570 |
line wrap: on
line source
/* * 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 /* Helper code to wait for the TWCR to do something */ #define WAITFORTWINT() do { \ uint8_t i; \ for (i = 0; i < 255 && (TWCR & _BV(TWINT)) == 0; i++) \ _delay_ms(1); \ if (i == 255) \ return(IIC_NORESP); \ } while (0) /* * ds1307_init * * Setup TWI interface * */ int ds1307_init(void) { #ifdef PRR PRR &= ~_BV(PRTWI); /* Power TWI on - note that the * datasheet says this is already 0 at * power on.. */ #endif 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 */ WAITFORTWINT(); 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 */ WAITFORTWINT(); #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 */ WAITFORTWINT(); #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); /* wait for transmission */ WAITFORTWINT(); #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 */ WAITFORTWINT(); #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 */ WAITFORTWINT(); #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 */ WAITFORTWINT(); 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 */ WAITFORTWINT(); #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 */ WAITFORTWINT(); #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 */ WAITFORTWINT(); #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); }