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