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