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