Use the RTC only as the main timer.

This is a major change to how the main tick interrupt is handled on
the mc1322x platforms. Instead of using two timer resources, TMR0 and
RTC, this patch unifies all the timers to use the RTC. This is enabled by
implementing etimers as scheduled rtimers. The main advantage (aside
from freeing TMR0 for general use) is have the Contiki timebase come
from the same source that will be used for sleeping and wakeup.
This commit is contained in:
Mariano Alvira 2013-02-03 14:50:30 -05:00
parent ad55ccdec4
commit fb8bbf37cf
9 changed files with 128 additions and 233 deletions

View file

@ -10,7 +10,7 @@ CONTIKI_CPU=$(CONTIKI)/cpu/mc1322x
CONTIKI_CPU_DIRS = . lib src board dev ../arm/common/dbg-io
MC1322X = debug-uart.c rtimer-arch.c watchdog.c contiki-crm.c contiki-maca.c contiki-misc.c leds-arch.c leds.c contiki-uart.c slip-uart1.c init.c config.c
MC1322X = debug-uart.c rtimer-arch.c watchdog.c contiki-maca.c contiki-misc.c leds-arch.c leds.c contiki-uart.c slip-uart1.c init.c config.c
DBG_IO = dbg-printf.c dbg-snprintf.c dbg-sprintf.c strformat.c

View file

@ -36,87 +36,46 @@
#include <sys/clock.h>
#include <sys/cc.h>
#include <sys/etimer.h>
#include "dev/leds.h"
#include <sys/rtimer.h>
#include "contiki-conf.h"
#include "mc1322x.h"
#include "contiki-conf.h"
#define MAX_TICKS (~((clock_time_t)0) / 2)
static volatile clock_time_t current_clock = 0;
volatile unsigned long seconds = 0;
#define TCF 15
#define TCF1 4
#define TCF2 5
static struct rtimer rt_clock;
/* the typical clock things like incrementing current_clock and etimer checks */
/* are performed as a periodically scheduled rtimer */
void
clock_init()
rt_do_clock(struct rtimer *t, void *ptr)
{
/* timer setup */
/* CTRL */
#define COUNT_MODE 1 /* use rising edge of primary source */
#define PRIME_SRC 0xf /* Perip. clock with 128 prescale (for 24Mhz = 187500Hz)*/
#define SEC_SRC 0 /* don't need this */
#define ONCE 0 /* keep counting */
#define LEN 1 /* count until compare then reload with value in LOAD */
#define DIR 0 /* count up */
#define CO_INIT 0 /* other counters cannot force a re-initialization of this counter */
#define OUT_MODE 0 /* OFLAG is asserted while counter is active */
rtimer_set(t, RTIMER_TIME(t) + (rtc_freq/CLOCK_CONF_SECOND) , 1,
(rtimer_callback_t)rt_do_clock, ptr);
*TMR_ENBL = 0; /* tmrs reset to enabled */
*TMR0_SCTRL = 0;
*TMR0_CSCTRL =0x0040;
*TMR0_LOAD = 0; /* reload to zero */
*TMR0_COMP_UP = 1875; /* trigger a reload at the end */
*TMR0_CMPLD1 = 1875; /* compare 1 triggered reload level, 10HZ maybe? */
*TMR0_CNTR = 0; /* reset count register */
*TMR0_CTRL = (COUNT_MODE<<13) | (PRIME_SRC<<9) | (SEC_SRC<<7) | (ONCE<<6) | (LEN<<5) | (DIR<<4) | (CO_INIT<<3) | (OUT_MODE);
*TMR_ENBL = 0xf; /* enable all the timers --- why not? */
enable_irq(TMR);
/* Do startup scan of the ADC */
#if CLOCK_CONF_SAMPLEADC
adc_reading[8]=0;
adc_init();
while (adc_reading[8]==0) adc_service();
#endif
}
void tmr0_isr(void) {
if(bit_is_set(*TMR(0,CSCTRL),TCF1)) {
current_clock++;
if((current_clock % CLOCK_CONF_SECOND) == 0) {
seconds++;
#if BLINK_SECONDS
leds_toggle(LEDS_GREEN);
#endif
/* ADC can be serviced every tick or every second */
#if CLOCK_CONF_SAMPLEADC > 1
adc_service();
#endif
}
#if CLOCK_CONF_SAMPLEADC == 1
adc_service();
#endif
if(etimer_pending() &&
(etimer_next_expiration_time() - current_clock - 1) > MAX_TICKS) {
etimer_request_poll();
}
}
/* clear the compare flags */
clear_bit(*TMR(0,SCTRL),TCF);
clear_bit(*TMR(0,CSCTRL),TCF1);
clear_bit(*TMR(0,CSCTRL),TCF2);
return;
} else {
/* this timer didn't create an interrupt condition */
return;
}
/* RTC MUST have been already setup by mc1322x init */
void
clock_init(void)
{
rtimer_set(&rt_clock, RTIMER_NOW() + rtc_freq/CLOCK_CONF_SECOND, 1, (rtimer_callback_t)rt_do_clock, NULL);
}
clock_time_t

