448 lines
15 KiB
C
448 lines
15 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#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;
|
|
}
|