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