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