0
|
1 /*
|
|
2 * Interface to a DS1307
|
|
3 *
|
|
4 * Copyright (c) 2008
|
|
5 * Daniel O'Connor <darius@dons.net.au>. All rights reserved.
|
|
6 *
|
|
7 * Redistribution and use in source and binary forms, with or without
|
|
8 * modification, are permitted provided that the following conditions
|
|
9 * are met:
|
|
10 * 1. Redistributions of source code must retain the above copyright
|
|
11 * notice, this list of conditions and the following disclaimer.
|
|
12 * 2. Redistributions in binary form must reproduce the above copyright
|
|
13 * notice, this list of conditions and the following disclaimer in the
|
|
14 * documentation and/or other materials provided with the distribution.
|
|
15 *
|
|
16 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
26 * SUCH DAMAGE.
|
|
27 */
|
|
28
|
|
29 #include <stdio.h>
|
|
30 #include <stdlib.h>
|
|
31 #include <inttypes.h>
|
|
32 #include <avr/io.h>
|
|
33 #include <avr/pgmspace.h>
|
|
34 #include <util/twi.h>
|
|
35 #include <util/delay.h>
|
|
36
|
|
37 #include "ds1307.h"
|
|
38
|
|
39 // #define TWDEBUG
|
|
40
|
|
41 /*
|
|
42 * ds1307_init
|
|
43 *
|
|
44 * Setup TWI interface
|
|
45 *
|
|
46 */
|
|
47 int
|
|
48 ds1307_init(void) {
|
|
49 PRR &= _BV(PRTWI); /* Power TWI on - note that the
|
|
50 * datasheet says this is already 0 at
|
|
51 * power on.. */
|
|
52 TWSR = 0; /* TWI Prescaler = 1 */
|
|
53 #if F_CPU < 3600000UL
|
|
54 TWBR = 10; /* Smallest valid TWBR */
|
|
55 #else
|
|
56 TWBR = (F_CPU / 100000UL - 16) / 2;
|
|
57 #endif
|
|
58
|
|
59 TWCR = _BV(TWEN);
|
|
60
|
|
61 return(0);
|
|
62 }
|
|
63
|
|
64 /*
|
|
65 * iic_read
|
|
66 *
|
|
67 * Read len bytes of data from address adr in slave sla into
|
|
68 * data. Presume that the slave auto-increments the address on
|
|
69 * successive reads.
|
|
70 *
|
|
71 * Returns the number of bytes read, or the following on failure.
|
|
72 * IIC_STFAIL Could generate START condition (broken bus or busy).
|
|
73 * IIC_FAILARB Failed bus arbitration.
|
|
74 * IIC_SLNAK Slave NAK'd.
|
|
75 * IIC_NOREPLY No reply (no such slave?)
|
|
76 * IIC_UNKNOWN Unexpected return from TWI reg.
|
|
77 *
|
|
78 * Heaviy cribbed from twitest.c by Joerg Wunsch
|
|
79 */
|
|
80 int8_t
|
|
81 iic_read(uint8_t *data, uint8_t len, uint8_t adr, uint8_t sla) {
|
|
82 uint8_t twst, twcr, cnt;
|
|
83
|
|
84 /* Generate START */
|
|
85 TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
|
|
86
|
|
87 /* Spin waiting for START to be generated */
|
|
88 while ((TWCR & _BV(TWINT)) == 0)
|
|
89 ;
|
|
90 switch (twst = TW_STATUS) {
|
|
91 case TW_REP_START: /* OK but shouldn't happen */
|
|
92 case TW_START:
|
|
93 break;
|
|
94
|
|
95 case TW_MT_ARB_LOST:
|
|
96 return IIC_FAILARB;
|
|
97 break;
|
|
98
|
|
99 default:
|
|
100 /* Not in start condition, bail */
|
|
101 return IIC_UNKNOWN;
|
|
102 }
|
|
103 #ifdef TWDEBUG
|
|
104 printf_P(PSTR("Sent START\r\n"));
|
|
105 #endif
|
|
106 /* Send SLA+W */
|
|
107 TWDR = sla | TW_WRITE;
|
|
108 TWCR = _BV(TWINT) | _BV(TWEN);
|
|
109
|
|
110 /* Spin waiting for a response to be generated */
|
|
111 while ((TWCR & _BV(TWINT)) == 0)
|
|
112 ;
|
|
113
|
|
114 #ifdef TWDEBUG
|
|
115 printf_P(PSTR("Sent SLA+W\r\n"));
|
|
116 #endif
|
|
117 switch (twst = TW_STATUS) {
|
|
118 case TW_MT_SLA_ACK:
|
|
119 break;
|
|
120
|
|
121 case TW_MT_SLA_NACK:
|
|
122 /* Send STOP */
|
|
123 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
124 return IIC_SLNAK;
|
|
125
|
|
126 case TW_MT_ARB_LOST:
|
|
127 return IIC_FAILARB;
|
|
128 break;
|
|
129
|
|
130 default:
|
|
131 /* Send STOP */
|
|
132 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
133 return IIC_UNKNOWN;
|
|
134 }
|
|
135 /* Send address */
|
|
136 TWDR = adr;
|
|
137 TWCR = _BV(TWINT) | _BV(TWEN);
|
|
138
|
|
139 /* Spin waiting for a response to be generated */
|
|
140 while ((TWCR & _BV(TWINT)) == 0)
|
|
141 ;
|
|
142 #ifdef TWDEBUG
|
|
143 printf_P(PSTR("Sent address\r\n"));
|
|
144 #endif
|
|
145 switch ((twst = TW_STATUS)) {
|
|
146 case TW_MT_DATA_ACK:
|
|
147 break;
|
|
148
|
|
149 case TW_MT_DATA_NACK:
|
|
150 /* Send STOP */
|
|
151 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
152 return IIC_SLNAK;
|
|
153
|
|
154 case TW_MT_ARB_LOST:
|
|
155 return IIC_FAILARB;
|
|
156
|
|
157 default:
|
|
158 /* Send STOP */
|
|
159 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
160 return IIC_UNKNOWN;
|
|
161 }
|
|
162
|
|
163 /* Master receive cycle */
|
|
164 TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
|
|
165 while ((TWCR & _BV(TWINT)) == 0) /* wait for transmission */
|
|
166 ;
|
|
167
|
|
168 #ifdef TWDEBUG
|
|
169 printf_P(PSTR("Sent START\r\n"));
|
|
170 #endif
|
|
171 switch ((twst = TW_STATUS)) {
|
|
172 case TW_REP_START: /* OK but shouldn't happen */
|
|
173 case TW_START:
|
|
174 break;
|
|
175
|
|
176 case TW_MT_ARB_LOST:
|
|
177 return IIC_FAILARB;
|
|
178
|
|
179 default:
|
|
180 /* Send STOP */
|
|
181 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
182 return IIC_UNKNOWN;
|
|
183 }
|
|
184
|
|
185 /* send SLA+R */
|
|
186 TWDR = sla | TW_READ;
|
|
187 TWCR = _BV(TWINT) | _BV(TWEN); /* clear interrupt to start transmission */
|
|
188
|
|
189 /* Spin waiting for a response to be generated */
|
|
190 while ((TWCR & _BV(TWINT)) == 0)
|
|
191 ;
|
|
192 #ifdef TWDEBUG
|
|
193 printf_P(PSTR("Sent SLA+R\r\n"));
|
|
194 #endif
|
|
195 switch ((twst = TW_STATUS)) {
|
|
196 case TW_MR_SLA_ACK:
|
|
197 break;
|
|
198
|
|
199 case TW_MR_SLA_NACK:
|
|
200 /* Send STOP */
|
|
201 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
202 return IIC_SLNAK;
|
|
203
|
|
204 case TW_MR_ARB_LOST:
|
|
205 return IIC_FAILARB;
|
|
206
|
|
207 default:
|
|
208 /* Send STOP */
|
|
209 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
210 return IIC_UNKNOWN;
|
|
211 }
|
|
212
|
|
213 cnt = 0;
|
|
214 for (twcr = _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
|
|
215 len > 0; len--) {
|
|
216 /* Send NAK on last byte */
|
|
217 if (len == 1)
|
|
218 twcr = _BV(TWINT) | _BV(TWEN);
|
|
219 TWCR = twcr; /* clear int to start transmission */
|
|
220 /* Spin waiting for a response to be generated */
|
|
221 while ((TWCR & _BV(TWINT)) == 0)
|
|
222 ;
|
|
223 #ifdef TWDEBUG
|
|
224 printf_P(PSTR("Data request done\r\n"));
|
|
225 #endif
|
|
226 switch ((twst = TW_STATUS)) {
|
|
227 case TW_MR_DATA_NACK:
|
|
228 /* Send STOP */
|
|
229 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
230 //printf_P(PSTR("NACK on byte %d\r\n"), cnt);
|
|
231 return cnt;
|
|
232
|
|
233 case TW_MR_DATA_ACK:
|
|
234 *data++ = TWDR;
|
|
235 //printf_P(PSTR("ACK on byte %d for 0x%02x\r\n"), cnt, *(data - 1));
|
|
236 cnt++;
|
|
237 break;
|
|
238
|
|
239 default:
|
|
240 return IIC_UNKNOWN;
|
|
241 }
|
|
242 }
|
|
243
|
|
244 /* Send STOP */
|
|
245 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
246 return cnt;
|
|
247 }
|
|
248
|
|
249 /*
|
|
250 * iic_write
|
|
251 *
|
|
252 * Write len bytes of data from address adr in slave sla into
|
|
253 * data. Presume that the slave auto-increments the address on
|
|
254 * successive writes.
|
|
255 *
|
|
256 * Returns the number of bytes read, or the following on failure.
|
|
257 * IIC_STFAIL Could generate START condition (broken bus or busy).
|
|
258 * IIC_FAILARB Failed bus arbitration.
|
|
259 * IIC_SLNAK Slave NAK'd.
|
|
260 * IIC_NOREPLY No reply (no such slave?)
|
|
261 * IIC_UNKNOWN Unexpected return from TWI reg.
|
|
262 *
|
|
263 * Heaviy cribbed from twitest.c by Joerg Wunsch
|
|
264 */
|
|
265 int8_t
|
|
266 iic_write(uint8_t *data, uint8_t len, uint8_t adr, uint8_t sla) {
|
|
267 uint8_t twst, cnt;
|
|
268
|
|
269 /* Generate START */
|
|
270 TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);
|
|
271
|
|
272 /* Spin waiting for START to be generated */
|
|
273 while ((TWCR & _BV(TWINT)) == 0)
|
|
274 ;
|
|
275 switch (twst = TW_STATUS) {
|
|
276 case TW_REP_START: /* OK but shouldn't happen */
|
|
277 case TW_START:
|
|
278 break;
|
|
279
|
|
280 case TW_MT_ARB_LOST:
|
|
281 return IIC_FAILARB;
|
|
282 break;
|
|
283
|
|
284 default:
|
|
285 /* Not in start condition, bail */
|
|
286 return IIC_UNKNOWN;
|
|
287 }
|
|
288 #ifdef TWDEBUG
|
|
289 printf_P(PSTR("Sent START\r\n"));
|
|
290 #endif
|
|
291
|
|
292 /* Send SLA+W */
|
|
293 TWDR = sla | TW_WRITE;
|
|
294 TWCR = _BV(TWINT) | _BV(TWEN);
|
|
295
|
|
296 /* Spin waiting for a response to be generated */
|
|
297 while ((TWCR & _BV(TWINT)) == 0)
|
|
298 ;
|
|
299
|
|
300 #ifdef TWDEBUG
|
|
301 printf_P(PSTR("Sent SLA+W\r\n"));
|
|
302 #endif
|
|
303 switch (twst = TW_STATUS) {
|
|
304 case TW_MT_SLA_ACK:
|
|
305 break;
|
|
306
|
|
307 case TW_MT_SLA_NACK:
|
|
308 /* Send STOP */
|
|
309 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
310 return IIC_SLNAK;
|
|
311
|
|
312 case TW_MT_ARB_LOST:
|
|
313 return IIC_FAILARB;
|
|
314 break;
|
|
315
|
|
316 default:
|
|
317 /* Send STOP */
|
|
318 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
319 return IIC_UNKNOWN;
|
|
320 }
|
|
321 /* Send address */
|
|
322 TWDR = adr;
|
|
323 TWCR = _BV(TWINT) | _BV(TWEN);
|
|
324
|
|
325 /* Spin waiting for a response to be generated */
|
|
326 while ((TWCR & _BV(TWINT)) == 0)
|
|
327 ;
|
|
328 #ifdef TWDEBUG
|
|
329 printf_P(PSTR("Sent address\r\n"));
|
|
330 #endif
|
|
331 switch ((twst = TW_STATUS)) {
|
|
332 case TW_MT_DATA_ACK:
|
|
333 break;
|
|
334
|
|
335 case TW_MT_DATA_NACK:
|
|
336 /* Send STOP */
|
|
337 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
338 return IIC_SLNAK;
|
|
339
|
|
340 case TW_MT_ARB_LOST:
|
|
341 return IIC_FAILARB;
|
|
342
|
|
343 default:
|
|
344 /* Send STOP */
|
|
345 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
346 return IIC_UNKNOWN;
|
|
347 }
|
|
348
|
|
349 cnt = 0;
|
|
350 for (; len > 0; len--) {
|
|
351 TWDR = *data++;
|
|
352 TWCR = _BV(TWINT) | _BV(TWEN);
|
|
353
|
|
354 /* Spin waiting for a response to be generated */
|
|
355 while ((TWCR & _BV(TWINT)) == 0)
|
|
356 ;
|
|
357 #ifdef TWDEBUG
|
|
358 printf_P(PSTR("Data sent\r\n"));
|
|
359 #endif
|
|
360 switch ((twst = TW_STATUS)) {
|
|
361 case TW_MT_DATA_NACK:
|
|
362 /* Send STOP */
|
|
363 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
364 return cnt;
|
|
365
|
|
366 case TW_MT_DATA_ACK:
|
|
367 cnt++;
|
|
368 break;
|
|
369
|
|
370 default:
|
|
371 return IIC_UNKNOWN;
|
|
372 }
|
|
373 }
|
|
374
|
|
375 /* Send STOP */
|
|
376 TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);
|
|
377 return cnt;
|
|
378 }
|
|
379
|
|
380 /*
|
|
381 * ds1307_gettod
|
|
382 *
|
|
383 * Read time of day from DS1307 into time
|
|
384 *
|
|
385 * Note that we canonify to 24hr mode.
|
|
386 *
|
|
387 */
|
|
388 int8_t
|
|
389 ds1307_gettod(ds1307raw_t *time) {
|
|
390 int8_t len;
|
|
391
|
|
392 len = iic_read((uint8_t *)time, sizeof(ds1307raw_t) + 1, 0, DS1307_ADR);
|
|
393 if (len < 0) {
|
|
394 printf_P(PSTR("iic_read failed - %d\r\n"), len);
|
|
395 return(0);
|
|
396 }
|
|
397
|
|
398 #if 1
|
|
399 if (len != sizeof(ds1307raw_t)) {
|
|
400 printf_P(PSTR("Only got %d bytes (vs %d)\r\n"), len, sizeof(ds1307raw_t));
|
|
401 return(0);
|
|
402 }
|
|
403 #endif
|
|
404
|
|
405 #ifdef TWDEBUG
|
|
406 int i;
|
|
407
|
|
408 for (i = 0; i < len; i++)
|
|
409 printf_P(PSTR("0x%02x: 0x%02x\r\n"), i, *(((uint8_t *)time) + i));
|
|
410 #endif
|
|
411
|
|
412 return(1);
|
|
413 }
|
|
414
|
|
415 /*
|
|
416 * ds1307_settod
|
|
417 *
|
|
418 * Set the DS1307 with the supplied time, format like so
|
|
419 * sc 2008/10/29 23:45:30
|
|
420 *
|
|
421 */
|
|
422 int8_t
|
|
423 ds1307_settod(char *date) {
|
|
424 ds1307raw_t rtime;
|
|
425 uint16_t year;
|
|
426 uint8_t i, month, day, hour, min, sec;
|
|
427
|
|
428 if ((i = sscanf_P(date, PSTR("%hu/%hhd/%hhd %hhd:%hhd:%hhd"), &year, &month, &day, &hour, &min, &sec)) != 6) {
|
|
429 printf_P(PSTR("Can't parse date\r\n"));
|
|
430 return(0);
|
|
431 }
|
|
432
|
|
433 if (year > 1900)
|
|
434 year -= 1900;
|
|
435
|
|
436 rtime.split.year10 = year / 10;
|
|
437 rtime.split.year = year % 10;
|
|
438 rtime.split.month10 = month / 10;
|
|
439 rtime.split.month = month % 10;
|
|
440 rtime.split.day10 = day / 10;
|
|
441 rtime.split.day = day % 10;
|
|
442 rtime.split.pmam = ((hour / 10) & 0x02) >> 1;
|
|
443 rtime.split.hour10 = (hour / 10) & 0x01;
|
|
444 rtime.split.hour = hour % 10;
|
|
445 rtime.split.min10 = min / 10;
|
|
446 rtime.split.min = min % 10;
|
|
447 rtime.split.sec10 = sec / 10;
|
|
448 rtime.split.sec = sec % 10;
|
|
449
|
|
450 rtime.split.ch = 0; // Enable clock
|
|
451 rtime.split.s1224 = 0; // 24 hour mode
|
|
452 rtime.split.dow = 0; // XXX: unused
|
|
453 rtime.split.out = 0; // No clock out
|
|
454
|
|
455 #ifdef TWDEBUG
|
|
456 for (i = 0; i < sizeof(ds1307raw_t); i++)
|
|
457 printf_P(PSTR("0x%02x: 0x%02x\r\n"), i, *(((uint8_t *)&rtime) + i));
|
|
458 #endif
|
|
459 if ((i = iic_write((uint8_t *)&rtime, sizeof(ds1307raw_t), 0, DS1307_ADR)) != sizeof(ds1307raw_t))
|
|
460 printf_P(PSTR("Can't write to RTC, sent %d (vs %d)\r\n"), i, sizeof(ds1307raw_t));
|
|
461
|
|
462 return(1);
|
|
463 }
|
|
464
|
|
465 /*
|
|
466 * ds1307_printtime
|
|
467 *
|
|
468 * Print the time in rtime with trailer
|
|
469 *
|
|
470 */
|
|
471 void
|
|
472 ds1307_printtime(char *leader, char *trailer) {
|
|
473 ds1307raw_t rtime;
|
|
474 uint8_t hour;
|
|
475
|
|
476 if (ds1307_gettod(&rtime) != 1)
|
|
477 return;
|
|
478
|
|
479 // Handle 12/24 hour time
|
|
480 hour = rtime.split.hour10 * 10 + rtime.split.hour;
|
|
481 if (rtime.split.s1224) {
|
|
482 if (rtime.split.pmam)
|
|
483 hour += 12;
|
|
484 } else
|
|
485 hour += (rtime.split.pmam << 1) * 10;
|
|
486
|
|
487 printf_P(PSTR("%S%04d/%02d/%02d %02d:%02d:%02d%S"), leader,
|
|
488 1900 + rtime.split.year10 * 10 + rtime.split.year,
|
|
489 rtime.split.month10 * 10 + rtime.split.month,
|
|
490 rtime.split.day10 * 10 + rtime.split.day,
|
|
491 hour,
|
|
492 rtime.split.min10 * 10 + rtime.split.min,
|
|
493 rtime.split.sec10 * 10 + rtime.split.sec,
|
|
494 trailer);
|
|
495 }
|