Merge pull request #10592 from iNavFlight/MrD_Update-parameter-description
[inav.git] / src / main / drivers / timer_impl_stdperiph_at32.c
blob0cc194897d99610ec88e2d46e96d5d43b8830ffd
1 /*
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/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <math.h>
22 #include "platform.h"
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)
43 (void)timCtx; // NoOp
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] = {
94 1*1, // fDTS !
95 1*2, 1*4, 1*8, // fCK_INT
96 2*6, 2*8, // fDTS/2
97 4*6, 4*8,
98 8*6, 8*8,
99 16*5, 16*6, 16*8,
100 32*5, 32*6, 32*8
103 for (unsigned i = 1; i < ARRAYLEN(ftab); i++) {
104 if (ftab[i] > ticks) {
105 return i - 1;
109 return 0x0f;
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;
128 while (tim_status) {
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);
133 tim->ists = mask;
134 tim_status &= mask;
136 if (timerCtx) {
137 switch (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);
152 break;
154 case __builtin_clz(TMR_C1_INT):
155 timerCtx->ch[0].cb->callbackEdge(&timerCtx->ch[0], tim->c1dt);
156 break;
157 case __builtin_clz(TMR_C2_INT):
158 timerCtx->ch[1].cb->callbackEdge(&timerCtx->ch[1], tim->c2dt);
159 break;
160 case __builtin_clz(TMR_C3_INT):
161 timerCtx->ch[2].cb->callbackEdge(&timerCtx->ch[2], tim->c3dt);
162 break;
163 case __builtin_clz(TMR_C4_INT):
164 timerCtx->ch[3].cb->callbackEdge(&timerCtx->ch[3], tim->c4dt);
165 break;
168 else {
169 // timerConfig == NULL
170 volatile uint32_t tmp;
172 switch (bit) {
173 case __builtin_clz(TMR_OVF_INT):
174 tmp = tim->pr;
175 break;
176 case __builtin_clz(TMR_C1_INT):
177 tmp = tim->c1dt;
178 break;
179 case __builtin_clz(TMR_C2_INT):
180 tmp = tim->c2dt;
181 break;
182 case __builtin_clz(TMR_C3_INT):
183 tmp = tim->c3dt;
184 break;
185 case __builtin_clz(TMR_C4_INT):
186 tmp = tim->c4dt;
187 break;
190 (void)tmp;
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;
208 } else {
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) {
215 case 0:
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);
219 break;
220 case 1:
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);
225 break;
226 case 2:
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);
231 break;
232 case 3:
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);
237 break;
242 volatile timCCR_t * impl_timerCCR(TCH_t * tch)
244 switch (tch->timHw->channelIndex) {
245 case 0:
246 return &tch->timHw->tim->c1dt;
247 break;
248 case 1:
249 return &tch->timHw->tim->c2dt;
250 break;
251 case 2:
252 return &tch->timHw->tim->c3dt;
253 break;
254 case 3:
255 return &tch->timHw->tim->c4dt;
256 break;
258 return NULL;
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) {
286 return false;
289 // If DMA is already in use - abort
290 if (tch->dma->owner != OWNER_FREE) {
291 return false;
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) {
319 case 1:
320 dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
321 dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
322 break;
323 case 2:
324 dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
325 dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
326 break;
327 case 4:
328 dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_WORD;
329 dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_WORD;
330 break;
331 default:
332 // Programmer error
333 while(1) {
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);
346 return true;
349 void impl_timerPWMPrepareDMA(TCH_t * tch, uint32_t dmaBufferElementCount)
351 tch->dma = dmaGetByTag(tch->timHw->dmaTag);
352 if (tch->dma == NULL) {
353 return ;
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);
362 // clear dma flag
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;
397 if (dmaSources) {
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);