mirror of
https://github.com/oliverschmidt/contiki.git
synced 2024-09-01 17:29:13 +00:00
203 lines
5.7 KiB
C
203 lines
5.7 KiB
C
/*
|
|
* Copyright (c) 2010, Mariano Alvira <mar@devl.org> 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 <mc1322x.h>
|
|
#include <stdlib.h>
|
|
#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;
|
|
}
|