bfbc286ae5da0714c60f646115d966079611af01
[fw/altos] / src / stm32f1 / ao_fast_timer.c
1 /*
2  * Copyright © 2024 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18 #include <ao.h>
19 #include <ao_fast_timer.h>
20
21 static void (*ao_fast_timer_callback[AO_FAST_TIMER_MAX])(void);
22 static uint8_t ao_fast_timer_count;
23 static uint8_t ao_fast_timer_users;
24
25 static void
26 ao_fast_timer_enable(void)
27 {
28         stm_tim1.cr1 = ((0 << STM_TIM18_CR1_CKD) |
29                         (0 << STM_TIM18_CR1_ARPE) |
30                         (0 << STM_TIM18_CR1_CMS) |
31                         (0 << STM_TIM18_CR1_DIR) |
32                         (0 << STM_TIM18_CR1_OPM) |
33                         (1 << STM_TIM18_CR1_URS) |
34                         (0 << STM_TIM18_CR1_UDIS) |
35                         (1 << STM_TIM18_CR1_CEN));
36 }
37
38 static void
39 ao_fast_timer_disable(void)
40 {
41         stm_tim1.cr1 = ((0 << STM_TIM18_CR1_CKD) |
42                         (0 << STM_TIM18_CR1_ARPE) |
43                         (0 << STM_TIM18_CR1_CMS) |
44                         (0 << STM_TIM18_CR1_DIR) |
45                         (0 << STM_TIM18_CR1_OPM) |
46                         (1 << STM_TIM18_CR1_URS) |
47                         (0 << STM_TIM18_CR1_UDIS) |
48                         (0 << STM_TIM18_CR1_CEN));
49 }
50
51 void
52 ao_fast_timer_on(void (*callback)(void))
53 {
54         ao_fast_timer_callback[ao_fast_timer_count] = callback;
55         if (!ao_fast_timer_count++)
56                 ao_fast_timer_enable();
57 }
58
59 void
60 ao_fast_timer_off(void (*callback)(void))
61 {
62         uint8_t n;
63
64         for (n = 0; n < ao_fast_timer_count; n++)
65                 if (ao_fast_timer_callback[n] == callback) {
66                         for (; n < ao_fast_timer_count-1; n++) {
67                                 ao_fast_timer_callback[n] = ao_fast_timer_callback[n+1];
68                         }
69                         if (!--ao_fast_timer_count)
70                                 ao_fast_timer_disable();
71                         break;
72                 }
73 }
74
75 void stm_tim1_up_isr(void)
76 {
77         uint8_t i;
78         if (stm_tim1.sr & (1 << STM_TIM18_SR_UIF)) {
79                 stm_tim1.sr = 0;
80
81                 for (i = 0; i < ao_fast_timer_count; i++)
82                         (*ao_fast_timer_callback[i])();
83         }
84 }
85
86 /*
87  * According to the STM clock-configuration, timers run
88  * twice as fast as the APB2 clock *if* the APB2 prescaler
89  * is greater than 1.
90  */
91
92 #if AO_APB1_PRESCALER > 1
93 #define TIMER_18_SCALER 2
94 #else
95 #define TIMER_18_SCALER 1
96 #endif
97
98 #ifndef FAST_TIMER_FREQ
99 #define FAST_TIMER_FREQ 10000
100 #endif
101
102 #define TIMER_FAST      ((AO_PCLK2 * TIMER_18_SCALER) / FAST_TIMER_FREQ)
103
104 void
105 ao_fast_timer_init(void)
106 {
107         if (!ao_fast_timer_users) {
108                 stm_nvic_set_enable(STM_ISR_TIM1_UP_POS);
109                 stm_nvic_set_priority(STM_ISR_TIM1_UP_POS, AO_STM_NVIC_CLOCK_PRIORITY);
110
111                 /* Turn on timer 1 */
112                 stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_TIM1EN);
113
114                 stm_tim1.psc = TIMER_FAST;
115                 stm_tim1.arr = 9;
116                 stm_tim1.cnt = 0;
117
118                 /* Enable update interrupt */
119                 stm_tim1.dier = (1 << STM_TIM18_DIER_UIE);
120
121                 /* Poke timer to reload values */
122                 stm_tim1.egr |= (1 << STM_TIM18_EGR_UG);
123
124                 stm_tim1.cr2 = (STM_TIM18_CR2_MMS_RESET << STM_TIM18_CR2_MMS);
125                 ao_fast_timer_disable();
126         }
127         if (ao_fast_timer_users == AO_FAST_TIMER_MAX)
128                 ao_panic(AO_PANIC_FAST_TIMER);
129         ao_fast_timer_users++;
130 }
131