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

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