2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
22 * Based on https://github.com/ExpressLRS/ExpressLRS
23 * Thanks to AlessandroAU, original creator of the ExpressLRS project.
26 * Dominic Clifton/Hydra - Timer-based timeout implementation.
27 * AlessandroAU - stdperiph Timer-based timeout implementation.
33 #ifdef USE_RX_EXPRESSLRS
35 #include "build/debug.h"
36 #include "build/debug_pin.h"
38 #include "drivers/timer.h"
39 #include "drivers/nvic.h"
40 #include "drivers/rx/expresslrs_driver.h"
42 #include "common/maths.h"
44 #define TIMER_INTERVAL_US_DEFAULT 20000
45 #define TICK_TOCK_COUNT 2
54 typedef struct elrsTimerState_s
{
56 volatile tickTock_e tickTock
;
58 int32_t frequencyOffsetTicks
;
62 // Use a little ram to keep the amount of CPU cycles used in the ISR lower.
63 typedef struct elrsPhaseShiftLimits_s
{
66 } elrsPhaseShiftLimits_t
;
68 elrsPhaseShiftLimits_t phaseShiftLimits
;
70 static elrsTimerState_t timerState
= {
72 TOCK
, // Start on TOCK (in ELRS isTick is initialised to false)
73 TIMER_INTERVAL_US_DEFAULT
,
78 void expressLrsTimerDebug(void)
80 DEBUG_SET(DEBUG_RX_EXPRESSLRS_PHASELOCK
, 2, timerState
.frequencyOffsetTicks
);
81 DEBUG_SET(DEBUG_RX_EXPRESSLRS_PHASELOCK
, 3, timerState
.phaseShiftUs
);
84 static void expressLrsRecalculatePhaseShiftLimits(void)
86 phaseShiftLimits
.max
= (timerState
.intervalUs
/ TICK_TOCK_COUNT
);
87 phaseShiftLimits
.min
= -phaseShiftLimits
.max
;
90 static uint16_t expressLrsCalculateMaximumExpectedPeriod(uint16_t intervalUs
)
92 // The timer reload register must not overflow when frequencyOffsetTicks is added to it.
93 // frequencyOffsetTicks is not expected to be higher than 1/4 of the interval.
94 // also, timer resolution must be as high as possible.
95 const uint16_t maximumExpectedPeriod
= (intervalUs
/ TICK_TOCK_COUNT
) + (timerState
.intervalUs
/ 4);
96 return maximumExpectedPeriod
;
99 void expressLrsUpdateTimerInterval(uint16_t intervalUs
)
101 timerState
.intervalUs
= intervalUs
;
102 expressLrsRecalculatePhaseShiftLimits();
104 #ifdef USE_HAL_DRIVER
105 timerReconfigureTimeBase(timer
, expressLrsCalculateMaximumExpectedPeriod(timerState
.intervalUs
), MHZ_TO_HZ(1));
106 LL_TIM_SetAutoReload(timer
, (timerState
.intervalUs
/ TICK_TOCK_COUNT
) - 1);
108 configTimeBase(timer
, expressLrsCalculateMaximumExpectedPeriod(timerState
.intervalUs
), MHZ_TO_HZ(1));
109 TIM_SetAutoreload(timer
, (timerState
.intervalUs
/ TICK_TOCK_COUNT
) - 1);
113 void expressLrsUpdatePhaseShift(int32_t newPhaseShift
)
115 timerState
.phaseShiftUs
= constrain(newPhaseShift
, phaseShiftLimits
.min
, phaseShiftLimits
.max
);
118 void expressLrsTimerIncreaseFrequencyOffset(void)
120 timerState
.frequencyOffsetTicks
++;
123 void expressLrsTimerDecreaseFrequencyOffset(void)
125 timerState
.frequencyOffsetTicks
--;
128 void expressLrsTimerResetFrequencyOffset(void)
130 timerState
.frequencyOffsetTicks
= 0;
133 static void expressLrsOnTimerUpdate(timerOvrHandlerRec_t
*cbRec
, captureCompare_t capture
)
138 if (timerState
.tickTock
== TICK
) {
141 uint32_t adjustedPeriod
= (timerState
.intervalUs
/ TICK_TOCK_COUNT
) + timerState
.frequencyOffsetTicks
;
143 #ifdef USE_HAL_DRIVER
144 LL_TIM_SetAutoReload(timer
, adjustedPeriod
- 1);
146 TIM_SetAutoreload(timer
, adjustedPeriod
- 1);
149 expressLrsOnTimerTickISR();
151 timerState
.tickTock
= TOCK
;
155 uint32_t adjustedPeriod
= (timerState
.intervalUs
/ TICK_TOCK_COUNT
) + timerState
.phaseShiftUs
+ timerState
.frequencyOffsetTicks
;
157 #ifdef USE_HAL_DRIVER
158 LL_TIM_SetAutoReload(timer
, adjustedPeriod
- 1);
160 TIM_SetAutoreload(timer
, adjustedPeriod
- 1);
163 timerState
.phaseShiftUs
= 0;
165 expressLrsOnTimerTockISR();
167 timerState
.tickTock
= TICK
;
171 bool expressLrsTimerIsRunning(void)
173 return timerState
.running
;
176 void expressLrsTimerStop(void)
178 #ifdef USE_HAL_DRIVER
179 LL_TIM_DisableIT_UPDATE(timer
);
180 LL_TIM_DisableCounter(timer
);
181 LL_TIM_SetCounter(timer
, 0);
183 TIM_ITConfig(timer
, TIM_IT_Update
, DISABLE
);
184 TIM_Cmd(timer
, DISABLE
);
185 TIM_SetCounter(timer
, 0);
187 timerState
.running
= false;
190 void expressLrsTimerResume(void)
192 timerState
.tickTock
= TOCK
;
194 #ifdef USE_HAL_DRIVER
195 LL_TIM_SetAutoReload(timer
, (timerState
.intervalUs
/ TICK_TOCK_COUNT
));
196 LL_TIM_SetCounter(timer
, 0);
198 LL_TIM_ClearFlag_UPDATE(timer
);
199 LL_TIM_EnableIT_UPDATE(timer
);
201 TIM_SetAutoreload(timer
, (timerState
.intervalUs
/ TICK_TOCK_COUNT
));
202 TIM_SetCounter(timer
, 0);
204 TIM_ClearFlag(timer
, TIM_FLAG_Update
);
205 TIM_ITConfig(timer
, TIM_IT_Update
, ENABLE
);
208 timerState
.running
= true;
210 #ifdef USE_HAL_DRIVER
211 LL_TIM_EnableCounter(timer
);
212 LL_TIM_GenerateEvent_UPDATE(timer
);
214 TIM_Cmd(timer
, ENABLE
);
215 TIM_GenerateEvent(timer
, TIM_EventSource_Update
);
219 void expressLrsInitialiseTimer(TIM_TypeDef
*t
, timerOvrHandlerRec_t
*timerUpdateCb
)
223 configTimeBase(timer
, expressLrsCalculateMaximumExpectedPeriod(timerState
.intervalUs
), MHZ_TO_HZ(1));
225 expressLrsUpdateTimerInterval(timerState
.intervalUs
);
227 timerChOvrHandlerInit(timerUpdateCb
, expressLrsOnTimerUpdate
);
229 timerConfigUpdateCallback(timer
, timerUpdateCb
);
232 void expressLrsTimerEnableIRQs(void)
234 uint8_t irq
= timerInputIrq(timer
);
236 // Use the NVIC TIMER priority for now
237 #ifdef USE_HAL_DRIVER
238 HAL_NVIC_SetPriority(irq
, NVIC_PRIORITY_BASE(NVIC_PRIO_TIMER
), NVIC_PRIORITY_SUB(NVIC_PRIO_TIMER
));
239 HAL_NVIC_EnableIRQ(irq
);
241 NVIC_SetPriority(irq
, NVIC_PRIORITY_BASE(NVIC_PRIO_TIMER
));