1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-15 20:35:33 +03:00
betaflight/src/main/drivers/pwm_mapping.c

409 lines
15 KiB
C
Executable file

/*
* 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"
void pwmBrushedMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint16_t motorPwmRate, uint16_t idlePulse);
void pwmBrushlessMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint16_t motorPwmRate, uint16_t idlePulse);
void pwmFastPwmMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint8_t fastPwmProtocolType, uint16_t motorPwmRate, uint16_t idlePulse);
void pwmServoConfig(const timerHardware_t *timerHardware, uint8_t servoIndex, uint16_t servoPwmRate, uint16_t servoCenterPulse);
/*
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
*/
#if defined(NAZE) || defined(OLIMEXINO) || defined(NAZE32PRO) || defined(STM32F3DISCOVERY) || defined(EUSTM32F103RC) || defined(PORT103R)
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
};
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
};
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
};
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
const uint16_t * const hardwareMaps[] = {
multiPWM,
multiPPM,
airPWM,
airPPM,
};
#ifdef CC3D
const uint16_t * const hardwareMapsBP6[] = {
multiPWM_BP6,
multiPPM_BP6,
airPWM_BP6,
airPPM_BP6,
};
#endif
static pwmOutputConfiguration_t pwmOutputConfiguration;
pwmOutputConfiguration_t *pwmGetOutputConfiguration(void)
{
return &pwmOutputConfiguration;
}
pwmOutputConfiguration_t *pwmInit(drv_pwm_config_t *init)
{
int i = 0;
const uint16_t *setup;
int channelIndex = 0;
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
#ifdef CC3D
if (init->useBuzzerP6) {
setup = hardwareMapsBP6[i];
} else {
setup = hardwareMaps[i];
}
#else
setup = hardwareMaps[i];
#endif
for (i = 0; i < USABLE_TIMER_CHANNEL_COUNT && setup[i] != 0xFFFF; i++) {
uint8_t timerIndex = setup[i] & 0x00FF;
uint8_t type = (setup[i] & 0xFF00) >> 8;
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
#if defined(STM32F303xC) && defined(USE_USART3)
// skip UART3 ports (PB10/PB11)
if (init->useUART3 && timerHardwarePtr->gpio == UART3_GPIO && (timerHardwarePtr->pin == UART3_TX_PIN || timerHardwarePtr->pin == UART3_RX_PIN))
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 VBAT_ADC_GPIO
if (init->useVbat && timerHardwarePtr->gpio == VBAT_ADC_GPIO && timerHardwarePtr->pin == VBAT_ADC_GPIO_PIN) {
continue;
}
#endif
#ifdef RSSI_ADC_GPIO
if (init->useRSSIADC && timerHardwarePtr->gpio == RSSI_ADC_GPIO && timerHardwarePtr->pin == RSSI_ADC_GPIO_PIN) {
continue;
}
#endif
#ifdef CURRENT_METER_ADC_GPIO
if (init->useCurrentMeterADC && timerHardwarePtr->gpio == CURRENT_METER_ADC_GPIO && timerHardwarePtr->pin == CURRENT_METER_ADC_GPIO_PIN) {
continue;
}
#endif
#ifdef SONAR
if (init->sonarGPIOConfig && timerHardwarePtr->gpio == init->sonarGPIOConfig->gpio &&
(
timerHardwarePtr->pin == init->sonarGPIOConfig->triggerPin ||
timerHardwarePtr->pin == init->sonarGPIOConfig->echoPin
)
) {
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;
#ifdef USE_SERVOS
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(COLIBRI_RACE) || defined(LUX_RACE)
// remap PWM1+2 as servos
if ((timerIndex == PWM6 || timerIndex == PWM7 || timerIndex == PWM8 || timerIndex == PWM9) && timerHardwarePtr->tim == TIM2)
type = MAP_TO_SERVO_OUTPUT;
#endif
#if defined(CC3D)
// 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(SPRACINGF3)
// remap PWM15+16 as servos
if ((timerIndex == PWM15 || timerIndex == PWM16) && timerHardwarePtr->tim == TIM15)
type = MAP_TO_SERVO_OUTPUT;
#endif
#if defined(SPRACINGF3MINI)
// remap PWM6+7 as servos
if ((timerIndex == PWM6 || timerIndex == PWM7) && 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 defined(MOTOLAB)
// remap PWM 7+8 as servos
if (timerIndex == PWM7 || timerIndex == PWM8)
type = MAP_TO_SERVO_OUTPUT;
#endif
#if defined(SINGULARITY)
// remap PWM6+7 as servos
if (timerIndex == PWM6 || timerIndex == PWM7)
type = MAP_TO_SERVO_OUTPUT;
#endif
}
if (init->useChannelForwarding && !init->airplane) {
#if defined(NAZE) && defined(LED_STRIP_TIMER)
// if LED strip is active, PWM5-8 are unavailable, so map AUX1+AUX2 to PWM13+PWM14
if (init->useLEDStrip) {
if (timerIndex >= PWM13 && timerIndex <= PWM14) {
type = MAP_TO_SERVO_OUTPUT;
}
} else
#endif
#if defined(SPRACINGF3) || defined(NAZE)
// remap PWM5..8 as servos when used in extended servo mode
if (timerIndex >= PWM5 && timerIndex <= PWM8)
type = MAP_TO_SERVO_OUTPUT;
#endif
}
#endif // USE_SERVOS
#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) {
#if defined(SPARKY) || defined(ALIENFLIGHTF3)
if (init->useFastPwm || init->pwmProtocolType == PWM_TYPE_BRUSHED) {
ppmAvoidPWMTimerClash(timerHardwarePtr, TIM2);
}
#endif
ppmInConfig(timerHardwarePtr);
} else if (type == MAP_TO_PWM_INPUT) {
pwmInConfig(timerHardwarePtr, channelIndex);
channelIndex++;
} else if (type == MAP_TO_MOTOR_OUTPUT) {
#ifdef CC3D
if (init->useFastPwm || init->pwmProtocolType == PWM_TYPE_BRUSHED) {
// Skip it if it would cause PPM capture timer to be reconfigured or manually overflowed
if (timerHardwarePtr->tim == TIM2)
continue;
}
#endif
if (init->useFastPwm) {
pwmFastPwmMotorConfig(timerHardwarePtr, pwmOutputConfiguration.motorCount, init->pwmProtocolType, init->motorPwmRate, init->idlePulse);
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].flags = PWM_PF_MOTOR | PWM_PF_OUTPUT_PROTOCOL_ONESHOT|PWM_PF_OUTPUT_PROTOCOL_PWM ;
} else if (init->pwmProtocolType == PWM_TYPE_BRUSHED) {
pwmBrushedMotorConfig(timerHardwarePtr, pwmOutputConfiguration.motorCount, init->motorPwmRate, init->idlePulse);
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].flags = PWM_PF_MOTOR | PWM_PF_MOTOR_MODE_BRUSHED | PWM_PF_OUTPUT_PROTOCOL_PWM;
} else {
pwmBrushlessMotorConfig(timerHardwarePtr, pwmOutputConfiguration.motorCount, init->motorPwmRate, init->idlePulse);
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].flags = PWM_PF_MOTOR | PWM_PF_OUTPUT_PROTOCOL_PWM ;
}
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].index = pwmOutputConfiguration.motorCount;
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].timerHardware = timerHardwarePtr;
pwmOutputConfiguration.motorCount++;
pwmOutputConfiguration.outputCount++;
} else if (type == MAP_TO_SERVO_OUTPUT) {
#ifdef USE_SERVOS
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].index = pwmOutputConfiguration.servoCount;
pwmServoConfig(timerHardwarePtr, pwmOutputConfiguration.servoCount, init->servoPwmRate, init->servoCenterPulse);
pwmOutputConfiguration.portConfigurations[pwmOutputConfiguration.outputCount].flags = PWM_PF_SERVO | PWM_PF_OUTPUT_PROTOCOL_PWM;
pwmOutputConfiguration.servoCount++;
pwmOutputConfiguration.outputCount++;
#endif
}
}
return &pwmOutputConfiguration;
}