Update pwm_mapping.c
[inav.git] / src / main / drivers / pwm_mapping.c
blob2d01127a504f9511f991dcaa51138d710140a4df
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 #if !defined(SITL_BUILD)
26 #include "build/debug.h"
27 #include "common/log.h"
28 #include "common/memory.h"
30 #include "config/feature.h"
32 #include "fc/config.h"
34 #include "drivers/io.h"
35 #include "drivers/io_impl.h"
36 #include "drivers/timer.h"
37 #include "drivers/pwm_output.h"
38 #include "drivers/pwm_mapping.h"
39 #include "drivers/serial.h"
40 #include "drivers/serial_uart.h"
42 #include "sensors/rangefinder.h"
44 #include "io/serial.h"
45 #include "io/servo_sbus.h"
47 enum {
48 MAP_TO_NONE,
49 MAP_TO_MOTOR_OUTPUT,
50 MAP_TO_SERVO_OUTPUT,
51 MAP_TO_LED_OUTPUT
54 typedef struct {
55 int maxTimMotorCount;
56 int maxTimServoCount;
57 const timerHardware_t * timMotors[MAX_PWM_OUTPUTS];
58 const timerHardware_t * timServos[MAX_PWM_OUTPUTS];
59 } timMotorServoHardware_t;
61 static pwmInitError_e pwmInitError = PWM_INIT_ERROR_NONE;
63 static const char * pwmInitErrorMsg[] = {
64 /* PWM_INIT_ERROR_NONE */ "No error",
65 /* PWM_INIT_ERROR_TOO_MANY_MOTORS */ "Mixer defines too many motors",
66 /* PWM_INIT_ERROR_TOO_MANY_SERVOS */ "Mixer defines too many servos",
67 /* PWM_INIT_ERROR_NOT_ENOUGH_MOTOR_OUTPUTS */ "Not enough motor outputs/timers",
68 /* PWM_INIT_ERROR_NOT_ENOUGH_SERVO_OUTPUTS */ "Not enough servo outputs/timers",
69 /* PWM_INIT_ERROR_TIMER_INIT_FAILED */ "Output timer init failed"
72 static const motorProtocolProperties_t motorProtocolProperties[] = {
73 [PWM_TYPE_STANDARD] = { .usesHwTimer = true, .isDSHOT = false },
74 [PWM_TYPE_ONESHOT125] = { .usesHwTimer = true, .isDSHOT = false },
75 [PWM_TYPE_MULTISHOT] = { .usesHwTimer = true, .isDSHOT = false },
76 [PWM_TYPE_BRUSHED] = { .usesHwTimer = true, .isDSHOT = false },
77 [PWM_TYPE_DSHOT150] = { .usesHwTimer = true, .isDSHOT = true },
78 [PWM_TYPE_DSHOT300] = { .usesHwTimer = true, .isDSHOT = true },
79 [PWM_TYPE_DSHOT600] = { .usesHwTimer = true, .isDSHOT = true },
82 pwmInitError_e getPwmInitError(void)
84 return pwmInitError;
87 const char * getPwmInitErrorMessage(void)
89 return pwmInitErrorMsg[pwmInitError];
92 const motorProtocolProperties_t * getMotorProtocolProperties(motorPwmProtocolTypes_e proto)
94 return &motorProtocolProperties[proto];
97 static bool checkPwmTimerConflicts(const timerHardware_t *timHw)
99 serialPortPins_t uartPins;
101 #if defined(USE_UART2)
102 uartGetPortPins(UARTDEV_2, &uartPins);
103 if (doesConfigurationUsePort(SERIAL_PORT_USART2) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
104 return true;
106 #endif
108 #if defined(USE_UART3)
109 uartGetPortPins(UARTDEV_3, &uartPins);
110 if (doesConfigurationUsePort(SERIAL_PORT_USART3) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
111 return true;
113 #endif
115 #if defined(USE_UART4)
116 uartGetPortPins(UARTDEV_4, &uartPins);
117 if (doesConfigurationUsePort(SERIAL_PORT_USART4) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
118 return true;
120 #endif
122 #if defined(USE_UART5)
123 uartGetPortPins(UARTDEV_5, &uartPins);
124 if (doesConfigurationUsePort(SERIAL_PORT_USART5) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
125 return true;
127 #endif
129 #if defined(USE_UART6)
130 uartGetPortPins(UARTDEV_6, &uartPins);
131 if (doesConfigurationUsePort(SERIAL_PORT_USART6) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
132 return true;
134 #endif
136 #if defined(USE_UART7)
137 uartGetPortPins(UARTDEV_7, &uartPins);
138 if (doesConfigurationUsePort(SERIAL_PORT_USART7) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
139 return true;
141 #endif
143 #if defined(USE_UART8)
144 uartGetPortPins(UARTDEV_8, &uartPins);
145 if (doesConfigurationUsePort(SERIAL_PORT_USART8) && (timHw->tag == uartPins.txPin || timHw->tag == uartPins.rxPin)) {
146 return true;
148 #endif
150 #if defined(USE_SOFTSERIAL1)
151 if (feature(FEATURE_SOFTSERIAL)) {
152 const timerHardware_t *ssrx = timerGetByTag(IO_TAG(SOFTSERIAL_1_RX_PIN), TIM_USE_ANY);
153 const timerHardware_t *sstx = timerGetByTag(IO_TAG(SOFTSERIAL_1_TX_PIN), TIM_USE_ANY);
154 if ((ssrx != NULL && ssrx->tim == timHw->tim) || (sstx != NULL && sstx->tim == timHw->tim)) {
155 return true;
158 #endif
160 #if defined(USE_SOFTSERIAL2)
161 if (feature(FEATURE_SOFTSERIAL)) {
162 const timerHardware_t *ssrx = timerGetByTag(IO_TAG(SOFTSERIAL_2_RX_PIN), TIM_USE_ANY);
163 const timerHardware_t *sstx = timerGetByTag(IO_TAG(SOFTSERIAL_2_TX_PIN), TIM_USE_ANY);
164 if ((ssrx != NULL && ssrx->tim == timHw->tim) || (sstx != NULL && sstx->tim == timHw->tim)) {
165 return true;
168 #endif
170 #if defined(USE_LED_STRIP)
171 if (feature(FEATURE_LED_STRIP)) {
172 for (int i = 0; i < timerHardwareCount; i++) {
173 if (timHw->tim == timerHardware[i].tim && timerHardware[i].usageFlags & TIM_USE_LED) {
174 return true;
178 //const timerHardware_t * ledTimHw = timerGetByTag(IO_TAG(WS2811_PIN), TIM_USE_ANY);
179 //if (ledTimHw != NULL && timHw->tim == ledTimHw->tim) {
180 // return true;
183 #endif
185 #if defined(USE_ADC)
186 #if defined(ADC_CHANNEL_1_PIN)
187 if (timHw->tag == IO_TAG(ADC_CHANNEL_1_PIN)) {
188 return true;
190 #endif
191 #if defined(ADC_CHANNEL_2_PIN)
192 if (timHw->tag == IO_TAG(ADC_CHANNEL_2_PIN)) {
193 return true;
195 #endif
196 #if defined(ADC_CHANNEL_3_PIN)
197 if (timHw->tag == IO_TAG(ADC_CHANNEL_3_PIN)) {
198 return true;
200 #endif
201 #if defined(ADC_CHANNEL_4_PIN)
202 if (timHw->tag == IO_TAG(ADC_CHANNEL_4_PIN)) {
203 return true;
205 #endif
206 #if defined(ADC_CHANNEL_5_PIN)
207 if (timHw->tag == IO_TAG(ADC_CHANNEL_5_PIN)) {
208 return true;
210 #endif
211 #if defined(ADC_CHANNEL_6_PIN)
212 if (timHw->tag == IO_TAG(ADC_CHANNEL_6_PIN)) {
213 return true;
215 #endif
216 #endif
218 return false;
221 static void timerHardwareOverride(timerHardware_t * timer) {
222 switch (timerOverrides(timer2id(timer->tim))->outputMode) {
223 case OUTPUT_MODE_MOTORS:
224 timer->usageFlags &= ~(TIM_USE_SERVO|TIM_USE_LED);
225 timer->usageFlags |= TIM_USE_MOTOR;
226 break;
227 case OUTPUT_MODE_SERVOS:
228 timer->usageFlags &= ~(TIM_USE_MOTOR|TIM_USE_LED);
229 timer->usageFlags |= TIM_USE_SERVO;
230 break;
231 case OUTPUT_MODE_LED:
232 timer->usageFlags &= ~(TIM_USE_MOTOR|TIM_USE_SERVO);
233 timer->usageFlags |= TIM_USE_LED;
234 break;
238 bool pwmHasMotorOnTimer(timMotorServoHardware_t * timOutputs, HAL_Timer_t *tim)
240 for (int i = 0; i < timOutputs->maxTimMotorCount; ++i) {
241 if (timOutputs->timMotors[i]->tim == tim) {
242 return true;
246 return false;
249 bool pwmHasServoOnTimer(timMotorServoHardware_t * timOutputs, HAL_Timer_t *tim)
251 for (int i = 0; i < timOutputs->maxTimServoCount; ++i) {
252 if (timOutputs->timServos[i]->tim == tim) {
253 return true;
257 return false;
260 uint8_t pwmClaimTimer(HAL_Timer_t *tim, uint32_t usageFlags) {
261 uint8_t changed = 0;
262 for (int idx = 0; idx < timerHardwareCount; idx++) {
263 timerHardware_t *timHw = &timerHardware[idx];
264 if (timHw->tim == tim && timHw->usageFlags != usageFlags) {
265 timHw->usageFlags = usageFlags;
266 changed++;
270 return changed;
273 void pwmEnsureEnoughtMotors(uint8_t motorCount)
275 uint8_t motorOnlyOutputs = 0;
277 for (int idx = 0; idx < timerHardwareCount; idx++) {
278 timerHardware_t *timHw = &timerHardware[idx];
280 timerHardwareOverride(timHw);
282 if (checkPwmTimerConflicts(timHw)) {
283 continue;
286 if (TIM_IS_MOTOR_ONLY(timHw->usageFlags)) {
287 motorOnlyOutputs++;
288 motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
292 for (int idx = 0; idx < timerHardwareCount; idx++) {
293 timerHardware_t *timHw = &timerHardware[idx];
295 if (checkPwmTimerConflicts(timHw)) {
296 continue;
299 if (TIM_IS_MOTOR(timHw->usageFlags) && !TIM_IS_MOTOR_ONLY(timHw->usageFlags)) {
300 if (motorOnlyOutputs < motorCount) {
301 timHw->usageFlags &= ~TIM_USE_SERVO;
302 timHw->usageFlags |= TIM_USE_MOTOR;
303 motorOnlyOutputs++;
304 motorOnlyOutputs += pwmClaimTimer(timHw->tim, timHw->usageFlags);
305 } else {
306 timHw->usageFlags &= ~TIM_USE_MOTOR;
307 pwmClaimTimer(timHw->tim, timHw->usageFlags);
313 void pwmBuildTimerOutputList(timMotorServoHardware_t * timOutputs, bool isMixerUsingServos)
315 UNUSED(isMixerUsingServos);
316 timOutputs->maxTimMotorCount = 0;
317 timOutputs->maxTimServoCount = 0;
319 uint8_t motorCount = getMotorCount();
320 uint8_t motorIdx = 0;
322 pwmEnsureEnoughtMotors(motorCount);
324 for (int idx = 0; idx < timerHardwareCount; idx++) {
325 timerHardware_t *timHw = &timerHardware[idx];
327 int type = MAP_TO_NONE;
329 // Check for known conflicts (i.e. UART, LEDSTRIP, Rangefinder and ADC)
330 if (checkPwmTimerConflicts(timHw)) {
331 LOG_WARNING(PWM, "Timer output %d skipped", idx);
332 continue;
335 // Make sure first motorCount motor outputs get assigned to motor
336 if (TIM_IS_MOTOR(timHw->usageFlags) && (motorIdx < motorCount)) {
337 timHw->usageFlags &= ~TIM_USE_SERVO;
338 pwmClaimTimer(timHw->tim, timHw->usageFlags);
339 motorIdx += 1;
342 if (TIM_IS_SERVO(timHw->usageFlags) && !pwmHasMotorOnTimer(timOutputs, timHw->tim)) {
343 type = MAP_TO_SERVO_OUTPUT;
344 } else if (TIM_IS_MOTOR(timHw->usageFlags) && !pwmHasServoOnTimer(timOutputs, timHw->tim)) {
345 type = MAP_TO_MOTOR_OUTPUT;
346 } else if (TIM_IS_LED(timHw->usageFlags) && !pwmHasMotorOnTimer(timOutputs, timHw->tim) && !pwmHasServoOnTimer(timOutputs, timHw->tim)) {
347 type = MAP_TO_LED_OUTPUT;
350 switch(type) {
351 case MAP_TO_MOTOR_OUTPUT:
352 timHw->usageFlags &= TIM_USE_MOTOR;
353 timOutputs->timMotors[timOutputs->maxTimMotorCount++] = timHw;
354 pwmClaimTimer(timHw->tim, timHw->usageFlags);
355 break;
356 case MAP_TO_SERVO_OUTPUT:
357 timHw->usageFlags &= TIM_USE_SERVO;
358 timOutputs->timServos[timOutputs->maxTimServoCount++] = timHw;
359 pwmClaimTimer(timHw->tim, timHw->usageFlags);
360 break;
361 case MAP_TO_LED_OUTPUT:
362 timHw->usageFlags &= TIM_USE_LED;
363 pwmClaimTimer(timHw->tim, timHw->usageFlags);
364 break;
365 default:
366 break;
371 static bool motorsUseHardwareTimers(void)
373 return getMotorProtocolProperties(motorConfig()->motorPwmProtocol)->usesHwTimer;
376 static bool servosUseHardwareTimers(void)
378 return servoConfig()->servo_protocol == SERVO_TYPE_PWM ||
379 servoConfig()->servo_protocol == SERVO_TYPE_SBUS_PWM;
382 static void pwmInitMotors(timMotorServoHardware_t * timOutputs)
384 const int motorCount = getMotorCount();
386 // Check if too many motors
387 if (motorCount > MAX_MOTORS) {
388 pwmInitError = PWM_INIT_ERROR_TOO_MANY_MOTORS;
389 LOG_ERROR(PWM, "Too many motors. Mixer requested %d, max %d", motorCount, MAX_MOTORS);
390 return;
393 // Do the pre-configuration. For motors w/o hardware timers this should be sufficient
394 pwmMotorPreconfigure();
396 // Now if we need to configure individual motor outputs - do that
397 if (!motorsUseHardwareTimers()) {
398 LOG_INFO(PWM, "Skipped timer init for motors");
399 return;
402 // If mixer requests more motors than we have timer outputs - throw an error
403 if (motorCount > timOutputs->maxTimMotorCount) {
404 pwmInitError = PWM_INIT_ERROR_NOT_ENOUGH_MOTOR_OUTPUTS;
405 LOG_ERROR(PWM, "Not enough motor outputs. Mixer requested %d, outputs %d", motorCount, timOutputs->maxTimMotorCount);
406 return;
409 // Finally initialize individual motor outputs
410 for (int idx = 0; idx < motorCount; idx++) {
411 const timerHardware_t *timHw = timOutputs->timMotors[idx];
412 if (!pwmMotorConfig(timHw, idx, feature(FEATURE_PWM_OUTPUT_ENABLE))) {
413 pwmInitError = PWM_INIT_ERROR_TIMER_INIT_FAILED;
414 LOG_ERROR(PWM, "Timer allocation failed for motor %d", idx);
415 return;
420 static void pwmInitServos(timMotorServoHardware_t * timOutputs)
422 const int servoCount = getServoCount();
424 if (!isMixerUsingServos()) {
425 LOG_INFO(PWM, "Mixer does not use servos");
426 return;
429 // Check if too many servos
430 if (servoCount > MAX_SERVOS) {
431 pwmInitError = PWM_INIT_ERROR_TOO_MANY_SERVOS;
432 LOG_ERROR(PWM, "Too many servos. Mixer requested %d, max %d", servoCount, MAX_SERVOS);
433 return;
436 // Do the pre-configuration. This should configure non-timer PWM drivers
437 pwmServoPreconfigure();
439 // Check if we need to init timer output for servos
440 if (!servosUseHardwareTimers()) {
441 // External PWM servo driver
442 LOG_INFO(PWM, "Skipped timer init for servos - using external servo driver");
443 return;
447 // If mixer requests more servos than we have timer outputs - throw an error
448 uint16_t maxServos = timOutputs->maxTimServoCount;
449 if (servoConfig()->servo_protocol == SERVO_TYPE_SBUS_PWM) {
450 maxServos = MAX(SERVO_SBUS_MAX_SERVOS, timOutputs->maxTimServoCount);
453 if (servoCount > maxServos) {
454 pwmInitError = PWM_INIT_ERROR_NOT_ENOUGH_SERVO_OUTPUTS;
455 LOG_ERROR(PWM, "Too many servos. Mixer requested %d, timer outputs %d", servoCount, timOutputs->maxTimServoCount);
456 return;
459 // Configure individual servo outputs
460 for (int idx = 0; idx < MIN(servoCount, timOutputs->maxTimServoCount); idx++) {
461 const timerHardware_t *timHw = timOutputs->timServos[idx];
463 if (!pwmServoConfig(timHw, idx, servoConfig()->servoPwmRate, servoConfig()->servoCenterPulse, feature(FEATURE_PWM_OUTPUT_ENABLE))) {
464 pwmInitError = PWM_INIT_ERROR_TIMER_INIT_FAILED;
465 LOG_ERROR(PWM, "Timer allocation failed for servo %d", idx);
466 return;
472 bool pwmMotorAndServoInit(void)
474 timMotorServoHardware_t timOutputs;
476 // Build temporary timer mappings for motor and servo
477 pwmBuildTimerOutputList(&timOutputs, isMixerUsingServos());
479 // At this point we have built tables of timers suitable for motor and servo mappings
480 // Now we can actually initialize them according to motor/servo count from mixer
481 pwmInitMotors(&timOutputs);
482 pwmInitServos(&timOutputs);
484 return (pwmInitError == PWM_INIT_ERROR_NONE);
487 #endif