From 0fee90eca2cd56674ba7708dc113e8794327cee0 Mon Sep 17 00:00:00 2001 From: bernie Date: Sun, 23 May 2004 18:23:30 +0000 Subject: [PATCH 1/1] Import drv/timer module. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@6 38d2e660-2303-0410-9eaa-f027e97ec537 --- drv/timer.c | 254 +++++++++++++++++++++++++++++++++++++++++++++ drv/timer.h | 105 +++++++++++++++++++ drv/timer_avr.h | 150 ++++++++++++++++++++++++++ drv/timer_dsp56k.h | 95 +++++++++++++++++ drv/timer_i196.h | 46 ++++++++ 5 files changed, 650 insertions(+) create mode 100755 drv/timer.c create mode 100755 drv/timer.h create mode 100755 drv/timer_avr.h create mode 100755 drv/timer_dsp56k.h create mode 100755 drv/timer_i196.h diff --git a/drv/timer.c b/drv/timer.c new file mode 100755 index 00000000..a01f6f3c --- /dev/null +++ b/drv/timer.c @@ -0,0 +1,254 @@ +/*! + * \file + * + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief Hardware independent timer driver (implementation) + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:23:30 bernie + * Import drv/timer module. + * + */ + +#include "hw.h" +#include "kdebug.h" +#include "timer.h" + +#ifdef CONFIG_KERN_SIGNALS +#include +#endif + +#if (ARCH & ARCH_EMUL) + +# error To be recoded + +#elif defined(__AVR__) +# include "timer_avr.h" +#elif defined(__IAR_SYSTEMS_ICC) || defined(__IAR_SYSTEMS_ICC__) /* 80C196 */ +# include "timer_i196.h" +#elif defined (__m56800__) +# include "timer_dsp56k.h" +#else +# error Unknown system +#endif + + +/*! Number of available timers */ +#define MAX_TIMERS 4 + + +//! Master system clock (1ms accuracy) +volatile time_t _clock; + +static Timer soft_timers[MAX_TIMERS]; /*!< Pool of Timer structures */ +static List timers_pool; /*!< Pool of free timers */ +REGISTER static List timers_queue; /*!< Active timers */ + + +/*! + * Return a new timer picking and removing it from the available + * timers pool. Return NULL if no more timers are available. + */ +Timer *timer_new(void) +{ + Timer *timer; + cpuflags_t flags; + + DISABLE_IRQSAVE(flags); + + /* Should never happen */ + if (ISLISTEMPTY(&timers_pool)) + { + ENABLE_IRQRESTORE(flags); + DB(kprintf("Tmrspool empty\n");) + return NULL; + } + + /* Get a timer from the free pool */ + timer = (Timer *)timers_pool.head; + REMOVE((Node *)timer); + + ENABLE_IRQRESTORE(flags); + + return timer; +} + + +/*! + * Delete a timer, putting it in the available timers queue. + */ +void timer_delete(Timer *timer) +{ + cpuflags_t flags; + DISABLE_IRQSAVE(flags); + ADDHEAD(&timers_pool, &timer->link); + ENABLE_IRQRESTORE(flags); +} + + +/*! + * Add the specified timer to the software timer service queue. + * When the delay indicated by the timer expires, the timer + * device will execute the event associated with it. + * + * \note Interrupt safe + */ +void timer_add(Timer *timer) +{ + Timer *node; + cpuflags_t flags; + + DISABLE_IRQSAVE(flags); + + /* Calculate expiration time for this timer */ + timer->tick = _clock + timer->delay; + + /* Search for the first node whose expiration time is + * greater than the timer we want to add. + */ + node = (Timer *)timers_queue.head; + while (node->link.succ) + { + /* Stop just after the insert point */ + if (node->tick > timer->tick) + break; + + /* Go to next node */ + node = (Timer *)node->link.succ; + } + + /* Enqueue timer request into the list */ + INSERTBEFORE((Node *)timer, (Node *)node); + + ENABLE_IRQRESTORE(flags); +} + + +/*! + * Remove a timer from the timer queue before it has expired + */ +Timer *timer_abort(Timer *timer) +{ + cpuflags_t flags; + DISABLE_IRQSAVE(flags); + REMOVE((Node *)timer); + ENABLE_IRQRESTORE(flags); + + return timer; +} + + +/*! + * Wait for the specified amount of time (expressed in ms) + */ +void timer_delay(time_t time) +{ +#ifdef CONFIG_KERN_SIGNALS + Timer t; + + ASSERT(!sig_check(SIG_SINGLE)); + timer_set_event_signal(&t, proc_current(), SIG_SINGLE); + timer_set_delay(&t, time); + timer_add(&t); + sig_wait(SIG_SINGLE); +#else + time_t start = timer_gettick(); + + while (timer_gettick() - start < time) { /* nop */ } +#endif +} + +/*! + * Wait for the specified amount of time (expressed in microseconds) + */ +void timer_udelay(utime_t usec_delay) +{ + if (usec_delay > 1000) + { + timer_delay(usec_delay / 1000); + usec_delay %= 1000; + } + + // FIXME: This multiplication is too slow at run-time. We should try and move it + // to compile-time by exposing the TIMER_HW_HPTICKS_PER_SEC in the header + // file. + hptime_t start = timer_hw_hpread(); + hptime_t delay = (uint32_t)usec_delay * TIMER_HW_HPTICKS_PER_SEC / 1000000ul; + + while (timer_hw_hpread() - start < delay) + {} +} + + +/*! + * Timer interrupt handler. Find soft timers expired and + * trigger corresponding events. + */ +DEFINE_TIMER_ISR +{ + /* With the Metrowerks compiler, the only way to force the compiler generate + an interrupt service routine is to put a pragma directive within the function + body. */ + #ifdef __MWERKS__ + #pragma interrupt saveall + #endif + + Timer *timer; + + timer_hw_irq(); + + /* Update the master ms counter */ + ++_clock; + + /* + * Check the first timer request in the list and process + * it when it has expired. Repeat this check until the + * first node has not yet expired. Since the list is sorted + * by expiry time, all the following requests are guaranteed + * to expire later. + */ + while ((timer = (Timer *)timers_queue.head)->link.succ) + { + /* This request in list has not yet expired? */ + if (_clock < timer->tick) + break; + + /* Retreat the expired timer */ + REMOVE((Node *)timer); + + /* Execute the associated event */ + DOEVENT_INTR(&timer->expire); + } +} + + + +/*! + * Initialize timer + */ +void timer_init(void) +{ + int i; + + INITLIST(&timers_queue); + INITLIST(&timers_pool); + + /* Init all software timers in the free pool */ + for (i = 0; i < MAX_TIMERS; i++) + ADDTAIL(&timers_pool, (Node *)&soft_timers[i]); + + _clock = 0; + + timer_hw_init(); +} + diff --git a/drv/timer.h b/drv/timer.h new file mode 100755 index 00000000..e1a37860 --- /dev/null +++ b/drv/timer.h @@ -0,0 +1,105 @@ +/*! + * \file + * + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief Hardware independent timer driver (interface) + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:23:30 bernie + * Import drv/timer module. + * + */ +#ifndef DRV_TIMER_H +#define DRV_TIMER_H + +#include "cpu.h" +#include "compiler.h" +#include +#include + +/*! Number of timer ticks per second. */ +#define TICKS_PER_SEC 1000 + +typedef struct Timer +{ + Node link; /*!< Link into timers queue */ + time_t delay; /*!< Timer delay in ms */ + time_t tick; /*!< Timer will expire at this tick */ + Event expire; /*!< Event to execute when the timer expires */ +} Timer; + +extern void timer_init(void); +extern Timer *timer_new(void); +extern void timer_delete(Timer *timer); +extern void timer_add(Timer *timer); +extern Timer *timer_abort(Timer *timer); +extern void timer_delay(time_t time); +extern void timer_udelay(utime_t utime); +extern inline time_t timer_gettick(void); +extern inline time_t timer_gettick_irq(void); + +#ifdef CONFIG_KERN_SIGNALS +/*! Set the timer so that it sends a signal when it expires */ +INLINE void timer_set_event_signal(Timer* timer, struct Process* proc, sigset_t sigs) +{ + INITEVENT_SIG(&timer->expire, proc, sigs); +} +#endif + +/*! Set the timer so that it calls an user hook when it expires */ +INLINE void timer_set_event_softint(Timer* timer, Hook func, void* user_data) +{ + INITEVENT_INT(&timer->expire, func, user_data); +} + +/*! Set the timer delay (the time before the event will be triggered) */ +INLINE void timer_set_delay(Timer* timer, time_t delay) +{ + timer->delay = delay; +} + + +/*! + * Return the system tick counter (expressed in ms) + * This function must disable interrupts on 8/16bit CPUs because the + * clock variable is larger than the processor word size and can't + * be copied atomically. + */ +extern inline time_t timer_gettick(void) +{ + extern volatile time_t _clock; + + time_t result; + cpuflags_t flags; + + DISABLE_IRQSAVE(flags); + result = _clock; + ENABLE_IRQRESTORE(flags); + + return result; +} + + +/*! + * Like \c timer_gettick, faster version to be called + * from interrupt context only. + */ +extern inline time_t timer_gettick_irq(void) +{ + extern volatile time_t _clock; + + return _clock; +} + +#endif /* DRV_TIMER_H */ + diff --git a/drv/timer_avr.h b/drv/timer_avr.h new file mode 100755 index 00000000..359299db --- /dev/null +++ b/drv/timer_avr.h @@ -0,0 +1,150 @@ +/*! + * \file + * + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief Low-level timer module for AVR + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:23:30 bernie + * Import drv/timer module. + * + */ + +#ifndef DRV_TIMER_AVR_H +#define DRV_TIMER_AVR_H + +#include + +# define TIMER_RETRIGGER /* Not needed, timer retriggers automatically */ + + /*! + * System timer: additional division after the prescaler + * 12288000 / 64 / 192 (0..191) = 1 ms + */ +# define OCR_DIVISOR 191 + + /*! HW dependent timer initialization */ +#if defined(CONFIG_TIMER_ON_TIMER0) + +# define TIMER_INIT \ + do { \ + DISABLE_INTS; \ + \ + /* Reset Timer flags */ \ + TIFR = BV(OCF0) | BV(TOV0); \ + \ + /* Setup Timer/Counter interrupt */ \ + ASSR = 0x00; /* internal system clock */ \ + TCCR0 = BV(WGM01) | BV(CS02); /* Clear on Compare match & prescaler = 64 */ \ + TCNT0 = 0x00; /* initialization of Timer/Counter */ \ + OCR0 = OCR_DIVISOR; /* Timer/Counter Output Compare Register */ \ + \ + /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */ \ + TIMSK &= ~BV(TOIE0); \ + TIMSK |= BV(OCIE0); \ + \ + ENABLE_INTS; \ + } while (0) + +#elif defined(CONFIG_TIMER_ON_TIMER1_OVERFLOW) + +# define TIMER_INIT \ + do { \ + DISABLE_INTS; \ + \ + /* Reset Timer overflow flag */ \ + TIFR |= BV(TOV1); \ + \ + /* Fast PWM mode, 24 kHz, no prescaling */ \ + TCCR1A |= BV(WGM11); \ + TCCR1A &= ~BV(WGM10); \ + TCCR1B |= BV(WGM12) | BV(CS10); \ + TCCR1B &= ~(BV(WGM13) | BV(CS11) | BV(CS12)); \ + \ + TCNT1 = 0x00; /* initialization of Timer/Counter */ \ + \ + /* Enable timer interrupt: Timer/Counter1 Overflow */ \ + TIMSK |= BV(TOIE1); \ + \ + ENABLE_INTS; \ + } while (0) + +#elif defined(CONFIG_TIMER_ON_TIMER2) + +# define TIMER_INIT \ + do { \ + DISABLE_INTS; \ + \ + /* Reset Timer flags */ \ + TIFR = BV(OCF2) | BV(TOV2); \ + \ + /* Setup Timer/Counter interrupt */ \ + TCCR2 = BV(WGM21) | BV(CS21) | BV(CS20); \ + /* Clear on Compare match & prescaler = 64, internal sys clock */ \ + TCNT2 = 0x00; /* initialization of Timer/Counter */ \ + OCR2 = OCR_DIVISOR; /* Timer/Counter Output Compare Register */ \ + \ + /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */ \ + TIMSK &= ~BV(TOIE2); \ + TIMSK |= BV(OCIE2); \ + \ + ENABLE_INTS; \ + } while (0) + +#else +# error Choose witch timer to use with CONFIG_TIMER_ON_TIMERx +#endif /* CONFIG_TIMER_ON_TIMERx */ + + +#if defined(CONFIG_TIMER_ON_TIMER1_OVERFLOW) + + #define DEFINE_TIMER_ISR \ + static void timer_handler(void) + + /* + * Timer 1 overflow irq handler. + */ + SIGNAL(SIG_OVERFLOW1) + { + /*! + * How many overflow we have to count before calling the true timer handler. + * If timer overflow is at 24 kHz, with a value of 24 we have 1 ms between + * each call. + */ + #define TIMER1_OVF_COUNT 24 + + static uint8_t count = TIMER1_OVF_COUNT; + + count--; + if (!count) + { + timer_handler(); + count = TIMER1_OVF_COUNT; + } + } + +#elif defined (CONFIG_TIMER_ON_TIMER0) + + #define DEFINE_TIMER_ISR \ + SIGNAL(SIG_OUTPUT_COMPARE0) + +#elif defined(CONFIG_TIMER_ON_TIMER2) + + #define DEFINE_TIMER_ISR \ + SIGNAL(SIG_OUTPUT_COMPARE2) + +#else +# error Choose witch timer to use with CONFIG_TIMER_ON_TIMERx +#endif /* CONFIG_TIMER_ON_TIMERx */ + +#endif /* DRV_TIMER_AVR_H */ diff --git a/drv/timer_dsp56k.h b/drv/timer_dsp56k.h new file mode 100755 index 00000000..2b61e584 --- /dev/null +++ b/drv/timer_dsp56k.h @@ -0,0 +1,95 @@ +/*! + * \file + * + * + * \version $Id$ + * + * \author Giovanni Bajo + * + * \brief Driver module for DSP56K + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:23:30 bernie + * Import drv/timer module. + * + */ + +#ifndef DRV_TIMER_DSP56K_H +#define DRV_TIMER_DSP56K_H + +#include +#include + +//! The system timer for events is currently TMRA0. We prefer A/B over C/D because +// A/B share the pin with the quadrature decoder module, and we do not need +// pins for our system timer. +// If you want to change this setting, you need also to modify the IRQ vector table. +#define REG_SYSTEM_TIMER (REG_TIMER_A + 0) +#define SYSTEM_TIMER_IRQ_VECTOR 42 /* TMRA0 */ + +#define TIMER_PRESCALER 16 + +//! Frequency of the hardware high precision timer +#define TIMER_HW_HPTICKS_PER_SEC (IPBUS_FREQ / TIMER_PRESCALER) + +//! Type of time expressed in ticks of the hardware high precision timer +typedef uint16_t hptime_t; + +static void timer_hw_init(void) +{ + uint16_t compare; + + // Clear compare flag status and enable interrupt on compare + REG_SYSTEM_TIMER->SCR &= ~REG_TIMER_SCR_TCF; + REG_SYSTEM_TIMER->SCR |= REG_TIMER_SCR_TCFIE; + + // Calculate the compare value needed to generate an interrupt exactly + // TICKS_PER_SEC times each second (usually, every millisecond). Check that + // the calculation is accurate, otherwise there is a precision error + // (probably the prescaler is too big or too small). + compare = TIMER_HW_HPTICKS_PER_SEC / TICKS_PER_SEC; + ASSERT((uint32_t)compare * TICKS_PER_SEC == IPBUS_FREQ / TIMER_PRESCALER); + REG_SYSTEM_TIMER->CMP1 = compare; + + // The value for reload (at initializationa and after compare is met) is zero + REG_SYSTEM_TIMER->LOAD = 0; + + // Set the interrupt priority + irq_setpriority(SYSTEM_TIMER_IRQ_VECTOR, IRQ_PRIORITY_SYSTEM_TIMER); + + // Small preprocessor trick to generate the REG_TIMER_CTRL_PRIMARY_IPBYNN macro + // needed to set the prescaler + #define PP_CAT2(x,y) x ## y + #define PP_CAT(x,y) PP_CAT2(x,y) + #define REG_CONTROL_PRESCALER PP_CAT(REG_TIMER_CTRL_PRIMARY_IPBY, TIMER_PRESCALER) + + // Setup the counter and start counting + REG_SYSTEM_TIMER->CTRL = + REG_TIMER_CTRL_MODE_RISING | // count rising edges (normal) + REG_CONTROL_PRESCALER | // frequency (IPbus / TIMER_PRESCALER) + REG_TIMER_CTRL_LENGTH; // up to CMP1, then reload +} + +INLINE void timer_hw_irq(void) +{ + // Clear the overflow flag so that we are ready for another interrupt + REG_SYSTEM_TIMER->SCR &= ~REG_TIMER_SCR_TCF; +} + +INLINE hptime_t timer_hw_hpread(void) +{ + return REG_SYSTEM_TIMER->CNTR; +} + +void system_timer_isr(void); + +#define DEFINE_TIMER_ISR \ + void system_timer_isr(void) + +#endif /* DRV_TIMER_DSP56_H */ diff --git a/drv/timer_i196.h b/drv/timer_i196.h new file mode 100755 index 00000000..d36abf93 --- /dev/null +++ b/drv/timer_i196.h @@ -0,0 +1,46 @@ +/*! + * \file + * + * + * \version $Id$ + * + * \author Bernardo Innocenti + * + * \brief Low-level timer module for AVR + */ + +/* + * $Log$ + * Revision 1.1 2004/05/23 18:23:30 bernie + * Import drv/timer module. + * + */ + +#ifndef TIMER_I196_H +#define TIMER_I196_H + + /*! + * Retrigger TIMER2, adjusting the time to account for + * the interrupt prologue latency. + */ +# define TIMER_RETRIGGER (TIMER2 -= TICKS_RATE) + +# define TIMER_INIT \ + TIMER2 = (65535 - TICKS_RATE); \ + INT_MASK1 |= INT1F_T2OVF; \ + \ + DISABLE_INTS; \ + WSR = 1; \ + IOC3 |= IOC3F_T2_ENA; \ + WSR = 0; \ + ENABLE_INTS + +#define DEFINE_TIMER_ISR \ + INTERRUPT(0x38) void TM2_OVFL_interrupt(void); \ + INTERRUPT(0x38) void TM2_OVFL_interrupt(void) + +#endif /* DRV_TIMER_I196_H */ -- 2.25.1