x12rtc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2005-2007 by egnite Software GmbH. All rights reserved.
00003  *
00004  * Redistribution and use in source and binary forms, with or without
00005  * modification, are permitted provided that the following conditions
00006  * are met:
00007  *
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the copyright holders nor the names of
00014  *    contributors may be used to endorse or promote products derived
00015  *    from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS
00018  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00019  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00020  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE
00021  * SOFTWARE GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00023  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00024  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00025  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00026  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00027  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * For additional information see http://www.ethernut.de/
00031  *
00032  */
00033 
00067 #include <cfg/os.h>
00068 #include <dev/twif.h>
00069 #include <sys/event.h>
00070 #include <sys/timer.h>
00071 
00072 #include <time.h>
00073 #include <stdlib.h>
00074 #include <string.h>
00075 
00076 #include <dev/x12rtc.h>
00077 
00078 #if 0
00079 /* Use for local debugging. */
00080 #define NUTDEBUG
00081 #include <stdio.h>
00082 #endif
00083 
00084 #ifndef I2C_SLA_RTC
00085 #define I2C_SLA_RTC     0x6F
00086 #endif
00087 
00088 #ifndef I2C_SLA_EEPROM
00089 #define I2C_SLA_EEPROM  0x57
00090 #endif
00091 
00092 #ifndef EEPROM_PAGE_SIZE
00093 #define EEPROM_PAGE_SIZE    64
00094 #endif
00095 
00096 static u_char rtc_chip;
00097 static u_long rtc_status;
00098 
00107 static int X12WriteEnable(int on)
00108 {
00109     int rc;
00110     u_char buf[3];
00111 
00112     buf[0] = 0;
00113     buf[1] = 0x3F;
00114     if (on) {
00115         buf[2] = 0x02;
00116         if ((rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE)) == 0) {
00117             buf[2] = 0x06;
00118             rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00119         }
00120     } else {
00121         buf[2] = 0x00;
00122         rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00123     }
00124     return rc;
00125 }
00126 
00132 static int X12WaitReady(void)
00133 {
00134     u_char poll;
00135     int cnt = 200; /* Ethernut 3 needs about 50 loops, so this is quite save. */
00136 
00137     /* 
00138      * Poll for write cycle finished. We can't use a sleep here, because our
00139      * X12xx routines are not re-entrant.
00140      */
00141     while (--cnt && TwMasterTransact(I2C_SLA_EEPROM, 0, 0, &poll, 1, NUT_WAIT_INFINITE) == -1);
00142 #ifdef NUTDEBUG
00143     printf("[RtcRdy:%d]", 200 - cnt);
00144 #endif
00145 
00146     return cnt ? 0 : -1;
00147 }
00148 
00158 int X12RtcReadRegs(u_char reg, u_char *buff, size_t cnt)
00159 {
00160     int rc = -1;
00161     u_char wbuf[2];
00162 
00163     wbuf[0] = 0;
00164     wbuf[1] = reg;
00165     if (TwMasterTransact(I2C_SLA_RTC, wbuf, 2, buff, cnt, NUT_WAIT_INFINITE) == cnt) {
00166 #ifdef NUTDEBUG
00167         printf("[Rtc$%02x>", reg);
00168         while(cnt--) {
00169             printf(" %02x", *buff++);
00170         }
00171         putchar(']');
00172 #endif
00173         rc = 0;
00174     }
00175     return rc;
00176 }
00177 
00191 int X12RtcWrite(int nv, CONST u_char *buff, size_t cnt)
00192 {
00193     int rc;
00194 
00195     if ((rc = X12WriteEnable(1)) == 0) {
00196         rc = TwMasterTransact(I2C_SLA_RTC, buff, cnt, 0, 0, NUT_WAIT_INFINITE);
00197         if (rc == 0 && nv) {
00198             rc = X12WaitReady();
00199         }
00200         X12WriteEnable(0);
00201 #ifdef NUTDEBUG
00202         printf("[Rtc$%02X<", *++buff);
00203         cnt -= 2;
00204         while(cnt--) {
00205             printf(" %02x", *++buff);
00206         }
00207         putchar(']');
00208 #endif
00209     }
00210     return rc;
00211 }
00212 
00223 int X12RtcGetClock(struct _tm *tm)
00224 {
00225     int rc;
00226     u_char data[8];
00227 
00228     if ((rc = X12RtcReadRegs(X12RTC_SC, data, 8)) == 0) {
00229         tm->tm_sec = BCD2BIN(data[0]);
00230         tm->tm_min = BCD2BIN(data[1]);
00231         tm->tm_hour = BCD2BIN(data[2] & 0x3F);
00232         tm->tm_mday = BCD2BIN(data[3]);
00233         tm->tm_mon = BCD2BIN(data[4]) - 1;
00234         if (rtc_chip) {
00235             tm->tm_year = BCD2BIN(data[5]) + 100;
00236         }
00237         else {
00238             tm->tm_year = BCD2BIN(data[5]);
00239             if (data[7] == 0x20) {
00240                 tm->tm_year += 100;
00241             }
00242         }
00243         tm->tm_wday = data[6];
00244     }
00245     return rc;
00246 }
00247 
00260 int X12RtcSetClock(CONST struct _tm *tm)
00261 {
00262     u_char data[10];
00263 
00264     memset(data, 0, sizeof(data));
00265     if (tm) {
00266         data[1] = X12RTC_SC;
00267         data[2] = BIN2BCD(tm->tm_sec);
00268         data[3] = BIN2BCD(tm->tm_min);
00269         data[4] = BIN2BCD(tm->tm_hour) | 0x80;
00270         data[5] = BIN2BCD(tm->tm_mday);
00271         data[6] = BIN2BCD(tm->tm_mon + 1);
00272 
00273         if (rtc_chip) {
00274             /* X1286. */
00275             data[7] = BIN2BCD(tm->tm_year % 100);
00276         }
00277         /* X1226. */
00278         else if (tm->tm_year > 99) {
00279             data[7] = BIN2BCD(tm->tm_year - 100);
00280             data[9] = 0x20;
00281         }
00282         else {
00283             data[7] = BIN2BCD(tm->tm_year);
00284             data[9] = 0x19;
00285         }
00286         data[8] = tm->tm_wday;
00287     }
00288     return X12RtcWrite(0, data, 10);
00289 }
00290 
00304 int X12RtcGetAlarm(int idx, struct _tm *tm, int *aflgs)
00305 {
00306     int rc;
00307     u_char data[8];
00308 
00309     *aflgs = 0;
00310     memset(tm, 0, sizeof(struct _tm));
00311     if ((rc = X12RtcReadRegs(idx * 8, data, 8)) == 0) {
00312         if (data[0] & X12RTC_SCA_ESC) {
00313             *aflgs |= RTC_ALARM_SECOND;
00314             tm->tm_sec = BCD2BIN(data[0] & 0x7F);
00315         }
00316         if (data[1] & X12RTC_MNA_EMN) {
00317             *aflgs |= RTC_ALARM_MINUTE;
00318             tm->tm_min = BCD2BIN(data[1]);
00319         }
00320         if (data[2] & X12RTC_HRA_EHR) {
00321             *aflgs |= RTC_ALARM_HOUR;
00322             tm->tm_hour = BCD2BIN(data[2] & ~0x80);
00323         }
00324         if (data[3] & X12RTC_DTA_EDT) {
00325             *aflgs |= RTC_ALARM_MDAY;
00326             tm->tm_mday = BCD2BIN(data[3]);
00327         }
00328         if (data[4] & X12RTC_MOA_EMO) {
00329             *aflgs |= RTC_ALARM_MONTH;
00330             tm->tm_mon = BCD2BIN(data[4]) - 1;
00331         }
00332         if (data[6] & X12RTC_DWA_EDW) {
00333             *aflgs |= RTC_ALARM_WDAY;
00334             tm->tm_wday = BCD2BIN(data[6]);
00335         }
00336     }
00337     return rc;
00338 }
00339 
00358 int X12RtcSetAlarm(int idx, CONST struct _tm *tm, int aflgs)
00359 {
00360     u_char data[10];
00361 
00362     memset(data, 0, sizeof(data));
00363     data[1] = idx * 8;
00364     if (tm) {
00365         if (aflgs & RTC_ALARM_SECOND) {
00366             data[2] = BIN2BCD(tm->tm_sec) | X12RTC_SCA_ESC;
00367         }
00368         if (aflgs & RTC_ALARM_MINUTE) {
00369             data[3] = BIN2BCD(tm->tm_min) | X12RTC_MNA_EMN;
00370         }
00371         if (aflgs & RTC_ALARM_HOUR) {
00372             data[4] = BIN2BCD(tm->tm_hour) | X12RTC_HRA_EHR;
00373         }
00374         if (aflgs & RTC_ALARM_MDAY) {
00375             data[5] = BIN2BCD(tm->tm_mday) | X12RTC_DTA_EDT;
00376         }
00377         if (aflgs & RTC_ALARM_MONTH) {
00378             data[6] = BIN2BCD(tm->tm_mon + 1) | X12RTC_MOA_EMO;
00379         }
00380         if (aflgs & RTC_ALARM_WDAY) {
00381             data[8] = BIN2BCD(tm->tm_wday) | X12RTC_DWA_EDW;
00382         }
00383     }
00384     return X12RtcWrite(1, data, 10);
00385 }
00386 
00399 int X12RtcGetStatus(u_long *sflgs)
00400 {
00401     int rc;
00402     u_char data;
00403 
00404     if ((rc = X12RtcReadRegs(X12RTC_SR, &data, 1)) == 0) {
00405         rtc_status |= data;
00406         *sflgs = rtc_status;
00407     }
00408     return rc;
00409 }
00410 
00420 int X12RtcClearStatus(u_long sflgs)
00421 {
00422     rtc_status &= ~sflgs;
00423 
00424     return 0;
00425 }
00426 
00427 NUTRTC rtcX12x6 = {
00428     X12Init,            
00429     X12RtcGetClock,     
00430     X12RtcSetClock,     
00431     X12RtcGetAlarm,     
00432     X12RtcSetAlarm,     
00433     X12RtcGetStatus,    
00434     X12RtcClearStatus   
00435 };
00436 
00437 
00447 int X12EepromRead(u_int addr, void *buff, size_t len)
00448 {
00449     int rc = -1;
00450     u_char wbuf[2];
00451 
00452     wbuf[0] = (u_char)(addr >> 8);
00453     wbuf[1] = (u_char)addr;
00454     if (TwMasterTransact(I2C_SLA_EEPROM, wbuf, 2, buff, len, NUT_WAIT_INFINITE) == len) {
00455         rc = 0;
00456     }
00457     return rc;
00458 }
00459 
00472 int X12EepromWrite(u_int addr, CONST void *buff, size_t len)
00473 {
00474     int rc = 0;
00475     u_char *wbuf;
00476     size_t wlen;
00477     CONST u_char *wp = buff;
00478 
00479     /*
00480      * Loop for each page to be written to.
00481      */
00482     while (len) {
00483         /* Do not cross page boundaries. */
00484         wlen = EEPROM_PAGE_SIZE - (addr & (EEPROM_PAGE_SIZE - 1));
00485         if (wlen > len) {
00486             wlen = len;
00487         }
00488 
00489         /* Allocate and set a TWI write buffer. */
00490         if ((wbuf = malloc(wlen + 2)) == 0) {
00491             rc = -1;
00492             break;
00493         }
00494         wbuf[0] = (u_char)(addr >> 8);
00495         wbuf[1] = (u_char)addr;
00496         memcpy(wbuf + 2, (void *)wp, wlen);
00497 
00498         /* Enable EEPROM write access and send the write buffer. */
00499         if ((rc = X12WriteEnable(1)) == 0) {
00500             rc = TwMasterTransact(I2C_SLA_EEPROM, wbuf, wlen + 2, 0, 0, NUT_WAIT_INFINITE);
00501         }
00502 
00503         /* Release the buffer and check the result. */
00504         free(wbuf);
00505         if (rc) {
00506             break;
00507         }
00508         len -= wlen;
00509         addr += wlen;
00510         wp += wlen;
00511 
00512         /* Poll for write cycle finished. */
00513         if ((rc = X12WaitReady()) != 0) {
00514             break;
00515         }
00516     }
00517     X12WriteEnable(0);
00518 
00519     return rc;
00520 }
00521 
00530 int X12Init(void)
00531 {
00532     int rc;
00533     u_long tmp;
00534     u_char data[2];
00535 
00536     /* Initialize I2C bus. */
00537     if ((rc = TwInit(0)) == 0) {
00538         /* Query RTC status. */
00539         if ((rc = X12RtcGetStatus(&tmp)) == 0) {
00540             /*
00541              * If I2C initialization and RTC status query succeeded, try
00542              * to determine the chip we got. On Ethernut 3.0 Rev-D the
00543              * X1226 had been mounted, while the X1286 is used on Rev-E
00544              * boards. We read register address 0x0037, which is the
00545              * century on the X1226, but the hundreds of seconds on the
00546              * X1286. Thus, the register contents on the latter will cange
00547              * every 10 milliseconds, while it is fixed to 19 or 20 on
00548              * the first one.
00549              */
00550             X12RtcReadRegs(X128xRTC_SSEC, &data[0], 1);
00551             NutSleep(20);
00552             X12RtcReadRegs(X128xRTC_SSEC, &data[1], 1);
00553             if (data[0] != data[1]) {
00554                 rtc_chip = 1;
00555             }
00556 #ifdef NUTDEBUG
00557             printf("[RTC=X12%c6]", rtc_chip ? '8' : '2');
00558 #endif
00559         }
00560     }
00561     return rc;
00562 }

© 2000-2007 by egnite Software GmbH - visit http://www.ethernut.de/