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