diff --git a/lib/include/rtc.h b/lib/include/rtc.h new file mode 100644 index 000000000..cda1d60cb --- /dev/null +++ b/lib/include/rtc.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010, Mariano Alvira and other contributors + * to the MC1322x project (http://mc1322x.devl.org) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of libmc1322x: see http://mc1322x.devl.org + * for details. + * + * + */ + +#ifndef RTC_H +#define RTC_H + +/* Init RTC (and calibrate, if using ring oscillator) */ +void rtc_init_osc(int use_32khz); + +#ifdef USE_32KHZ +#define rtc_init() rtc_init_osc(USE_32KHZ) +#else +#define rtc_init() rtc_init_osc(0) +#endif + +/* Calibrate the ring oscillator */ +void rtc_calibrate(void); + +/* Delay for the specified number of milliseconds by polling RTC */ +void rtc_delay_ms(uint32_t msec); + +/* Calibrated frequency of the RTC, in Hz */ +extern int rtc_freq; + +#endif diff --git a/lib/rtc.c b/lib/rtc.c new file mode 100644 index 000000000..383384c38 --- /dev/null +++ b/lib/rtc.c @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2010, Mariano Alvira and other contributors + * to the MC1322x project (http://mc1322x.devl.org) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of libmc1322x: see http://mc1322x.devl.org + * for details. + * + * + */ + +#include +#include +#include "rtc.h" + +/* Define USE_32KHZ in board.h to start and use the 32 KHz + oscillator, otherwise the 2 KHz ring oscillator is used. */ + +int rtc_freq = 0; +static int __use_32khz = 0; + +/* Init RTC */ +void rtc_init_osc(int use_32khz) +{ + __use_32khz = use_32khz; + + if (use_32khz) + { + uint32_t old; + + /* You have to hold its hand with this one */ + /* once you start the 32KHz crystal it can only be + * stopped with a reset (hard or soft). */ + + /* first, disable the ring osc */ + CRM->RINGOSC_CNTLbits.ROSC_EN = 0; + + /* enable the 32kHZ crystal */ + CRM->XTAL32_CNTLbits.XTAL32_EN = 1; + + /* set the XTAL32_EXISTS bit */ + /* the datasheet says to do this after you check that RTC_COUNT + is changing, but it is not correct; it needs to be set first */ + CRM->SYS_CNTLbits.XTAL32_EXISTS = 1; + + old = CRM->RTC_COUNT; + while (CRM->RTC_COUNT == old) + continue; + + /* RTC has started up */ + rtc_freq = 32000; + } + else + { + /* Enable ring osc */ + CRM->RINGOSC_CNTLbits.ROSC_EN = 1; + CRM->XTAL32_CNTLbits.XTAL32_EN = 0; + + /* Set default tune values from datasheet */ + CRM->RINGOSC_CNTLbits.ROSC_CTUNE = 0x6; + CRM->RINGOSC_CNTLbits.ROSC_FTUNE = 0x17; + + /* Trigger calibration */ + rtc_calibrate(); + } +} + +uint32_t __rtc_try(int loading, int timeout) +{ + /* Total loading is + ctune * 1000 fF + ftune * 160 fF + ctune = 0-15 + ftune = 0-31 + max = 19960 fF + */ + +#define RTC_LOADING_MIN 0 +#define RTC_LOADING_MAX 19960 + + /* The fine tune covers a range larger than a single coarse + step. Check all coarse steps within the fine tune range to + find the optimal CTUNE, FTUNE pairs. */ +#define CTUNE_MAX 15 +#define FTUNE_MAX 31 +#define CSTEP 1000 +#define FSTEP 160 +#define MAX_F (FSTEP*FTUNE_MAX) /* actually lcm(CSTEP,FSTEP) would be better, + but in this case it's basically the same */ + int ctune; + int ftune; + int ctune_start = (loading - MAX_F) / CSTEP; + int ctune_end = loading / CSTEP; + int best_err = loading, best_ctune = 0, best_ftune = 0; + + uint32_t count; + + if (ctune_start < 0) ctune_start = 0; + if (ctune_end > CTUNE_MAX) ctune_end = CTUNE_MAX; + + for (ctune = ctune_start; ctune <= ctune_end; ctune++) + { + int this_loading, this_err; + + ftune = ((loading - (ctune * CSTEP)) + (FSTEP / 2)) / FSTEP; + if (ftune < 0) ftune = 0; + if (ftune > FTUNE_MAX) ftune = FTUNE_MAX; + + this_loading = ctune * CSTEP + ftune * FSTEP; + this_err = abs(this_loading - loading); + if (this_err < best_err) { + best_err = this_err; + best_ctune = ctune; + best_ftune = ftune; + } + } + +// printf("requested loading %d, actual loading %d\r\n", loading, +// best_ctune * CSTEP + best_ftune * FSTEP); + + /* Run the calibration */ + CRM->RINGOSC_CNTLbits.ROSC_CTUNE = best_ctune; + CRM->RINGOSC_CNTLbits.ROSC_FTUNE = best_ftune; + CRM->CAL_CNTLbits.CAL_TIMEOUT = timeout; + CRM->STATUSbits.CAL_DONE = 1; + CRM->CAL_CNTLbits.CAL_EN = 1; + while (CRM->STATUSbits.CAL_DONE == 0) + continue; + + /* Result should ideally be close to (REF_OSC * (timeout / 2000)) */ + count = CRM->CAL_COUNT; + if (count == 0) count = 1; /* avoid divide by zero problems */ + return count; +} + +/* Calibrate the ring oscillator */ +void rtc_calibrate(void) +{ + /* Just bisect a few times. Our best tuning accuracy is about + 1/500 of the full scale, so doing this 8-9 times is about + as accurate as we can get */ + int i; + int low = RTC_LOADING_MIN, high = RTC_LOADING_MAX; + int mid; + uint32_t count; + + if (__use_32khz) { + rtc_freq = 32000; + return; + } + +#define TIMEOUT 100 /* 50 msec per attempt */ + + for (i = 0; i < 9; i++) + { + mid = (low + high) / 2; + count = __rtc_try(mid, TIMEOUT); + // careful about overflow + rtc_freq = REF_OSC / ((count + TIMEOUT/2) / TIMEOUT); + + if (rtc_freq > 2000) + low = mid; // increase loading + else + high = mid; // decrease loading + } + +// printf("RTC calibrated to %d Hz\r\n", rtc_freq); +} + + +/* Delay for the specified number of milliseconds by polling RTC */ +void rtc_delay_ms(uint32_t msec) +{ + uint32_t start; + + start = CRM->RTC_COUNT; + while ((CRM->RTC_COUNT - start) < ((msec * rtc_freq) / 1000)) + continue; +}