Save some more RAM on AVR.
[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.20  2004/11/16 20:59:46  bernie
19  *#* Include <avr/io.h> explicitly.
20  *#*
21  *#* Revision 1.19  2004/10/19 08:56:41  bernie
22  *#* TIMER_STROBE_ON, TIMER_STROBE_OFF, TIMER_STROBE_INIT: Move from timer_avr.h to timer.h, where they really belong.
23  *#*
24  *#* Revision 1.18  2004/09/20 03:31:03  bernie
25  *#* Fix racy racy code.
26  *#*
27  *#* Revision 1.17  2004/09/14 21:07:09  bernie
28  *#* Include hw.h explicitly.
29  *#*
30  *#* Revision 1.16  2004/09/06 21:49:26  bernie
31  *#* CONFIG_TIMER_STROBE: be tolerant with missing optional macro.
32  *#*
33  *#* Revision 1.15  2004/08/25 14:12:08  rasky
34  *#* Aggiornato il comment block dei log RCS
35  *#*
36  *#* Revision 1.14  2004/08/24 16:27:01  bernie
37  *#* Add missing headers.
38  *#*
39  *#* Revision 1.13  2004/08/24 14:30:11  bernie
40  *#* Use new-style config macros for drv/timer.c
41  *#*
42  *#* Revision 1.12  2004/08/10 06:59:45  bernie
43  *#* CONFIG_TIMER_STROBE: Define no-op default macros.
44  *#*
45  *#* Revision 1.11  2004/08/03 15:53:17  aleph
46  *#* Fix spacing
47  *#*
48  *#* Revision 1.10  2004/08/02 20:20:29  aleph
49  *#* Merge from project_ks
50  *#*
51  *#* Revision 1.9  2004/07/22 02:01:14  bernie
52  *#* Use TIMER_PRESCALER consistently.
53  *#*/
54 #ifndef DRV_TIMER_AVR_H
55 #define DRV_TIMER_AVR_H
56
57 #include <arch_config.h> // ARCH_BOARD_KC
58 #include "hw.h"
59
60 #include <avr/signal.h>
61 #include <avr/io.h>
62
63 #if defined(ARCH_BOARD_KC) && (ARCH & ARCH_BOARD_KC)
64         #include <drv/adc.h>
65 #endif
66
67
68 /*!
69  * Values for CONFIG_TIMER.
70  *
71  * Select which hardware timer interrupt to use for system clock and softtimers.
72  * \note The timer 1 overflow mode set the timer as a 24 kHz PWM.
73  */
74 #define TIMER_ON_OUTPUT_COMPARE0  1
75 #define TIMER_ON_OVERFLOW1        2
76 #define TIMER_ON_OUTPUT_COMPARE2  3
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         #if (ARCH & ARCH_BOARD_KC)
229                 /*
230                  * Super-optimization-hack: switch CPU ADC mux here, ASAP after the start
231                  * of conversion (auto-triggered with timer 1 overflow).
232                  * The switch can be done 2 ADC cycles after start of conversion.
233                  * The handler prologue takes a little more than 32 CPU cycles: with
234                  * the prescaler at 1/16 the timing should be correct even at the start
235                  * of the handler.
236                  *
237                  * The switch is synchronized with the ADC handler using _adc_trigger_lock.
238                  *
239                  *      Mel (A Real Programmer)
240                  */
241                 extern uint8_t _adc_idx_next;
242                 extern bool _adc_trigger_lock;
243
244                 if (!_adc_trigger_lock)
245                 {
246                         /*
247                          * Disable free-running mode to avoid starting a
248                          * new conversion before the ADC handler has read
249                          * the ongoing one.  This condition could occur
250                          * under very high interrupt load and would have the
251                          * unwanted effect of reading from the wrong ADC
252                          * channel.
253                          *
254                          * NOTE: writing 0 to ADSC and ADIF has no effect.
255                          */
256                         ADCSRA = ADCSRA & ~(BV(ADFR) | BV(ADIF) | BV(ADSC));
257
258                         ADC_SETCHN(_adc_idx_next);
259                         _adc_trigger_lock = true;
260                 }
261         #endif // ARCH_BOARD_KC
262
263                 /*!
264                  * How many timer overflows we must count before calling the real
265                  * timer handler.
266                  * When the timer is programmed to overflow at 24 kHz, a value of
267                  * 24 will result in 1ms between each call.
268                  */
269                 #define TIMER1_OVF_COUNT 24
270                 //#warning TIMER1_OVF_COUNT for timer at 12 kHz
271                 //#define TIMER1_OVF_COUNT 12
272
273                 static uint8_t count = TIMER1_OVF_COUNT;
274
275                 count--;
276                 if (!count)
277                 {
278                         timer_handler();
279                         count = TIMER1_OVF_COUNT;
280                 }
281         }
282
283 #elif (CONFIG_TIMER == TIMER_ON_OUTPUT_COMPARE0)
284
285         #define DEFINE_TIMER_ISR        \
286                 SIGNAL(SIG_OUTPUT_COMPARE0)
287
288 #elif (CONFIG_TIMER == TIMER_ON_OUTPUT_COMPARE2)
289
290         #define DEFINE_TIMER_ISR        \
291                 SIGNAL(SIG_OUTPUT_COMPARE2)
292
293 #else
294         #error Unimplemented value for CONFIG_TIMER
295 #endif /* CONFIG_TIMER */
296
297 #endif /* DRV_TIMER_AVR_H */