/* * 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 . */ #include #include #include #include #include "platform.h" #include "gpio.h" #include "timer.h" #include "pwm_output.h" #include "pwm_rx.h" #include "pwm_mapping.h" /* Configuration maps Note: this documentation is only valid for STM32F10x, for STM32F30x please read the code itself. 1) multirotor PPM input PWM1 used for PPM PWM5..8 used for motors PWM9..10 used for servo or else motors PWM11..14 used for motors 2) multirotor PPM input with more servos PWM1 used for PPM PWM5..8 used for motors PWM9..10 used for servo or else motors PWM11..14 used for servos 2) multirotor PWM input PWM1..8 used for input PWM9..10 used for servo or else motors PWM11..14 used for motors 3) airplane / flying wing w/PWM PWM1..8 used for input PWM9 used for motor throttle +PWM10 for 2nd motor PWM11.14 used for servos 4) airplane / flying wing with PPM PWM1 used for PPM PWM5..8 used for servos PWM9 used for motor throttle +PWM10 for 2nd motor PWM11.14 used for servos */ enum { MAP_TO_PPM_INPUT = 1, MAP_TO_PWM_INPUT, MAP_TO_MOTOR_OUTPUT, MAP_TO_SERVO_OUTPUT, }; #if defined(NAZE) || defined(OLIMEXINO) || defined(NAZE32PRO) || defined(STM32F3DISCOVERY) || defined(EUSTM32F103RC) || defined(MASSIVEF3) || defined(PORT103R) static const uint16_t multiPPM[] = { PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM11 | (MAP_TO_MOTOR_OUTPUT << 8), PWM12 | (MAP_TO_MOTOR_OUTPUT << 8), PWM13 | (MAP_TO_MOTOR_OUTPUT << 8), PWM14 | (MAP_TO_MOTOR_OUTPUT << 8), PWM5 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM6 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed 0xFFFF }; static const uint16_t multiPWM[] = { PWM1 | (MAP_TO_PWM_INPUT << 8), // input #1 PWM2 | (MAP_TO_PWM_INPUT << 8), PWM3 | (MAP_TO_PWM_INPUT << 8), PWM4 | (MAP_TO_PWM_INPUT << 8), PWM5 | (MAP_TO_PWM_INPUT << 8), PWM6 | (MAP_TO_PWM_INPUT << 8), PWM7 | (MAP_TO_PWM_INPUT << 8), PWM8 | (MAP_TO_PWM_INPUT << 8), // input #8 PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 or servo #1 (swap to servo if needed) PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2 or servo #2 (swap to servo if needed) PWM11 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 or #3 PWM12 | (MAP_TO_MOTOR_OUTPUT << 8), PWM13 | (MAP_TO_MOTOR_OUTPUT << 8), PWM14 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #4 or #6 0xFFFF }; static const uint16_t airPPM[] = { PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2 PWM11 | (MAP_TO_SERVO_OUTPUT << 8), // servo #1 PWM12 | (MAP_TO_SERVO_OUTPUT << 8), PWM13 | (MAP_TO_SERVO_OUTPUT << 8), PWM14 | (MAP_TO_SERVO_OUTPUT << 8), // servo #4 PWM5 | (MAP_TO_SERVO_OUTPUT << 8), // servo #5 PWM6 | (MAP_TO_SERVO_OUTPUT << 8), PWM7 | (MAP_TO_SERVO_OUTPUT << 8), PWM8 | (MAP_TO_SERVO_OUTPUT << 8), // servo #8 0xFFFF }; static const uint16_t airPWM[] = { PWM1 | (MAP_TO_PWM_INPUT << 8), // input #1 PWM2 | (MAP_TO_PWM_INPUT << 8), PWM3 | (MAP_TO_PWM_INPUT << 8), PWM4 | (MAP_TO_PWM_INPUT << 8), PWM5 | (MAP_TO_PWM_INPUT << 8), PWM6 | (MAP_TO_PWM_INPUT << 8), PWM7 | (MAP_TO_PWM_INPUT << 8), PWM8 | (MAP_TO_PWM_INPUT << 8), // input #8 PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2 PWM11 | (MAP_TO_SERVO_OUTPUT << 8), // servo #1 PWM12 | (MAP_TO_SERVO_OUTPUT << 8), PWM13 | (MAP_TO_SERVO_OUTPUT << 8), PWM14 | (MAP_TO_SERVO_OUTPUT << 8), // servo #4 0xFFFF }; #endif #ifdef CC3D static const uint16_t multiPPM[] = { PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), PWM11 | (MAP_TO_MOTOR_OUTPUT << 8), PWM12 | (MAP_TO_MOTOR_OUTPUT << 8), PWM2 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM3 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM4 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM5 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed PWM6 | (MAP_TO_MOTOR_OUTPUT << 8), // Swap to servo if needed 0xFFFF }; static const uint16_t multiPWM[] = { PWM1 | (MAP_TO_PWM_INPUT << 8), // input #1 PWM2 | (MAP_TO_PWM_INPUT << 8), PWM3 | (MAP_TO_PWM_INPUT << 8), PWM4 | (MAP_TO_PWM_INPUT << 8), PWM5 | (MAP_TO_PWM_INPUT << 8), PWM6 | (MAP_TO_PWM_INPUT << 8), // input #6 PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 or servo #1 (swap to servo if needed) PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2 or servo #2 (swap to servo if needed) PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 or #3 PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), PWM11 | (MAP_TO_MOTOR_OUTPUT << 8), PWM12 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #4 or #6 0xFFFF }; static const uint16_t airPPM[] = { PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), PWM9 | (MAP_TO_SERVO_OUTPUT << 8), PWM10 | (MAP_TO_SERVO_OUTPUT << 8), PWM11 | (MAP_TO_SERVO_OUTPUT << 8), PWM12 | (MAP_TO_SERVO_OUTPUT << 8), PWM2 | (MAP_TO_SERVO_OUTPUT << 8), PWM3 | (MAP_TO_SERVO_OUTPUT << 8), PWM4 | (MAP_TO_SERVO_OUTPUT << 8), PWM5 | (MAP_TO_SERVO_OUTPUT << 8), PWM6 | (MAP_TO_SERVO_OUTPUT << 8), 0xFFFF }; static const uint16_t airPWM[] = { PWM1 | (MAP_TO_PWM_INPUT << 8), // input #1 PWM2 | (MAP_TO_PWM_INPUT << 8), PWM3 | (MAP_TO_PWM_INPUT << 8), PWM4 | (MAP_TO_PWM_INPUT << 8), PWM5 | (MAP_TO_PWM_INPUT << 8), PWM6 | (MAP_TO_PWM_INPUT << 8), // input #6 PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #1 PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), // motor #2 PWM9 | (MAP_TO_SERVO_OUTPUT << 8), // servo #1 PWM10 | (MAP_TO_SERVO_OUTPUT << 8), // servo #2 PWM11 | (MAP_TO_SERVO_OUTPUT << 8), // servo #3 PWM12 | (MAP_TO_SERVO_OUTPUT << 8), // servo #4 0xFFFF }; #endif #ifdef CJMCU static const uint16_t multiPPM[] = { PWM1 | (MAP_TO_PPM_INPUT << 8), // PPM input PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), PWM14 | (MAP_TO_MOTOR_OUTPUT << 8), PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), PWM13 | (MAP_TO_MOTOR_OUTPUT << 8), 0xFFFF }; static const uint16_t multiPWM[] = { PWM1 | (MAP_TO_PWM_INPUT << 8), PWM2 | (MAP_TO_PWM_INPUT << 8), PWM3 | (MAP_TO_PWM_INPUT << 8), PWM4 | (MAP_TO_PWM_INPUT << 8), PWM9 | (MAP_TO_PWM_INPUT << 8), PWM10 | (MAP_TO_PWM_INPUT << 8), PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), PWM14 | (MAP_TO_MOTOR_OUTPUT << 8), PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), PWM13 | (MAP_TO_MOTOR_OUTPUT << 8), 0xFFFF }; static const uint16_t airPPM[] = { 0xFFFF }; static const uint16_t airPWM[] = { 0xFFFF }; #endif #ifdef SPARKY static const uint16_t multiPPM[] = { PWM11 | (MAP_TO_PPM_INPUT << 8), // PPM input PWM1 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM15 PWM2 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM15 PWM3 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM1 PWM4 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM3 PWM5 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM3 PWM6 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM2 PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM3 PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM17 PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM3 PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), // TIM2 0xFFFF }; static const uint16_t multiPWM[] = { PWM1 | (MAP_TO_MOTOR_OUTPUT << 8), PWM2 | (MAP_TO_MOTOR_OUTPUT << 8), PWM3 | (MAP_TO_MOTOR_OUTPUT << 8), PWM4 | (MAP_TO_MOTOR_OUTPUT << 8), PWM5 | (MAP_TO_MOTOR_OUTPUT << 8), PWM6 | (MAP_TO_MOTOR_OUTPUT << 8), PWM7 | (MAP_TO_MOTOR_OUTPUT << 8), PWM8 | (MAP_TO_MOTOR_OUTPUT << 8), PWM9 | (MAP_TO_MOTOR_OUTPUT << 8), PWM10 | (MAP_TO_MOTOR_OUTPUT << 8), 0xFFFF }; static const uint16_t airPPM[] = { 0xFFFF }; static const uint16_t airPWM[] = { 0xFFFF }; #endif static const uint16_t * const hardwareMaps[] = { multiPWM, multiPPM, airPWM, airPPM, }; pwmOutputConfiguration_t *pwmInit(drv_pwm_config_t *init) { int i = 0; const uint16_t *setup; int channelIndex = 0; static pwmOutputConfiguration_t pwmOutputConfiguration; memset(&pwmOutputConfiguration, 0, sizeof(pwmOutputConfiguration)); // this is pretty hacky shit, but it will do for now. array of 4 config maps, [ multiPWM multiPPM airPWM airPPM ] if (init->airplane) i = 2; // switch to air hardware config if (init->usePPM || init->useSerialRx) i++; // next index is for PPM setup = hardwareMaps[i]; for (i = 0; i < USABLE_TIMER_CHANNEL_COUNT; i++) { uint8_t timerIndex = setup[i] & 0x00FF; uint8_t type = (setup[i] & 0xFF00) >> 8; if (setup[i] == 0xFFFF) // terminator break; const timerHardware_t *timerHardwarePtr = &timerHardware[timerIndex]; #ifdef OLIMEXINO_UNCUT_LED2_E_JUMPER // PWM2 is connected to LED2 on the board and cannot be connected unless you cut LED2_E if (timerIndex == PWM2) continue; #endif #ifdef STM32F10X // skip UART2 ports if (init->useUART2 && (timerIndex == PWM3 || timerIndex == PWM4)) continue; #endif #ifdef SOFTSERIAL_1_TIMER if (init->useSoftSerial && timerHardwarePtr->tim == SOFTSERIAL_1_TIMER) continue; #endif #ifdef SOFTSERIAL_2_TIMER if (init->useSoftSerial && timerHardwarePtr->tim == SOFTSERIAL_2_TIMER) continue; #endif #ifdef LED_STRIP_TIMER // skip LED Strip output if (init->useLEDStrip) { if (timerHardwarePtr->tim == LED_STRIP_TIMER) continue; #if defined(STM32F303xC) && defined(WS2811_GPIO) && defined(WS2811_PIN_SOURCE) if (timerHardwarePtr->gpio == WS2811_GPIO && timerHardwarePtr->gpioPinSource == WS2811_PIN_SOURCE) continue; #endif } #endif #ifdef STM32F10X // skip ADC for RSSI if (init->useRSSIADC && timerIndex == PWM2) continue; #endif #ifdef CC3D if (init->useVbat && timerIndex == Vbat_TIMER) { continue; } #endif #ifdef CC3D if (init->useCurrentMeterADC && timerIndex == CurrentMeter_TIMER) { continue; } #endif #ifdef CC3D if (init->useRSSIADC && timerIndex == RSSI_TIMER) { continue; } #endif // hacks to allow current functionality if (type == MAP_TO_PWM_INPUT && !init->useParallelPWM) continue; if (type == MAP_TO_PPM_INPUT && !init->usePPM) continue; if (init->useServos && !init->airplane) { #if defined(NAZE) // remap PWM9+10 as servos if ((timerIndex == PWM9 || timerIndex == PWM10) && timerHardwarePtr->tim == TIM1) type = MAP_TO_SERVO_OUTPUT; #endif #if defined(SPARKY) // remap PWM1+2 as servos if ((timerIndex == PWM1 || timerIndex == PWM2) && timerHardwarePtr->tim == TIM15) type = MAP_TO_SERVO_OUTPUT; #endif #if defined(NAZE32PRO) || (defined(STM32F3DISCOVERY) && !defined(CHEBUZZF3)) // remap PWM 5+6 or 9+10 as servos - softserial pin pairs require timer ports that use the same timer if (init->useSoftSerial) { if (timerIndex == PWM5 || timerIndex == PWM6) type = MAP_TO_SERVO_OUTPUT; } else { if (timerIndex == PWM9 || timerIndex == PWM10) type = MAP_TO_SERVO_OUTPUT; } #endif } if (init->extraServos && !init->airplane) { // remap PWM5..8 as servos when used in extended servo mode if (timerIndex >= PWM5 && timerIndex <= PWM8) type = MAP_TO_SERVO_OUTPUT; } #ifdef CC3D if (init->useParallelPWM) { // Skip PWM inputs that conflict with timers used outputs. if ((type == MAP_TO_SERVO_OUTPUT || type == MAP_TO_MOTOR_OUTPUT) && (timerHardwarePtr->tim == TIM2 || timerHardwarePtr->tim == TIM3)) { continue; } if (type == MAP_TO_PWM_INPUT && timerHardwarePtr->tim == TIM4) { continue; } } #endif if (type == MAP_TO_PPM_INPUT) { #ifdef CC3D if (init->useOneshot) { ppmAvoidPWMTimerClash(timerHardwarePtr, TIM4); } #endif ppmInConfig(timerHardwarePtr); } else if (type == MAP_TO_PWM_INPUT) { pwmInConfig(timerHardwarePtr, channelIndex); channelIndex++; } else if (type == MAP_TO_MOTOR_OUTPUT) { if (init->useOneshot) { pwmOneshotMotorConfig(timerHardwarePtr, pwmOutputConfiguration.motorCount, init->idlePulse); } else if (init->motorPwmRate > 500) { pwmBrushedMotorConfig(timerHardwarePtr, pwmOutputConfiguration.motorCount, init->motorPwmRate, init->idlePulse); } else { pwmBrushlessMotorConfig(timerHardwarePtr, pwmOutputConfiguration.motorCount, init->motorPwmRate, init->idlePulse); } pwmOutputConfiguration.motorCount++; } else if (type == MAP_TO_SERVO_OUTPUT) { pwmServoConfig(timerHardwarePtr, pwmOutputConfiguration.servoCount, init->servoPwmRate, init->servoCenterPulse); pwmOutputConfiguration.servoCount++; } } return &pwmOutputConfiguration; }