Nut/OS  4.10.3
API Reference
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 X12DEBUG
00099 #endif
00100 
00101 #ifdef X12DEBUG
00102 #define NUTDEBUG
00103 #include <stdio.h>
00104 #endif
00105 
00106 #ifndef I2C_SLA_RTC
00107 #define I2C_SLA_RTC     0x6F
00108 #endif
00109 
00110 #ifndef I2C_SLA_EEPROM
00111 #define I2C_SLA_EEPROM  0x57
00112 #endif
00113 
00114 #ifndef EEPROM_PAGE_SIZE
00115 #define EEPROM_PAGE_SIZE    64
00116 #endif
00117 
00118 static uint8_t rtc_chip;
00119 static uint32_t rtc_status;
00120 
00129 static int X12WriteEnable(int on)
00130 {
00131     int rc;
00132     uint8_t buf[3];
00133 
00134     buf[0] = 0;
00135     buf[1] = 0x3F;
00136     if (on) {
00137         buf[2] = 0x02;
00138         if ((rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE)) == 0) {
00139             buf[2] = 0x06;
00140             rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00141         }
00142     } else {
00143         buf[2] = 0x00;
00144         rc = TwMasterTransact(I2C_SLA_RTC, buf, 3, 0, 0, NUT_WAIT_INFINITE);
00145     }
00146     return rc;
00147 }
00148 
00154 static int X12WaitReady(void)
00155 {
00156     uint8_t poll;
00157     int cnt = 200; /* Ethernut 3 needs about 50 loops, so this is quite save. */
00158 
00159     /*
00160      * Poll for write cycle finished. We can't use a sleep here, because our
00161      * X12xx routines are not re-entrant.
00162      */
00163     while (--cnt && TwMasterTransact(I2C_SLA_EEPROM, 0, 0, &poll, 1, NUT_WAIT_INFINITE) == -1);
00164 #ifdef X12DEBUG
00165     printf("[RtcRdy:%d]", 200 - cnt);
00166 #endif
00167 
00168     return cnt ? 0 : -1;
00169 }
00170 
00180 int X12RtcReadRegs(uint8_t reg, uint8_t *buff, size_t cnt)
00181 {
00182     int rc = -1;
00183     uint8_t wbuf[2];
00184 
00185     wbuf[0] = 0;
00186     wbuf[1] = reg;
00187     if (TwMasterTransact(I2C_SLA_RTC, wbuf, 2, buff, cnt, NUT_WAIT_INFINITE) == cnt) {
00188 #ifdef X12DEBUG
00189         printf("[Rtc$%02x>", reg);
00190         while(cnt--) {
00191             printf(" %02x", *buff++);
00192         }
00193         putchar(']');
00194 #endif
00195         rc = 0;
00196     }
00197     return rc;
00198 }
00199 
00213 int X12RtcWrite(int nv, CONST uint8_t *buff, size_t cnt)
00214 {
00215     int rc;
00216 
00217     if ((rc = X12WriteEnable(1)) == 0) {
00218         rc = TwMasterTransact(I2C_SLA_RTC, buff, cnt, 0, 0, NUT_WAIT_INFINITE);
00219         if (rc == 0 && nv) {
00220             rc = X12WaitReady();
00221         }
00222         X12WriteEnable(0);
00223 #ifdef X12DEBUG
00224         printf("[Rtc$%02X<", *++buff);
00225         cnt -= 2;
00226         while(cnt--) {
00227             printf(" %02x", *++buff);
00228         }
00229         putchar(']');
00230 #endif
00231     }
00232     return rc;
00233 }
00234 
00245 int X12RtcGetClock(struct _tm *tm)
00246 {
00247     int rc;
00248     uint8_t data[8];
00249 
00250     if ((rc = X12RtcReadRegs(X12RTC_SC, data, 8)) == 0) {
00251         tm->tm_sec = BCD2BIN(data[0]);
00252         tm->tm_min = BCD2BIN(data[1]);
00253         tm->tm_hour = BCD2BIN(data[2] & 0x3F);
00254         tm->tm_mday = BCD2BIN(data[3]);
00255         tm->tm_mon = BCD2BIN(data[4]) - 1;
00256         if (rtc_chip) {
00257             tm->tm_year = BCD2BIN(data[5]) + 100;
00258         }
00259         else {
00260             tm->tm_year = BCD2BIN(data[5]);
00261             if (data[7] == 0x20) {
00262                 tm->tm_year += 100;
00263             }
00264         }
00265         tm->tm_wday = data[6];
00266     }
00267     return rc;
00268 }
00269 
00282 int X12RtcSetClock(CONST struct _tm *tm)
00283 {
00284     uint8_t data[10];
00285 
00286     memset(data, 0, sizeof(data));
00287     if (tm) {
00288         data[1] = X12RTC_SC;
00289         data[2] = BIN2BCD(tm->tm_sec);
00290         data[3] = BIN2BCD(tm->tm_min);
00291         data[4] = BIN2BCD(tm->tm_hour) | 0x80;
00292         data[5] = BIN2BCD(tm->tm_mday);
00293         data[6] = BIN2BCD(tm->tm_mon + 1);
00294 
00295         if (rtc_chip) {
00296             /* X1286. */
00297             data[7] = BIN2BCD(tm->tm_year % 100);
00298         }
00299         /* X1226. */
00300         else if (tm->tm_year > 99) {
00301             data[7] = BIN2BCD(tm->tm_year - 100);
00302             data[9] = 0x20;
00303         }
00304         else {
00305             data[7] = BIN2BCD(tm->tm_year);
00306             data[9] = 0x19;
00307         }
00308         data[8] = tm->tm_wday;
00309     }
00310     return X12RtcWrite(0, data, 10);
00311 }
00312 
00326 int X12RtcGetAlarm(int idx, struct _tm *tm, int *aflgs)
00327 {
00328     int rc;
00329     uint8_t data[8];
00330 
00331     *aflgs = 0;
00332     memset(tm, 0, sizeof(struct _tm));
00333     if ((rc = X12RtcReadRegs(idx * 8, data, 8)) == 0) {
00334         if (data[0] & X12RTC_SCA_ESC) {
00335             *aflgs |= RTC_ALARM_SECOND;
00336             tm->tm_sec = BCD2BIN(data[0] & 0x7F);
00337         }
00338         if (data[1] & X12RTC_MNA_EMN) {
00339             *aflgs |= RTC_ALARM_MINUTE;
00340             tm->tm_min = BCD2BIN(data[1]);
00341         }
00342         if (data[2] & X12RTC_HRA_EHR) {
00343             *aflgs |= RTC_ALARM_HOUR;
00344             tm->tm_hour = BCD2BIN(data[2] & ~0x80);
00345         }
00346         if (data[3] & X12RTC_DTA_EDT) {
00347             *aflgs |= RTC_ALARM_MDAY;
00348             tm->tm_mday = BCD2BIN(data[3]);
00349         }
00350         if (data[4] & X12RTC_MOA_EMO) {
00351             *aflgs |= RTC_ALARM_MONTH;
00352             tm->tm_mon = BCD2BIN(data[4]) - 1;
00353         }
00354         if (data[6] & X12RTC_DWA_EDW) {
00355             *aflgs |= RTC_ALARM_WDAY;
00356             tm->tm_wday = BCD2BIN(data[6]);
00357         }
00358     }
00359     return rc;
00360 }
00361 
00380 int X12RtcSetAlarm(int idx, CONST struct _tm *tm, int aflgs)
00381 {
00382     uint8_t data[10];
00383 
00384     memset(data, 0, sizeof(data));
00385     data[1] = idx * 8;
00386     if (tm) {
00387         if (aflgs & RTC_ALARM_SECOND) {
00388             data[2] = BIN2BCD(tm->tm_sec) | X12RTC_SCA_ESC;
00389         }
00390         if (aflgs & RTC_ALARM_MINUTE) {
00391             data[3] = BIN2BCD(tm->tm_min) | X12RTC_MNA_EMN;
00392         }
00393         if (aflgs & RTC_ALARM_HOUR) {
00394             data[4] = BIN2BCD(tm->tm_hour) | X12RTC_HRA_EHR;
00395         }
00396         if (aflgs & RTC_ALARM_MDAY) {
00397             data[5] = BIN2BCD(tm->tm_mday) | X12RTC_DTA_EDT;
00398         }
00399         if (aflgs & RTC_ALARM_MONTH) {
00400             data[6] = BIN2BCD(tm->tm_mon + 1) | X12RTC_MOA_EMO;
00401         }
00402         if (aflgs & RTC_ALARM_WDAY) {
00403             data[8] = BIN2BCD(tm->tm_wday) | X12RTC_DWA_EDW;
00404         }
00405     }
00406     return X12RtcWrite(1, data, 10);
00407 }
00408 
00421 int X12RtcGetStatus(uint32_t *sflgs)
00422 {
00423     int rc;
00424     uint8_t data;
00425 
00426     if ((rc = X12RtcReadRegs(X12RTC_SR, &data, 1)) == 0) {
00427         rtc_status |= data;
00428         *sflgs = rtc_status;
00429     }
00430     return rc;
00431 }
00432 
00442 int X12RtcClearStatus(uint32_t sflgs)
00443 {
00444     rtc_status &= ~sflgs;
00445 
00446     return 0;
00447 }
00448 
00449 NUTRTC rtcX12x6 = {
00450     X12Init,            
00451     X12RtcGetClock,     
00452     X12RtcSetClock,     
00453     X12RtcGetAlarm,     
00454     X12RtcSetAlarm,     
00455     X12RtcGetStatus,    
00456     X12RtcClearStatus   
00457 };
00458 
00459 
00469 int X12EepromRead(unsigned int addr, void *buff, size_t len)
00470 {
00471     int rc = -1;
00472     uint8_t wbuf[2];
00473 
00474     wbuf[0] = (uint8_t)(addr >> 8);
00475     wbuf[1] = (uint8_t)addr;
00476     if (TwMasterTransact(I2C_SLA_EEPROM, wbuf, 2, buff, len, NUT_WAIT_INFINITE) == len) {
00477         rc = 0;
00478     }
00479     return rc;
00480 }
00481 
00494 int X12EepromWrite(unsigned int addr, CONST void *buff, size_t len)
00495 {
00496     int rc = 0;
00497     uint8_t *wbuf;
00498     size_t wlen;
00499     CONST uint8_t *wp = buff;
00500 
00501     /*
00502      * Loop for each page to be written to.
00503      */
00504     while (len) {
00505         /* Do not cross page boundaries. */
00506         wlen = EEPROM_PAGE_SIZE - (addr & (EEPROM_PAGE_SIZE - 1));
00507         if (wlen > len) {
00508             wlen = len;
00509         }
00510 
00511         /* Allocate and set a TWI write buffer. */
00512         if ((wbuf = malloc(wlen + 2)) == 0) {
00513             rc = -1;
00514             break;
00515         }
00516         wbuf[0] = (uint8_t)(addr >> 8);
00517         wbuf[1] = (uint8_t)addr;
00518         memcpy(wbuf + 2, (void *)wp, wlen);
00519 
00520         /* Enable EEPROM write access and send the write buffer. */
00521         if ((rc = X12WriteEnable(1)) == 0) {
00522             rc = TwMasterTransact(I2C_SLA_EEPROM, wbuf, wlen + 2, 0, 0, NUT_WAIT_INFINITE);
00523         }
00524 
00525         /* Release the buffer and check the result. */
00526         free(wbuf);
00527         if (rc) {
00528             break;
00529         }
00530         len -= wlen;
00531         addr += wlen;
00532         wp += wlen;
00533 
00534         /* Poll for write cycle finished. */
00535         if ((rc = X12WaitReady()) != 0) {
00536             break;
00537         }
00538     }
00539     X12WriteEnable(0);
00540 
00541     return rc;
00542 }
00543 
00552 int X12Init(void)
00553 {
00554     int rc;
00555     uint32_t tmp;
00556     uint8_t data[2];
00557 
00558     /* Initialize I2C bus. */
00559     if ((rc = TwInit(0)) == 0) {
00560         /* Query RTC status. */
00561         if ((rc = X12RtcGetStatus(&tmp)) == 0) {
00562             /*
00563              * If I2C initialization and RTC status query succeeded, try
00564              * to determine the chip we got. On Ethernut 3.0 Rev-D the
00565              * X1226 had been mounted, while the X1286 is used on Rev-E
00566              * boards. We read register address 0x0037, which is the
00567              * century on the X1226, but the hundreds of seconds on the
00568              * X1286. Thus, the register contents on the latter will cange
00569              * every 10 milliseconds, while it is fixed to 19 or 20 on
00570              * the first one.
00571              */
00572             X12RtcReadRegs(X128xRTC_SSEC, &data[0], 1);
00573             NutSleep(20);
00574             X12RtcReadRegs(X128xRTC_SSEC, &data[1], 1);
00575             if (data[0] != data[1]) {
00576                 rtc_chip = 1;
00577             }
00578 #ifdef X12DEBUG
00579             printf("[RTC=X12%c6]", rtc_chip ? '8' : '2');
00580 #endif
00581         }
00582     }
00583     return rc;
00584 }