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