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