View file

@ -1,121 +0,0 @@
/*
* Copyright (c) 2010, Mariano Alvira <mar@devl.org> and other contributors
* to the MC1322x project (http://mc1322x.devl.org) and Contiki.
*
* 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 the Contiki OS.
*
*
*/
#include "mc1322x.h"
#define CRM_DEBUG 1
#if CRM_DEBUG
#define PRINTF(...) printf(__VA_ARGS__)
#else
#define PRINTF(...)
#endif
uint32_t cal_rtc_secs; /* calibrated 2khz rtc seconds */
void sleep(uint32_t opts, uint32_t mode)
{
/* the maca must be off before going to sleep */
/* otherwise the mcu will reboot on wakeup */
// maca_off();
*CRM_SLEEP_CNTL = opts;
*CRM_SLEEP_CNTL = (opts | mode);
/* wait for the sleep cycle to complete */
while(!bit_is_set(*CRM_STATUS,0)) { continue; }
/* write 1 to sleep_sync --- this clears the bit (it's a r1wc bit) and powers down */
set_bit(*CRM_STATUS,0);
/* now we are asleep */
/* and waiting for something to wake us up */
/* you did tell us how to wake up right? */
/* waking up */
while(!bit_is_set(*CRM_STATUS,0)) { continue; }
/* write 1 to sleep_sync --- this clears the bit (it's a r1wc bit) and finishes the wakeup */
set_bit(*CRM_STATUS,0);
/* you may also need to do other recovery */
/* such as interrupt handling */
/* peripheral init */
/* and turning the radio back on */
}
/* turn on the 32kHz crystal */
/* once you start the 32xHz crystal it can only be stopped with a reset (hard or soft) */
void enable_32khz_xtal(void)
{
static volatile uint32_t rtc_count;
PRINTF("enabling 32kHz crystal\n\r");
/* first, disable the ring osc */
ring_osc_off();
/* enable the 32kHZ crystal */
xtal32_on();
/* set the XTAL32_EXISTS bit */
/* the datasheet says to do this after you've check that RTC_COUNT is changing */
/* the datasheet is not correct */
xtal32_exists();
/* now check that the crystal starts */
/* this blocks until it starts */
/* it would be better to timeout and return an error */
rtc_count = *CRM_RTC_COUNT;
PRINTF("waiting for xtal\n\r");
while(*CRM_RTC_COUNT == rtc_count) {
continue;
}
/* RTC has started up */
PRINTF("32kHZ xtal started\n\r");
}
void cal_ring_osc(void)
{
uint32_t cal_factor;
PRINTF("performing ring osc cal\n\r");
PRINTF("crm_status: 0x%0x\n\r",*CRM_STATUS);
PRINTF("sys_cntl: 0x%0x\n\r",*CRM_SYS_CNTL);
*CRM_CAL_CNTL = (1<<16) | (20000);
while((*CRM_STATUS & (1<<9)) == 0);
PRINTF("ring osc cal complete\n\r");
PRINTF("cal_count: 0x%0x\n\r",*CRM_CAL_COUNT);
cal_factor = (REF_OSC*1000) / *CRM_CAL_COUNT;
cal_rtc_secs = (NOMINAL_RING_OSC_SEC * cal_factor)/100;
PRINTF("cal factor: %d\n\r", cal_factor);
PRINTF("hib_wake_secs: %d\n\r", cal_rtc_secs);
set_bit(*CRM_STATUS,9);
}

