Boot-time event logging implementation (#536)
[betaflight.git] / src / main / drivers / pwm_mapping.c
blob62e0de1bcde27593bc88d923167205cf3d60cd0e
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 "logging.h"
31 #include "pwm_output.h"
32 #include "pwm_rx.h"
33 #include "pwm_mapping.h"
35 void pwmBrushedMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint16_t motorPwmRate, uint16_t idlePulse);
36 void pwmBrushlessMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint16_t motorPwmRate, uint16_t idlePulse);
37 void pwmOneshotMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex);
38 void pwmServoConfig(const timerHardware_t *timerHardware, uint8_t servoIndex, uint16_t servoPwmRate, uint16_t servoCenterPulse);
41 Configuration maps
43 Note: this documentation is only valid for STM32F10x, for STM32F30x please read the code itself.
45 1) multirotor PPM input
46 PWM1 used for PPM
47 PWM5..8 used for motors
48 PWM9..10 used for servo or else motors
49 PWM11..14 used for motors
51 2) multirotor PPM input with more servos
52 PWM1 used for PPM
53 PWM5..8 used for motors
54 PWM9..10 used for servo or else motors
55 PWM11..14 used for servos
57 2) multirotor PWM input
58 PWM1..8 used for input
59 PWM9..10 used for servo or else motors
60 PWM11..14 used for motors
62 3) airplane / flying wing w/PWM
63 PWM1..8 used for input
64 PWM9 used for motor throttle +PWM10 for 2nd motor
65 PWM11.14 used for servos
67 4) airplane / flying wing with PPM
68 PWM1 used for PPM
69 PWM5..8 used for servos
70 PWM9 used for motor throttle +PWM10 for 2nd motor
71 PWM11.14 used for servos
74 const uint16_t * const hardwareMaps[] = {
75 multiPWM,
76 multiPPM,
77 airPWM,
78 airPPM,
81 static pwmIOConfiguration_t pwmIOConfiguration;
83 pwmIOConfiguration_t *pwmGetOutputConfiguration(void)
85 return &pwmIOConfiguration;
88 bool CheckGPIOPin(ioTag_t tag, GPIO_TypeDef *gpio, uint16_t pin)
90 return IO_GPIOBYTAG(tag) == gpio && IO_PINBYTAG(tag) == pin;
93 bool CheckGPIOPinSource(ioTag_t tag, GPIO_TypeDef *gpio, uint16_t pin)
95 return IO_GPIOBYTAG(tag) == gpio && IO_GPIO_PinSource(IOGetByTag(tag)) == pin;
98 pwmIOConfiguration_t *pwmInit(drv_pwm_config_t *init)
100 #ifndef SKIP_RX_PWM_PPM
101 int channelIndex = 0;
102 #endif
104 memset(&pwmIOConfiguration, 0, sizeof(pwmIOConfiguration));
106 // this is pretty hacky shit, but it will do for now. array of 4 config maps, [ multiPWM multiPPM airPWM airPPM ]
107 int i = 0;
108 if (init->airplane)
109 i = 2; // switch to air hardware config
110 if (init->usePPM || init->useSerialRx)
111 i++; // next index is for PPM
113 const uint16_t *setup = hardwareMaps[i];
115 for (i = 0; i < USABLE_TIMER_CHANNEL_COUNT && setup[i] != 0xFFFF; i++) {
116 uint8_t timerIndex = setup[i] & 0x00FF;
117 uint8_t type = (setup[i] & 0xFF00) >> 8;
119 const timerHardware_t *timerHardwarePtr = &timerHardware[timerIndex];
121 #ifdef OLIMEXINO_UNCUT_LED2_E_JUMPER
122 // PWM2 is connected to LED2 on the board and cannot be connected unless you cut LED2_E
123 if (timerIndex == PWM2) {
124 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
125 continue;
127 #endif
129 #ifdef STM32F10X
130 // skip UART2 ports
131 if (init->useUART2 && (timerIndex == PWM3 || timerIndex == PWM4)) {
132 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
133 continue;
135 #endif
137 #if defined(STM32F303xC) && defined(USE_UART3)
138 // skip UART3 ports (PB10/PB11)
139 if (init->useUART3 && (timerHardwarePtr->tag == IO_TAG(UART3_TX_PIN) || timerHardwarePtr->tag == IO_TAG(UART3_RX_PIN))) {
140 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
141 continue;
143 #endif
145 #ifdef SOFTSERIAL_1_TIMER
146 if (init->useSoftSerial && timerHardwarePtr->tim == SOFTSERIAL_1_TIMER) {
147 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
148 continue;
150 #endif
151 #ifdef SOFTSERIAL_2_TIMER
152 if (init->useSoftSerial && timerHardwarePtr->tim == SOFTSERIAL_2_TIMER) {
153 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
154 continue;
156 #endif
158 #ifdef WS2811_TIMER
159 // skip LED Strip output
160 if (init->useLEDStrip) {
161 if (timerHardwarePtr->tim == WS2811_TIMER) {
162 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
163 continue;
165 #if defined(STM32F303xC) && defined(WS2811_PIN)
166 if (timerHardwarePtr->tag == IO_TAG(WS2811_PIN)) {
167 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
168 continue;
170 #endif
173 #endif
175 #ifdef VBAT_ADC_PIN
176 if (init->useVbat && timerHardwarePtr->tag == IO_TAG(VBAT_ADC_PIN)) {
177 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
178 continue;
180 #endif
182 #ifdef RSSI_ADC_GPIO
183 if (init->useRSSIADC && timerHardwarePtr->tag == IO_TAG(RSSI_ADC_PIN)) {
184 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
185 continue;
187 #endif
189 #ifdef CURRENT_METER_ADC_GPIO
190 if (init->useCurrentMeterADC && timerHardwarePtr->tag == IO_TAG(CURRENT_METER_ADC_PIN)) {
191 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
192 continue;
194 #endif
196 #ifdef SONAR
197 if (init->useSonar &&
199 timerHardwarePtr->tag == init->sonarIOConfig.triggerTag ||
200 timerHardwarePtr->tag == init->sonarIOConfig.echoTag
201 )) {
202 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
203 continue;
205 #endif
207 // hacks to allow current functionality
208 if (type == MAP_TO_PWM_INPUT && !init->useParallelPWM)
209 continue;
211 if (type == MAP_TO_PPM_INPUT && !init->usePPM)
212 continue;
214 #ifdef USE_SERVOS
215 if (init->useServos && !init->airplane) {
216 #if defined(NAZE)
217 // remap PWM9+10 as servos
218 if ((timerIndex == PWM9 || timerIndex == PWM10) && timerHardwarePtr->tim == TIM1)
219 type = MAP_TO_SERVO_OUTPUT;
220 #endif
222 #if defined(DOGE)
223 // remap outputs 1+2 (PWM2+3) as servos
224 if ((timerIndex == PWM2 || timerIndex == PWM3) && timerHardwarePtr->tim == TIM4)
225 type = MAP_TO_SERVO_OUTPUT;
226 #endif
228 #if defined(COLIBRI_RACE) || defined(LUX_RACE)
229 // remap PWM1+2 as servos
230 if ((timerIndex == PWM6 || timerIndex == PWM7 || timerIndex == PWM8 || timerIndex == PWM9) && timerHardwarePtr->tim == TIM2)
231 type = MAP_TO_SERVO_OUTPUT;
232 #endif
234 #if defined(CC3D)
235 // remap PWM9+10 as servos
236 if ((timerIndex == PWM9 || timerIndex == PWM10) && timerHardwarePtr->tim == TIM1)
237 type = MAP_TO_SERVO_OUTPUT;
238 #endif
240 #if defined(SPARKY)
241 // remap PWM1+2 as servos
242 if ((timerIndex == PWM1 || timerIndex == PWM2) && timerHardwarePtr->tim == TIM15)
243 type = MAP_TO_SERVO_OUTPUT;
244 #endif
246 #if defined(SPRACINGF3)
247 // remap PWM15+16 as servos
248 if ((timerIndex == PWM15 || timerIndex == PWM16) && timerHardwarePtr->tim == TIM15)
249 type = MAP_TO_SERVO_OUTPUT;
250 #endif
252 #if defined(SPRACINGF3MINI) || defined(OMNIBUS)
253 // remap PWM6+7 as servos
254 if ((timerIndex == PWM6 || timerIndex == PWM7) && timerHardwarePtr->tim == TIM15)
255 type = MAP_TO_SERVO_OUTPUT;
256 #endif
258 #if defined(RCEXPLORERF3)
259 if (timerIndex == PWM2)
261 type = MAP_TO_SERVO_OUTPUT;
263 #endif
265 #if defined(SPRACINGF3EVO)
266 // remap PWM6+7 as servos
267 if ((timerIndex == PWM8 || timerIndex == PWM9) && timerHardwarePtr->tim == TIM3)
268 type = MAP_TO_SERVO_OUTPUT;
269 #endif
271 #if (defined(STM32F3DISCOVERY) && !defined(CHEBUZZF3))
272 // remap PWM 5+6 or 9+10 as servos - softserial pin pairs require timer ports that use the same timer
273 if (init->useSoftSerial) {
274 if (timerIndex == PWM5 || timerIndex == PWM6)
275 type = MAP_TO_SERVO_OUTPUT;
276 } else {
277 if (timerIndex == PWM9 || timerIndex == PWM10)
278 type = MAP_TO_SERVO_OUTPUT;
280 #endif
282 #if defined(MOTOLAB)
283 // remap PWM 7+8 as servos
284 if (timerIndex == PWM7 || timerIndex == PWM8)
285 type = MAP_TO_SERVO_OUTPUT;
286 #endif
288 #if defined(SINGULARITY)
289 // remap PWM6+7 as servos
290 if (timerIndex == PWM6 || timerIndex == PWM7)
291 type = MAP_TO_SERVO_OUTPUT;
292 #endif
295 if (init->useChannelForwarding && !init->airplane) {
296 #if defined(NAZE) && defined(WS2811_TIMER)
297 // if LED strip is active, PWM5-8 are unavailable, so map AUX1+AUX2 to PWM13+PWM14
298 if (init->useLEDStrip) {
299 if (timerIndex >= PWM13 && timerIndex <= PWM14) {
300 type = MAP_TO_SERVO_OUTPUT;
302 } else
303 #endif
305 #if defined(SPRACINGF3) || defined(NAZE)
306 // remap PWM5..8 as servos when used in extended servo mode
307 if (timerIndex >= PWM5 && timerIndex <= PWM8)
308 type = MAP_TO_SERVO_OUTPUT;
309 #endif
312 #endif // USE_SERVOS
314 #ifdef CC3D
315 // This part of code is unnecessary and can be removed - timer clash is resolved by forcing configuration with the same
316 // timer tick rate - PWM_TIMER_MHZ
318 if (init->useParallelPWM) {
319 // Skip PWM inputs that conflict with timers used outputs.
320 if ((type == MAP_TO_SERVO_OUTPUT || type == MAP_TO_MOTOR_OUTPUT) && (timerHardwarePtr->tim == TIM2 || timerHardwarePtr->tim == TIM3)) {
321 continue;
323 if (type == MAP_TO_PWM_INPUT && timerHardwarePtr->tim == TIM4) {
324 continue;
329 #endif
331 if (type == MAP_TO_PPM_INPUT) {
332 #ifndef SKIP_RX_PWM_PPM
333 #ifdef CC3D_PPM1
334 if (init->useOneshot || isMotorBrushed(init->motorPwmRate)) {
335 ppmAvoidPWMTimerClash(timerHardwarePtr, TIM4);
337 #endif
338 #if defined(SPARKY) || defined(ALIENFLIGHTF3)
339 if (init->useOneshot || isMotorBrushed(init->motorPwmRate)) {
340 ppmAvoidPWMTimerClash(timerHardwarePtr, TIM2);
342 #endif
343 ppmInConfig(timerHardwarePtr);
344 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_PPM;
345 pwmIOConfiguration.ppmInputCount++;
347 addBootlogEvent6(BOOT_EVENT_TIMER_CH_MAPPED, BOOT_EVENT_FLAGS_NONE, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 0);
348 #endif
349 } else if (type == MAP_TO_PWM_INPUT) {
350 #ifndef SKIP_RX_PWM_PPM
351 pwmInConfig(timerHardwarePtr, channelIndex);
352 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_PWM;
353 pwmIOConfiguration.pwmInputCount++;
354 channelIndex++;
356 addBootlogEvent6(BOOT_EVENT_TIMER_CH_MAPPED, BOOT_EVENT_FLAGS_NONE, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 1);
357 #endif
358 } else if (type == MAP_TO_MOTOR_OUTPUT) {
359 /* Check if we already configured maximum supported number of motors and skip the rest */
360 if (pwmIOConfiguration.motorCount >= MAX_MOTORS) {
361 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 1);
362 continue;
365 #if defined(CC3D) && !defined(CC3D_PPM1)
366 if (init->useOneshot || isMotorBrushed(init->motorPwmRate)) {
367 // Skip it if it would cause PPM capture timer to be reconfigured or manually overflowed
368 if (timerHardwarePtr->tim == TIM2) {
369 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
370 continue;
373 #endif
374 if (init->useOneshot) {
376 pwmOneshotMotorConfig(timerHardwarePtr, pwmIOConfiguration.motorCount);
377 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_MOTOR | PWM_PF_OUTPUT_PROTOCOL_ONESHOT|PWM_PF_OUTPUT_PROTOCOL_PWM;
379 } else if (isMotorBrushed(init->motorPwmRate)) {
381 pwmBrushedMotorConfig(timerHardwarePtr, pwmIOConfiguration.motorCount, init->motorPwmRate, init->idlePulse);
382 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_MOTOR | PWM_PF_MOTOR_MODE_BRUSHED | PWM_PF_OUTPUT_PROTOCOL_PWM;
384 } else {
386 pwmBrushlessMotorConfig(timerHardwarePtr, pwmIOConfiguration.motorCount, init->motorPwmRate, init->idlePulse);
387 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_MOTOR | PWM_PF_OUTPUT_PROTOCOL_PWM ;
390 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].index = pwmIOConfiguration.motorCount;
391 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].timerHardware = timerHardwarePtr;
393 pwmIOConfiguration.motorCount++;
395 addBootlogEvent6(BOOT_EVENT_TIMER_CH_MAPPED, BOOT_EVENT_FLAGS_NONE, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 2);
396 } else if (type == MAP_TO_SERVO_OUTPUT) {
397 if (pwmIOConfiguration.servoCount >= MAX_SERVOS) {
398 addBootlogEvent6(BOOT_EVENT_TIMER_CH_SKIPPED, BOOT_EVENT_FLAGS_WARNING, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 2);
399 continue;
402 #ifdef USE_SERVOS
403 pwmServoConfig(timerHardwarePtr, pwmIOConfiguration.servoCount, init->servoPwmRate, init->servoCenterPulse);
405 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].flags = PWM_PF_SERVO | PWM_PF_OUTPUT_PROTOCOL_PWM;
406 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].index = pwmIOConfiguration.servoCount;
407 pwmIOConfiguration.ioConfigurations[pwmIOConfiguration.ioCount].timerHardware = timerHardwarePtr;
409 pwmIOConfiguration.servoCount++;
411 addBootlogEvent6(BOOT_EVENT_TIMER_CH_MAPPED, BOOT_EVENT_FLAGS_NONE, i, pwmIOConfiguration.motorCount, pwmIOConfiguration.servoCount, 3);
412 #endif
413 } else {
414 continue;
417 pwmIOConfiguration.ioCount++;
420 return &pwmIOConfiguration;