Contiki 2.5
rtc.c
1 /*
2  * Copyright (c) 2010, Mariano Alvira <mar@devl.org> and other contributors
3  * to the MC1322x project (http://mc1322x.devl.org)
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the Institute nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * This file is part of libmc1322x: see http://mc1322x.devl.org
31  * for details.
32  *
33  *
34  */
35 
36 #include <mc1322x.h>
37 #include <stdlib.h>
38 #include "rtc.h"
39 
40 /* Define USE_32KHZ in board.h to start and use the 32 KHz
41  oscillator, otherwise the 2 KHz ring oscillator is used. */
42 
43 int rtc_freq = 0;
44 static int __use_32khz = 0;
45 
46 /* Init RTC */
47 void rtc_init_osc(int use_32khz)
48 {
49  __use_32khz = use_32khz;
50 
51  if (use_32khz)
52  {
53  uint32_t old;
54 
55  /* You have to hold its hand with this one */
56  /* once you start the 32KHz crystal it can only be
57  * stopped with a reset (hard or soft). */
58 
59  /* first, disable the ring osc */
60  CRM->RINGOSC_CNTLbits.ROSC_EN = 0;
61 
62  /* enable the 32kHZ crystal */
63  CRM->XTAL32_CNTLbits.XTAL32_EN = 1;
64 
65  /* set the XTAL32_EXISTS bit */
66  /* the datasheet says to do this after you check that RTC_COUNT
67  is changing, but it is not correct; it needs to be set first */
68  CRM->SYS_CNTLbits.XTAL32_EXISTS = 1;
69 
70  old = CRM->RTC_COUNT;
71  while (CRM->RTC_COUNT == old)
72  continue;
73 
74  /* RTC has started up */
75  rtc_freq = 32000;
76  }
77  else
78  {
79  /* Enable ring osc */
80  CRM->RINGOSC_CNTLbits.ROSC_EN = 1;
81  CRM->XTAL32_CNTLbits.XTAL32_EN = 0;
82 
83  /* Set default tune values from datasheet */
84  CRM->RINGOSC_CNTLbits.ROSC_CTUNE = 0x6;
85  CRM->RINGOSC_CNTLbits.ROSC_FTUNE = 0x17;
86 
87  /* Trigger calibration */
88  rtc_calibrate();
89  }
90 }
91 
92 uint32_t __rtc_try(int loading, int timeout)
93 {
94  /* Total loading is
95  ctune * 1000 fF + ftune * 160 fF
96  ctune = 0-15
97  ftune = 0-31
98  max = 19960 fF
99  */
100 
101 #define RTC_LOADING_MIN 0
102 #define RTC_LOADING_MAX 19960
103 
104  /* The fine tune covers a range larger than a single coarse
105  step. Check all coarse steps within the fine tune range to
106  find the optimal CTUNE, FTUNE pairs. */
107 #define CTUNE_MAX 15
108 #define FTUNE_MAX 31
109 #define CSTEP 1000
110 #define FSTEP 160
111 #define MAX_F (FSTEP*FTUNE_MAX) /* actually lcm(CSTEP,FSTEP) would be better,
112  but in this case it's basically the same */
113  int ctune;
114  int ftune;
115  int ctune_start = (loading - MAX_F) / CSTEP;
116  int ctune_end = loading / CSTEP;
117  int best_err = loading, best_ctune = 0, best_ftune = 0;
118 
119  uint32_t count;
120 
121  if (ctune_start < 0) ctune_start = 0;
122  if (ctune_end > CTUNE_MAX) ctune_end = CTUNE_MAX;
123 
124  for (ctune = ctune_start; ctune <= ctune_end; ctune++)
125  {
126  int this_loading, this_err;
127 
128  ftune = ((loading - (ctune * CSTEP)) + (FSTEP / 2)) / FSTEP;
129  if (ftune < 0) ftune = 0;
130  if (ftune > FTUNE_MAX) ftune = FTUNE_MAX;
131 
132  this_loading = ctune * CSTEP + ftune * FSTEP;
133  this_err = abs(this_loading - loading);
134  if (this_err < best_err) {
135  best_err = this_err;
136  best_ctune = ctune;
137  best_ftune = ftune;
138  }
139  }
140 
141 // printf("requested loading %d, actual loading %d\r\n", loading,
142 // best_ctune * CSTEP + best_ftune * FSTEP);
143 
144  /* Run the calibration */
145  CRM->RINGOSC_CNTLbits.ROSC_CTUNE = best_ctune;
146  CRM->RINGOSC_CNTLbits.ROSC_FTUNE = best_ftune;
147  CRM->CAL_CNTLbits.CAL_TIMEOUT = timeout;
148  CRM->STATUSbits.CAL_DONE = 1;
149  CRM->CAL_CNTLbits.CAL_EN = 1;
150  while (CRM->STATUSbits.CAL_DONE == 0)
151  continue;
152 
153  /* Result should ideally be close to (REF_OSC * (timeout / 2000)) */
154  count = CRM->CAL_COUNT;
155  if (count == 0) count = 1; /* avoid divide by zero problems */
156  return count;
157 }
158 
159 /* Calibrate the ring oscillator */
160 void rtc_calibrate(void)
161 {
162  /* Just bisect a few times. Our best tuning accuracy is about
163  1/500 of the full scale, so doing this 8-9 times is about
164  as accurate as we can get */
165  int i;
166  int low = RTC_LOADING_MIN, high = RTC_LOADING_MAX;
167  int mid;
168  uint32_t count;
169 
170  if (__use_32khz) {
171  rtc_freq = 32000;
172  return;
173  }
174 
175 #define TIMEOUT 100 /* 50 msec per attempt */
176 
177  for (i = 0; i < 9; i++)
178  {
179  mid = (low + high) / 2;
180  count = __rtc_try(mid, TIMEOUT);
181  // careful about overflow
182  rtc_freq = REF_OSC / ((count + TIMEOUT/2) / TIMEOUT);
183 
184  if (rtc_freq > 2000)
185  low = mid; // increase loading
186  else
187  high = mid; // decrease loading
188  }
189 
190 // printf("RTC calibrated to %d Hz\r\n", rtc_freq);
191 }
192 
193 
194 /* Delay for the specified number of milliseconds by polling RTC */
195 void rtc_delay_ms(uint32_t msec)
196 {
197  uint32_t start;
198 
199  start = CRM->RTC_COUNT;
200  while ((CRM->RTC_COUNT - start) < ((msec * rtc_freq) / 1000))
201  continue;
202 }