diff --git a/Makefile b/Makefile index b9fce24af..f804d2bb2 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ # Makefile for building the cleanflight firmware. # # Invoke this with 'make help' to see the list of supported targets. -# +# ############################################################################### # Things that the user might override on the commandline @@ -48,8 +48,8 @@ SRC_DIR = $(ROOT)/src/main OBJECT_DIR = $(ROOT)/obj/main BIN_DIR = $(ROOT)/obj CMSIS_DIR = $(ROOT)/lib/main/CMSIS -INCLUDE_DIRS = $(SRC_DIR) -LINKER_DIR = $(ROOT)/src/main/target +INCLUDE_DIRS = $(SRC_DIR) +LINKER_DIR = $(ROOT)/src/main/target # Search path for sources VPATH := $(SRC_DIR):$(SRC_DIR)/startup @@ -57,12 +57,12 @@ VPATH := $(SRC_DIR):$(SRC_DIR)/startup ifeq ($(TARGET),$(filter $(TARGET),STM32F3DISCOVERY CHEBUZZF3 NAZE32PRO MASSIVEF3)) STDPERIPH_DIR = $(ROOT)/lib/main/STM32F30x_StdPeriph_Driver -USBFS_DIR = $(ROOT)/lib/main/STM32_USB-FS-Device_Driver +USBFS_DIR = $(ROOT)/lib/main/STM32_USB-FS-Device_Driver USBPERIPH_SRC = $(notdir $(wildcard $(USBFS_DIR)/src/*.c)) STDPERIPH_SRC = $(notdir $(wildcard $(STDPERIPH_DIR)/src/*.c)) -EXCLUDES = stm32f30x_crc.c \ +EXCLUDES = stm32f30x_crc.c \ stm32f30x_can.c STDPERIPH_SRC := $(filter-out ${EXCLUDES}, $(STDPERIPH_SRC)) @@ -244,6 +244,7 @@ NAZE_SRC = startup_stm32f10x_md_gcc.S \ drivers/sound_beeper_stm32f10x.c \ drivers/system_stm32f10x.c \ drivers/timer.c \ + drivers/timer_stm32f10x.c \ hardware_revision.c \ $(HIGHEND_SRC) \ $(COMMON_SRC) @@ -280,6 +281,7 @@ EUSTM32F103RC_SRC = startup_stm32f10x_hd_gcc.S \ drivers/sound_beeper_stm32f10x.c \ drivers/system_stm32f10x.c \ drivers/timer.c \ + drivers/timer_stm32f10x.c \ $(HIGHEND_SRC) \ $(COMMON_SRC) @@ -307,6 +309,7 @@ OLIMEXINO_SRC = startup_stm32f10x_md_gcc.S \ drivers/sound_beeper_stm32f10x.c \ drivers/system_stm32f10x.c \ drivers/timer.c \ + drivers/timer_stm32f10x.c \ $(HIGHEND_SRC) \ $(COMMON_SRC) @@ -339,6 +342,7 @@ CJMCU_SRC = startup_stm32f10x_md_gcc.S \ drivers/sound_beeper_stm32f10x.c \ drivers/system_stm32f10x.c \ drivers/timer.c \ + drivers/timer_stm32f10x.c \ $(COMMON_SRC) CC3D_SRC = startup_stm32f10x_md_gcc.S \ @@ -360,6 +364,7 @@ CC3D_SRC = startup_stm32f10x_md_gcc.S \ drivers/sound_beeper_stm32f10x.c \ drivers/system_stm32f10x.c \ drivers/timer.c \ + drivers/timer_stm32f10x.c \ $(HIGHEND_SRC) \ $(COMMON_SRC) @@ -381,6 +386,7 @@ STM32F30x_COMMON_SRC = startup_stm32f30x_md_gcc.S \ drivers/sound_beeper_stm32f30x.c \ drivers/system_stm32f30x.c \ drivers/timer.c \ + drivers/timer_stm32f30x.c \ vcp/hw_config.c \ vcp/stm32_it.c \ vcp/usb_desc.c \ @@ -527,7 +533,7 @@ $(OBJECT_DIR)/$(TARGET)/%.o: %.s $(OBJECT_DIR)/$(TARGET)/%.o: %.S @mkdir -p $(dir $@) @echo %% $(notdir $<) - @$(CC) -c -o $@ $(ASFLAGS) $< + @$(CC) -c -o $@ $(ASFLAGS) $< clean: rm -f $(TARGET_BIN) $(TARGET_HEX) $(TARGET_ELF) $(TARGET_OBJS) $(TARGET_MAP) diff --git a/src/main/common/atomic.h b/src/main/common/atomic.h new file mode 100644 index 000000000..6fc63a804 --- /dev/null +++ b/src/main/common/atomic.h @@ -0,0 +1,110 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cleanflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cleanflight. If not, see . + */ + +#pragma once + +// only set_BASEPRI is implemented in device library. It does always create memory barrirer +// missing versions are implemented here + +// set BASEPRI and BASEPRI_MAX register, but do not create memory barrier +__attribute__( ( always_inline ) ) static inline void __set_BASEPRI_nb(uint32_t basePri) +{ + __ASM volatile ("\tMSR basepri, %0\n" : : "r" (basePri) ); +} + +__attribute__( ( always_inline ) ) static inline void __set_BASEPRI_MAX_nb(uint32_t basePri) +{ + __ASM volatile ("\tMSR basepri_max\n, %0" : : "r" (basePri) ); +} + +__attribute__( ( always_inline ) ) static inline void __set_BASEPRI_MAX(uint32_t basePri) +{ + __ASM volatile ("\tMSR basepri_max, %0\n" : : "r" (basePri) : "memory" ); +} + +// cleanup BASEPRI restore function, with global memory barrier +static inline void __basepriRestoreMem(uint8_t *val) +{ + __set_BASEPRI(*val); +} + +// set BASEPRI_MAX function, with global memory barrier, returns true +static inline uint8_t __basepriSetMemRetVal(uint8_t prio) +{ + __set_BASEPRI_MAX(prio); + return 1; +} + +// cleanup BASEPRI restore function, no memory barrier +static inline void __basepriRestore(uint8_t *val) +{ + __set_BASEPRI_nb(*val); +} + +// set BASEPRI_MAX function, no memory barrier, returns true +static inline uint8_t __basepriSetRetVal(uint8_t prio) +{ + __set_BASEPRI_MAX_nb(prio); + return 1; +} + +// Run block with elevated BASEPRI (using BASEPRI_MAX), restoring BASEPRI on exit. All exit paths are handled +// Full memory barrier is placed at start and exit of block +#define ATOMIC_BLOCK(prio) for ( uint8_t __basepri_save __attribute__((__cleanup__(__basepriRestoreMem))) = __get_BASEPRI(), \ + __ToDo = __basepriSetMemRetVal(prio); __ToDo ; __ToDo = 0 ) + +// Run block with elevated BASEPRI (using BASEPRI_MAX), but do not create any (explicit) memory barrier. +// Be carefull when using this, you must use some method to prevent optimizer form breaking things +// - lto is used for baseflight compillation, so function call is not memory barrier +// - use ATOMIC_BARRIER or propper volatile to protect used variables +// - gcc 4.8.4 does write all values in registes to memory before 'asm volatile', so this optimization does not help much +// but that can change in future versions +#define ATOMIC_BLOCK_NB(prio) for ( uint8_t __basepri_save __attribute__((__cleanup__(__basepriRestore))) = __get_BASEPRI(), \ + __ToDo = __basepriSetRetVal(prio); __ToDo ; __ToDo = 0 ) \ + +// ATOMIC_BARRIER +// Create memory barrier +// - at the beginning (all data must be reread from memory) +// - at exit of block (all exit paths) (all data must be written, but may be cached in register for subsequent use) +// ideally this would only protect memory passed as parameter (any type should work), but gcc is curently creating almost full barrier +// this macro can be used only ONCE PER LINE, but multiple uses per block are fine + +#if (__GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 8))) +# warn "Please verify that ATOMIC_BARRIER works as intended" +// increment version number is BARRIER works +// TODO - use flag to disable ATOMIC_BARRIER and use full barrier instead +// you should check that local variable scope with cleanup spans entire block +#endif + +#ifndef __UNIQL +# define __UNIQL_CONCAT2(x,y) x ## y +# define __UNIQL_CONCAT(x,y) __UNIQL_CONCAT2(x,y) +# define __UNIQL(x) __UNIQL_CONCAT(x,__LINE__) +#endif + +// this macro uses local function for cleanup. CLang block can be substituded +#define ATOMIC_BARRIER(data) \ + __extension__ void __UNIQL(__barrierEnd)(typeof(data) **__d) { \ + __asm__ volatile ("\t# barier(" #data ") end\n" : : "m" (**__d)); \ + } \ + typeof(data) __attribute__((__cleanup__(__UNIQL(__barrierEnd)))) *__UNIQL(__barrier) = &data; \ + __asm__ volatile ("\t# barier (" #data ") start\n" : "=m" (*__UNIQL(__barrier))) + + +// define these wrappers for atomic operations, use gcc buildins +#define ATOMIC_OR(ptr, val) __sync_fetch_and_or(ptr, val) +#define ATOMIC_AND(ptr, val) __sync_fetch_and_and(ptr, val) diff --git a/src/main/common/utils.h b/src/main/common/utils.h new file mode 100644 index 000000000..d9f22c01e --- /dev/null +++ b/src/main/common/utils.h @@ -0,0 +1,44 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cleanflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cleanflight. If not, see . + */ + +#pragma once + +#include + +#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0])) + +/* +http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html +*/ +#define BITCOUNT(x) (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255) +#define BX_(x) ((x) - (((x)>>1)&0x77777777) - (((x)>>2)&0x33333333) - (((x)>>3)&0x11111111)) + +#define UNUSED(x) (void)(x) + +#if 0 +// ISO C version, but no type checking +#define container_of(ptr, type, member) \ + ((type *) ((char *)(ptr) - offsetof(type, member))) +#else +// non ISO variant from linux kernel; checks ptr type, but triggers 'ISO C forbids braced-groups within expressions [-Wpedantic]' +// __extension__ is here to disable this warning +#define container_of(ptr, type, member) __extension__ ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + + +#endif diff --git a/src/main/drivers/pwm_output.c b/src/main/drivers/pwm_output.c index 1fbcfad79..904e79f19 100644 --- a/src/main/drivers/pwm_output.c +++ b/src/main/drivers/pwm_output.c @@ -34,13 +34,7 @@ typedef void (*pwmWriteFuncPtr)(uint8_t index, uint16_t value); // function pointer used to write motors typedef struct { -#ifdef STM32F303xC - volatile uint32_t *ccr; -#endif - -#ifdef STM32F10X - volatile uint16_t *ccr; -#endif + volatile timCCR_t *ccr; uint16_t period; pwmWriteFuncPtr pwmWritePtr; } pwmOutputPort_t; diff --git a/src/main/drivers/pwm_rx.c b/src/main/drivers/pwm_rx.c index 5a7547bef..2ecd4aa64 100644 --- a/src/main/drivers/pwm_rx.c +++ b/src/main/drivers/pwm_rx.c @@ -23,6 +23,9 @@ #include "platform.h" #include "build_config.h" +#include "common/utils.h" +#include "system.h" + #include "nvic.h" #include "gpio.h" #include "timer.h" @@ -64,14 +67,16 @@ typedef struct { uint8_t missedEvents; const timerHardware_t *timerHardware; + timerCCHandlerRec_t edgeCb; + timerOvrHandlerRec_t overflowCb; } pwmInputPort_t; static pwmInputPort_t pwmInputPorts[PWM_INPUT_PORT_COUNT]; static uint16_t captures[PWM_PORTS_OR_PPM_CAPTURE_COUNT]; -#define PPM_TIMER_PERIOD 0xFFFF -#define PWM_TIMER_PERIOD 0xFFFF +#define PPM_TIMER_PERIOD 0x10000 +#define PWM_TIMER_PERIOD 0x10000 static uint8_t ppmFrameCount = 0; static uint8_t lastPPMFrameCount = 0; @@ -132,15 +137,15 @@ static void ppmInit(void) ppmDev.tracking = false; } -static void ppmOverflowCallback(uint8_t port, captureCompare_t capture) +static void ppmOverflowCallback(timerOvrHandlerRec_t* cbRec, captureCompare_t capture) { - UNUSED(port); - ppmDev.largeCounter += capture; + UNUSED(cbRec); + ppmDev.largeCounter += capture + 1; } -static void ppmEdgeCallback(uint8_t port, captureCompare_t capture) +static void ppmEdgeCallback(timerCCHandlerRec_t* cbRec, captureCompare_t capture) { - UNUSED(port); + UNUSED(cbRec); int32_t i; /* Shift the last measurement out */ @@ -218,10 +223,10 @@ static void ppmEdgeCallback(uint8_t port, captureCompare_t capture) #define MAX_MISSED_PWM_EVENTS 10 -static void pwmOverflowCallback(uint8_t port, captureCompare_t capture) +static void pwmOverflowCallback(timerOvrHandlerRec_t* cbRec, captureCompare_t capture) { UNUSED(capture); - pwmInputPort_t *pwmInputPort = &pwmInputPorts[port]; + pwmInputPort_t *pwmInputPort = container_of(cbRec, pwmInputPort_t, overflowCb); if (++pwmInputPort->missedEvents > MAX_MISSED_PWM_EVENTS) { if (pwmInputPort->state == 0) { @@ -231,9 +236,9 @@ static void pwmOverflowCallback(uint8_t port, captureCompare_t capture) } } -static void pwmEdgeCallback(uint8_t port, captureCompare_t capture) +static void pwmEdgeCallback(timerCCHandlerRec_t *cbRec, captureCompare_t capture) { - pwmInputPort_t *pwmInputPort = &pwmInputPorts[port]; + pwmInputPort_t *pwmInputPort = container_of(cbRec, pwmInputPort_t, edgeCb); const timerHardware_t *timerHardwarePtr = pwmInputPort->timerHardware; if (pwmInputPort->state == 0) { @@ -285,27 +290,22 @@ void pwmICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity) void pwmInConfig(const timerHardware_t *timerHardwarePtr, uint8_t channel) { - pwmInputPort_t *p = &pwmInputPorts[channel]; + pwmInputPort_t *self = &pwmInputPorts[channel]; - p->state = 0; - p->missedEvents = 0; - p->channel = channel; - p->mode = INPUT_MODE_PWM; - p->timerHardware = timerHardwarePtr; + self->state = 0; + self->missedEvents = 0; + self->channel = channel; + self->mode = INPUT_MODE_PWM; + self->timerHardware = timerHardwarePtr; pwmGPIOConfig(timerHardwarePtr->gpio, timerHardwarePtr->pin, timerHardwarePtr->gpioInputMode); pwmICConfig(timerHardwarePtr->tim, timerHardwarePtr->channel, TIM_ICPolarity_Rising); - timerConfigure(timerHardwarePtr, PWM_TIMER_PERIOD, PWM_TIMER_MHZ); + timerConfigure(timerHardwarePtr, (uint16_t)PWM_TIMER_PERIOD, PWM_TIMER_MHZ); -#ifdef STM32F303xC - // If overflow monitoring is enabled on STM32F3 then the IRQ handler TIM1_UP_TIM16_IRQHandler is continually called. - if (timerHardwarePtr->tim == TIM1) { - configureTimerCaptureCompareInterrupt(timerHardwarePtr, channel, pwmEdgeCallback, NULL); - return; - } -#endif - configureTimerCaptureCompareInterrupt(timerHardwarePtr, channel, pwmEdgeCallback, pwmOverflowCallback); + timerChCCHandlerInit(&self->edgeCb, pwmEdgeCallback); + timerChOvrHandlerInit(&self->overflowCb, pwmOverflowCallback); + timerChConfigCallbacks(timerHardwarePtr, &self->edgeCb, &self->overflowCb); } #define UNUSED_PPM_TIMER_REFERENCE 0 @@ -315,16 +315,19 @@ void ppmInConfig(const timerHardware_t *timerHardwarePtr) { ppmInit(); - pwmInputPort_t *p = &pwmInputPorts[FIRST_PWM_PORT]; + pwmInputPort_t *self = &pwmInputPorts[FIRST_PWM_PORT]; - p->mode = INPUT_MODE_PPM; - p->timerHardware = timerHardwarePtr; + self->mode = INPUT_MODE_PPM; + self->timerHardware = timerHardwarePtr; pwmGPIOConfig(timerHardwarePtr->gpio, timerHardwarePtr->pin, timerHardwarePtr->gpioInputMode); pwmICConfig(timerHardwarePtr->tim, timerHardwarePtr->channel, TIM_ICPolarity_Rising); - timerConfigure(timerHardwarePtr, PPM_TIMER_PERIOD, PWM_TIMER_MHZ); - configureTimerCaptureCompareInterrupt(timerHardwarePtr, UNUSED_PPM_TIMER_REFERENCE, ppmEdgeCallback, ppmOverflowCallback); + timerConfigure(timerHardwarePtr, (uint16_t)PPM_TIMER_PERIOD, PWM_TIMER_MHZ); + + timerChCCHandlerInit(&self->edgeCb, ppmEdgeCallback); + timerChOvrHandlerInit(&self->overflowCb, ppmOverflowCallback); + timerChConfigCallbacks(timerHardwarePtr, &self->edgeCb, &self->overflowCb); } uint16_t pwmRead(uint8_t channel) diff --git a/src/main/drivers/serial_softserial.c b/src/main/drivers/serial_softserial.c index f588bb14f..82a0eb468 100644 --- a/src/main/drivers/serial_softserial.c +++ b/src/main/drivers/serial_softserial.c @@ -25,6 +25,9 @@ #include "build_config.h" +#include "common/utils.h" +#include "common/atomic.h" + #include "nvic.h" #include "system.h" #include "gpio.h" @@ -44,8 +47,8 @@ softSerial_t softSerialPorts[MAX_SOFTSERIAL_PORTS]; -void onSerialTimer(uint8_t portIndex, captureCompare_t capture); -void onSerialRxPinChange(uint8_t portIndex, captureCompare_t capture); +void onSerialTimer(timerCCHandlerRec_t *cbRec, captureCompare_t capture); +void onSerialRxPinChange(timerCCHandlerRec_t *cbRec, captureCompare_t capture); void setTxSignal(softSerial_t *softSerial, uint8_t state) { @@ -88,7 +91,7 @@ static void serialTimerTxConfig(const timerHardware_t *timerHardwarePtr, uint8_t timerPeriod = clock / baud; if (isTimerPeriodTooLarge(timerPeriod)) { if (clock > 1) { - clock = clock / 2; + clock = clock / 2; // this is wrong - mhz stays the same ... This will double baudrate until ok (but minimum baudrate is < 1200) } else { // TODO unable to continue, unable to determine clock and timerPeriods for the given baud } @@ -98,7 +101,8 @@ static void serialTimerTxConfig(const timerHardware_t *timerHardwarePtr, uint8_t uint8_t mhz = SystemCoreClock / 1000000; timerConfigure(timerHardwarePtr, timerPeriod, mhz); - configureTimerCaptureCompareInterrupt(timerHardwarePtr, reference, onSerialTimer, NULL); + timerChCCHandlerInit(&softSerialPorts[reference].timerCb, onSerialTimer); + timerChConfigCallbacks(timerHardwarePtr, &softSerialPorts[reference].timerCb, NULL); } static void serialICConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t polarity) @@ -119,7 +123,8 @@ static void serialTimerRxConfig(const timerHardware_t *timerHardwarePtr, uint8_t { // start bit is usually a FALLING signal serialICConfig(timerHardwarePtr->tim, timerHardwarePtr->channel, inversion == SERIAL_INVERTED ? TIM_ICPolarity_Rising : TIM_ICPolarity_Falling); - configureTimerCaptureCompareInterrupt(timerHardwarePtr, reference, onSerialRxPinChange, NULL); + timerChCCHandlerInit(&softSerialPorts[reference].edgeCb, onSerialRxPinChange); + timerChConfigCallbacks(timerHardwarePtr, &softSerialPorts[reference].edgeCb, NULL); } static void serialOutputPortConfig(const timerHardware_t *timerHardwarePtr) @@ -309,20 +314,20 @@ void processRxState(softSerial_t *softSerial) } } -void onSerialTimer(uint8_t portIndex, captureCompare_t capture) +void onSerialTimer(timerCCHandlerRec_t *cbRec, captureCompare_t capture) { UNUSED(capture); - softSerial_t *softSerial = &(softSerialPorts[portIndex]); + softSerial_t *softSerial = container_of(cbRec, softSerial_t, timerCb); processTxState(softSerial); processRxState(softSerial); } -void onSerialRxPinChange(uint8_t portIndex, captureCompare_t capture) +void onSerialRxPinChange(timerCCHandlerRec_t *cbRec, captureCompare_t capture) { UNUSED(capture); - softSerial_t *softSerial = &(softSerialPorts[portIndex]); + softSerial_t *softSerial = container_of(cbRec, softSerial_t, edgeCb); if ((softSerial->port.mode & MODE_RX) == 0) { return; @@ -332,7 +337,7 @@ void onSerialRxPinChange(uint8_t portIndex, captureCompare_t capture) // synchronise bit counter // FIXME this reduces functionality somewhat as receiving breaks concurrent transmission on all ports because // the next callback to the onSerialTimer will happen too early causing transmission errors. - TIM_SetCounter(softSerial->rxTimerHardware->tim, 0); + TIM_SetCounter(softSerial->rxTimerHardware->tim, softSerial->rxTimerHardware->tim->ARR / 2); if (softSerial->isTransmittingData) { softSerial->transmissionErrors++; } diff --git a/src/main/drivers/serial_softserial.h b/src/main/drivers/serial_softserial.h index b5725e7dd..89eda56c3 100644 --- a/src/main/drivers/serial_softserial.h +++ b/src/main/drivers/serial_softserial.h @@ -32,7 +32,7 @@ typedef struct softSerial_s { const timerHardware_t *txTimerHardware; volatile uint8_t txBuffer[SOFTSERIAL_BUFFER_SIZE]; - + uint8_t isSearchingForStartBit; uint8_t rxBitIndex; uint8_t rxLastLeadingEdgeAtBitIndex; @@ -48,6 +48,9 @@ typedef struct softSerial_s { uint16_t receiveErrors; uint8_t softSerialPortIndex; + + timerCCHandlerRec_t timerCb; + timerCCHandlerRec_t edgeCb; } softSerial_t; extern timerHardware_t* serialTimerHardware; diff --git a/src/main/drivers/serial_uart.c b/src/main/drivers/serial_uart.c index 9af5f5af0..04dbbf85d 100644 --- a/src/main/drivers/serial_uart.c +++ b/src/main/drivers/serial_uart.c @@ -28,6 +28,7 @@ #include "build_config.h" +#include "common/utils.h" #include "gpio.h" #include "inverter.h" diff --git a/src/main/drivers/timer.c b/src/main/drivers/timer.c index 14cf26720..c7b01d204 100644 --- a/src/main/drivers/timer.c +++ b/src/main/drivers/timer.c @@ -21,12 +21,18 @@ #include #include "platform.h" +#include "common/utils.h" +#include "common/atomic.h" + #include "nvic.h" #include "gpio.h" #include "system.h" #include "timer.h" +#include "timer_impl.h" + +#define TIM_N(n) (1 << (n)) /* FreeFlight/Naze32 timer layout TIM2_CH1 RC1 PWM1 @@ -86,11 +92,7 @@ const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = { { TIM4, GPIOB, Pin_9, TIM_Channel_4, TIM4_IRQn, 0, Mode_IPD} // PWM14 }; -#define MAX_TIMERS 4 // TIM1..TIM4 - -static const TIM_TypeDef const *timers[MAX_TIMERS] = { - TIM1, TIM2, TIM3, TIM4 -}; +#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(4)) #define TIMER_APB1_PERIPHERALS (RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4) #define TIMER_APB2_PERIPHERALS (RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB) @@ -113,11 +115,7 @@ const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = { { TIM2, GPIOA, Pin_2, TIM_Channel_3, TIM2_IRQn, 1, GPIO_Mode_AF_PP}, // S6_OUT }; -#define MAX_TIMERS 4 // TIM1..TIM4 - -static const TIM_TypeDef const *timers[MAX_TIMERS] = { - TIM1, TIM2, TIM3, TIM4 -}; +#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(4)) #define TIMER_APB1_PERIPHERALS (RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4) #define TIMER_APB2_PERIPHERALS (RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB) @@ -141,11 +139,7 @@ const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = { { TIM2, GPIOA, Pin_2, TIM_Channel_3, TIM2_IRQn, 0, Mode_AF_PP, GPIO_PinSource2, GPIO_AF_1} // PWM14 - PA2 }; -#define MAX_TIMERS 7 - -static const TIM_TypeDef const *timers[MAX_TIMERS] = { - TIM1, TIM2, TIM3, TIM4, TIM8, TIM16, TIM17 -}; +#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(4) | TIM_N(8) | TIM_N(16) | TIM_N(17)) #define TIMER_APB1_PERIPHERALS (RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4) #define TIMER_APB2_PERIPHERALS (RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8 | RCC_APB2Periph_TIM16 | RCC_APB2Periph_TIM17) @@ -178,11 +172,7 @@ const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = { { TIM3, GPIOA, Pin_4, TIM_Channel_2, TIM3_IRQn, 0, Mode_AF_PP, GPIO_PinSource4, GPIO_AF_2} // PWM18 - PA4 }; -#define MAX_TIMERS 8 - -static const TIM_TypeDef const *timers[MAX_TIMERS] = { - TIM1, TIM2, TIM3, TIM4, TIM8, TIM15, TIM16, TIM17 -}; +#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(4) | TIM_N(8) | TIM_N(15) | TIM_N(16) | TIM_N(17)) #define TIMER_APB1_PERIPHERALS (RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4) #define TIMER_APB2_PERIPHERALS (RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM8 | RCC_APB2Periph_TIM15 | RCC_APB2Periph_TIM16 | RCC_APB2Periph_TIM17) @@ -209,11 +199,7 @@ const timerHardware_t timerHardware[USABLE_TIMER_CHANNEL_COUNT] = { { TIM17, GPIOA, Pin_7, TIM_Channel_1, TIM1_TRG_COM_TIM17_IRQn, 1, Mode_AF_PP, GPIO_PinSource7, GPIO_AF_1}, // PA7 - untested }; -#define MAX_TIMERS 7 - -static const TIM_TypeDef const *timers[MAX_TIMERS] = { - TIM1, TIM2, TIM3, TIM4, TIM15, TIM16, TIM17 -}; +#define USED_TIMERS (TIM_N(1) | TIM_N(2) | TIM_N(3) | TIM_N(4) | TIM_N(15) | TIM_N(16) | TIM_N(17)) #define TIMER_APB1_PERIPHERALS (RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM3 | RCC_APB1Periph_TIM4) #define TIMER_APB2_PERIPHERALS (RCC_APB2Periph_TIM1 | RCC_APB2Periph_TIM15 | RCC_APB2Periph_TIM16 | RCC_APB2Periph_TIM17) @@ -221,91 +207,102 @@ static const TIM_TypeDef const *timers[MAX_TIMERS] = { #endif +#define USED_TIMER_COUNT BITCOUNT(USED_TIMERS) +#define CC_CHANNELS_PER_TIMER 4 // TIM_Channel_1..4 -#define CC_CHANNELS_PER_TIMER 4 // TIM_Channel_1..4 -static const uint16_t const channels[CC_CHANNELS_PER_TIMER] = { - TIM_Channel_1, TIM_Channel_2, TIM_Channel_3, TIM_Channel_4 -}; +#define TIM_IT_CCx(ch) (TIM_IT_CC1 << ((ch) / 4)) typedef struct timerConfig_s { - TIM_TypeDef *tim; - uint8_t channel; - timerCCCallbackPtr *edgeCallback; - timerCCCallbackPtr *overflowCallback; - uint8_t reference; + timerCCHandlerRec_t *edgeCallback[CC_CHANNELS_PER_TIMER]; + timerOvrHandlerRec_t *overflowCallback[CC_CHANNELS_PER_TIMER]; + timerOvrHandlerRec_t *overflowCallbackActive; // null-terminated linkded list of active overflow callbacks } timerConfig_t; +timerConfig_t timerConfig[USED_TIMER_COUNT]; -static timerConfig_t timerConfig[MAX_TIMERS * CC_CHANNELS_PER_TIMER]; +typedef struct { + channelType_t type; +} timerChannelInfo_t; +timerChannelInfo_t timerChannelInfo[USABLE_TIMER_CHANNEL_COUNT]; + +typedef struct { + uint8_t priority; +} timerInfo_t; +timerInfo_t timerInfo[USED_TIMER_COUNT]; + +// return index of timer in timer table. Lowest timer has index 0 +#define TIMER_INDEX(i) BITCOUNT((TIM_N(i) - 1) & USED_TIMERS) static uint8_t lookupTimerIndex(const TIM_TypeDef *tim) { - uint8_t timerIndex = 0; - while (timers[timerIndex] != tim) { - timerIndex++; +#define _CASE_SHF 10 // amount we can safely shift timer address to the right. gcc will throw error if some timers overlap +#define _CASE_(tim, index) case ((unsigned)tim >> _CASE_SHF): return index; break +#define _CASE(i) _CASE_(TIM##i##_BASE, TIMER_INDEX(i)) + +// let gcc do the work, switch should be quite optimized + switch((unsigned)tim >> _CASE_SHF) { +#if USED_TIMERS & TIM_N(1) + _CASE(1); +#endif +#if USED_TIMERS & TIM_N(2) + _CASE(2); +#endif +#if USED_TIMERS & TIM_N(3) + _CASE(3); +#endif +#if USED_TIMERS & TIM_N(4) + _CASE(4); +#endif +#if USED_TIMERS & TIM_N(8) + _CASE(8); +#endif +#if USED_TIMERS & TIM_N(15) + _CASE(15); +#endif +#if USED_TIMERS & TIM_N(16) + _CASE(16); +#endif +#if USED_TIMERS & TIM_N(17) + _CASE(17); +#endif + default: return ~1; // make sure final index is out of range } - return timerIndex; +#undef _CASE +#undef _CASE_ } -static uint8_t lookupChannelIndex(const uint16_t channel) +TIM_TypeDef * const usedTimers[USED_TIMER_COUNT] = { +#define _DEF(i) TIM##i + +#if USED_TIMERS & TIM_N(1) + _DEF(1), +#endif +#if USED_TIMERS & TIM_N(2) + _DEF(2), +#endif +#if USED_TIMERS & TIM_N(3) + _DEF(3), +#endif +#if USED_TIMERS & TIM_N(4) + _DEF(4), +#endif +#if USED_TIMERS & TIM_N(8) + _DEF(8), +#endif +#if USED_TIMERS & TIM_N(15) + _DEF(15), +#endif +#if USED_TIMERS & TIM_N(16) + _DEF(16), +#endif +#if USED_TIMERS & TIM_N(17) + _DEF(17), +#endif +#undef _DEF +}; + +static inline uint8_t lookupChannelIndex(const uint16_t channel) { - uint8_t channelIndex = 0; - while (channels[channelIndex] != channel) { - channelIndex++; - } - return channelIndex; -} - -static uint8_t lookupTimerConfigIndex(TIM_TypeDef *tim, const uint16_t channel) -{ - return lookupTimerIndex(tim) + (MAX_TIMERS * lookupChannelIndex(channel)); -} - -void configureTimerChannelCallback(TIM_TypeDef *tim, uint8_t channel, uint8_t reference, timerCCCallbackPtr *edgeCallback) -{ - configureTimerChannelCallbacks(tim, channel, reference, edgeCallback, NULL); -} - -void configureTimerChannelCallbacks(TIM_TypeDef *tim, uint8_t channel, uint8_t reference, timerCCCallbackPtr *edgeCallback, timerCCCallbackPtr *overflowCallback) -{ - assert_param(IS_TIM_CHANNEL(channel)); - - uint8_t timerConfigIndex = lookupTimerConfigIndex(tim, channel); - - if (timerConfigIndex >= MAX_TIMERS * CC_CHANNELS_PER_TIMER) { - return; - } - - timerConfig[timerConfigIndex].edgeCallback = edgeCallback; - timerConfig[timerConfigIndex].overflowCallback = overflowCallback; - timerConfig[timerConfigIndex].channel = channel; - timerConfig[timerConfigIndex].reference = reference; -} - -void configureTimerInputCaptureCompareChannel(TIM_TypeDef *tim, const uint8_t channel) -{ - switch (channel) { - case TIM_Channel_1: - TIM_ITConfig(tim, TIM_IT_CC1, ENABLE); - break; - case TIM_Channel_2: - TIM_ITConfig(tim, TIM_IT_CC2, ENABLE); - break; - case TIM_Channel_3: - TIM_ITConfig(tim, TIM_IT_CC3, ENABLE); - break; - case TIM_Channel_4: - TIM_ITConfig(tim, TIM_IT_CC4, ENABLE); - break; - } -} - -void configureTimerCaptureCompareInterrupt(const timerHardware_t *timerHardwarePtr, uint8_t reference, timerCCCallbackPtr *edgeCallback, timerCCCallbackPtr *overflowCallback) -{ - configureTimerChannelCallbacks(timerHardwarePtr->tim, timerHardwarePtr->channel, reference, edgeCallback, overflowCallback); - configureTimerInputCaptureCompareChannel(timerHardwarePtr->tim, timerHardwarePtr->channel); - if (overflowCallback) { - TIM_ITConfig(timerHardwarePtr->tim, TIM_IT_Update, ENABLE); - } + return channel >> 2; } void timerNVICConfigure(uint8_t irq) @@ -330,12 +327,12 @@ void configTimeBase(TIM_TypeDef *tim, uint16_t period, uint8_t mhz) // Thus for 1Mhz: 72000000 / 1000000 = 72, 72 - 1 = 71 = TIM_Prescaler TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / ((uint32_t)mhz * 1000000)) - 1; - TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(tim, &TIM_TimeBaseStructure); } +// old interface for PWM inputs. It should be replaced void timerConfigure(const timerHardware_t *timerHardwarePtr, uint16_t period, uint8_t mhz) { configTimeBase(timerHardwarePtr->tim, period, mhz); @@ -343,108 +340,382 @@ void timerConfigure(const timerHardware_t *timerHardwarePtr, uint16_t period, ui timerNVICConfigure(timerHardwarePtr->irq); } -timerConfig_t *findTimerConfig(TIM_TypeDef *tim, uint16_t channel) +// allocate and configure timer channel. Timer priority is set to highest priority of its channels +void timerChInit(const timerHardware_t *timHw, channelType_t type, int irqPriority) { - uint8_t timerConfigIndex = lookupTimerConfigIndex(tim, channel); - return &(timerConfig[timerConfigIndex]); + unsigned channel = timHw - timerHardware; + if(channel >= USABLE_TIMER_CHANNEL_COUNT) + return; + + timerChannelInfo[channel].type = type; + unsigned timer = lookupTimerIndex(timHw->tim); + if(timer >= USED_TIMER_COUNT) + return; + if(irqPriority < timerInfo[timer].priority) { + // it would be better to set priority in the end, but current startup sequence is not ready + configTimeBase(usedTimers[timer], 0, 1); + TIM_Cmd(usedTimers[timer], ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure; + + NVIC_InitStructure.NVIC_IRQChannel = timHw->irq; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_PRIORITY_BASE(irqPriority); + NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_PRIORITY_SUB(irqPriority); + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + timerInfo[timer].priority = irqPriority; + } } -static void timCCxHandler(TIM_TypeDef *tim) +void timerChCCHandlerInit(timerCCHandlerRec_t *self, timerCCHandlerCallback *fn) { - captureCompare_t capture; - timerConfig_t *timerConfig; - uint8_t channel; - uint8_t channelIndex; + self->fn = fn; +} - if (TIM_GetITStatus(tim, TIM_IT_Update) == SET) { - TIM_ClearITPendingBit(tim, TIM_IT_Update); - capture = tim->ARR; +void timerChOvrHandlerInit(timerOvrHandlerRec_t *self, timerOvrHandlerCallback *fn) +{ + self->fn = fn; + self->next = NULL; +} - for (channelIndex = 0; channelIndex < CC_CHANNELS_PER_TIMER; channelIndex++) { - channel = channels[channelIndex]; - timerConfig = findTimerConfig(tim, channel); - - if (!timerConfig->overflowCallback) { - continue; +// update overflow callback list +// some synchronization mechanism is neccesary to avoid disturbing other channels (BASEPRI used now) +static void timerChConfig_UpdateOverflow(timerConfig_t *cfg, TIM_TypeDef* tim) { + timerOvrHandlerRec_t **chain = &cfg->overflowCallbackActive; + ATOMIC_BLOCK(NVIC_PRIO_TIMER) { + for(int i = 0; i < CC_CHANNELS_PER_TIMER; i++) + if(cfg->overflowCallback[i]) { + *chain = cfg->overflowCallback[i]; + chain = &cfg->overflowCallback[i]->next; } - timerConfig->overflowCallback(timerConfig->reference, capture); - } + *chain = NULL; + } + // enable or disable IRQ + TIM_ITConfig(tim, TIM_IT_Update, cfg->overflowCallbackActive ? ENABLE : DISABLE); +} + +// config edge and overflow callback for channel. Try to avoid overflowCallback, it is a bit expensive +void timerChConfigCallbacks(const timerHardware_t* timHw, timerCCHandlerRec_t *edgeCallback, timerOvrHandlerRec_t *overflowCallback) +{ + uint8_t timerIndex = lookupTimerIndex(timHw->tim); + if (timerIndex >= USED_TIMER_COUNT) { + return; + } + uint8_t channelIndex = lookupChannelIndex(timHw->channel); + if(edgeCallback == NULL) // disable irq before changing callback to NULL + TIM_ITConfig(timHw->tim, TIM_IT_CCx(timHw->channel), DISABLE); + // setup callback info + timerConfig[timerIndex].edgeCallback[channelIndex] = edgeCallback; + timerConfig[timerIndex].overflowCallback[channelIndex] = overflowCallback; + // enable channel IRQ + if(edgeCallback) + TIM_ITConfig(timHw->tim, TIM_IT_CCx(timHw->channel), ENABLE); + + timerChConfig_UpdateOverflow(&timerConfig[timerIndex], timHw->tim); +} + +// configure callbacks for pair of channels (1+2 or 3+4). +// Hi(2,4) and Lo(1,3) callbacks are specified, it is not important which timHw channel is used. +// This is intended for dual capture mode (each channel handles one transition) +void timerChConfigCallbacksDual(const timerHardware_t* timHw, timerCCHandlerRec_t *edgeCallbackLo, timerCCHandlerRec_t *edgeCallbackHi, timerOvrHandlerRec_t *overflowCallback) +{ + uint8_t timerIndex = lookupTimerIndex(timHw->tim); + if (timerIndex >= USED_TIMER_COUNT) { + return; + } + uint16_t chLo = timHw->channel & ~TIM_Channel_2; // lower channel + uint16_t chHi = timHw->channel | TIM_Channel_2; // upper channel + uint8_t channelIndex = lookupChannelIndex(chLo); // get index of lower channel + + if(edgeCallbackLo == NULL) // disable irq before changing setting callback to NULL + TIM_ITConfig(timHw->tim, TIM_IT_CCx(chLo), DISABLE); + if(edgeCallbackHi == NULL) // disable irq before changing setting callback to NULL + TIM_ITConfig(timHw->tim, TIM_IT_CCx(chHi), DISABLE); + + // setup callback info + timerConfig[timerIndex].edgeCallback[channelIndex] = edgeCallbackLo; + timerConfig[timerIndex].edgeCallback[channelIndex + 1] = edgeCallbackHi; + timerConfig[timerIndex].overflowCallback[channelIndex] = overflowCallback; + timerConfig[timerIndex].overflowCallback[channelIndex + 1] = NULL; + + // enable channel IRQs + if(edgeCallbackLo) { + TIM_ClearFlag(timHw->tim, TIM_IT_CCx(chLo)); + TIM_ITConfig(timHw->tim, TIM_IT_CCx(chLo), ENABLE); + } + if(edgeCallbackHi) { + TIM_ClearFlag(timHw->tim, TIM_IT_CCx(chHi)); + TIM_ITConfig(timHw->tim, TIM_IT_CCx(chHi), ENABLE); } - for (channelIndex = 0; channelIndex < CC_CHANNELS_PER_TIMER; channelIndex++) { - channel = channels[channelIndex]; + timerChConfig_UpdateOverflow(&timerConfig[timerIndex], timHw->tim); +} - if (channel == TIM_Channel_1 && TIM_GetITStatus(tim, TIM_IT_CC1) == SET) { - TIM_ClearITPendingBit(tim, TIM_IT_CC1); +// enable/disable IRQ for low channel in dual configuration +void timerChITConfigDualLo(const timerHardware_t* timHw, FunctionalState newState) { + TIM_ITConfig(timHw->tim, TIM_IT_CCx(timHw->channel&~TIM_Channel_2), newState); +} - timerConfig = findTimerConfig(tim, TIM_Channel_1); - capture = TIM_GetCapture1(tim); - } else if (channel == TIM_Channel_2 && TIM_GetITStatus(tim, TIM_IT_CC2) == SET) { - TIM_ClearITPendingBit(tim, TIM_IT_CC2); +// enable or disable IRQ +void timerChITConfig(const timerHardware_t* timHw, FunctionalState newState) +{ + TIM_ITConfig(timHw->tim, TIM_IT_CCx(timHw->channel), newState); +} - timerConfig = findTimerConfig(tim, TIM_Channel_2); - capture = TIM_GetCapture2(tim); - } else if (channel == TIM_Channel_3 && TIM_GetITStatus(tim, TIM_IT_CC3) == SET) { - TIM_ClearITPendingBit(tim, TIM_IT_CC3); +// clear Compare/Capture flag for channel +void timerChClearCCFlag(const timerHardware_t* timHw) +{ + TIM_ClearFlag(timHw->tim, TIM_IT_CCx(timHw->channel)); +} - timerConfig = findTimerConfig(tim, TIM_Channel_3); - capture = TIM_GetCapture3(tim); - } else if (channel == TIM_Channel_4 && TIM_GetITStatus(tim, TIM_IT_CC4) == SET) { - TIM_ClearITPendingBit(tim, TIM_IT_CC4); +// configure timer channel GPIO mode +void timerChConfigGPIO(const timerHardware_t* timHw, GPIO_Mode mode) +{ + gpio_config_t cfg; - timerConfig = findTimerConfig(tim, TIM_Channel_4); - capture = TIM_GetCapture4(tim); - } else { - continue; // avoid uninitialised variable dereference - } + cfg.pin = timHw->pin; + cfg.mode = mode; + cfg.speed = Speed_2MHz; + gpioInit(timHw->gpio, &cfg); +} - if (!timerConfig->edgeCallback) { - continue; - } - timerConfig->edgeCallback(timerConfig->reference, capture); +// calculate input filter constant +// TODO - we should probably setup DTS to higher value to allow reasonable input filtering +// - notice that prescaler[0] does use DTS for sampling - the sequence won't be monotonous anymore +static unsigned getFilter(unsigned ticks) +{ + static const unsigned ftab[16] = { + 1*1, // fDTS ! + 1*2, 1*4, 1*8, // fCK_INT + 2*6, 2*8, // fDTS/2 + 4*6, 4*8, + 8*6, 8*8, + 16*5, 16*6, 16*8, + 32*5, 32*6, 32*8 + }; + for(unsigned i = 1; i < ARRAYLEN(ftab); i++) + if(ftab[i] > ticks) + return i - 1; + return 0x0f; +} + +// Configure input captupre +void timerChConfigIC(const timerHardware_t* timHw, bool polarityRising, unsigned inputFilterTicks) +{ + TIM_ICInitTypeDef TIM_ICInitStructure; + + TIM_ICStructInit(&TIM_ICInitStructure); + TIM_ICInitStructure.TIM_Channel = timHw->channel; + TIM_ICInitStructure.TIM_ICPolarity = polarityRising ? TIM_ICPolarity_Rising : TIM_ICPolarity_Falling; + TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; + TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; + TIM_ICInitStructure.TIM_ICFilter = getFilter(inputFilterTicks); + + TIM_ICInit(timHw->tim, &TIM_ICInitStructure); +} + +// configure dual channel input channel for capture +// polarity is for Low channel (capture order is always Lo - Hi) +void timerChConfigICDual(const timerHardware_t* timHw, bool polarityRising, unsigned inputFilterTicks) +{ + TIM_ICInitTypeDef TIM_ICInitStructure; + bool directRising = (timHw->channel & TIM_Channel_2) ? !polarityRising : polarityRising; + // configure direct channel + TIM_ICStructInit(&TIM_ICInitStructure); + + TIM_ICInitStructure.TIM_Channel = timHw->channel; + TIM_ICInitStructure.TIM_ICPolarity = directRising ? TIM_ICPolarity_Rising : TIM_ICPolarity_Falling; + TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; + TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; + TIM_ICInitStructure.TIM_ICFilter = getFilter(inputFilterTicks); + TIM_ICInit(timHw->tim, &TIM_ICInitStructure); + // configure indirect channel + TIM_ICInitStructure.TIM_Channel = timHw->channel ^ TIM_Channel_2; // get opposite channel no + TIM_ICInitStructure.TIM_ICPolarity = directRising ? TIM_ICPolarity_Falling : TIM_ICPolarity_Rising; + TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI; + TIM_ICInit(timHw->tim, &TIM_ICInitStructure); +} + + + +void timerChICPolarity(const timerHardware_t* timHw, bool polarityRising) +{ + timCCER_t tmpccer = timHw->tim->CCER; + tmpccer &= ~(TIM_CCER_CC1P << timHw->channel); + tmpccer |= polarityRising ? (TIM_ICPolarity_Rising << timHw->channel) : (TIM_ICPolarity_Falling << timHw->channel); + timHw->tim->CCER = tmpccer; +} + +volatile timCCR_t* timerChCCRHi(const timerHardware_t* timHw) +{ + return (volatile timCCR_t*)((volatile char*)&timHw->tim->CCR1 + (timHw->channel | TIM_Channel_2)); +} + +volatile timCCR_t* timerChCCRLo(const timerHardware_t* timHw) +{ + return (volatile timCCR_t*)((volatile char*)&timHw->tim->CCR1 + (timHw->channel & ~TIM_Channel_2)); +} + + + +volatile timCCR_t* timerChCCR(const timerHardware_t* timHw) +{ + return (volatile timCCR_t*)((volatile char*)&timHw->tim->CCR1 + timHw->channel); +} + +void timerChConfigOC(const timerHardware_t* timHw, bool outEnable, bool stateHigh) +{ + TIM_OCInitTypeDef TIM_OCInitStructure; + + TIM_OCStructInit(&TIM_OCInitStructure); + if(outEnable) { + TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive; + TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; + TIM_OCInitStructure.TIM_OCPolarity = stateHigh ? TIM_OCPolarity_High : TIM_OCPolarity_Low; + } else { + TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing; + } + + switch (timHw->channel) { + case TIM_Channel_1: + TIM_OC1Init(timHw->tim, &TIM_OCInitStructure); + TIM_OC1PreloadConfig(timHw->tim, TIM_OCPreload_Disable); + break; + case TIM_Channel_2: + TIM_OC2Init(timHw->tim, &TIM_OCInitStructure); + TIM_OC2PreloadConfig(timHw->tim, TIM_OCPreload_Disable); + break; + case TIM_Channel_3: + TIM_OC3Init(timHw->tim, &TIM_OCInitStructure); + TIM_OC3PreloadConfig(timHw->tim, TIM_OCPreload_Disable); + break; + case TIM_Channel_4: + TIM_OC4Init(timHw->tim, &TIM_OCInitStructure); + TIM_OC4PreloadConfig(timHw->tim, TIM_OCPreload_Disable); + break; } } -void TIM1_CC_IRQHandler(void) + + +static void timCCxHandler(TIM_TypeDef *tim, timerConfig_t* timerConfig) { - timCCxHandler(TIM1); + uint16_t capture; + unsigned tim_status; + tim_status = tim->SR & tim->DIER; +#if 1 + while(tim_status) { + // flags will be cleared by reading CCR in dual capture, make sure we call handler correctly + // currrent order is highest bit first. Code should not rely on specific order (it will introduce race conditions anyway) + unsigned bit = __builtin_clz(tim_status); + unsigned mask = ~(0x80000000 >> bit); + tim->SR = mask; + tim_status &= mask; + switch(bit) { + case __builtin_clz(TIM_IT_Update): + capture = tim->ARR; + timerOvrHandlerRec_t *cb = timerConfig->overflowCallbackActive; + while(cb) { + cb->fn(cb, capture); + cb = cb->next; + } + break; + case __builtin_clz(TIM_IT_CC1): + timerConfig->edgeCallback[0]->fn(timerConfig->edgeCallback[0], tim->CCR1); + break; + case __builtin_clz(TIM_IT_CC2): + timerConfig->edgeCallback[1]->fn(timerConfig->edgeCallback[1], tim->CCR2); + break; + case __builtin_clz(TIM_IT_CC3): + timerConfig->edgeCallback[2]->fn(timerConfig->edgeCallback[2], tim->CCR3); + break; + case __builtin_clz(TIM_IT_CC4): + timerConfig->edgeCallback[3]->fn(timerConfig->edgeCallback[3], tim->CCR4); + break; + } + } +#else + if (tim_status & (int)TIM_IT_Update) { + tim->SR = ~TIM_IT_Update; + capture = tim->ARR; + timerOvrHandlerRec_t *cb = timerConfig->overflowCallbackActive; + while(cb) { + cb->fn(cb, capture); + cb = cb->next; + } + } + if (tim_status & (int)TIM_IT_CC1) { + tim->SR = ~TIM_IT_CC1; + timerConfig->edgeCallback[0]->fn(timerConfig->edgeCallback[0], tim->CCR1); + } + if (tim_status & (int)TIM_IT_CC2) { + tim->SR = ~TIM_IT_CC2; + timerConfig->edgeCallback[2]->fn(timerConfig->edgeCallback[1], tim->CCR2); + } + if (tim_status & (int)TIM_IT_CC3) { + tim->SR = ~TIM_IT_CC3; + timerConfig->edgeCallback[2]->fn(timerConfig->edgeCallback[2], tim->CCR3); + } + if (tim_status & (int)TIM_IT_CC4) { + tim->SR = ~TIM_IT_CC4; + timerConfig->edgeCallback[3]->fn(timerConfig->edgeCallback[3], tim->CCR4); + } +#endif } -void TIM2_IRQHandler(void) -{ - timCCxHandler(TIM2); -} +// handler for shared interrupts when both timers need to check status bits +#define _TIM_IRQ_HANDLER2(name, i, j) \ + void name(void) \ + { \ + timCCxHandler(TIM ## i, &timerConfig[TIMER_INDEX(i)]); \ + timCCxHandler(TIM ## j, &timerConfig[TIMER_INDEX(j)]); \ + } struct dummy -void TIM3_IRQHandler(void) -{ - timCCxHandler(TIM3); -} +#define _TIM_IRQ_HANDLER(name, i) \ + void name(void) \ + { \ + timCCxHandler(TIM ## i, &timerConfig[TIMER_INDEX(i)]); \ + } struct dummy -void TIM4_IRQHandler(void) -{ - timCCxHandler(TIM4); -} - -#if defined(STM32F303) || defined(STM32F3DISCOVERY) -void TIM8_CC_IRQHandler(void) -{ - timCCxHandler(TIM8); -} - -void TIM1_BRK_TIM15_IRQHandler(void) -{ - timCCxHandler(TIM15); -} - -void TIM1_UP_TIM16_IRQHandler(void) -{ - timCCxHandler(TIM16); -} - -void TIM1_TRG_COM_TIM17_IRQHandler(void) -{ - timCCxHandler(TIM17); -} +#if USED_TIMERS & TIM_N(1) +_TIM_IRQ_HANDLER(TIM1_CC_IRQHandler, 1); +# if defined(STM32F10X) +_TIM_IRQ_HANDLER(TIM1_UP_IRQHandler, 1); // timer can't be shared +# endif +# ifdef STM32F303xC +# if USED_TIMERS & TIM_N(16) +_TIM_IRQ_HANDLER2(TIM1_UP_TIM16_IRQHandler, 1, 16); // both timers are in use +# else +_TIM_IRQ_HANDLER(TIM1_UP_TIM16_IRQHandler, 1); // timer16 is not used +# endif +# endif +#endif +#if USED_TIMERS & TIM_N(2) +_TIM_IRQ_HANDLER(TIM2_IRQHandler, 2); +#endif +#if USED_TIMERS & TIM_N(3) +_TIM_IRQ_HANDLER(TIM3_IRQHandler, 3); +#endif +#if USED_TIMERS & TIM_N(4) +_TIM_IRQ_HANDLER(TIM4_IRQHandler, 4); +#endif +#if USED_TIMERS & TIM_N(8) +_TIM_IRQ_HANDLER(TIM8_CC_IRQHandler, 8); +# if defined(STM32F10X_XL) +_TIM_IRQ_HANDLER(TIM8_UP_TIM13_IRQHandler, 8); +# else // f10x_hd, f30x +_TIM_IRQ_HANDLER(TIM8_UP_IRQHandler, 8); +# endif +#endif +#if USED_TIMERS & TIM_N(15) +_TIM_IRQ_HANDLER(TIM1_BRK_TIM15_IRQHandler, 15); +#endif +#if defined(STM32F303xC) && ((USED_TIMERS & (TIM_N(1)|TIM_N(16))) == (TIM_N(16))) +_TIM_IRQ_HANDLER(TIM1_UP_TIM16_IRQHandler, 16); // only timer16 is used, not timer1 +#endif +#if USED_TIMERS & TIM_N(17) +_TIM_IRQ_HANDLER(TIM1_TRG_COM_TIM17_IRQHandler, 17); #endif void timerInit(void) @@ -482,4 +753,41 @@ void timerInit(void) #endif #endif +// initialize timer channel structures + for(int i = 0; i < USABLE_TIMER_CHANNEL_COUNT; i++) { + timerChannelInfo[i].type = TYPE_FREE; + } + for(int i = 0; i < USED_TIMER_COUNT; i++) { + timerInfo[i].priority = ~0; + } +} + +// finish configuring timers after allocation phase +// start timers +// TODO - Work in progress - initialization routine must be modified/verified to start correctly without timers +void timerStart(void) +{ +#if 0 + for(unsigned timer = 0; timer < USED_TIMER_COUNT; timer++) { + int priority = -1; + int irq = -1; + for(unsigned hwc = 0; hwc < USABLE_TIMER_CHANNEL_COUNT; hwc++) + if((timerChannelInfo[hwc].type != TYPE_FREE) && (timerHardware[hwc].tim == usedTimers[timer])) { + // TODO - move IRQ to timer info + irq = timerHardware[hwc].irq; + } + // TODO - aggregate required timer paramaters + configTimeBase(usedTimers[timer], 0, 1); + TIM_Cmd(usedTimers[timer], ENABLE); + if(priority >= 0) { // maybe none of the channels was configured + NVIC_InitTypeDef NVIC_InitStructure; + + NVIC_InitStructure.NVIC_IRQChannel = irq; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_SPLIT_PRIORITY_BASE(priority); + NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_SPLIT_PRIORITY_SUB(priority); + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + } + } +#endif } diff --git a/src/main/drivers/timer.h b/src/main/drivers/timer.h index 2c7693b66..08fbab597 100644 --- a/src/main/drivers/timer.h +++ b/src/main/drivers/timer.h @@ -29,14 +29,36 @@ #define USABLE_TIMER_CHANNEL_COUNT 14 #endif -#ifdef STM32F303xC -typedef uint32_t captureCompare_t; -#endif -#ifdef STM32F10X -typedef uint16_t captureCompare_t; +typedef uint16_t captureCompare_t; // 16 bit on both 103 and 303, just register access must be 32bit sometimes (use timCCR_t) + +#if defined(STM32F303) +typedef uint32_t timCCR_t; +typedef uint32_t timCCER_t; +typedef uint32_t timSR_t; +typedef uint32_t timCNT_t; +#elif defined(STM32F10X) +typedef uint16_t timCCR_t; +typedef uint16_t timCCER_t; +typedef uint16_t timSR_t; +typedef uint16_t timCNT_t; +#else +# error "Unknown CPU defined" #endif -typedef void timerCCCallbackPtr(uint8_t port, captureCompare_t capture); +// use different types from capture and overflow - multiple overflow handlers are implemented as linked list +struct timerCCHandlerRec_s; +struct timerOvrHandlerRec_s; +typedef void timerCCHandlerCallback(struct timerCCHandlerRec_s* self, uint16_t capture); +typedef void timerOvrHandlerCallback(struct timerOvrHandlerRec_s* self, uint16_t capture); + +typedef struct timerCCHandlerRec_s { + timerCCHandlerCallback* fn; +} timerCCHandlerRec_t; + +typedef struct timerOvrHandlerRec_s { + timerOvrHandlerCallback* fn; + struct timerOvrHandlerRec_s* next; +} timerOvrHandlerRec_t; typedef struct { TIM_TypeDef *tim; @@ -47,18 +69,53 @@ typedef struct { uint8_t outputEnable; GPIO_Mode gpioInputMode; #ifdef STM32F303 - uint8_t gpioPinSource; + uint8_t gpioPinSource; // TODO - this can be removed and pinSource calculated from pin uint8_t alternateFunction; #endif } timerHardware_t; extern const timerHardware_t timerHardware[]; -void configTimeBase(TIM_TypeDef *tim, uint16_t period, uint8_t mhz); -void timerConfigure(const timerHardware_t *timerHardwarePtr, uint16_t period, uint8_t mhz); -void timerNVICConfigure(uint8_t irq); +typedef enum { + TYPE_FREE, + TYPE_PWMINPUT, + TYPE_PPMINPUT, + TYPE_PWMOUTPUT_MOTOR, + TYPE_PWMOUTPUT_FAST, + TYPE_PWMOUTPUT_SERVO, + TYPE_SOFTSERIAL_RX, + TYPE_SOFTSERIAL_TX, + TYPE_SOFTSERIAL_RXTX, // bidirectional pin for softserial + TYPE_SOFTSERIAL_AUXTIMER, // timer channel is used for softserial. No IO function on pin + TYPE_ADC, + TYPE_SERIAL_RX, + TYPE_SERIAL_TX, + TYPE_SERIAL_RXTX, + TYPE_TIMER +} channelType_t; -void configureTimerInputCaptureCompareChannel(TIM_TypeDef *tim, const uint8_t channel); -void configureTimerCaptureCompareInterrupt(const timerHardware_t *timerHardwarePtr, uint8_t reference, timerCCCallbackPtr *edgeCallback, timerCCCallbackPtr *overflowCallback); -void configureTimerChannelCallback(TIM_TypeDef *tim, uint8_t channel, uint8_t reference, timerCCCallbackPtr *edgeCallback); -void configureTimerChannelCallbacks(TIM_TypeDef *tim, uint8_t channel, uint8_t reference, timerCCCallbackPtr *edgeCallback, timerCCCallbackPtr *overflowCallback); +void timerConfigure(const timerHardware_t *timHw, uint16_t period, uint8_t mhz); // This interface should be replaced. + +void timerChConfigIC(const timerHardware_t *timHw, bool polarityRising, unsigned inputFilterSamples); +void timerChConfigICDual(const timerHardware_t* timHw, bool polarityRising, unsigned inputFilterSamples); +void timerChICPolarity(const timerHardware_t *timHw, bool polarityRising); +volatile timCCR_t* timerChCCR(const timerHardware_t* timHw); +volatile timCCR_t* timerChCCRLo(const timerHardware_t* timHw); +volatile timCCR_t* timerChCCRHi(const timerHardware_t* timHw); +void timerChConfigOC(const timerHardware_t* timHw, bool outEnable, bool stateHigh); +void timerChConfigGPIO(const timerHardware_t* timHw, GPIO_Mode mode); + +void timerChCCHandlerInit(timerCCHandlerRec_t *self, timerCCHandlerCallback *fn); +void timerChOvrHandlerInit(timerOvrHandlerRec_t *self, timerOvrHandlerCallback *fn); +void timerChConfigCallbacks(const timerHardware_t *channel, timerCCHandlerRec_t *edgeCallback, timerOvrHandlerRec_t *overflowCallback); +void timerChConfigCallbacksDual(const timerHardware_t *channel, timerCCHandlerRec_t *edgeCallbackLo, timerCCHandlerRec_t *edgeCallbackHi, timerOvrHandlerRec_t *overflowCallback); +void timerChITConfigDualLo(const timerHardware_t* timHw, FunctionalState newState); +void timerChITConfig(const timerHardware_t* timHw, FunctionalState newState); +void timerChClearCCFlag(const timerHardware_t* timHw); + +void timerChInit(const timerHardware_t *timHw, channelType_t type, int irqPriority); + +void timerInit(void); +void timerStart(void); + +void configTimeBase(TIM_TypeDef *tim, uint16_t period, uint8_t mhz); // TODO - just for migration diff --git a/src/main/drivers/timer_impl.h b/src/main/drivers/timer_impl.h new file mode 100644 index 000000000..5a8868b37 --- /dev/null +++ b/src/main/drivers/timer_impl.h @@ -0,0 +1,20 @@ +/* + * This file is part of Cleanflight. + * + * Cleanflight is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Cleanflight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Cleanflight. If not, see . + */ + +#pragma once + +void timerInitTarget(void); diff --git a/src/main/drivers/timer_stm32f10x.c b/src/main/drivers/timer_stm32f10x.c new file mode 100644 index 000000000..f8016950f --- /dev/null +++ b/src/main/drivers/timer_stm32f10x.c @@ -0,0 +1,67 @@ +/* + modified version of StdPeriph function is located here. + TODO - what license does apply here? + original file was lincesed under MCD-ST Liberty SW License Agreement V2 + http://www.st.com/software_license_agreement_liberty_v2 +*/ + +#include "stm32f10x.h" + +/** + * @brief Selects the TIM Output Compare Mode. + * @note This function does NOT disable the selected channel before changing the Output + * Compare Mode. + * @param TIMx: where x can be 1 to 17 except 6 and 7 to select the TIM peripheral. + * @param TIM_Channel: specifies the TIM Channel + * This parameter can be one of the following values: + * @arg TIM_Channel_1: TIM Channel 1 + * @arg TIM_Channel_2: TIM Channel 2 + * @arg TIM_Channel_3: TIM Channel 3 + * @arg TIM_Channel_4: TIM Channel 4 + * @param TIM_OCMode: specifies the TIM Output Compare Mode. + * This parameter can be one of the following values: + * @arg TIM_OCMode_Timing + * @arg TIM_OCMode_Active + * @arg TIM_OCMode_Toggle + * @arg TIM_OCMode_PWM1 + * @arg TIM_OCMode_PWM2 + * @arg TIM_ForcedAction_Active + * @arg TIM_ForcedAction_InActive + * @retval None + */ + +#define CCMR_Offset ((uint16_t)0x0018) + +void TIM_SelectOCxM_NoDisable(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode) +{ + uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST8_PERIPH(TIMx)); + assert_param(IS_TIM_CHANNEL(TIM_Channel)); + assert_param(IS_TIM_OCM(TIM_OCMode)); + + tmp = (uint32_t) TIMx; + tmp += CCMR_Offset; + + if((TIM_Channel == TIM_Channel_1) ||(TIM_Channel == TIM_Channel_3)) + { + tmp += (TIM_Channel>>1); + + /* Reset the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp &= (uint32_t)~((uint32_t)TIM_CCMR1_OC1M); + + /* Configure the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp |= TIM_OCMode; + } + else + { + tmp += (uint16_t)(TIM_Channel - (uint16_t)4)>> (uint16_t)1; + + /* Reset the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp &= (uint32_t)~((uint32_t)TIM_CCMR1_OC2M); + + /* Configure the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp |= (uint16_t)(TIM_OCMode << 8); + } +} diff --git a/src/main/drivers/timer_stm32f10x.h b/src/main/drivers/timer_stm32f10x.h new file mode 100644 index 000000000..be46e4f37 --- /dev/null +++ b/src/main/drivers/timer_stm32f10x.h @@ -0,0 +1,6 @@ + +#pragma once + +#include "stm32f10x.h" + +void TIM_SelectOCxM_NoDisable(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode); diff --git a/src/main/drivers/timer_stm32f30x.c b/src/main/drivers/timer_stm32f30x.c new file mode 100644 index 000000000..458ef824f --- /dev/null +++ b/src/main/drivers/timer_stm32f30x.c @@ -0,0 +1,75 @@ +/* + modified version of StdPeriph function is located here. + TODO - what license does apply here? + original file was lincesed under MCD-ST Liberty SW License Agreement V2 + http://www.st.com/software_license_agreement_liberty_v2 +*/ + +#include "stm32f30x.h" + +/** + * @brief Selects the TIM Output Compare Mode. + * @note This function does NOT disable the selected channel before changing the Output + * Compare Mode. If needed, user has to enable this channel using + * TIM_CCxCmd() and TIM_CCxNCmd() functions. + * @param TIMx: where x can be 1, 2, 3, 4, 8, 15, 16 or 17 to select the TIM peripheral. + * @param TIM_Channel: specifies the TIM Channel + * This parameter can be one of the following values: + * @arg TIM_Channel_1: TIM Channel 1 + * @arg TIM_Channel_2: TIM Channel 2 + * @arg TIM_Channel_3: TIM Channel 3 + * @arg TIM_Channel_4: TIM Channel 4 + * @param TIM_OCMode: specifies the TIM Output Compare Mode. + * This parameter can be one of the following values: + * @arg TIM_OCMode_Timing + * @arg TIM_OCMode_Active + * @arg TIM_OCMode_Toggle + * @arg TIM_OCMode_PWM1 + * @arg TIM_OCMode_PWM2 + * @arg TIM_ForcedAction_Active + * @arg TIM_ForcedAction_InActive + * @arg TIM_OCMode_Retrigerrable_OPM1 + * @arg TIM_OCMode_Retrigerrable_OPM2 + * @arg TIM_OCMode_Combined_PWM1 + * @arg TIM_OCMode_Combined_PWM2 + * @arg TIM_OCMode_Asymmetric_PWM1 + * @arg TIM_OCMode_Asymmetric_PWM2 + * @retval None + */ +#define CCMR_OFFSET ((uint16_t)0x0018) +#define CCMR_OC13M_MASK ((uint32_t)0xFFFEFF8F) +#define CCMR_OC24M_MASK ((uint32_t)0xFEFF8FFF) + +void TIM_SelectOCxM_NoDisable(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint32_t TIM_OCMode) +{ + uint32_t tmp = 0; + + /* Check the parameters */ + assert_param(IS_TIM_LIST1_PERIPH(TIMx)); + assert_param(IS_TIM_CHANNEL(TIM_Channel)); + assert_param(IS_TIM_OCM(TIM_OCMode)); + + tmp = (uint32_t) TIMx; + tmp += CCMR_OFFSET; + + if((TIM_Channel == TIM_Channel_1) ||(TIM_Channel == TIM_Channel_3)) + { + tmp += (TIM_Channel>>1); + + /* Reset the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp &= CCMR_OC13M_MASK; + + /* Configure the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp |= TIM_OCMode; + } + else + { + tmp += (uint32_t)(TIM_Channel - (uint32_t)4)>> (uint32_t)1; + + /* Reset the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp &= CCMR_OC24M_MASK; + + /* Configure the OCxM bits in the CCMRx register */ + *(__IO uint32_t *) tmp |= (uint32_t)(TIM_OCMode << 8); + } +} diff --git a/src/main/drivers/timer_stm32f30x.h b/src/main/drivers/timer_stm32f30x.h new file mode 100644 index 000000000..781385875 --- /dev/null +++ b/src/main/drivers/timer_stm32f30x.h @@ -0,0 +1,6 @@ + +#pragma once + +#include "stm32f30x.h" + +void TIM_SelectOCxM_NoDisable(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode); diff --git a/src/main/main.c b/src/main/main.c index 6608e010e..a7d0d3153 100755 --- a/src/main/main.c +++ b/src/main/main.c @@ -24,6 +24,8 @@ #include "common/axis.h" #include "common/color.h" +#include "common/atomic.h" +#include "drivers/nvic.h" #include "drivers/system.h" #include "drivers/gpio.h" @@ -153,6 +155,8 @@ void init(void) delay(100); + timerInit(); // timer must be initialized before any channel is allocated + ledInit(); #ifdef BEEPER @@ -250,8 +254,6 @@ void init(void) compassInit(); #endif - timerInit(); - serialInit(&masterConfig.serialConfig); memset(&pwm_params, 0, sizeof(pwm_params)); @@ -333,6 +335,10 @@ void init(void) baroSetCalibrationCycles(CALIBRATING_BARO_CYCLES); #endif + // start all timers + // TODO - not implemented yet + timerStart(); + ENABLE_STATE(SMALL_ANGLE); DISABLE_ARMING_FLAG(PREVENT_ARMING); diff --git a/src/main/startup/startup_stm32f30x_md_gcc.S b/src/main/startup/startup_stm32f30x_md_gcc.S index be696fd0b..9c2af124c 100644 --- a/src/main/startup/startup_stm32f30x_md_gcc.S +++ b/src/main/startup/startup_stm32f30x_md_gcc.S @@ -4,7 +4,7 @@ * @author MCD Application Team * @version V1.0.0 * @date 04-Spetember-2012 - * @brief STM32F30x Devices vector table for RIDE7 toolchain. + * @brief STM32F30x Devices vector table for RIDE7 toolchain. * This module performs: * - Set the initial SP * - Set the initial PC == Reset_Handler,