2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
29 #include "pwm_output.h"
31 #include "pwm_mapping.h"
33 void pwmBrushedMotorConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
, uint16_t motorPwmRate
, uint16_t idlePulse
);
34 void pwmBrushlessMotorConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
, uint16_t motorPwmRate
, uint16_t idlePulse
);
35 void pwmOneshotMotorConfig(const timerHardware_t
*timerHardware
, uint8_t motorIndex
);
36 void pwmServoConfig(const timerHardware_t
*timerHardware
, uint8_t servoIndex
, uint16_t servoPwmRate
, uint16_t servoCenterPulse
);
41 Note: this documentation is only valid for STM32F10x, for STM32F30x please read the code itself.
43 1) multirotor PPM input
45 PWM5..8 used for motors
46 PWM9..10 used for servo or else motors
47 PWM11..14 used for motors
49 2) multirotor PPM input with more servos
51 PWM5..8 used for motors
52 PWM9..10 used for servo or else motors
53 PWM11..14 used for servos
55 2) multirotor PWM input
56 PWM1..8 used for input
57 PWM9..10 used for servo or else motors
58 PWM11..14 used for motors
60 3) airplane / flying wing w/PWM
61 PWM1..8 used for input
62 PWM9 used for motor throttle +PWM10 for 2nd motor
63 PWM11.14 used for servos
65 4) airplane / flying wing with PPM
67 PWM5..8 used for servos
68 PWM9 used for motor throttle +PWM10 for 2nd motor
69 PWM11.14 used for servos
72 const uint16_t * const hardwareMaps
[] = {
79 static pwmIOConfiguration_t pwmIOConfiguration
;
81 pwmIOConfiguration_t
*pwmGetOutputConfiguration(void)
83 return &pwmIOConfiguration
;
86 bool CheckGPIOPin(ioTag_t tag
, GPIO_TypeDef
*gpio
, uint16_t pin
)
88 return IO_GPIOBYTAG(tag
) == gpio
&& IO_PINBYTAG(tag
) == pin
;
91 bool CheckGPIOPinSource(ioTag_t tag
, GPIO_TypeDef
*gpio
, uint16_t pin
)
93 return IO_GPIOBYTAG(tag
) == gpio
&& IO_GPIO_PinSource(IOGetByTag(tag
)) == pin
;
96 pwmIOConfiguration_t
*pwmInit(drv_pwm_config_t
*init
)
98 #ifndef SKIP_RX_PWM_PPM
102 memset(&pwmIOConfiguration
, 0, sizeof(pwmIOConfiguration
));
104 // this is pretty hacky shit, but it will do for now. array of 4 config maps, [ multiPWM multiPPM airPWM airPPM ]
107 i
= 2; // switch to air hardware config
108 if (init
->usePPM
|| init
->useSerialRx
)
109 i
++; // next index is for PPM
111 const uint16_t *setup
= hardwareMaps
[i
];
113 for (i
= 0; i
< USABLE_TIMER_CHANNEL_COUNT
&& setup
[i
] != 0xFFFF; i
++) {
114 uint8_t timerIndex
= setup
[i
] & 0x00FF;
115 uint8_t type
= (setup
[i
] & 0xFF00) >> 8;
117 const timerHardware_t
*timerHardwarePtr
= &timerHardware
[timerIndex
];
119 #ifdef OLIMEXINO_UNCUT_LED2_E_JUMPER
120 // PWM2 is connected to LED2 on the board and cannot be connected unless you cut LED2_E
121 if (timerIndex
== PWM2
)
127 if (init
->useUART2
&& (timerIndex
== PWM3
|| timerIndex
== PWM4
))
131 #if defined(STM32F303xC) && defined(USE_UART3)
132 // skip UART3 ports (PB10/PB11)
133 if (init
->useUART3
&& (timerHardwarePtr
->tag
== IO_TAG(UART3_TX_PIN
) || timerHardwarePtr
->tag
== IO_TAG(UART3_RX_PIN
)))
137 #ifdef SOFTSERIAL_1_TIMER
138 if (init
->useSoftSerial
&& timerHardwarePtr
->tim
== SOFTSERIAL_1_TIMER
)
141 #ifdef SOFTSERIAL_2_TIMER
142 if (init
->useSoftSerial
&& timerHardwarePtr
->tim
== SOFTSERIAL_2_TIMER
)
147 // skip LED Strip output
148 if (init
->useLEDStrip
) {
149 if (timerHardwarePtr
->tim
== WS2811_TIMER
)
151 #if defined(STM32F303xC) && defined(WS2811_PIN)
152 if (timerHardwarePtr
->tag
== IO_TAG(WS2811_PIN
))
160 if (init
->useVbat
&& timerHardwarePtr
->tag
== IO_TAG(VBAT_ADC_PIN
)) {
166 if (init
->useRSSIADC
&& timerHardwarePtr
->tag
== IO_TAG(RSSI_ADC_PIN
)) {
171 #ifdef CURRENT_METER_ADC_GPIO
172 if (init
->useCurrentMeterADC
&& timerHardwarePtr
->tag
== IO_TAG(CURRENT_METER_ADC_PIN
)) {
178 if (init
->useSonar
&&
180 timerHardwarePtr
->tag
== init
->sonarIOConfig
.triggerTag
||
181 timerHardwarePtr
->tag
== init
->sonarIOConfig
.echoTag
187 // hacks to allow current functionality
188 if (type
== MAP_TO_PWM_INPUT
&& !init
->useParallelPWM
)
191 if (type
== MAP_TO_PPM_INPUT
&& !init
->usePPM
)
195 if (init
->useServos
&& !init
->airplane
) {
197 // remap PWM9+10 as servos
198 if ((timerIndex
== PWM9
|| timerIndex
== PWM10
) && timerHardwarePtr
->tim
== TIM1
)
199 type
= MAP_TO_SERVO_OUTPUT
;
203 // remap outputs 1+2 (PWM2+3) as servos
204 if ((timerIndex
== PWM2
|| timerIndex
== PWM3
) && timerHardwarePtr
->tim
== TIM4
)
205 type
= MAP_TO_SERVO_OUTPUT
;
208 #if defined(COLIBRI_RACE) || defined(LUX_RACE)
209 // remap PWM1+2 as servos
210 if ((timerIndex
== PWM6
|| timerIndex
== PWM7
|| timerIndex
== PWM8
|| timerIndex
== PWM9
) && timerHardwarePtr
->tim
== TIM2
)
211 type
= MAP_TO_SERVO_OUTPUT
;
215 // remap PWM9+10 as servos
216 if ((timerIndex
== PWM9
|| timerIndex
== PWM10
) && timerHardwarePtr
->tim
== TIM1
)
217 type
= MAP_TO_SERVO_OUTPUT
;
221 // remap PWM1+2 as servos
222 if ((timerIndex
== PWM1
|| timerIndex
== PWM2
) && timerHardwarePtr
->tim
== TIM15
)
223 type
= MAP_TO_SERVO_OUTPUT
;
226 #if defined(SPRACINGF3)
227 // remap PWM15+16 as servos
228 if ((timerIndex
== PWM15
|| timerIndex
== PWM16
) && timerHardwarePtr
->tim
== TIM15
)
229 type
= MAP_TO_SERVO_OUTPUT
;
232 #if defined(SPRACINGF3MINI) || defined(OMNIBUS)
233 // remap PWM6+7 as servos
234 if ((timerIndex
== PWM6
|| timerIndex
== PWM7
) && timerHardwarePtr
->tim
== TIM15
)
235 type
= MAP_TO_SERVO_OUTPUT
;
238 #if defined(RCEXPLORERF3)
239 if (timerIndex
== PWM2
)
241 type
= MAP_TO_SERVO_OUTPUT
;
245 #if (defined(STM32F3DISCOVERY) && !defined(CHEBUZZF3))
246 // remap PWM 5+6 or 9+10 as servos - softserial pin pairs require timer ports that use the same timer
247 if (init
->useSoftSerial
) {
248 if (timerIndex
== PWM5
|| timerIndex
== PWM6
)
249 type
= MAP_TO_SERVO_OUTPUT
;
251 if (timerIndex
== PWM9
|| timerIndex
== PWM10
)
252 type
= MAP_TO_SERVO_OUTPUT
;
257 // remap PWM 7+8 as servos
258 if (timerIndex
== PWM7
|| timerIndex
== PWM8
)
259 type
= MAP_TO_SERVO_OUTPUT
;
262 #if defined(SINGULARITY)
263 // remap PWM6+7 as servos
264 if (timerIndex
== PWM6
|| timerIndex
== PWM7
)
265 type
= MAP_TO_SERVO_OUTPUT
;
269 if (init
->useChannelForwarding
&& !init
->airplane
) {
270 #if defined(NAZE) && defined(WS2811_TIMER)
271 // if LED strip is active, PWM5-8 are unavailable, so map AUX1+AUX2 to PWM13+PWM14
272 if (init
->useLEDStrip
) {
273 if (timerIndex
>= PWM13
&& timerIndex
<= PWM14
) {
274 type
= MAP_TO_SERVO_OUTPUT
;
279 #if defined(SPRACINGF3) || defined(NAZE)
280 // remap PWM5..8 as servos when used in extended servo mode
281 if (timerIndex
>= PWM5
&& timerIndex
<= PWM8
)
282 type
= MAP_TO_SERVO_OUTPUT
;
289 // This part of code is unnecessary and can be removed - timer clash is resolved by forcing configuration with the same
290 // timer tick rate - PWM_TIMER_MHZ
292 if (init->useParallelPWM) {
293 // Skip PWM inputs that conflict with timers used outputs.
294 if ((type == MAP_TO_SERVO_OUTPUT || type == MAP_TO_MOTOR_OUTPUT) && (timerHardwarePtr->tim == TIM2 || timerHardwarePtr->tim == TIM3)) {
297 if (type == MAP_TO_PWM_INPUT && timerHardwarePtr->tim == TIM4) {
305 if (type
== MAP_TO_PPM_INPUT
) {
306 #ifndef SKIP_RX_PWM_PPM
308 if (init
->useOneshot
|| isMotorBrushed(init
->motorPwmRate
)) {
309 ppmAvoidPWMTimerClash(timerHardwarePtr
, TIM4
);
312 #if defined(SPARKY) || defined(ALIENFLIGHTF3)
313 if (init
->useOneshot
|| isMotorBrushed(init
->motorPwmRate
)) {
314 ppmAvoidPWMTimerClash(timerHardwarePtr
, TIM2
);
317 ppmInConfig(timerHardwarePtr
);
318 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].flags
= PWM_PF_PPM
;
319 pwmIOConfiguration
.ppmInputCount
++;
321 } else if (type
== MAP_TO_PWM_INPUT
) {
322 #ifndef SKIP_RX_PWM_PPM
323 pwmInConfig(timerHardwarePtr
, channelIndex
);
324 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].flags
= PWM_PF_PWM
;
325 pwmIOConfiguration
.pwmInputCount
++;
328 } else if (type
== MAP_TO_MOTOR_OUTPUT
) {
329 /* Check if we already configured maximum supported number of motors and skip the rest */
330 if (pwmIOConfiguration
.motorCount
>= MAX_MOTORS
) {
334 #if defined(CC3D) && !defined(CC3D_PPM1)
335 if (init
->useOneshot
|| isMotorBrushed(init
->motorPwmRate
)) {
336 // Skip it if it would cause PPM capture timer to be reconfigured or manually overflowed
337 if (timerHardwarePtr
->tim
== TIM2
)
341 if (init
->useOneshot
) {
343 pwmOneshotMotorConfig(timerHardwarePtr
, pwmIOConfiguration
.motorCount
);
344 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].flags
= PWM_PF_MOTOR
| PWM_PF_OUTPUT_PROTOCOL_ONESHOT
|PWM_PF_OUTPUT_PROTOCOL_PWM
;
346 } else if (isMotorBrushed(init
->motorPwmRate
)) {
348 pwmBrushedMotorConfig(timerHardwarePtr
, pwmIOConfiguration
.motorCount
, init
->motorPwmRate
, init
->idlePulse
);
349 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].flags
= PWM_PF_MOTOR
| PWM_PF_MOTOR_MODE_BRUSHED
| PWM_PF_OUTPUT_PROTOCOL_PWM
;
353 pwmBrushlessMotorConfig(timerHardwarePtr
, pwmIOConfiguration
.motorCount
, init
->motorPwmRate
, init
->idlePulse
);
354 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].flags
= PWM_PF_MOTOR
| PWM_PF_OUTPUT_PROTOCOL_PWM
;
357 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].index
= pwmIOConfiguration
.motorCount
;
358 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].timerHardware
= timerHardwarePtr
;
360 pwmIOConfiguration
.motorCount
++;
362 } else if (type
== MAP_TO_SERVO_OUTPUT
) {
363 if (pwmIOConfiguration
.servoCount
>= MAX_SERVOS
) {
368 pwmServoConfig(timerHardwarePtr
, pwmIOConfiguration
.servoCount
, init
->servoPwmRate
, init
->servoCenterPulse
);
370 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].flags
= PWM_PF_SERVO
| PWM_PF_OUTPUT_PROTOCOL_PWM
;
371 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].index
= pwmIOConfiguration
.servoCount
;
372 pwmIOConfiguration
.ioConfigurations
[pwmIOConfiguration
.ioCount
].timerHardware
= timerHardwarePtr
;
374 pwmIOConfiguration
.servoCount
++;
380 pwmIOConfiguration
.ioCount
++;
383 return &pwmIOConfiguration
;