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

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