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