Merge pull request #1518 from blckmn/simplified_owners
[betaflight.git] / src / main / drivers / pwm_output.c
blob4102a8655e073f6b218dd358ba6118750d89d33a
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 <math.h>
22 #include "platform.h"
24 #include "io.h"
25 #include "timer.h"
26 #include "pwm_output.h"
28 #define MULTISHOT_5US_PW (MULTISHOT_TIMER_MHZ * 5)
29 #define MULTISHOT_20US_MULT (MULTISHOT_TIMER_MHZ * 20 / 1000.0f)
31 static pwmOutputPort_t motors[MAX_SUPPORTED_MOTORS];
32 static pwmCompleteWriteFuncPtr pwmCompleteWritePtr = NULL;
34 #ifdef USE_SERVOS
35 static pwmOutputPort_t servos[MAX_SUPPORTED_SERVOS];
36 #endif
38 bool pwmMotorsEnabled = true;
40 static void pwmOCConfig(TIM_TypeDef *tim, uint8_t channel, uint16_t value, uint8_t output)
42 TIM_OCInitTypeDef TIM_OCInitStructure;
44 TIM_OCStructInit(&TIM_OCInitStructure);
45 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
47 if (output & TIMER_OUTPUT_N_CHANNEL) {
48 TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
49 TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
50 TIM_OCInitStructure.TIM_OCNPolarity = (output & TIMER_OUTPUT_INVERTED) ? TIM_OCNPolarity_High : TIM_OCNPolarity_Low;
51 } else {
52 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
53 TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
54 TIM_OCInitStructure.TIM_OCPolarity = (output & TIMER_OUTPUT_INVERTED) ? TIM_OCPolarity_Low : TIM_OCPolarity_High;
56 TIM_OCInitStructure.TIM_Pulse = value;
58 timerOCInit(tim, channel, &TIM_OCInitStructure);
59 timerOCPreloadConfig(tim, channel, TIM_OCPreload_Enable);
62 static void pwmOutConfig(pwmOutputPort_t *port, const timerHardware_t *timerHardware, uint8_t mhz, uint16_t period, uint16_t value)
64 configTimeBase(timerHardware->tim, period, mhz);
65 pwmOCConfig(timerHardware->tim, timerHardware->channel, value, timerHardware->output);
67 if (timerHardware->output & TIMER_OUTPUT_ENABLED) {
68 TIM_CtrlPWMOutputs(timerHardware->tim, ENABLE);
70 TIM_Cmd(timerHardware->tim, ENABLE);
72 port->ccr = timerChCCR(timerHardware);
73 port->period = period;
74 port->tim = timerHardware->tim;
76 *port->ccr = 0;
79 static void pwmWriteBrushed(uint8_t index, uint16_t value)
81 *motors[index].ccr = (value - 1000) * motors[index].period / 1000;
84 static void pwmWriteStandard(uint8_t index, uint16_t value)
86 *motors[index].ccr = value;
89 static void pwmWriteOneShot125(uint8_t index, uint16_t value)
91 *motors[index].ccr = lrintf((float)(value * ONESHOT125_TIMER_MHZ/8.0f));
94 static void pwmWriteOneShot42(uint8_t index, uint16_t value)
96 *motors[index].ccr = lrintf((float)(value * ONESHOT42_TIMER_MHZ/24.0f));
99 static void pwmWriteMultiShot(uint8_t index, uint16_t value)
101 *motors[index].ccr = lrintf(((float)(value-1000) * MULTISHOT_20US_MULT) + MULTISHOT_5US_PW);
104 void pwmWriteMotor(uint8_t index, uint16_t value)
106 if (index < MAX_SUPPORTED_MOTORS && pwmMotorsEnabled && motors[index].pwmWritePtr) {
107 motors[index].pwmWritePtr(index, value);
111 void pwmShutdownPulsesForAllMotors(uint8_t motorCount)
113 for (int index = 0; index < motorCount; index++) {
114 // Set the compare register to 0, which stops the output pulsing if the timer overflows
115 if (motors[index].ccr) {
116 *motors[index].ccr = 0;
121 void pwmDisableMotors(void)
123 pwmShutdownPulsesForAllMotors(MAX_SUPPORTED_MOTORS);
124 pwmMotorsEnabled = false;
127 void pwmEnableMotors(void)
129 pwmMotorsEnabled = true;
132 static void pwmCompleteOneshotMotorUpdate(uint8_t motorCount)
134 for (int index = 0; index < motorCount; index++) {
135 bool overflowed = false;
136 // If we have not already overflowed this timer
137 for (int j = 0; j < index; j++) {
138 if (motors[j].tim == motors[index].tim) {
139 overflowed = true;
140 break;
143 if (!overflowed) {
144 timerForceOverflow(motors[index].tim);
146 // Set the compare register to 0, which stops the output pulsing if the timer overflows before the main loop completes again.
147 // This compare register will be set to the output value on the next main loop.
148 *motors[index].ccr = 0;
152 void pwmCompleteMotorUpdate(uint8_t motorCount)
154 if (pwmCompleteWritePtr) {
155 pwmCompleteWritePtr(motorCount);
159 void motorInit(const motorConfig_t *motorConfig, uint16_t idlePulse, uint8_t motorCount)
161 uint32_t timerMhzCounter = 0;
162 pwmWriteFuncPtr pwmWritePtr;
163 bool useUnsyncedPwm = motorConfig->useUnsyncedPwm;
164 bool isDigital = false;
166 switch (motorConfig->motorPwmProtocol) {
167 default:
168 case PWM_TYPE_ONESHOT125:
169 timerMhzCounter = ONESHOT125_TIMER_MHZ;
170 pwmWritePtr = pwmWriteOneShot125;
171 break;
172 case PWM_TYPE_ONESHOT42:
173 timerMhzCounter = ONESHOT42_TIMER_MHZ;
174 pwmWritePtr = pwmWriteOneShot42;
175 break;
176 case PWM_TYPE_MULTISHOT:
177 timerMhzCounter = MULTISHOT_TIMER_MHZ;
178 pwmWritePtr = pwmWriteMultiShot;
179 break;
180 case PWM_TYPE_BRUSHED:
181 timerMhzCounter = PWM_BRUSHED_TIMER_MHZ;
182 pwmWritePtr = pwmWriteBrushed;
183 useUnsyncedPwm = true;
184 idlePulse = 0;
185 break;
186 case PWM_TYPE_STANDARD:
187 timerMhzCounter = PWM_TIMER_MHZ;
188 pwmWritePtr = pwmWriteStandard;
189 useUnsyncedPwm = true;
190 idlePulse = 0;
191 break;
192 #ifdef USE_DSHOT
193 case PWM_TYPE_DSHOT600:
194 case PWM_TYPE_DSHOT300:
195 case PWM_TYPE_DSHOT150:
196 pwmCompleteWritePtr = pwmCompleteDigitalMotorUpdate;
197 isDigital = true;
198 break;
199 #endif
202 if (!useUnsyncedPwm && !isDigital) {
203 pwmCompleteWritePtr = pwmCompleteOneshotMotorUpdate;
206 for (int motorIndex = 0; motorIndex < MAX_SUPPORTED_MOTORS && motorIndex < motorCount; motorIndex++) {
207 const ioTag_t tag = motorConfig->ioTags[motorIndex];
209 if (!tag) {
210 break;
213 const timerHardware_t *timerHardware = timerGetByTag(tag, TIM_USE_ANY);
215 if (timerHardware == NULL) {
216 /* flag failure and disable ability to arm */
217 break;
220 motors[motorIndex].io = IOGetByTag(tag);
222 #ifdef USE_DSHOT
223 if (isDigital) {
224 pwmDigitalMotorHardwareConfig(timerHardware, motorIndex, motorConfig->motorPwmProtocol);
225 motors[motorIndex].pwmWritePtr = pwmWriteDigital;
226 motors[motorIndex].enabled = true;
227 continue;
229 #endif
231 IOInit(motors[motorIndex].io, OWNER_MOTOR, RESOURCE_INDEX(motorIndex));
232 IOConfigGPIO(motors[motorIndex].io, IOCFG_AF_PP);
234 motors[motorIndex].pwmWritePtr = pwmWritePtr;
235 if (useUnsyncedPwm) {
236 const uint32_t hz = timerMhzCounter * 1000000;
237 pwmOutConfig(&motors[motorIndex], timerHardware, timerMhzCounter, hz / motorConfig->motorPwmRate, idlePulse);
238 } else {
239 pwmOutConfig(&motors[motorIndex], timerHardware, timerMhzCounter, 0xFFFF, 0);
241 motors[motorIndex].enabled = true;
245 bool pwmIsSynced(void)
247 return pwmCompleteWritePtr != NULL;
250 pwmOutputPort_t *pwmGetMotors(void)
252 return motors;
255 #ifdef USE_SERVOS
256 void pwmWriteServo(uint8_t index, uint16_t value)
258 if (index < MAX_SUPPORTED_SERVOS && servos[index].ccr) {
259 *servos[index].ccr = value;
263 void servoInit(const servoConfig_t *servoConfig)
265 for (uint8_t servoIndex = 0; servoIndex < MAX_SUPPORTED_SERVOS; servoIndex++) {
266 const ioTag_t tag = servoConfig->ioTags[servoIndex];
268 if (!tag) {
269 break;
272 servos[servoIndex].io = IOGetByTag(tag);
274 IOInit(servos[servoIndex].io, OWNER_SERVO, RESOURCE_INDEX(servoIndex));
275 IOConfigGPIO(servos[servoIndex].io, IOCFG_AF_PP);
277 const timerHardware_t *timer = timerGetByTag(tag, TIM_USE_ANY);
279 if (timer == NULL) {
280 /* flag failure and disable ability to arm */
281 break;
284 pwmOutConfig(&servos[servoIndex], timer, PWM_TIMER_MHZ, 1000000 / servoConfig->servoPwmRate, servoConfig->servoCenterPulse);
285 servos[servoIndex].enabled = true;
289 #endif