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 
00070 #include <cfg/os.h>
00071 #include <cfg/eeprom.h>
00072 
00073 #include <dev/twif.h>
00074 #include <sys/event.h>
00075 #include <sys/timer.h>
00076 
00077 #include <time.h>
00078 #include <stdlib.h>
00079 #include <string.h>
00080 
00081 #include <dev/x12rtc.h>
00082 
00083 #if 0
00084 /* Use for local debugging. */
00085 #define NUTDEBUG
00086 #include <stdio.h>
00087 #endif
00088 
00089 #ifndef I2C_SLA_RTC
00090 #define I2C_SLA_RTC     0x6F
00091 #endif
00092 
00093 #ifndef I2C_SLA_EEPROM
00094 #define I2C_SLA_EEPROM  0x57
00095 #endif
00096 
00097 #ifndef EEPROM_PAGE_SIZE
00098 #define EEPROM_PAGE_SIZE    64
00099 #endif
00100 
00101 static u_char rtc_chip;
00102 static u_long rtc_status;
00103 
00112 static int X12WriteEnable(int on)
00113 {
00114     int rc;
00115     u_char buf[3];
00116 
00117     buf[0] = 0;
00118     buf[1] = 0x3F;
00119     if (on) {
00120         buf[2] = 0x02;
00121         if ((rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE)) == 0) {
00122             buf[2] = 0x06;
00123             rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00124         }
00125     } else {
00126         buf[2] = 0x00;
00127         rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00128     }
00129     return rc;
00130 }
00131 
00137 static int X12WaitReady(void)
00138 {
00139     u_char poll;
00140     int cnt = 200; /* Ethernut 3 needs about 50 loops, so this is quite save. */
00141 
00142     /* 
00143      * Poll for write cycle finished. We can't use a sleep here, because our
00144      * X12xx routines are not re-entrant.
00145      */
00146     while (--cnt && TwMasterTransact(I2C_SLA_EEPROM, 0, 0, &poll, 1, NUT_WAIT_INFINITE) == -1);
00147 #ifdef NUTDEBUG
00148     printf("[RtcRdy:%d]", 200 - cnt);
00149 #endif
00150 
00151     return cnt ? 0 : -1;
00152 }
00153 
00163 int X12RtcReadRegs(u_char reg, u_char *buff, size_t cnt)
00164 {
00165     int rc = -1;
00166     u_char wbuf[2];
00167 
00168     wbuf[0] = 0;
00169     wbuf[1] = reg;
00170     if (TwMasterTransact(I2C_SLA_RTC, wbuf, 2, buff, cnt, NUT_WAIT_INFINITE) == cnt) {
00171 #ifdef NUTDEBUG
00172         printf("[Rtc$%02x>", reg);
00173         while(cnt--) {
00174             printf(" %02x", *buff++);
00175         }
00176         putchar(']');
00177 #endif
00178         rc = 0;
00179     }
00180     return rc;
00181 }
00182 
00196 int X12RtcWrite(int nv, CONST u_char *buff, size_t cnt)
00197 {
00198     int rc;
00199 
00200     if ((rc = X12WriteEnable(1)) == 0) {
00201         rc = TwMasterTransact(I2C_SLA_RTC, buff, cnt, 0, 0, NUT_WAIT_INFINITE);
00202         if (rc == 0 && nv) {
00203             rc = X12WaitReady();
00204         }
00205         X12WriteEnable(0);
00206 #ifdef NUTDEBUG
00207         printf("[Rtc$%02X<", *++buff);
00208         cnt -= 2;
00209         while(cnt--) {
00210             printf(" %02x", *++buff);
00211         }
00212         putchar(']');
00213 #endif
00214     }
00215     return rc;
00216 }
00217 
00228 int X12RtcGetClock(struct _tm *tm)
00229 {
00230     int rc;
00231     u_char data[8];
00232 
00233     if ((rc = X12RtcReadRegs(X12RTC_SC, data, 8)) == 0) {
00234         tm->tm_sec = BCD2BIN(data[0]);
00235         tm->tm_min = BCD2BIN(data[1]);
00236         tm->tm_hour = BCD2BIN(data[2] & 0x3F);
00237         tm->tm_mday = BCD2BIN(data[3]);
00238         tm->tm_mon = BCD2BIN(data[4]) - 1;
00239         if (rtc_chip) {
00240             tm->tm_year = BCD2BIN(data[5]) + 100;
00241         }
00242         else {
00243             tm->tm_year = BCD2BIN(data[5]);
00244             if (data[7] == 0x20) {
00245                 tm->tm_year += 100;
00246             }
00247         }
00248         tm->tm_wday = data[6];
00249     }
00250     return rc;
00251 }
00252 
00265 int X12RtcSetClock(CONST struct _tm *tm)
00266 {
00267     u_char data[10];
00268 
00269     memset(data, 0, sizeof(data));
00270     if (tm) {
00271         data[1] = X12RTC_SC;
00272         data[2] = BIN2BCD(tm->tm_sec);
00273         data[3] = BIN2BCD(tm->tm_min);
00274         data[4] = BIN2BCD(tm->tm_hour) | 0x80;
00275         data[5] = BIN2BCD(tm->tm_mday);
00276         data[6] = BIN2BCD(tm->tm_mon + 1);
00277 
00278         if (rtc_chip) {
00279             /* X1286. */
00280             data[7] = BIN2BCD(tm->tm_year % 100);
00281         }
00282         /* X1226. */
00283         else if (tm->tm_year > 99) {
00284             data[7] = BIN2BCD(tm->tm_year - 100);
00285             data[9] = 0x20;
00286         }
00287         else {
00288             data[7] = BIN2BCD(tm->tm_year);
00289             data[9] = 0x19;
00290         }
00291         data[8] = tm->tm_wday;
00292     }
00293     return X12RtcWrite(0, data, 10);
00294 }
00295 
00309 int X12RtcGetAlarm(int idx, struct _tm *tm, int *aflgs)
00310 {
00311     int rc;
00312     u_char data[8];
00313 
00314     *aflgs = 0;
00315     memset(tm, 0, sizeof(struct _tm));
00316     if ((rc = X12RtcReadRegs(idx * 8, data, 8)) == 0) {
00317         if (data[0] & X12RTC_SCA_ESC) {
00318             *aflgs |= RTC_ALARM_SECOND;
00319             tm->tm_sec = BCD2BIN(data[0] & 0x7F);
00320         }
00321         if (data[1] & X12RTC_MNA_EMN) {
00322             *aflgs |= RTC_ALARM_MINUTE;
00323             tm->tm_min = BCD2BIN(data[1]);
00324         }
00325         if (data[2] & X12RTC_HRA_EHR) {
00326             *aflgs |= RTC_ALARM_HOUR;
00327             tm->tm_hour = BCD2BIN(data[2] & ~0x80);
00328         }
00329         if (data[3] & X12RTC_DTA_EDT) {
00330             *aflgs |= RTC_ALARM_MDAY;
00331             tm->tm_mday = BCD2BIN(data[3]);
00332         }
00333         if (data[4] & X12RTC_MOA_EMO) {
00334             *aflgs |= RTC_ALARM_MONTH;
00335             tm->tm_mon = BCD2BIN(data[4]) - 1;
00336         }
00337         if (data[6] & X12RTC_DWA_EDW) {
00338             *aflgs |= RTC_ALARM_WDAY;
00339             tm->tm_wday = BCD2BIN(data[6]);
00340         }
00341     }
00342     return rc;
00343 }
00344 
00363 int X12RtcSetAlarm(int idx, CONST struct _tm *tm, int aflgs)
00364 {
00365     u_char data[10];
00366 
00367     memset(data, 0, sizeof(data));
00368     data[1] = idx * 8;
00369     if (tm) {
00370         if (aflgs & RTC_ALARM_SECOND) {
00371             data[2] = BIN2BCD(tm->tm_sec) | X12RTC_SCA_ESC;
00372         }
00373         if (aflgs & RTC_ALARM_MINUTE) {
00374             data[3] = BIN2BCD(tm->tm_min) | X12RTC_MNA_EMN;
00375         }
00376         if (aflgs & RTC_ALARM_HOUR) {
00377             data[4] = BIN2BCD(tm->tm_hour) | X12RTC_HRA_EHR;
00378         }
00379         if (aflgs & RTC_ALARM_MDAY) {
00380             data[5] = BIN2BCD(tm->tm_mday) | X12RTC_DTA_EDT;
00381         }
00382         if (aflgs & RTC_ALARM_MONTH) {
00383             data[6] = BIN2BCD(tm->tm_mon + 1) | X12RTC_MOA_EMO;
00384         }
00385         if (aflgs & RTC_ALARM_WDAY) {
00386             data[8] = BIN2BCD(tm->tm_wday) | X12RTC_DWA_EDW;
00387         }
00388     }
00389     return X12RtcWrite(1, data, 10);
00390 }
00391 
00404 int X12RtcGetStatus(u_long *sflgs)
00405 {
00406     int rc;
00407     u_char data;
00408 
00409     if ((rc = X12RtcReadRegs(X12RTC_SR, &data, 1)) == 0) {
00410         rtc_status |= data;
00411         *sflgs = rtc_status;
00412     }
00413     return rc;
00414 }
00415 
00425 int X12RtcClearStatus(u_long sflgs)
00426 {
00427     rtc_status &= ~sflgs;
00428 
00429     return 0;
00430 }
00431 
00432 NUTRTC rtcX12x6 = {
00433     X12Init,            
00434     X12RtcGetClock,     
00435     X12RtcSetClock,     
00436     X12RtcGetAlarm,     
00437     X12RtcSetAlarm,     
00438     X12RtcGetStatus,    
00439     X12RtcClearStatus   
00440 };
00441 
00442 
00452 int X12EepromRead(u_int addr, void *buff, size_t len)
00453 {
00454     int rc = -1;
00455     u_char wbuf[2];
00456 
00457     wbuf[0] = (u_char)(addr >> 8);
00458     wbuf[1] = (u_char)addr;
00459     if (TwMasterTransact(I2C_SLA_EEPROM, wbuf, 2, buff, len, NUT_WAIT_INFINITE) == len) {
00460         rc = 0;
00461     }
00462     return rc;
00463 }
00464 
00477 int X12EepromWrite(u_int addr, CONST void *buff, size_t len)
00478 {
00479     int rc = 0;
00480     u_char *wbuf;
00481     size_t wlen;
00482     CONST u_char *wp = buff;
00483 
00484     /*
00485      * Loop for each page to be written to.
00486      */
00487     while (len) {
00488         /* Do not cross page boundaries. */
00489         wlen = EEPROM_PAGE_SIZE - (addr & (EEPROM_PAGE_SIZE - 1));
00490         if (wlen > len) {
00491             wlen = len;
00492         }
00493 
00494         /* Allocate and set a TWI write buffer. */
00495         if ((wbuf = malloc(wlen + 2)) == 0) {
00496             rc = -1;
00497             break;
00498         }
00499         wbuf[0] = (u_char)(addr >> 8);
00500         wbuf[1] = (u_char)addr;
00501         memcpy(wbuf + 2, (void *)wp, wlen);
00502 
00503         /* Enable EEPROM write access and send the write buffer. */
00504         if ((rc = X12WriteEnable(1)) == 0) {
00505             rc = TwMasterTransact(I2C_SLA_EEPROM, wbuf, wlen + 2, 0, 0, NUT_WAIT_INFINITE);
00506         }
00507 
00508         /* Release the buffer and check the result. */
00509         free(wbuf);
00510         if (rc) {
00511             break;
00512         }
00513         len -= wlen;
00514         addr += wlen;
00515         wp += wlen;
00516 
00517         /* Poll for write cycle finished. */
00518         if ((rc = X12WaitReady()) != 0) {
00519             break;
00520         }
00521     }
00522     X12WriteEnable(0);
00523 
00524     return rc;
00525 }
00526 
00535 int X12Init(void)
00536 {
00537     int rc;
00538     u_long tmp;
00539     u_char data[2];
00540 
00541     /* Initialize I2C bus. */
00542     if ((rc = TwInit(0)) == 0) {
00543         /* Query RTC status. */
00544         if ((rc = X12RtcGetStatus(&tmp)) == 0) {
00545             /*
00546              * If I2C initialization and RTC status query succeeded, try
00547              * to determine the chip we got. On Ethernut 3.0 Rev-D the
00548              * X1226 had been mounted, while the X1286 is used on Rev-E
00549              * boards. We read register address 0x0037, which is the
00550              * century on the X1226, but the hundreds of seconds on the
00551              * X1286. Thus, the register contents on the latter will cange
00552              * every 10 milliseconds, while it is fixed to 19 or 20 on
00553              * the first one.
00554              */
00555             X12RtcReadRegs(X128xRTC_SSEC, &data[0], 1);
00556             NutSleep(20);
00557             X12RtcReadRegs(X128xRTC_SSEC, &data[1], 1);
00558             if (data[0] != data[1]) {
00559                 rtc_chip = 1;
00560             }
00561 #ifdef NUTDEBUG
00562             printf("[RTC=X12%c6]", rtc_chip ? '8' : '2');
00563 #endif
00564         }
00565     }
00566     return rc;
00567 }

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