blob: c668039b2eacfb9ed266def95610a2c24798403c [file] [log] [blame]
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "hal/PWM.h"
#include <algorithm>
#include <cmath>
#include "ConstantsInternal.h"
#include "DigitalInternal.h"
#include "HALInitializer.h"
#include "HALInternal.h"
#include "PortsInternal.h"
#include "hal/handles/HandlesInternal.h"
#include "mockdata/PWMDataInternal.h"
using namespace hal;
namespace hal::init {
void InitializePWM() {}
} // namespace hal::init
static inline int32_t GetMaxPositivePwm(DigitalPort* port) {
return port->maxPwm;
}
static inline int32_t GetMinPositivePwm(DigitalPort* port) {
if (port->eliminateDeadband) {
return port->deadbandMaxPwm;
} else {
return port->centerPwm + 1;
}
}
static inline int32_t GetCenterPwm(DigitalPort* port) {
return port->centerPwm;
}
static inline int32_t GetMaxNegativePwm(DigitalPort* port) {
if (port->eliminateDeadband) {
return port->deadbandMinPwm;
} else {
return port->centerPwm - 1;
}
}
static inline int32_t GetMinNegativePwm(DigitalPort* port) {
return port->minPwm;
}
static inline int32_t GetPositiveScaleFactor(DigitalPort* port) {
return GetMaxPositivePwm(port) - GetMinPositivePwm(port);
} ///< The scale for positive speeds.
static inline int32_t GetNegativeScaleFactor(DigitalPort* port) {
return GetMaxNegativePwm(port) - GetMinNegativePwm(port);
} ///< The scale for negative speeds.
static inline int32_t GetFullRangeScaleFactor(DigitalPort* port) {
return GetMaxPositivePwm(port) - GetMinNegativePwm(port);
} ///< The scale for positions.
extern "C" {
HAL_DigitalHandle HAL_InitializePWMPort(HAL_PortHandle portHandle,
const char* allocationLocation,
int32_t* status) {
hal::init::CheckInit();
int16_t channel = getPortHandleChannel(portHandle);
if (channel == InvalidHandleIndex) {
*status = RESOURCE_OUT_OF_RANGE;
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0,
kNumPWMChannels, channel);
return HAL_kInvalidHandle;
}
uint8_t origChannel = static_cast<uint8_t>(channel);
if (origChannel < kNumPWMHeaders) {
channel += kNumDigitalChannels; // remap Headers to end of allocations
} else {
channel = remapMXPPWMChannel(channel) + 10; // remap MXP to proper channel
}
HAL_DigitalHandle handle;
auto port = digitalChannelHandles->Allocate(channel, HAL_HandleEnum::PWM,
&handle, status);
if (*status != 0) {
if (port) {
hal::SetLastErrorPreviouslyAllocated(status, "PWM or DIO", channel,
port->previousAllocation);
} else {
hal::SetLastErrorIndexOutOfRange(status, "Invalid Index for PWM", 0,
kNumPWMChannels, channel);
}
return HAL_kInvalidHandle; // failed to allocate. Pass error back.
}
port->channel = origChannel;
SimPWMData[origChannel].initialized = true;
// Defaults to allow an always valid config.
HAL_SetPWMConfigMicroseconds(handle, 2000, 1501, 1500, 1499, 1000, status);
port->previousAllocation = allocationLocation ? allocationLocation : "";
return handle;
}
void HAL_FreePWMPort(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
SimPWMData[port->channel].initialized = false;
digitalChannelHandles->Free(pwmPortHandle, HAL_HandleEnum::PWM);
}
HAL_Bool HAL_CheckPWMChannel(int32_t channel) {
return channel < kNumPWMChannels && channel >= 0;
}
void HAL_SetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle, int32_t max,
int32_t deadbandMax, int32_t center,
int32_t deadbandMin, int32_t min,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
port->maxPwm = max;
port->deadbandMaxPwm = deadbandMax;
port->deadbandMinPwm = deadbandMin;
port->centerPwm = center;
port->minPwm = min;
port->configSet = true;
}
void HAL_GetPWMConfigMicroseconds(HAL_DigitalHandle pwmPortHandle,
int32_t* maxPwm, int32_t* deadbandMaxPwm,
int32_t* centerPwm, int32_t* deadbandMinPwm,
int32_t* minPwm, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
*maxPwm = port->maxPwm;
*deadbandMaxPwm = port->deadbandMaxPwm;
*deadbandMinPwm = port->deadbandMinPwm;
*centerPwm = port->centerPwm;
*minPwm = port->minPwm;
}
void HAL_SetPWMEliminateDeadband(HAL_DigitalHandle pwmPortHandle,
HAL_Bool eliminateDeadband, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
port->eliminateDeadband = eliminateDeadband;
}
HAL_Bool HAL_GetPWMEliminateDeadband(HAL_DigitalHandle pwmPortHandle,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return false;
}
return port->eliminateDeadband;
}
void HAL_SetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
int32_t value, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
SimPWMData[port->channel].pulseMicrosecond = value;
DigitalPort* dPort = port.get();
double speed = 0.0;
if (value == kPwmDisabled) {
speed = 0.0;
} else if (value > GetMaxPositivePwm(dPort)) {
speed = 1.0;
} else if (value < GetMinNegativePwm(dPort)) {
speed = -1.0;
} else if (value > GetMinPositivePwm(dPort)) {
speed = static_cast<double>(value - GetMinPositivePwm(dPort)) /
static_cast<double>(GetPositiveScaleFactor(dPort));
} else if (value < GetMaxNegativePwm(dPort)) {
speed = static_cast<double>(value - GetMaxNegativePwm(dPort)) /
static_cast<double>(GetNegativeScaleFactor(dPort));
} else {
speed = 0.0;
}
SimPWMData[port->channel].speed = speed;
double pos = 0.0;
if (value < GetMinNegativePwm(dPort)) {
pos = 0.0;
} else if (value > GetMaxPositivePwm(dPort)) {
pos = 1.0;
} else {
pos = static_cast<double>(value - GetMinNegativePwm(dPort)) /
static_cast<double>(GetFullRangeScaleFactor(dPort));
}
SimPWMData[port->channel].position = pos;
}
void HAL_SetPWMSpeed(HAL_DigitalHandle pwmPortHandle, double speed,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
if (!port->configSet) {
*status = INCOMPATIBLE_STATE;
return;
}
if (std::isfinite(speed)) {
speed = std::clamp(speed, -1.0, 1.0);
} else {
speed = 0.0;
}
DigitalPort* dPort = port.get();
// calculate the desired output pwm value by scaling the speed appropriately
int32_t rawValue;
if (speed == 0.0) {
rawValue = GetCenterPwm(dPort);
} else if (speed > 0.0) {
rawValue =
std::lround(speed * static_cast<double>(GetPositiveScaleFactor(dPort)) +
static_cast<double>(GetMinPositivePwm(dPort)));
} else {
rawValue =
std::lround(speed * static_cast<double>(GetNegativeScaleFactor(dPort)) +
static_cast<double>(GetMaxNegativePwm(dPort)));
}
if (!((rawValue >= GetMinNegativePwm(dPort)) &&
(rawValue <= GetMaxPositivePwm(dPort))) ||
rawValue == kPwmDisabled) {
*status = HAL_PWM_SCALE_ERROR;
return;
}
HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
}
void HAL_SetPWMPosition(HAL_DigitalHandle pwmPortHandle, double pos,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
if (!port->configSet) {
*status = INCOMPATIBLE_STATE;
return;
}
if (pos < 0.0) {
pos = 0.0;
} else if (pos > 1.0) {
pos = 1.0;
}
DigitalPort* dPort = port.get();
// note, need to perform the multiplication below as floating point before
// converting to int
int32_t rawValue = static_cast<int32_t>(
(pos * static_cast<double>(GetFullRangeScaleFactor(dPort))) +
GetMinNegativePwm(dPort));
if (rawValue == kPwmDisabled) {
*status = HAL_PWM_SCALE_ERROR;
return;
}
HAL_SetPWMPulseTimeMicroseconds(pwmPortHandle, rawValue, status);
}
void HAL_SetPWMDisabled(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
SimPWMData[port->channel].pulseMicrosecond = 0;
SimPWMData[port->channel].position = 0;
SimPWMData[port->channel].speed = 0;
}
int32_t HAL_GetPWMPulseTimeMicroseconds(HAL_DigitalHandle pwmPortHandle,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return 0;
}
return SimPWMData[port->channel].pulseMicrosecond;
}
double HAL_GetPWMSpeed(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return 0;
}
if (!port->configSet) {
*status = INCOMPATIBLE_STATE;
return 0;
}
double speed = SimPWMData[port->channel].speed;
if (speed > 1) {
speed = 1;
}
if (speed < -1) {
speed = -1;
}
return speed;
}
double HAL_GetPWMPosition(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return 0;
}
if (!port->configSet) {
*status = INCOMPATIBLE_STATE;
return 0;
}
double position = SimPWMData[port->channel].position;
if (position > 1) {
position = 1;
}
if (position < 0) {
position = 0;
}
return position;
}
void HAL_LatchPWMZero(HAL_DigitalHandle pwmPortHandle, int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
SimPWMData[port->channel].zeroLatch = true;
SimPWMData[port->channel].zeroLatch = false;
}
void HAL_SetPWMAlwaysHighMode(HAL_DigitalHandle pwmPortHandle,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
SimPWMData[port->channel].pulseMicrosecond = 0xFFFF;
SimPWMData[port->channel].position = 0xFFFF;
SimPWMData[port->channel].speed = 0xFFFF;
}
void HAL_SetPWMPeriodScale(HAL_DigitalHandle pwmPortHandle, int32_t squelchMask,
int32_t* status) {
auto port = digitalChannelHandles->Get(pwmPortHandle, HAL_HandleEnum::PWM);
if (port == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
SimPWMData[port->channel].periodScale = squelchMask;
}
int32_t HAL_GetPWMLoopTiming(int32_t* status) {
return kExpectedLoopTiming;
}
uint64_t HAL_GetPWMCycleStartTime(int32_t* status) {
return 0;
}
} // extern "C"