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