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 
00080 #include <cfg/os.h>
00081 #include <cfg/eeprom.h>
00082 
00083 #include <dev/twif.h>
00084 #include <sys/event.h>
00085 #include <sys/timer.h>
00086 
00087 #include <time.h>
00088 #include <stdlib.h>
00089 #include <string.h>
00090 #include <memdebug.h>
00091 
00092 #include <dev/x12rtc.h>
00093 
00094 #if 0
00095 /* Use for local debugging. */
00096 #define NUTDEBUG
00097 #include <stdio.h>
00098 #endif
00099 
00100 #ifndef I2C_SLA_RTC
00101 #define I2C_SLA_RTC     0x6F
00102 #endif
00103 
00104 #ifndef I2C_SLA_EEPROM
00105 #define I2C_SLA_EEPROM  0x57
00106 #endif
00107 
00108 #ifndef EEPROM_PAGE_SIZE
00109 #define EEPROM_PAGE_SIZE    64
00110 #endif
00111 
00112 static uint8_t rtc_chip;
00113 static uint32_t rtc_status;
00114 
00123 static int X12WriteEnable(int on)
00124 {
00125     int rc;
00126     uint8_t buf[3];
00127 
00128     buf[0] = 0;
00129     buf[1] = 0x3F;
00130     if (on) {
00131         buf[2] = 0x02;
00132         if ((rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE)) == 0) {
00133             buf[2] = 0x06;
00134             rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00135         }
00136     } else {
00137         buf[2] = 0x00;
00138         rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00139     }
00140     return rc;
00141 }
00142 
00148 static int X12WaitReady(void)
00149 {
00150     uint8_t poll;
00151     int cnt = 200; /* Ethernut 3 needs about 50 loops, so this is quite save. */
00152 
00153     /* 
00154      * Poll for write cycle finished. We can't use a sleep here, because our
00155      * X12xx routines are not re-entrant.
00156      */
00157     while (--cnt && TwMasterTransact(I2C_SLA_EEPROM, 0, 0, &poll, 1, NUT_WAIT_INFINITE) == -1);
00158 #ifdef NUTDEBUG
00159     printf("[RtcRdy:%d]", 200 - cnt);
00160 #endif
00161 
00162     return cnt ? 0 : -1;
00163 }
00164 
00174 int X12RtcReadRegs(uint8_t reg, uint8_t *buff, size_t cnt)
00175 {
00176     int rc = -1;
00177     uint8_t wbuf[2];
00178 
00179     wbuf[0] = 0;
00180     wbuf[1] = reg;
00181     if (TwMasterTransact(I2C_SLA_RTC, wbuf, 2, buff, cnt, NUT_WAIT_INFINITE) == cnt) {
00182 #ifdef NUTDEBUG
00183         printf("[Rtc$%02x>", reg);
00184         while(cnt--) {
00185             printf(" %02x", *buff++);
00186         }
00187         putchar(']');
00188 #endif
00189         rc = 0;
00190     }
00191     return rc;
00192 }
00193 
00207 int X12RtcWrite(int nv, CONST uint8_t *buff, size_t cnt)
00208 {
00209     int rc;
00210 
00211     if ((rc = X12WriteEnable(1)) == 0) {
00212         rc = TwMasterTransact(I2C_SLA_RTC, buff, cnt, 0, 0, NUT_WAIT_INFINITE);
00213         if (rc == 0 && nv) {
00214             rc = X12WaitReady();
00215         }
00216         X12WriteEnable(0);
00217 #ifdef NUTDEBUG
00218         printf("[Rtc$%02X<", *++buff);
00219         cnt -= 2;
00220         while(cnt--) {
00221             printf(" %02x", *++buff);
00222         }
00223         putchar(']');
00224 #endif
00225     }
00226     return rc;
00227 }
00228 
00239 int X12RtcGetClock(struct _tm *tm)
00240 {
00241     int rc;
00242     uint8_t data[8];
00243 
00244     if ((rc = X12RtcReadRegs(X12RTC_SC, data, 8)) == 0) {
00245         tm->tm_sec = BCD2BIN(data[0]);
00246         tm->tm_min = BCD2BIN(data[1]);
00247         tm->tm_hour = BCD2BIN(data[2] & 0x3F);
00248         tm->tm_mday = BCD2BIN(data[3]);
00249         tm->tm_mon = BCD2BIN(data[4]) - 1;
00250         if (rtc_chip) {
00251             tm->tm_year = BCD2BIN(data[5]) + 100;
00252         }
00253         else {
00254             tm->tm_year = BCD2BIN(data[5]);
00255             if (data[7] == 0x20) {
00256                 tm->tm_year += 100;
00257             }
00258         }
00259         tm->tm_wday = data[6];
00260     }
00261     return rc;
00262 }
00263 
00276 int X12RtcSetClock(CONST struct _tm *tm)
00277 {
00278     uint8_t data[10];
00279 
00280     memset(data, 0, sizeof(data));
00281     if (tm) {
00282         data[1] = X12RTC_SC;
00283         data[2] = BIN2BCD(tm->tm_sec);
00284         data[3] = BIN2BCD(tm->tm_min);
00285         data[4] = BIN2BCD(tm->tm_hour) | 0x80;
00286         data[5] = BIN2BCD(tm->tm_mday);
00287         data[6] = BIN2BCD(tm->tm_mon + 1);
00288 
00289         if (rtc_chip) {
00290             /* X1286. */
00291             data[7] = BIN2BCD(tm->tm_year % 100);
00292         }
00293         /* X1226. */
00294         else if (tm->tm_year > 99) {
00295             data[7] = BIN2BCD(tm->tm_year - 100);
00296             data[9] = 0x20;
00297         }
00298         else {
00299             data[7] = BIN2BCD(tm->tm_year);
00300             data[9] = 0x19;
00301         }
00302         data[8] = tm->tm_wday;
00303     }
00304     return X12RtcWrite(0, data, 10);
00305 }
00306 
00320 int X12RtcGetAlarm(int idx, struct _tm *tm, int *aflgs)
00321 {
00322     int rc;
00323     uint8_t data[8];
00324 
00325     *aflgs = 0;
00326     memset(tm, 0, sizeof(struct _tm));
00327     if ((rc = X12RtcReadRegs(idx * 8, data, 8)) == 0) {
00328         if (data[0] & X12RTC_SCA_ESC) {
00329             *aflgs |= RTC_ALARM_SECOND;
00330             tm->tm_sec = BCD2BIN(data[0] & 0x7F);
00331         }
00332         if (data[1] & X12RTC_MNA_EMN) {
00333             *aflgs |= RTC_ALARM_MINUTE;
00334             tm->tm_min = BCD2BIN(data[1]);
00335         }
00336         if (data[2] & X12RTC_HRA_EHR) {
00337             *aflgs |= RTC_ALARM_HOUR;
00338             tm->tm_hour = BCD2BIN(data[2] & ~0x80);
00339         }
00340         if (data[3] & X12RTC_DTA_EDT) {
00341             *aflgs |= RTC_ALARM_MDAY;
00342             tm->tm_mday = BCD2BIN(data[3]);
00343         }
00344         if (data[4] & X12RTC_MOA_EMO) {
00345             *aflgs |= RTC_ALARM_MONTH;
00346             tm->tm_mon = BCD2BIN(data[4]) - 1;
00347         }
00348         if (data[6] & X12RTC_DWA_EDW) {
00349             *aflgs |= RTC_ALARM_WDAY;
00350             tm->tm_wday = BCD2BIN(data[6]);
00351         }
00352     }
00353     return rc;
00354 }
00355 
00374 int X12RtcSetAlarm(int idx, CONST struct _tm *tm, int aflgs)
00375 {
00376     uint8_t data[10];
00377 
00378     memset(data, 0, sizeof(data));
00379     data[1] = idx * 8;
00380     if (tm) {
00381         if (aflgs & RTC_ALARM_SECOND) {
00382             data[2] = BIN2BCD(tm->tm_sec) | X12RTC_SCA_ESC;
00383         }
00384         if (aflgs & RTC_ALARM_MINUTE) {
00385             data[3] = BIN2BCD(tm->tm_min) | X12RTC_MNA_EMN;
00386         }
00387         if (aflgs & RTC_ALARM_HOUR) {
00388             data[4] = BIN2BCD(tm->tm_hour) | X12RTC_HRA_EHR;
00389         }
00390         if (aflgs & RTC_ALARM_MDAY) {
00391             data[5] = BIN2BCD(tm->tm_mday) | X12RTC_DTA_EDT;
00392         }
00393         if (aflgs & RTC_ALARM_MONTH) {
00394             data[6] = BIN2BCD(tm->tm_mon + 1) | X12RTC_MOA_EMO;
00395         }
00396         if (aflgs & RTC_ALARM_WDAY) {
00397             data[8] = BIN2BCD(tm->tm_wday) | X12RTC_DWA_EDW;
00398         }
00399     }
00400     return X12RtcWrite(1, data, 10);
00401 }
00402 
00415 int X12RtcGetStatus(uint32_t *sflgs)
00416 {
00417     int rc;
00418     uint8_t data;
00419 
00420     if ((rc = X12RtcReadRegs(X12RTC_SR, &data, 1)) == 0) {
00421         rtc_status |= data;
00422         *sflgs = rtc_status;
00423     }
00424     return rc;
00425 }
00426 
00436 int X12RtcClearStatus(uint32_t sflgs)
00437 {
00438     rtc_status &= ~sflgs;
00439 
00440     return 0;
00441 }
00442 
00443 NUTRTC rtcX12x6 = {
00444     X12Init,            
00445     X12RtcGetClock,     
00446     X12RtcSetClock,     
00447     X12RtcGetAlarm,     
00448     X12RtcSetAlarm,     
00449     X12RtcGetStatus,    
00450     X12RtcClearStatus   
00451 };
00452 
00453 
00463 int X12EepromRead(unsigned int addr, void *buff, size_t len)
00464 {
00465     int rc = -1;
00466     uint8_t wbuf[2];
00467 
00468     wbuf[0] = (uint8_t)(addr >> 8);
00469     wbuf[1] = (uint8_t)addr;
00470     if (TwMasterTransact(I2C_SLA_EEPROM, wbuf, 2, buff, len, NUT_WAIT_INFINITE) == len) {
00471         rc = 0;
00472     }
00473     return rc;
00474 }
00475 
00488 int X12EepromWrite(unsigned int addr, CONST void *buff, size_t len)
00489 {
00490     int rc = 0;
00491     uint8_t *wbuf;
00492     size_t wlen;
00493     CONST uint8_t *wp = buff;
00494 
00495     /*
00496      * Loop for each page to be written to.
00497      */
00498     while (len) {
00499         /* Do not cross page boundaries. */
00500         wlen = EEPROM_PAGE_SIZE - (addr & (EEPROM_PAGE_SIZE - 1));
00501         if (wlen > len) {
00502             wlen = len;
00503         }
00504 
00505         /* Allocate and set a TWI write buffer. */
00506         if ((wbuf = malloc(wlen + 2)) == 0) {
00507             rc = -1;
00508             break;
00509         }
00510         wbuf[0] = (uint8_t)(addr >> 8);
00511         wbuf[1] = (uint8_t)addr;
00512         memcpy(wbuf + 2, (void *)wp, wlen);
00513 
00514         /* Enable EEPROM write access and send the write buffer. */
00515         if ((rc = X12WriteEnable(1)) == 0) {
00516             rc = TwMasterTransact(I2C_SLA_EEPROM, wbuf, wlen + 2, 0, 0, NUT_WAIT_INFINITE);
00517         }
00518 
00519         /* Release the buffer and check the result. */
00520         free(wbuf);
00521         if (rc) {
00522             break;
00523         }
00524         len -= wlen;
00525         addr += wlen;
00526         wp += wlen;
00527 
00528         /* Poll for write cycle finished. */
00529         if ((rc = X12WaitReady()) != 0) {
00530             break;
00531         }
00532     }
00533     X12WriteEnable(0);
00534 
00535     return rc;
00536 }
00537 
00546 int X12Init(void)
00547 {
00548     int rc;
00549     uint32_t tmp;
00550     uint8_t data[2];
00551 
00552     /* Initialize I2C bus. */
00553     if ((rc = TwInit(0)) == 0) {
00554         /* Query RTC status. */
00555         if ((rc = X12RtcGetStatus(&tmp)) == 0) {
00556             /*
00557              * If I2C initialization and RTC status query succeeded, try
00558              * to determine the chip we got. On Ethernut 3.0 Rev-D the
00559              * X1226 had been mounted, while the X1286 is used on Rev-E
00560              * boards. We read register address 0x0037, which is the
00561              * century on the X1226, but the hundreds of seconds on the
00562              * X1286. Thus, the register contents on the latter will cange
00563              * every 10 milliseconds, while it is fixed to 19 or 20 on
00564              * the first one.
00565              */
00566             X12RtcReadRegs(X128xRTC_SSEC, &data[0], 1);
00567             NutSleep(20);
00568             X12RtcReadRegs(X128xRTC_SSEC, &data[1], 1);
00569             if (data[0] != data[1]) {
00570                 rtc_chip = 1;
00571             }
00572 #ifdef NUTDEBUG
00573             printf("[RTC=X12%c6]", rtc_chip ? '8' : '2');
00574 #endif
00575         }
00576     }
00577     return rc;
00578 }

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