altos: Add fast-timer API. Use for quadrature and button drivers
[fw/altos] / src / stm / ao_fast_timer.c
1 /*
2  * Copyright © 2013 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17 #include <ao.h>
18 #include <ao_fast_timer.h>
19
20 static void (*ao_fast_timer_callback[AO_FAST_TIMER_MAX])(void);
21 static uint8_t ao_fast_timer_count;
22 static uint8_t ao_fast_timer_users;
23
24 static void
25 ao_fast_timer_enable(void)
26 {
27         stm_tim6.cr1 = ((0 << STM_TIM67_CR1_ARPE) |
28                         (0 << STM_TIM67_CR1_OPM) |
29                         (1 << STM_TIM67_CR1_URS) |
30                         (0 << STM_TIM67_CR1_UDIS) |
31                         (1 << STM_TIM67_CR1_CEN));
32 }
33
34 static void
35 ao_fast_timer_disable(void)
36 {
37         stm_tim6.cr1 = ((0 << STM_TIM67_CR1_ARPE) |
38                         (0 << STM_TIM67_CR1_OPM) |
39                         (1 << STM_TIM67_CR1_URS) |
40                         (0 << STM_TIM67_CR1_UDIS) |
41                         (0 << STM_TIM67_CR1_CEN));
42 }
43
44 void
45 ao_fast_timer_on(void (*callback)(void))
46 {
47         ao_fast_timer_callback[ao_fast_timer_count] = callback;
48         if (!ao_fast_timer_count++)
49                 ao_fast_timer_enable();
50 }
51
52 void
53 ao_fast_timer_off(void (*callback)(void))
54 {
55         uint8_t n;
56
57         for (n = 0; n < ao_fast_timer_count; n++)
58                 if (ao_fast_timer_callback[n] == callback) {
59                         for (; n < ao_fast_timer_count-1; n++) {
60                                 ao_fast_timer_callback[n] = ao_fast_timer_callback[n+1];
61                         }
62                         if (!--ao_fast_timer_count)
63                                 ao_fast_timer_disable();
64                         break;
65                 }
66 }
67
68 void stm_tim6_isr(void)
69 {
70         uint8_t i;
71         if (stm_tim6.sr & (1 << STM_TIM67_SR_UIF)) {
72                 stm_tim6.sr = 0;
73
74                 for (i = 0; i < ao_fast_timer_count; i++)
75                         (*ao_fast_timer_callback[i])();
76         }
77 }
78
79 /*
80  * According to the STM clock-configuration, timers run
81  * twice as fast as the APB1 clock *if* the APB1 prescaler
82  * is greater than 1.
83  */
84
85 #if AO_APB1_PRESCALER > 1
86 #define TIMER_23467_SCALER 2
87 #else
88 #define TIMER_23467_SCALER 1
89 #endif
90
91 #define TIMER_10kHz     ((AO_PCLK1 * TIMER_23467_SCALER) / 10000)
92
93 void
94 ao_fast_timer_init(void)
95 {
96         if (!ao_fast_timer_users) {
97                 stm_nvic_set_enable(STM_ISR_TIM6_POS);
98                 stm_nvic_set_priority(STM_ISR_TIM6_POS, AO_STM_NVIC_CLOCK_PRIORITY);
99
100                 /* Turn on timer 6 */
101                 stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM6EN);
102
103                 stm_tim6.psc = TIMER_10kHz;
104                 stm_tim6.arr = 9;
105                 stm_tim6.cnt = 0;
106
107                 /* Enable update interrupt */
108                 stm_tim6.dier = (1 << STM_TIM67_DIER_UIE);
109
110                 /* Poke timer to reload values */
111                 stm_tim6.egr |= (1 << STM_TIM67_EGR_UG);
112
113                 stm_tim6.cr2 = (STM_TIM67_CR2_MMS_RESET << STM_TIM67_CR2_MMS);
114                 ao_fast_timer_disable();
115         }
116         if (ao_fast_timer_users == AO_FAST_TIMER_MAX)
117                 ao_panic(AO_PANIC_FAST_TIMER);
118         ao_fast_timer_users++;
119 }
120