/*! \page License * Copyright (C) 2009, H&D Wireless AB 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. The name of H&D Wireless AB may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY H&D WIRELESS AB ``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 EXPRESSLY AND * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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. */ #include <stdint.h> #include <rtc.h> #include <intc.h> #include <timer.h> #ifdef FREERTOS_USED #include "FreeRTOS.h" #include "task.h" #endif #define TIMER_HZ 4 struct timeout_t { U32 tick; U32 expire_at_tick; Bool expired; U8 type; void (*cb)(void* ctx); void* ctx; }; struct timer_t { volatile U32 tick; struct timeout_t timeout[10]; void (*tick_isr) (void* ctx); const U32 MS_PER_TICK; void *ctx; }; #define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0]) static struct timer_t TIMER = { .tick = 0, #ifdef FREERTOS_USED .MS_PER_TICK = 1 / portTICK_RATE_MS, #else .MS_PER_TICK = TIMER_HZ, #endif .timeout = { { 0 } }, }; #ifdef FREERTOS_USED /* Use TICK-hook */ void vApplicationTickHook( void ) { struct timer_t* priv = &TIMER; priv->tick++; if(priv->tick_isr) { priv->tick_isr(priv->ctx); } } #else /* Use interrupt directly */ static __attribute__((__interrupt__)) void irq_handler(void) { volatile avr32_rtc_t *rtc = &AVR32_RTC; struct timer_t* priv = &TIMER; priv->tick++; if(priv->tick_isr) priv->tick_isr(priv->ctx); rtc->icr = AVR32_RTC_ICR_TOPI_MASK; rtc->isr; } #endif void timer_init(void (*tick_isr) (void* ctx), void* ctx) { struct timer_t* priv = &TIMER; uint8_t id; #ifndef FREERTOS_USED INTC_register_interrupt(&irq_handler, AVR32_RTC_IRQ, AVR32_INTC_INT0); if (!rtc_init(&AVR32_RTC, RTC_OSC_RC, 0)) Assert(0); rtc_set_top_value(&AVR32_RTC, 115 * priv->MS_PER_TICK / 2); rtc_enable_interrupt(&AVR32_RTC); rtc_enable(&AVR32_RTC); #else /* With FreeRTOS we use the OS tick instead */ #endif priv->tick_isr = tick_isr; priv->ctx = ctx; for (id = 0; id < ARRAY_SIZE(priv->timeout); id++) priv->timeout[id].expired = TRUE; } U32 timer_get_ms(void) { struct timer_t* priv = &TIMER; return priv->tick * priv->MS_PER_TICK; } void timer_delay(U32 ms) { struct timer_t* priv = &TIMER; U32 expire_at_tick = priv->tick + ms / priv->MS_PER_TICK; while (priv->tick < expire_at_tick); } /** * Called from application main loop to invoke any scheduled timeout cbs. * This function might be called as often as possible rather than at each tick * to support the timeout value '0', e.g a timeout within less than one tick. * */ void timer_poll(void) { struct timer_t* priv = &TIMER; U8 i; for (i = 0; i < ARRAY_SIZE(priv->timeout); i++) { struct timeout_t* tmo = &priv->timeout[i]; if (tmo->expired) continue; if (tmo->expire_at_tick > priv->tick) continue; if (tmo->cb) tmo->cb(tmo->ctx); if (tmo->type == TIMEOUT_PERIODIC) tmo->expire_at_tick = priv->tick + tmo->tick; else tmo->expired = TRUE; } } static U32 timer_sched_timeout(U32 ms, U8 type) { struct timer_t* priv = &TIMER; struct timeout_t* tmo; U8 id; Assert(type == TIMEOUT_ONESHOT || type == TIMEOUT_PERIODIC); for (id = 0; id < ARRAY_SIZE(priv->timeout); id++) { tmo = &priv->timeout[id]; if (tmo->expired) break; } Assert(id != ARRAY_SIZE(priv->timeout)); tmo->tick = ms / priv->MS_PER_TICK; tmo->expire_at_tick = priv->tick + tmo->tick; tmo->type = type; tmo->expired = FALSE; return id; } U32 timer_sched_timeout_cb(U32 ms, U8 type, void (*cb)(void *ctx), void* ctx) { struct timer_t* priv = &TIMER; struct timeout_t* tmo; U8 id; Assert(cb); id = timer_sched_timeout(ms, type); tmo = &priv->timeout[id]; tmo->cb = cb; tmo->ctx = ctx; return id; } U32 timer_mod(U32 id, U32 ms, U8 type, void (*cb)(void *ctx), void* ctx) { struct timer_t* priv = &TIMER; if (id != INVALID_TIMER_ID && !priv->timeout[id].expired) timer_cancel_timeout(id); return timer_sched_timeout_cb(ms, type, cb, ctx); } void timer_cancel_timeout(U32 id) { struct timer_t* priv = &TIMER; struct timeout_t* tmo; tmo = &priv->timeout[id]; tmo->expired = TRUE; } int timer_interval_passed(U32 old, U32 new, U32 diff) { /* New did not wrap */ if (new > old && new - old > diff) { return 1; } /* New did wrap */ else if (new < old && ( ( (U32)(-1) - old ) + new ) > diff ) { return 1; } return 0; }