2 * This file is part of INAV.
4 * INAV 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 3 of the License, or
7 * (at your option) any later version.
9 * INAV is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with INAV. If not, see <http://www.gnu.org/licenses/>.
24 #include "build/atomic.h"
25 #include "build/debug.h"
27 #include "common/utils.h"
29 #include "drivers/io.h"
30 #include "drivers/rcc.h"
31 #include "drivers/time.h"
32 #include "drivers/nvic.h"
33 #include "drivers/dma.h"
34 #include "drivers/timer.h"
35 #include "drivers/timer_impl.h"
37 const uint16_t lookupDMASourceTable
[4] = { TMR_C1_DMA_REQUEST
, TMR_C2_DMA_REQUEST
, TMR_C3_DMA_REQUEST
, TMR_C4_DMA_REQUEST
};
39 const uint8_t lookupTIMChannelTable
[4] = { TMR_C1_FLAG
, TMR_C2_FLAG
, TMR_C3_FLAG
, TMR_C4_FLAG
};
41 void impl_timerInitContext(timHardwareContext_t
* timCtx
)
45 // Configure the interrupt priority
46 void impl_timerNVICConfigure(TCH_t
* tch
, int irqPriority
)
48 if (tch
->timCtx
->timDef
->irq
) {
49 nvic_irq_enable(tch
->timCtx
->timDef
->irq
, irqPriority
, 0);
52 if (tch
->timCtx
->timDef
->secondIrq
) {
53 nvic_irq_enable(tch
->timCtx
->timDef
->secondIrq
, irqPriority
, 0);
57 void impl_timerConfigBase(TCH_t
* tch
, uint16_t period
, uint32_t hz
)
59 tmr_type
* tim
= tch
->timCtx
->timDef
->tim
;
60 tmr_base_init(tim
, (period
- 1) & 0xffff, lrintf((float)timerGetBaseClock(tch
) / hz
+ 0.01f
) - 1);
61 tmr_clock_source_div_set(tim
, TMR_CLOCK_DIV1
);
62 tmr_cnt_dir_set(tim
, TMR_COUNT_UP
); //Count up (default)
65 void impl_enableTimer(TCH_t
* tch
)
67 tmr_counter_enable(tch
->timHw
->tim
, TRUE
);
70 void impl_timerPWMStart(TCH_t
* tch
)
72 tmr_output_enable(tch
->timHw
->tim
,TRUE
);
75 void impl_timerEnableIT(TCH_t
* tch
, uint32_t interrupt
)
77 tmr_interrupt_enable(tch
->timHw
->tim
, interrupt
, TRUE
);
80 void impl_timerDisableIT(TCH_t
* tch
, uint32_t interrupt
)
82 tmr_interrupt_enable(tch
->timHw
->tim
, interrupt
, FALSE
);
85 void impl_timerClearFlag(TCH_t
* tch
, uint32_t flag
)
87 tmr_flag_clear(tch
->timHw
->tim
, flag
);
90 // calculate input filter constant
91 static unsigned getFilter(unsigned ticks
)
93 static const unsigned ftab
[16] = {
95 1*2, 1*4, 1*8, // fCK_INT
103 for (unsigned i
= 1; i
< ARRAYLEN(ftab
); i
++) {
104 if (ftab
[i
] > ticks
) {
112 void impl_timerChConfigIC(TCH_t
* tch
, bool polarityRising
, unsigned inputFilterTicks
)
114 tmr_input_config_type tmr_input_config_struct
;
116 tmr_input_default_para_init(&tmr_input_config_struct
);
117 tmr_input_config_struct
.input_channel_select
= lookupTIMChannelTable
[tch
->timHw
->channelIndex
];
118 tmr_input_config_struct
.input_mapped_select
= TMR_CC_CHANNEL_MAPPED_DIRECT
;
119 tmr_input_config_struct
.input_polarity_select
= polarityRising
? TMR_INPUT_RISING_EDGE
:TMR_INPUT_FALLING_EDGE
;
120 tmr_input_config_struct
.input_filter_value
= getFilter(inputFilterTicks
);
121 tmr_input_channel_init(tch
->timHw
->tim
,&tmr_input_config_struct
,TMR_CHANNEL_INPUT_DIV_1
);
124 void impl_timerCaptureCompareHandler(tmr_type
*tim
, timHardwareContext_t
*timerCtx
)
126 unsigned tim_status
= tim
->ists
& tim
->iden
;
129 // flags will be cleared by reading CCR in dual capture, make sure we call handler correctly
130 // currrent order is highest bit first. Code should not rely on specific order (it will introduce race conditions anyway)
131 unsigned bit
= __builtin_clz(tim_status
);
132 unsigned mask
= ~(0x80000000 >> bit
);
138 case __builtin_clz(TMR_OVF_INT
): {
139 const uint16_t capture
= tim
->pr
;
140 if (timerCtx
->ch
[0].cb
&& timerCtx
->ch
[0].cb
->callbackOvr
) {
141 timerCtx
->ch
[0].cb
->callbackOvr(&timerCtx
->ch
[0], capture
);
143 if (timerCtx
->ch
[1].cb
&& timerCtx
->ch
[1].cb
->callbackOvr
) {
144 timerCtx
->ch
[1].cb
->callbackOvr(&timerCtx
->ch
[1], capture
);
146 if (timerCtx
->ch
[2].cb
&& timerCtx
->ch
[2].cb
->callbackOvr
) {
147 timerCtx
->ch
[2].cb
->callbackOvr(&timerCtx
->ch
[2], capture
);
149 if (timerCtx
->ch
[3].cb
&& timerCtx
->ch
[3].cb
->callbackOvr
) {
150 timerCtx
->ch
[3].cb
->callbackOvr(&timerCtx
->ch
[3], capture
);
154 case __builtin_clz(TMR_C1_INT
):
155 timerCtx
->ch
[0].cb
->callbackEdge(&timerCtx
->ch
[0], tim
->c1dt
);
157 case __builtin_clz(TMR_C2_INT
):
158 timerCtx
->ch
[1].cb
->callbackEdge(&timerCtx
->ch
[1], tim
->c2dt
);
160 case __builtin_clz(TMR_C3_INT
):
161 timerCtx
->ch
[2].cb
->callbackEdge(&timerCtx
->ch
[2], tim
->c3dt
);
163 case __builtin_clz(TMR_C4_INT
):
164 timerCtx
->ch
[3].cb
->callbackEdge(&timerCtx
->ch
[3], tim
->c4dt
);
169 // timerConfig == NULL
170 volatile uint32_t tmp
;
173 case __builtin_clz(TMR_OVF_INT
):
176 case __builtin_clz(TMR_C1_INT
):
179 case __builtin_clz(TMR_C2_INT
):
182 case __builtin_clz(TMR_C3_INT
):
185 case __builtin_clz(TMR_C4_INT
):
195 void impl_timerPWMConfigChannel(TCH_t
* tch
, uint16_t value
)
197 const bool inverted
= tch
->timHw
->output
& TIMER_OUTPUT_INVERTED
;
198 tmr_output_config_type tmr_output_struct
;
199 tmr_output_default_para_init(&tmr_output_struct
);
200 tmr_output_struct
.oc_mode
= TMR_OUTPUT_CONTROL_PWM_MODE_A
;
202 if (tch
->timHw
->output
& TIMER_OUTPUT_N_CHANNEL
) {
203 tmr_output_struct
.oc_output_state
= FALSE
;
204 tmr_output_struct
.occ_output_state
= TRUE
;
205 tmr_output_struct
.occ_polarity
= inverted
? TMR_OUTPUT_ACTIVE_LOW
: TMR_OUTPUT_ACTIVE_HIGH
;
206 tmr_output_struct
.occ_idle_state
= FALSE
;
209 tmr_output_struct
.oc_output_state
= TRUE
;
210 tmr_output_struct
.occ_output_state
= FALSE
;
211 tmr_output_struct
.oc_polarity
= inverted
? TMR_OUTPUT_ACTIVE_LOW
: TMR_OUTPUT_ACTIVE_HIGH
;
212 tmr_output_struct
.oc_idle_state
= TRUE
;
214 switch (tch
->timHw
->channelIndex
) {
216 tmr_output_channel_config(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_1
, &tmr_output_struct
);
217 tmr_channel_value_set(tch
->timHw
->tim
, TMR_SELECT_CHANNEL_1
, value
);
218 tmr_output_channel_buffer_enable(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_1
,TRUE
);
221 tmr_output_channel_config(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_2
, &tmr_output_struct
);
222 tmr_channel_value_set(tch
->timHw
->tim
, TMR_SELECT_CHANNEL_2
, value
);
223 tmr_output_channel_buffer_enable(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_2
,TRUE
);
227 tmr_output_channel_config(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_3
, &tmr_output_struct
);
228 tmr_channel_value_set(tch
->timHw
->tim
, TMR_SELECT_CHANNEL_3
, value
);
229 tmr_output_channel_buffer_enable(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_3
,TRUE
);
233 tmr_output_channel_config(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_4
, &tmr_output_struct
);
234 tmr_channel_value_set(tch
->timHw
->tim
, TMR_SELECT_CHANNEL_4
, value
);
235 tmr_output_channel_buffer_enable(tch
->timHw
->tim
,TMR_SELECT_CHANNEL_4
,TRUE
);
242 volatile timCCR_t
* impl_timerCCR(TCH_t
* tch
)
244 switch (tch
->timHw
->channelIndex
) {
246 return &tch
->timHw
->tim
->c1dt
;
249 return &tch
->timHw
->tim
->c2dt
;
252 return &tch
->timHw
->tim
->c3dt
;
255 return &tch
->timHw
->tim
->c4dt
;
261 // Set the channel control register
262 void impl_timerChCaptureCompareEnable(TCH_t
* tch
, bool enable
)
264 tmr_channel_enable(tch
->timHw
->tim
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
],(enable
? TRUE
: FALSE
));
267 // lookupDMASourceTable
268 static void impl_timerDMA_IRQHandler(DMA_t descriptor
)
270 if (DMA_GET_FLAG_STATUS(descriptor
, DMA_IT_TCIF
)) {
271 TCH_t
* tch
= (TCH_t
*)descriptor
->userParam
;
272 tch
->dmaState
= TCH_DMA_IDLE
;
273 dma_channel_enable(tch
->dma
->ref
,FALSE
);
274 tmr_dma_request_enable(tch
->timHw
->tim
, lookupDMASourceTable
[tch
->timHw
->channelIndex
], FALSE
);
275 DMA_CLEAR_FLAG(descriptor
, DMA_IT_TCIF
);
279 bool impl_timerPWMConfigChannelDMA(TCH_t
* tch
, void * dmaBuffer
, uint8_t dmaBufferElementSize
, uint32_t dmaBufferElementCount
)
281 dma_init_type dma_init_struct
= {0};
282 tmr_type
* timer
= tch
->timHw
->tim
;
284 tch
->dma
= dmaGetByTag(tch
->timHw
->dmaTag
);
285 if (tch
->dma
== NULL
) {
289 // If DMA is already in use - abort
290 if (tch
->dma
->owner
!= OWNER_FREE
) {
294 // We assume that timer channels are already initialized by calls to
295 tmr_output_enable(timer
, TRUE
);
297 // enable The TMR periodic buffer register
298 tmr_period_buffer_enable(timer
,TRUE
);
300 tmr_channel_enable(timer
, lookupTIMChannelTable
[tch
->timHw
->channelIndex
],TRUE
);
302 tmr_counter_enable(timer
, TRUE
);
303 dmaInit(tch
->dma
, OWNER_TIMER
, 0);
305 dmaSetHandler(tch
->dma
, impl_timerDMA_IRQHandler
, NVIC_PRIO_TIMER_DMA
, (uint32_t)tch
);
306 dma_reset(tch
->dma
->ref
);
307 dma_channel_enable(tch
->dma
->ref
,FALSE
);
309 dma_reset(tch
->dma
->ref
);
310 dma_default_para_init(&dma_init_struct
);
312 dma_init_struct
.peripheral_base_addr
= (uint32_t)impl_timerCCR(tch
);
313 dma_init_struct
.buffer_size
= dmaBufferElementCount
;
314 dma_init_struct
.peripheral_inc_enable
= FALSE
;
315 dma_init_struct
.memory_inc_enable
= TRUE
;
316 dma_init_struct
.loop_mode_enable
= FALSE
;
318 switch (dmaBufferElementSize
) {
320 dma_init_struct
.memory_data_width
= DMA_MEMORY_DATA_WIDTH_BYTE
;
321 dma_init_struct
.peripheral_data_width
= DMA_PERIPHERAL_DATA_WIDTH_BYTE
;
324 dma_init_struct
.memory_data_width
= DMA_MEMORY_DATA_WIDTH_HALFWORD
;
325 dma_init_struct
.peripheral_data_width
= DMA_PERIPHERAL_DATA_WIDTH_HALFWORD
;
328 dma_init_struct
.memory_data_width
= DMA_MEMORY_DATA_WIDTH_WORD
;
329 dma_init_struct
.peripheral_data_width
= DMA_PERIPHERAL_DATA_WIDTH_WORD
;
337 dma_init_struct
.direction
= DMA_DIR_MEMORY_TO_PERIPHERAL
;
338 dma_init_struct
.memory_base_addr
= (uint32_t)dmaBuffer
;
339 dma_init_struct
.priority
= DMA_PRIORITY_HIGH
;
340 dma_init(tch
->dma
->ref
, &dma_init_struct
);
342 //Set DMA request Mux mapping
343 dmaMuxEnable(tch
->dma
, tch
->timHw
->dmaMuxid
);
344 dma_interrupt_enable(tch
->dma
->ref
, DMA_IT_TCIF
, TRUE
);
349 void impl_timerPWMPrepareDMA(TCH_t
* tch
, uint32_t dmaBufferElementCount
)
351 tch
->dma
= dmaGetByTag(tch
->timHw
->dmaTag
);
352 if (tch
->dma
== NULL
) {
356 // Make sure we terminate any DMA transaction currently in progress
357 // Clear the flag as well, so even if DMA transfer finishes while within ATOMIC_BLOCK
358 // the resulting IRQ won't mess up the DMA state
359 ATOMIC_BLOCK(NVIC_PRIO_MAX
) {
360 dma_channel_enable(tch
->dma
->ref
,FALSE
);
361 tmr_dma_request_enable(tch
->timHw
->tim
, lookupDMASourceTable
[tch
->timHw
->channelIndex
], FALSE
);
363 DMA_CLEAR_FLAG(tch
->dma
, DMA_IT_TCIF
);
366 dma_data_number_set(tch
->dma
->ref
, dmaBufferElementCount
);
368 dma_channel_enable(tch
->dma
->ref
,TRUE
);
369 tch
->dmaState
= TCH_DMA_READY
;
372 void impl_timerPWMStartDMA(TCH_t
* tch
)
374 uint16_t dmaSources
= 0;
375 timHardwareContext_t
* timCtx
= tch
->timCtx
;
377 if (timCtx
->ch
[0].dmaState
== TCH_DMA_READY
) {
378 timCtx
->ch
[0].dmaState
= TCH_DMA_ACTIVE
;
379 dmaSources
|= TMR_C1_DMA_REQUEST
;
382 if (timCtx
->ch
[1].dmaState
== TCH_DMA_READY
) {
383 timCtx
->ch
[1].dmaState
= TCH_DMA_ACTIVE
;
384 dmaSources
|= TMR_C2_DMA_REQUEST
;
387 if (timCtx
->ch
[2].dmaState
== TCH_DMA_READY
) {
388 timCtx
->ch
[2].dmaState
= TCH_DMA_ACTIVE
;
389 dmaSources
|= TMR_C3_DMA_REQUEST
;
392 if (timCtx
->ch
[3].dmaState
== TCH_DMA_READY
) {
393 timCtx
->ch
[3].dmaState
= TCH_DMA_ACTIVE
;
394 dmaSources
|= TMR_C4_DMA_REQUEST
;
398 tmr_counter_value_set(tch
->timHw
->tim
, 0);
399 tmr_dma_request_enable(tch
->timHw
->tim
, dmaSources
, TRUE
);
403 void impl_timerPWMStopDMA(TCH_t
* tch
)
405 dma_channel_enable(tch
->dma
->ref
,FALSE
);
406 tmr_dma_request_enable(tch
->timHw
->tim
, lookupDMASourceTable
[tch
->timHw
->channelIndex
], FALSE
);
407 tch
->dmaState
= TCH_DMA_IDLE
;
408 tmr_counter_enable(tch
->timHw
->tim
, TRUE
);