1
0
Fork 0
mirror of https://github.com/iNavFlight/inav.git synced 2025-07-26 01:35:35 +03:00
inav/src/main/drivers/pwm_output.c
2018-07-08 10:28:32 +02:00

273 lines
7.5 KiB
C

/*
* This file is part of Cleanflight.
*
* Cleanflight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cleanflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include "platform.h"
#include "build/debug.h"
#include "drivers/io.h"
#include "timer.h"
#include "pwm_mapping.h"
#include "pwm_output.h"
#include "io_pca9685.h"
#include "io/pwmdriver_i2c.h"
#include "config/feature.h"
#include "fc/config.h"
#include "fc/runtime_config.h"
#define MULTISHOT_5US_PW (MULTISHOT_TIMER_MHZ * 5)
#define MULTISHOT_20US_MULT (MULTISHOT_TIMER_MHZ * 20 / 1000.0f)
typedef void (*pwmWriteFuncPtr)(uint8_t index, uint16_t value); // function pointer used to write motors
typedef struct {
volatile timCCR_t *ccr;
TIM_TypeDef *tim;
uint16_t period;
pwmWriteFuncPtr pwmWritePtr;
} pwmOutputPort_t;
static pwmOutputPort_t pwmOutputPorts[MAX_PWM_OUTPUT_PORTS];
static pwmOutputPort_t *motors[MAX_PWM_MOTORS];
static pwmOutputPort_t *servos[MAX_PWM_SERVOS];
#ifdef BEEPER_PWM
static pwmOutputPort_t beeperPwmPort;
static pwmOutputPort_t *beeperPwm;
static uint16_t beeperFrequency = 0;
#endif
static uint8_t allocatedOutputPortCount = 0;
static bool pwmMotorsEnabled = true;
static void pwmOutConfigTimer(pwmOutputPort_t * p, const timerHardware_t *timerHardware, uint8_t mhz, uint16_t period, uint16_t value)
{
timerConfigBase(timerHardware->tim, period, mhz);
timerPWMConfigChannel(timerHardware->tim, timerHardware->channel, timerHardware->output & TIMER_OUTPUT_N_CHANNEL, timerHardware->output & TIMER_OUTPUT_INVERTED, value);
if (timerHardware->output & TIMER_OUTPUT_ENABLED) {
timerPWMStart(timerHardware->tim, timerHardware->channel, timerHardware->output & TIMER_OUTPUT_N_CHANNEL);
}
timerEnable(timerHardware->tim);
p->ccr = timerCCR(timerHardware->tim, timerHardware->channel);
p->period = period;
p->tim = timerHardware->tim;
}
static pwmOutputPort_t *pwmOutConfigMotor(const timerHardware_t *timerHardware, uint8_t mhz, uint16_t period, uint16_t value, bool enableOutput)
{
if (allocatedOutputPortCount >= MAX_PWM_OUTPUT_PORTS) {
DEBUG_TRACE("Attempt to allocate PWM output beyond MAX_PWM_OUTPUT_PORTS");
return NULL;
}
pwmOutputPort_t *p = &pwmOutputPorts[allocatedOutputPortCount++];
const IO_t io = IOGetByTag(timerHardware->tag);
IOInit(io, OWNER_MOTOR, RESOURCE_OUTPUT, allocatedOutputPortCount);
if (enableOutput) {
IOConfigGPIOAF(io, IOCFG_AF_PP, timerHardware->alternateFunction);
}
else {
// If PWM outputs are disabled - configure as GPIO and drive low
IOConfigGPIO(io, IOCFG_OUT_OD);
IOLo(io);
}
pwmOutConfigTimer(p, timerHardware, mhz, period, value);
return p;
}
static void pwmWriteBrushed(uint8_t index, uint16_t value)
{
*motors[index]->ccr = (value - 1000) * motors[index]->period / 1000;
}
#ifndef BRUSHED_MOTORS
static void pwmWriteStandard(uint8_t index, uint16_t value)
{
*motors[index]->ccr = value;
}
static void pwmWriteOneShot125(uint8_t index, uint16_t value)
{
*motors[index]->ccr = lrintf((float)(value * ONESHOT125_TIMER_MHZ/8.0f));
}
static void pwmWriteOneShot42(uint8_t index, uint16_t value)
{
*motors[index]->ccr = lrintf((float)(value * ONESHOT42_TIMER_MHZ/24.0f));
}
static void pwmWriteMultiShot(uint8_t index, uint16_t value)
{
*motors[index]->ccr = lrintf(((float)(value-1000) * MULTISHOT_20US_MULT) + MULTISHOT_5US_PW);
}
#endif
void pwmWriteMotor(uint8_t index, uint16_t value)
{
if (motors[index] && index < MAX_MOTORS && pwmMotorsEnabled) {
motors[index]->pwmWritePtr(index, value);
}
}
void pwmShutdownPulsesForAllMotors(uint8_t motorCount)
{
for (int index = 0; index < motorCount; index++) {
// Set the compare register to 0, which stops the output pulsing if the timer overflows
*motors[index]->ccr = 0;
}
}
void pwmDisableMotors(void)
{
pwmMotorsEnabled = false;
}
void pwmEnableMotors(void)
{
pwmMotorsEnabled = true;
}
bool isMotorBrushed(uint16_t motorPwmRate)
{
return (motorPwmRate > 500);
}
bool pwmMotorConfig(const timerHardware_t *timerHardware, uint8_t motorIndex, uint16_t motorPwmRate, uint16_t idlePulse, motorPwmProtocolTypes_e proto, bool enableOutput)
{
uint32_t timerMhzCounter;
pwmWriteFuncPtr pwmWritePtr;
switch (proto) {
#ifdef BRUSHED_MOTORS
default:
#endif
case PWM_TYPE_BRUSHED:
timerMhzCounter = PWM_BRUSHED_TIMER_MHZ;
pwmWritePtr = pwmWriteBrushed;
idlePulse = 0;
break;
#ifndef BRUSHED_MOTORS
case PWM_TYPE_ONESHOT125:
timerMhzCounter = ONESHOT125_TIMER_MHZ;
pwmWritePtr = pwmWriteOneShot125;
break;
case PWM_TYPE_ONESHOT42:
timerMhzCounter = ONESHOT42_TIMER_MHZ;
pwmWritePtr = pwmWriteOneShot42;
break;
case PWM_TYPE_MULTISHOT:
timerMhzCounter = MULTISHOT_TIMER_MHZ;
pwmWritePtr = pwmWriteMultiShot;
break;
case PWM_TYPE_STANDARD:
default:
timerMhzCounter = PWM_TIMER_MHZ;
pwmWritePtr = pwmWriteStandard;
break;
#endif
}
const uint32_t hz = timerMhzCounter * 1000000;
pwmOutputPort_t * port = pwmOutConfigMotor(timerHardware, timerMhzCounter, hz / motorPwmRate, idlePulse, enableOutput);
if (port) {
motors[motorIndex] = port;
motors[motorIndex]->pwmWritePtr = pwmWritePtr;
return true;
}
return false;
}
bool pwmServoConfig(const timerHardware_t *timerHardware, uint8_t servoIndex, uint16_t servoPwmRate, uint16_t servoCenterPulse, bool enableOutput)
{
pwmOutputPort_t * port = pwmOutConfigMotor(timerHardware, PWM_TIMER_MHZ, 1000000 / servoPwmRate, servoCenterPulse, enableOutput);
if (port) {
servos[servoIndex] = port;
return true;
}
return false;
}
void pwmWriteServo(uint8_t index, uint16_t value)
{
#ifdef USE_PMW_SERVO_DRIVER
/*
* If PCA9685 is enabled and but not detected, we do not want to write servo
* output anywhere
*/
if (feature(FEATURE_PWM_SERVO_DRIVER) && STATE(PWM_DRIVER_AVAILABLE)) {
pwmDriverSetPulse(index, value);
} else if (!feature(FEATURE_PWM_SERVO_DRIVER) && servos[index] && index < MAX_SERVOS) {
*servos[index]->ccr = value;
}
#else
if (servos[index] && index < MAX_SERVOS) {
*servos[index]->ccr = value;
}
#endif
}
#ifdef BEEPER_PWM
void pwmWriteBeeper(bool onoffBeep)
{
if (beeperPwm == NULL)
return;
if (onoffBeep == true) {
*beeperPwm->ccr = (1000000 / beeperFrequency) / 2;
} else {
*beeperPwm->ccr = 0;
}
}
void beeperPwmInit(ioTag_t tag, uint16_t frequency)
{
const timerHardware_t *timer = timerGetByTag(tag, TIM_USE_BEEPER);
if (timer) {
beeperPwm = &beeperPwmPort;
beeperFrequency = frequency;
IOConfigGPIOAF(IOGetByTag(tag), IOCFG_AF_PP, timer->alternateFunction);
pwmOutConfigTimer(beeperPwm, timer, PWM_TIMER_MHZ, 1000000 / beeperFrequency, (1000000 / beeperFrequency) / 2);
}
else {
beeperPwm = NULL;
}
}
#endif