New target: RCEXPLORERF3 (#439)
[inav/snaewe.git] / src / main / drivers / pwm_mapping.c
blob2cfc3fb90f1ef8ac543a2b6697dcbb33d8551f95
1 /*
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/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <string.h>
22 #include "platform.h"
24 #include "gpio.h"
25 #include "io.h"
26 #include "io_impl.h"
27 #include "timer.h"
29 #include "pwm_output.h"
30 #include "pwm_rx.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);
39 Configuration maps
41 Note: this documentation is only valid for STM32F10x, for STM32F30x please read the code itself.
43 1) multirotor PPM input
44 PWM1 used for PPM
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
50 PWM1 used for PPM
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
66 PWM1 used for 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[] = {
73 multiPWM,
74 multiPPM,
75 airPWM,
76 airPPM,
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
99 int channelIndex = 0;
100 #endif
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 ]
105 int i = 0;
106 if (init->airplane)
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)
122 continue;
123 #endif
125 #ifdef STM32F10X
126 // skip UART2 ports
127 if (init->useUART2 && (timerIndex == PWM3 || timerIndex == PWM4))
128 continue;
129 #endif
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)))
134 continue;
135 #endif
137 #ifdef SOFTSERIAL_1_TIMER
138 if (init->useSoftSerial && timerHardwarePtr->tim == SOFTSERIAL_1_TIMER)
139 continue;
140 #endif
141 #ifdef SOFTSERIAL_2_TIMER
142 if (init->useSoftSerial && timerHardwarePtr->tim == SOFTSERIAL_2_TIMER)
143 continue;
144 #endif
146 #ifdef WS2811_TIMER
147 // skip LED Strip output
148 if (init->useLEDStrip) {
149 if (timerHardwarePtr->tim == WS2811_TIMER)
150 continue;
151 #if defined(STM32F303xC) && defined(WS2811_PIN)
152 if (timerHardwarePtr->tag == IO_TAG(WS2811_PIN))
153 continue;
154 #endif
157 #endif
159 #ifdef VBAT_ADC_PIN
160 if (init->useVbat && timerHardwarePtr->tag == IO_TAG(VBAT_ADC_PIN)) {
161 continue;
163 #endif
165 #ifdef RSSI_ADC_GPIO
166 if (init->useRSSIADC && timerHardwarePtr->tag == IO_TAG(RSSI_ADC_PIN)) {
167 continue;
169 #endif
171 #ifdef CURRENT_METER_ADC_GPIO
172 if (init->useCurrentMeterADC && timerHardwarePtr->tag == IO_TAG(CURRENT_METER_ADC_PIN)) {
173 continue;
175 #endif
177 #ifdef SONAR
178 if (init->useSonar &&
180 timerHardwarePtr->tag == init->sonarIOConfig.triggerTag ||
181 timerHardwarePtr->tag == init->sonarIOConfig.echoTag
182 )) {
183 continue;
185 #endif
187 // hacks to allow current functionality
188 if (type == MAP_TO_PWM_INPUT && !init->useParallelPWM)
189 continue;
191 if (type == MAP_TO_PPM_INPUT && !init->usePPM)
192 continue;
194 #ifdef USE_SERVOS
195 if (init->useServos && !init->airplane) {
196 #if defined(NAZE)
197 // remap PWM9+10 as servos
198 if ((timerIndex == PWM9 || timerIndex == PWM10) && timerHardwarePtr->tim == TIM1)
199 type = MAP_TO_SERVO_OUTPUT;
200 #endif
202 #if defined(DOGE)
203 // remap outputs 1+2 (PWM2+3) as servos
204 if ((timerIndex == PWM2 || timerIndex == PWM3) && timerHardwarePtr->tim == TIM4)
205 type = MAP_TO_SERVO_OUTPUT;
206 #endif
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;
212 #endif
214 #if defined(CC3D)
215 // remap PWM9+10 as servos
216 if ((timerIndex == PWM9 || timerIndex == PWM10) && timerHardwarePtr->tim == TIM1)
217 type = MAP_TO_SERVO_OUTPUT;
218 #endif
220 #if defined(SPARKY)
221 // remap PWM1+2 as servos
222 if ((timerIndex == PWM1 || timerIndex == PWM2) && timerHardwarePtr->tim == TIM15)
223 type = MAP_TO_SERVO_OUTPUT;
224 #endif
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;
230 #endif
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;
236 #endif
238 #if defined(RCEXPLORERF3)
239 if (timerIndex == PWM2)
241 type = MAP_TO_SERVO_OUTPUT;
243 #endif
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;
250 } else {
251 if (timerIndex == PWM9 || timerIndex == PWM10)
252 type = MAP_TO_SERVO_OUTPUT;
254 #endif
256 #if defined(MOTOLAB)
257 // remap PWM 7+8 as servos
258 if (timerIndex == PWM7 || timerIndex == PWM8)
259 type = MAP_TO_SERVO_OUTPUT;
260 #endif
262 #if defined(SINGULARITY)
263 // remap PWM6+7 as servos
264 if (timerIndex == PWM6 || timerIndex == PWM7)
265 type = MAP_TO_SERVO_OUTPUT;
266 #endif
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;
276 } else
277 #endif
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;
283 #endif
286 #endif // USE_SERVOS
288 #ifdef CC3D
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)) {
295 continue;
297 if (type == MAP_TO_PWM_INPUT && timerHardwarePtr->tim == TIM4) {
298 continue;
303 #endif
305 if (type == MAP_TO_PPM_INPUT) {
306 #ifndef SKIP_RX_PWM_PPM
307 #ifdef CC3D_PPM1
308 if (init->useOneshot || isMotorBrushed(init->motorPwmRate)) {
309 ppmAvoidPWMTimerClash(timerHardwarePtr, TIM4);
311 #endif
312 #if defined(SPARKY) || defined(ALIENFLIGHTF3)
313 if (init->useOneshot || isMotorBrushed(init->motorPwmRate)) {
314 ppmAvoidPWMTimerClash(timerHardwarePtr, TIM2);
316 #endif
317 ppmInConfig(timerHardwarePtr);
318 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_PPM;
319 pwmIOConfiguration.ppmInputCount++;
320 #endif
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++;
326 channelIndex++;
327 #endif
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) {
331 continue;
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)
338 continue;
340 #endif
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;
351 } else {
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) {
364 continue;
367 #ifdef USE_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++;
375 #endif
376 } else {
377 continue;
380 pwmIOConfiguration.ioCount++;
383 return &pwmIOConfiguration;