View file

@ -55,4 +55,8 @@ typedef unsigned short uip_stats_t;
typedef uint32_t clock_time_t;
/* Core rtimer.h defaults to 16 bit timer unless RTIMER_CLOCK_LT is defined */
typedef unsigned long rtimer_clock_t;
#define RTIMER_CLOCK_LT(a,b) ((signed long)((a)-(b)) < 0)
#endif

View file

@ -102,6 +102,7 @@ void buck_setup(void) {
CRM->VREG_CNTLbits.VREG_1P5V_SEL = 3;
CRM->VREG_CNTLbits.VREG_1P5V_EN = 3;
CRM->VREG_CNTLbits.VREG_1P8V_EN = 1;
while(CRM->STATUSbits.VREG_1P5V_RDY == 0) { continue; }
while(CRM->STATUSbits.VREG_1P8V_RDY == 0) { continue; }
@ -149,6 +150,7 @@ void rtc_setup(void) {
PRINTF("RTC calibrated to %d Hz\r\n", rtc_freq);
} else {
PRINTF("32kHz xtal started\n\r");
rtc_freq = 32768;
}
}
@ -167,13 +169,11 @@ void mc1322x_init(void) {
PRINTF("mc1322x init\n\r");
adc_init();
clock_init();
ctimer_init();
process_init();
process_start(&etimer_process, NULL);
process_start(&contiki_maca_process, NULL);
buck_setup();
rtc_setup();
/* start with a default config */
@ -196,6 +196,11 @@ void mc1322x_init(void) {
set_demodulator_type(mc1322x_config.flags.demod);
set_prm_mode(mc1322x_config.flags.autoack);
/* must be done AFTER maca_init */
/* the radio calibration appears to clobber the RTC trim caps */
rtc_setup();
rtimer_init();
clock_init();
}

View file

@ -72,7 +72,7 @@ void rtc_init_osc(int use_32khz)
continue;
/* RTC has started up */
rtc_freq = 32000;
rtc_freq = 32768;
}
else
{
@ -168,7 +168,7 @@ void rtc_calibrate(void)
uint32_t count;
if (__use_32khz) {
rtc_freq = 32000;
rtc_freq = 32768;
return;
}

View file

@ -57,18 +57,41 @@
#define PRINTF(...)
#endif
void rtc_isr(void) {
PRINTF("rtc_wu_irq\n\r");
PRINTF("now is %u\n", rtimer_arch_now());
disable_rtc_wu();
disable_rtc_wu_irq();
static uint32_t last_rtc;
void
rtc_isr(void)
{
/* see note in table 5-13 of the reference manual: it takes at least two RTC clocks for the EVT bit to clear */
if ((CRM->RTC_COUNT - last_rtc) <= 2) {
CRM->STATUS = ~0; /* Clear all events */
// CRM->STATUSbits.RTC_WU_EVT = 1;
return;
}
last_rtc = CRM->RTC_COUNT;
/* Clear all events (for paranoia) */
/* clear RTC event flag (for paranoia)*/
// CRM->STATUSbits.RTC_WU_EVT = 1;
CRM->STATUS = ~0;
rtimer_run_next();
clear_rtc_wu_evt();
}
void
rtimer_arch_init(void)
{
last_rtc = CRM->RTC_COUNT;
/* enable timeout interrupts */
/* RTC WU is the periodic RTC timer */
/* TIMER WU is the wakeup timers (clocked from the RTC source) */
/* it does not appear you can have both enabled at the same time */
CRM->WU_CNTLbits.RTC_WU_EN = 1;
CRM->WU_CNTLbits.RTC_WU_IEN = 1;
enable_irq(CRM);
}
void
@ -76,23 +99,52 @@ rtimer_arch_schedule(rtimer_clock_t t)
{
volatile uint32_t now;
now = rtimer_arch_now();
PRINTF("rtimer_arch_schedule time %u; now is %u\n", t,now);
PRINTF("rtimer_arch_schedule time %u; now is %u\n", t, now);
#if 1
/* If specified time is always in the future, counter can wrap without harm */
*CRM_RTC_TIMEOUT = t - now;
#else
/* Immediate interrupt if specified time is before current time. This may also
happen on counter overflow. */
if(now>t) {
*CRM_RTC_TIMEOUT = 1;
if(now > t) {
CRM->RTC_TIMEOUT = 1;
} else {
*CRM_RTC_TIMEOUT = t - now;
CRM->RTC_TIMEOUT = t - now;
}
#endif
clear_rtc_wu_evt();
enable_rtc_wu();
enable_rtc_wu_irq();
PRINTF("rtimer_arch_schedule CRM_RTC_TIMEOUT is %u\n", *CRM_RTC_TIMEOUT);
}
void
rtimer_arch_sleep(rtimer_clock_t howlong)
{
CRM->WU_CNTLbits.TIMER_WU_EN = 1;
CRM->WU_CNTLbits.RTC_WU_EN = 0;
CRM->WU_TIMEOUT = howlong;
/* the maca must be off before going to sleep */
/* otherwise the mcu will reboot on wakeup */
maca_off();
CRM->SLEEP_CNTLbits.DOZE = 0;
CRM->SLEEP_CNTLbits.RAM_RET = 3;
CRM->SLEEP_CNTLbits.MCU_RET = 1;
CRM->SLEEP_CNTLbits.DIG_PAD_EN = 1;
CRM->SLEEP_CNTLbits.HIB = 1;
/* wait for the sleep cycle to complete */
while((*CRM_STATUS & 0x1) == 0) { continue; }
/* write 1 to sleep_sync --- this clears the bit (it's a r1wc bit) and powers down */
*CRM_STATUS = 1;
/* asleep */
/* wait for the awake cycle to complete */
while((*CRM_STATUS & 0x1) == 0) { continue; }
/* write 1 to sleep_sync --- this clears the bit (it's a r1wc bit) and finishes wakeup */
*CRM_STATUS = 1;
CRM->WU_CNTLbits.TIMER_WU_EN = 0;
CRM->WU_CNTLbits.RTC_WU_EN = 1;
/* reschedule clock ticks */
clock_init();
clock_adjust_ticks((CRM->WU_COUNT*CLOCK_CONF_SECOND)/rtc_freq);
}

View file

@ -47,15 +47,15 @@
#include "sys/rtimer.h"
/* mc1322x */
#include "crm.h"
#include "utils.h"
#include "mc1322x.h"
#if USE_32KHZ_XTAL
#define RTIMER_ARCH_SECOND 32768
#else
#define RTIMER_ARCH_SECOND 18778 /* close --- should get calibrated */
#define RTIMER_ARCH_SECOND 2000
#endif
#define rtimer_arch_now() (*CRM_RTC_COUNT)
#define rtimer_arch_now() (CRM->RTC_COUNT)
#endif /* __RTIMER_ARCH_H__ */

View file

@ -106,10 +106,6 @@
#define PLATFORM_HAS_LEDS 1
#define PLATFORM_HAS_BUTTON 1
/* Core rtimer.h defaults to 16 bit timer unless RTIMER_CLOCK_LT is defined */
typedef unsigned long rtimer_clock_t;
#define RTIMER_CLOCK_LT(a,b) ((signed long)((a)-(b)) < 0)
#define RIMEADDR_CONF_SIZE 8
#if WITH_UIP6