Contiki 2.6

rtc.c

00001 /*
00002  * Copyright (c) 2010, Mariano Alvira <mar@devl.org> and other contributors
00003  * to the MC1322x project (http://mc1322x.devl.org)
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice, this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright
00012  *    notice, this list of conditions and the following disclaimer in the
00013  *    documentation and/or other materials provided with the distribution.
00014  * 3. Neither the name of the Institute nor the names of its contributors
00015  *    may be used to endorse or promote products derived from this software
00016  *    without specific prior written permission.
00017  *
00018  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00019  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00020  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00021  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00022  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00023  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00024  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00025  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00026  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00027  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00028  * SUCH DAMAGE.
00029  *
00030  * This file is part of libmc1322x: see http://mc1322x.devl.org
00031  * for details.
00032  *
00033  *
00034  */
00035 
00036 #include <mc1322x.h>
00037 #include <stdlib.h>
00038 #include "rtc.h"
00039 
00040 /* Define USE_32KHZ in board.h to start and use the 32 KHz
00041    oscillator, otherwise the 2 KHz ring oscillator is used. */
00042 
00043 int rtc_freq = 0;
00044 static int __use_32khz = 0;
00045 
00046 /* Init RTC */
00047 void rtc_init_osc(int use_32khz)
00048 {
00049         __use_32khz = use_32khz;
00050 
00051         if (use_32khz)
00052         {
00053                 uint32_t old;
00054 
00055                 /* You have to hold its hand with this one */
00056                 /* once you start the 32KHz crystal it can only be
00057                  * stopped with a reset (hard or soft). */
00058 
00059                 /* first, disable the ring osc */
00060                 CRM->RINGOSC_CNTLbits.ROSC_EN = 0;
00061 
00062                 /* enable the 32kHZ crystal */
00063                 CRM->XTAL32_CNTLbits.XTAL32_EN = 1;
00064 
00065                 /* set the XTAL32_EXISTS bit */
00066                 /* the datasheet says to do this after you check that RTC_COUNT
00067                    is changing, but it is not correct; it needs to be set first */
00068                 CRM->SYS_CNTLbits.XTAL32_EXISTS = 1;
00069 
00070                 old = CRM->RTC_COUNT;
00071                 while (CRM->RTC_COUNT == old)
00072                         continue;
00073 
00074                 /* RTC has started up */
00075                 rtc_freq = 32000;
00076         }
00077         else
00078         {
00079                 /* Enable ring osc */
00080                 CRM->RINGOSC_CNTLbits.ROSC_EN = 1;
00081                 CRM->XTAL32_CNTLbits.XTAL32_EN = 0;
00082 
00083                 /* Set default tune values from datasheet */
00084                 CRM->RINGOSC_CNTLbits.ROSC_CTUNE = 0x6;
00085                 CRM->RINGOSC_CNTLbits.ROSC_FTUNE = 0x17;
00086 
00087                 /* Trigger calibration */
00088                 rtc_calibrate();
00089         }
00090 }
00091 
00092 uint32_t __rtc_try(int loading, int timeout)
00093 {
00094         /* Total loading is
00095              ctune * 1000 fF + ftune * 160 fF
00096            ctune = 0-15
00097            ftune = 0-31
00098            max = 19960 fF
00099         */
00100 
00101 #define RTC_LOADING_MIN 0
00102 #define RTC_LOADING_MAX 19960
00103 
00104         /* The fine tune covers a range larger than a single coarse
00105            step.  Check all coarse steps within the fine tune range to
00106            find the optimal CTUNE, FTUNE pairs. */
00107 #define CTUNE_MAX 15
00108 #define FTUNE_MAX 31
00109 #define CSTEP 1000
00110 #define FSTEP 160
00111 #define MAX_F (FSTEP*FTUNE_MAX)  /* actually lcm(CSTEP,FSTEP) would be better,
00112                                     but in this case it's basically the same */
00113         int ctune;
00114         int ftune;
00115         int ctune_start = (loading - MAX_F) / CSTEP;
00116         int ctune_end = loading / CSTEP;
00117         int best_err = loading, best_ctune = 0, best_ftune = 0;
00118 
00119         uint32_t count;
00120 
00121         if (ctune_start < 0) ctune_start = 0;
00122         if (ctune_end > CTUNE_MAX) ctune_end = CTUNE_MAX;
00123 
00124         for (ctune = ctune_start; ctune <= ctune_end; ctune++)
00125         {
00126                 int this_loading, this_err;
00127 
00128                 ftune = ((loading - (ctune * CSTEP)) + (FSTEP / 2)) / FSTEP;
00129                 if (ftune < 0) ftune = 0;
00130                 if (ftune > FTUNE_MAX) ftune = FTUNE_MAX;
00131 
00132                 this_loading = ctune * CSTEP + ftune * FSTEP;
00133                 this_err = abs(this_loading - loading);
00134                 if (this_err < best_err) {
00135                         best_err = this_err;
00136                         best_ctune = ctune;
00137                         best_ftune = ftune;
00138                 }
00139         }
00140 
00141 //      printf("requested loading %d, actual loading %d\r\n", loading,
00142 //                best_ctune * CSTEP + best_ftune * FSTEP);
00143 
00144         /* Run the calibration */
00145         CRM->RINGOSC_CNTLbits.ROSC_CTUNE = best_ctune;
00146         CRM->RINGOSC_CNTLbits.ROSC_FTUNE = best_ftune;
00147         CRM->CAL_CNTLbits.CAL_TIMEOUT = timeout;
00148         CRM->STATUSbits.CAL_DONE = 1;
00149         CRM->CAL_CNTLbits.CAL_EN = 1;
00150         while (CRM->STATUSbits.CAL_DONE == 0)
00151                 continue;
00152 
00153         /* Result should ideally be close to (REF_OSC * (timeout / 2000)) */
00154         count = CRM->CAL_COUNT;
00155         if (count == 0) count = 1;  /* avoid divide by zero problems */
00156         return count;
00157 }
00158 
00159 /* Calibrate the ring oscillator */
00160 void rtc_calibrate(void)
00161 {
00162         /* Just bisect a few times.  Our best tuning accuracy is about
00163            1/500 of the full scale, so doing this 8-9 times is about
00164            as accurate as we can get */
00165         int i;
00166         int low = RTC_LOADING_MIN, high = RTC_LOADING_MAX;
00167         int mid;
00168         uint32_t count;
00169 
00170         if (__use_32khz) {
00171                 rtc_freq = 32000;
00172                 return;
00173         }
00174 
00175 #define TIMEOUT 100  /* 50 msec per attempt */
00176 
00177         for (i = 0; i < 9; i++)
00178         {
00179                 mid = (low + high) / 2;
00180                 count = __rtc_try(mid, TIMEOUT);
00181                 // careful about overflow
00182                 rtc_freq = REF_OSC / ((count + TIMEOUT/2) / TIMEOUT);
00183 
00184                 if (rtc_freq > 2000)
00185                         low = mid;  // increase loading
00186                 else
00187                         high = mid; // decrease loading
00188         }
00189 
00190 //      printf("RTC calibrated to %d Hz\r\n", rtc_freq);
00191 }
00192 
00193 
00194 /* Delay for the specified number of milliseconds by polling RTC */
00195 void rtc_delay_ms(uint32_t msec)
00196 {
00197         uint32_t start;
00198 
00199         start = CRM->RTC_COUNT;
00200         while ((CRM->RTC_COUNT - start) < ((msec * rtc_freq) / 1000))
00201                 continue;
00202 }