Spelling/grammar fixes.
[bertos.git] / drv / timer_avr.h
1 /*!
2  * \file
3  * <!--
4  * Copyright 2003, 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 2000 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See devlib/README for information.
7  * -->
8  *
9  * \version $Id$
10  *
11  * \author Bernardo Innocenti <bernie@develer.com>
12  *
13  * \brief Low-level timer module for AVR
14  */
15
16 /*#*
17  *#* $Log$
18  *#* Revision 1.15  2004/08/25 14:12:08  rasky
19  *#* Aggiornato il comment block dei log RCS
20  *#*
21  *#* Revision 1.14  2004/08/24 16:27:01  bernie
22  *#* Add missing headers.
23  *#*
24  *#* Revision 1.13  2004/08/24 14:30:11  bernie
25  *#* Use new-style config macros for drv/timer.c
26  *#*
27  *#* Revision 1.12  2004/08/10 06:59:45  bernie
28  *#* CONFIG_TIMER_STROBE: Define no-op default macros.
29  *#*
30  *#* Revision 1.11  2004/08/03 15:53:17  aleph
31  *#* Fix spacing
32  *#*
33  *#* Revision 1.10  2004/08/02 20:20:29  aleph
34  *#* Merge from project_ks
35  *#*
36  *#* Revision 1.9  2004/07/22 02:01:14  bernie
37  *#* Use TIMER_PRESCALER consistently.
38  *#*/
39 #ifndef DRV_TIMER_AVR_H
40 #define DRV_TIMER_AVR_H
41
42 #include <arch_config.h> // ARCH_BOARD_KC
43 #include <avr/wdt.h>
44 #include <avr/signal.h>
45
46 #if defined(ARCH_BOARD_KC) && (ARCH & ARCH_BOARD_KC)
47         #include <drv/adc.h>
48 #endif
49
50
51 /*!
52  * Values for CONFIG_TIMER.
53  *
54  * Select which hardware timer interrupt to use for system clock and softtimers.
55  * \note The timer 1 overflow mode set the timer as a 24 kHz PWM.
56  */
57 #define TIMER_ON_OUTPUT_COMPARE0  1
58 #define TIMER_ON_OVERFLOW1        2
59 #define TIMER_ON_OUTPUT_COMPARE2  3
60
61
62 /*!
63  * \def CONFIG_TIMER_STROBE
64  *
65  * This is a debug facility that can be used to
66  * monitor timer interrupt activity on an external pin.
67  *
68  * To use strobes, redefine the macros TIMER_STROBE_ON,
69  * TIMER_STROBE_OFF and TIMER_STROBE_INIT and set
70  * CONFIG_TIMER_STROBE to 1.
71  */
72 #ifndef CONFIG_TIMER_STROBE
73         #define TIMER_STROBE_ON    do {/*nop*/} while(0)
74         #define TIMER_STROBE_OFF   do {/*nop*/} while(0)
75         #define TIMER_STROBE_INIT  do {/*nop*/} while(0)
76 #endif
77
78
79 /* Not needed, IRQ timer flag cleared automatically */
80 #define timer_hw_irq() do {} while (0)
81
82 #define TIMER_PRESCALER 64
83
84 /*!
85  * System timer: additional division after the prescaler
86  * 12288000 / 64 / 192 (0..191) = 1 ms
87  */
88 #define OCR_DIVISOR  (CLOCK_FREQ / TIMER_PRESCALER / TICKS_PER_SEC - 1) /* 191 */
89
90 /*! HW dependent timer initialization  */
91 #if (CONFIG_TIMER == TIMER_ON_OUTPUT_COMPARE0)
92
93         //! Type of time expressed in ticks of the hardware high-precision timer
94         typedef uint8_t hptime_t;
95
96         static void timer_hw_init(void)
97         {
98                 cpuflags_t flags;
99                 DISABLE_IRQSAVE(flags);
100
101                 /* Reset Timer flags */
102                 TIFR = BV(OCF0) | BV(TOV0);
103
104                 /* Setup Timer/Counter interrupt */
105                 ASSR = 0x00;                  /* Internal system clock */
106                 TCCR0 = BV(WGM01)             /* Clear on Compare match */
107                         #if TIMER_PRESCALER == 64
108                                 | BV(CS02)
109                         #else
110                                 #error Unsupported value of TIMER_PRESCALER
111                         #endif
112                 ;
113                 TCNT0 = 0x00;                 /* Initialization of Timer/Counter */
114                 OCR0 = OCR_DIVISOR;           /* Timer/Counter Output Compare Register */
115
116                 /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */
117                 TIMSK &= ~BV(TOIE0);
118                 TIMSK |= BV(OCIE0);
119
120                 ENABLE_IRQRESTORE(flags);
121         }
122
123         //! Frequency of the hardware high precision timer
124         #define TIMER_HW_HPTICKS_PER_SEC  (CLOCK_FREQ / TIMER_PRESCALER)
125
126         INLINE hptime_t timer_hw_hpread(void)
127         {
128                 return TCNT0;
129         }
130
131 #elif (CONFIG_TIMER == TIMER_ON_OVERFLOW1)
132
133         //! Type of time expressed in ticks of the hardware high precision timer
134         typedef uint16_t hptime_t;
135
136         static void timer_hw_init(void)
137         {
138                 cpuflags_t flags;
139                 DISABLE_IRQSAVE(flags);
140
141                 /* Reset Timer overflow flag */
142                 TIFR |= BV(TOV1);
143
144                 /* Fast PWM mode, 9 bit, 24 kHz, no prescaling. When changing freq or
145                    resolution (top of TCNT), change TIMER_HW_HPTICKS_PER_SEC too */
146                 TCCR1A |= BV(WGM11);
147                 TCCR1A &= ~BV(WGM10);
148                 TCCR1B |= BV(WGM12) | BV(CS10);
149                 TCCR1B &= ~(BV(WGM13) | BV(CS11) | BV(CS12));
150
151                 TCNT1 = 0x00;         /* initialization of Timer/Counter */
152
153                 /* Enable timer interrupt: Timer/Counter1 Overflow */
154                 TIMSK |= BV(TOIE1);
155
156                 ENABLE_IRQRESTORE(flags);
157         }
158
159         //! Frequency of the hardware high precision timer
160         #define TIMER_HW_HPTICKS_PER_SEC  (24000ul * 512)
161
162         INLINE hptime_t timer_hw_hpread(void)
163         {
164                 return TCNT1;
165         }
166
167 #elif (CONFIG_TIMER == TIMER_ON_OUTPUT_COMPARE2)
168
169         //! Type of time expressed in ticks of the hardware high precision timer
170         typedef uint8_t hptime_t;
171
172         static void timer_hw_init(void)
173         {
174                 cpuflags_t flags;
175                 DISABLE_IRQSAVE(flags);
176
177                 /* Reset Timer flags */
178                 TIFR = BV(OCF2) | BV(TOV2);
179
180                 /* Setup Timer/Counter interrupt */
181                 TCCR2 = BV(WGM21)
182                         #if TIMER_PRESCALER == 64
183                                 | BV(CS21) | BV(CS20)
184                         #else
185                                 #error Unsupported value of TIMER_PRESCALER
186                         #endif
187                 ;
188                 /* Clear on Compare match & prescaler = 64, internal sys clock.
189                    When changing prescaler change TIMER_HW_HPTICKS_PER_SEC too */
190                 TCNT2 = 0x00;         /* initialization of Timer/Counter */
191                 OCR2 = OCR_DIVISOR;   /* Timer/Counter Output Compare Register */
192
193                 /* Enable timer interrupts: Timer/Counter2 Output Compare (OCIE2) */
194                 TIMSK &= ~BV(TOIE2);
195                 TIMSK |= BV(OCIE2);
196
197                 ENABLE_IRQRESTORE(flags);
198         }
199
200         //! Frequency of the hardware high precision timer
201         #define TIMER_HW_HPTICKS_PER_SEC  (CLOCK_FREQ / TIMER_PRESCALER)
202
203         INLINE hptime_t timer_hw_hpread(void)
204         {
205                 return TCNT2;
206         }
207
208 #else
209         #error Unimplemented value for CONFIG_TIMER
210 #endif /* CONFIG_TIMER */
211
212
213 #if (CONFIG_TIMER == TIMER_ON_OVERFLOW1)
214
215         #define DEFINE_TIMER_ISR        \
216                 static void timer_handler(void)
217
218         DEFINE_TIMER_ISR;
219
220         /*
221          * Timer 1 overflow irq handler. It's called at the frequency of the timer 1
222          * PWM (should be 24 kHz). It's too much for timer purposes, so the interrupt
223          * handler is really a counter that call the true handler in timer.c
224          * every 1 ms.
225          */
226         SIGNAL(SIG_OVERFLOW1)
227         {
228                 /*!
229                  * How many overflow we have to count before calling the true timer handler.
230                  * If timer overflow is at 24 kHz, with a value of 24 we have 1 ms between
231                  * each call.
232                  */
233                 #define TIMER1_OVF_COUNT 24
234
235                 static uint8_t count = TIMER1_OVF_COUNT;
236
237                 count--;
238                 if (!count)
239                 {
240                         timer_handler();
241                         count = TIMER1_OVF_COUNT;
242                 }
243
244         #if (ARCH & ARCH_BOARD_KC)
245                 /*
246                  * Super-optimization-hack: switch CPU ADC mux here, ASAP after the start
247                  * of conversion (auto-triggered with timer 1 overflow).
248                  * The switch can be done 2 ADC cycles after start of conversion.
249                  * The handler prologue takes a little more than 32 CPU cycles: with
250                  * the prescaler at 1/16 the timing should be correct even at the start
251                  * of the handler.
252                  *
253                  * The switch is synchronized with the ADC handler using _adc_trigger_lock.
254                  */
255                 extern uint8_t _adc_idx_next;
256                 extern bool _adc_trigger_lock;
257
258                 if (!_adc_trigger_lock)
259                 {
260                         TIMER_STROBE_ON;
261                         ADC_SETCHN(_adc_idx_next);
262                         TIMER_STROBE_OFF;
263                         _adc_trigger_lock = true;
264                 }
265         #endif
266         }
267
268 #elif (CONFIG_TIMER == TIMER_ON_OUTPUT_COMPARE0)
269
270         #define DEFINE_TIMER_ISR        \
271                 SIGNAL(SIG_OUTPUT_COMPARE0)
272
273 #elif (CONFIG_TIMER == TIMER_ON_OUTPUT_COMPARE2)
274
275         #define DEFINE_TIMER_ISR        \
276                 SIGNAL(SIG_OUTPUT_COMPARE2)
277
278 #else
279         #error Unimplemented value for CONFIG_TIMER
280 #endif /* CONFIG_TIMER */
281
282 #endif /* DRV_TIMER_AVR_H */