blob: 000cf86ebc0beea5ebd98e38b735fb3413228dc0 [file] [log] [blame]
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in $(WIND_BASE)/WPILib. */
/*----------------------------------------------------------------------------*/
#include "DriverStationEnhancedIO.h"
#include "NetworkCommunication/UsageReporting.h"
#include "Synchronized.h"
#include "WPIErrors.h"
#include <strLib.h>
/**
* DriverStationEnhancedIO contructor.
*
* This is only called once when the DriverStation constructor is called.
*/
DriverStationEnhancedIO::DriverStationEnhancedIO()
: m_inputValid (false)
, m_outputValid (false)
, m_configChanged (false)
, m_requestEnhancedEnable (false)
{
bzero((char*)&m_inputData, sizeof(m_inputData));
bzero((char*)&m_outputData, sizeof(m_outputData));
m_outputData.size = sizeof(m_outputData) - 1;
m_outputData.id = kOutputBlockID;
// Expected to be active low, so initialize inactive.
m_outputData.data.fixed_digital_out = 0x3;
m_inputDataSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
m_outputDataSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
m_encoderOffsets[0] = 0;
m_encoderOffsets[1] = 0;
}
/**
* DriverStationEnhancedIO destructor.
*
* Called only when the DriverStation class is destroyed.
*/
DriverStationEnhancedIO::~DriverStationEnhancedIO()
{
semDelete(m_outputDataSemaphore);
semDelete(m_inputDataSemaphore);
}
/**
* Called by the DriverStation class when data is available.
* This function will set any modified configuration / output,
* then read the input and configuration from the IO.
*/
void DriverStationEnhancedIO::UpdateData()
{
INT32 retVal;
{
status_block_t tempOutputData;
Synchronized sync(m_outputDataSemaphore);
if (m_outputValid || m_configChanged || m_requestEnhancedEnable)
{
m_outputData.flags = kStatusValid;
if (m_requestEnhancedEnable)
{
// Someone called one of the get config APIs, but we are not in enhanced mode.
m_outputData.flags |= kForceEnhancedMode;
}
if (m_configChanged)
{
if (!m_outputValid)
{
// Someone called one of the set config APIs, but we are not in enhanced mode.
m_outputData.flags |= kForceEnhancedMode;
}
m_outputData.flags |= kStatusConfigChanged;
}
overrideIOConfig((char*)&m_outputData, 5);
}
retVal = getDynamicControlData(kOutputBlockID, (char*)&tempOutputData, sizeof(status_block_t), 5);
if (retVal == 0)
{
if (m_outputValid)
{
if (m_configChanged)
{
// If our config change made the round trip then clear the flag.
if (IsConfigEqual(tempOutputData, m_outputData))
{
m_configChanged = false;
}
}
else
{
// TODO: This won't work until artf1128 is fixed
//if (tempOutputData.flags & kStatusConfigChanged)
{
// Configuration was updated on the DS, so update our local cache.
MergeConfigIntoOutput(tempOutputData, m_outputData);
}
}
}
else
{
// Initialize the local cache.
MergeConfigIntoOutput(tempOutputData, m_outputData);
}
m_requestEnhancedEnable = false;
m_outputValid = true;
}
else
{
m_outputValid = false;
m_inputValid = false;
}
}
{
Synchronized sync(m_inputDataSemaphore);
control_block_t tempInputData;
retVal = getDynamicControlData(kInputBlockID, (char*)&tempInputData, sizeof(control_block_t), 5);
if (retVal == 0 && tempInputData.data.api_version == kSupportedAPIVersion)
{
m_inputData = tempInputData;
m_inputValid = true;
}
else
{
m_outputValid = false;
m_inputValid = false;
}
}
}
/**
* Merge the config portion of the DS output block into the local cache.
*/
void DriverStationEnhancedIO::MergeConfigIntoOutput(const status_block_t &dsOutputBlock, status_block_t &localCache)
{
localCache.data.digital = (localCache.data.digital & dsOutputBlock.data.digital_oe) |
(dsOutputBlock.data.digital & ~dsOutputBlock.data.digital_oe);
localCache.data.digital_oe = dsOutputBlock.data.digital_oe;
localCache.data.digital_pe = dsOutputBlock.data.digital_pe;
localCache.data.pwm_period[0] = dsOutputBlock.data.pwm_period[0];
localCache.data.pwm_period[1] = dsOutputBlock.data.pwm_period[1];
localCache.data.enables = dsOutputBlock.data.enables;
}
/**
* Compare the config portion of the output blocks.
*/
bool DriverStationEnhancedIO::IsConfigEqual(const status_block_t &dsOutputBlock, const status_block_t &localCache)
{
if (localCache.data.digital_oe != dsOutputBlock.data.digital_oe) return false;
if ((localCache.data.digital & ~dsOutputBlock.data.digital) !=
(dsOutputBlock.data.digital & ~dsOutputBlock.data.digital)) return false;
if (localCache.data.digital_pe != dsOutputBlock.data.digital_pe) return false;
if (localCache.data.pwm_period[0] != dsOutputBlock.data.pwm_period[0]) return false;
if (localCache.data.pwm_period[1] != dsOutputBlock.data.pwm_period[1]) return false;
if (localCache.data.enables != dsOutputBlock.data.enables) return false;
return true;
}
/**
* Query an accelerometer channel on the DS IO.
*
* @param channel The channel number to read.
* @return The current acceleration on the channel in Gs.
*/
double DriverStationEnhancedIO::GetAcceleration(tAccelChannel channel)
{
if (channel < 1 || channel > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 2");
return 0.0;
}
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0.0;
}
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_Acceleration);
reported_mask |= (1 >> channel);
}
Synchronized sync(m_inputDataSemaphore);
return (m_inputData.data.accel[channel] - kAccelOffset) / kAccelScale;
}
/**
* Query an analog input channel on the DS IO.
*
* @param channel The channel number to read. [1,8]
* @return The analog input voltage for the channel.
*/
double DriverStationEnhancedIO::GetAnalogIn(UINT32 channel)
{
// 3.3V is the analog reference voltage
return GetAnalogInRatio(channel) * kAnalogInputReference;
}
/**
* Query an analog input channel on the DS IO in ratiometric form.
*
* @param channel The channel number to read. [1,8]
* @return The analog input percentage for the channel.
*/
double DriverStationEnhancedIO::GetAnalogInRatio(UINT32 channel)
{
if (channel < 1 || channel > 8)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
return 0.0;
}
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0.0;
}
static UINT16 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_AnalogIn);
reported_mask |= (1 >> channel);
}
Synchronized sync(m_inputDataSemaphore);
return m_inputData.data.analog[channel-1] / kAnalogInputResolution;
}
/**
* Query the voltage currently being output.
*
* AO1 is pin 11 on the top connector (P2).
* AO2 is pin 12 on the top connector (P2).
*
* @param channel The analog output channel on the DS IO. [1,2]
* @return The voltage being output on the channel.
*/
double DriverStationEnhancedIO::GetAnalogOut(UINT32 channel)
{
if (channel < 1 || channel > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 2");
return 0.0;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0.0;
}
Synchronized sync(m_outputDataSemaphore);
return m_outputData.data.dac[channel-1] * kAnalogOutputReference / kAnalogOutputResolution;
}
/**
* Set the analog output voltage.
*
* AO1 is pin 11 on the top connector (P2).
* AO2 is pin 12 on the top connector (P2).
* AO1 is the reference voltage for the 2 analog comparators on DIO15 and DIO16.
*
* The output range is 0V to 4V, however due to the supply voltage don't expect more than about 3V.
* Current supply capability is only 100uA.
*
* @param channel The analog output channel on the DS IO. [1,2]
* @param value The voltage to output on the channel.
*/
void DriverStationEnhancedIO::SetAnalogOut(UINT32 channel, double value)
{
if (channel < 1 || channel > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 2");
return;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
if (value < 0.0) value = 0.0;
if (value > kAnalogOutputReference) value = kAnalogOutputReference;
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_AnalogOut);
reported_mask |= (1 >> channel);
}
Synchronized sync(m_outputDataSemaphore);
m_outputData.data.dac[channel-1] = (UINT8)(value / kAnalogOutputReference * kAnalogOutputResolution);
}
/**
* Get the state of a button on the IO board.
*
* Button1 is the physical button "S1".
* Button2 is pin 4 on the top connector (P2).
* Button3 is pin 6 on the top connector (P2).
* Button4 is pin 8 on the top connector (P2).
* Button5 is pin 10 on the top connector (P2).
* Button6 is pin 7 on the top connector (P2).
*
* Button2 through Button6 are Capacitive Sense buttons.
*
* @param channel The button channel to read. [1,6]
* @return The state of the selected button.
*/
bool DriverStationEnhancedIO::GetButton(UINT32 channel)
{
if (channel < 1 || channel > 6)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 6");
return false;
}
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_Button);
reported_mask |= (1 >> channel);
}
return ((GetButtons() >> (channel-1)) & 1) != 0;
}
/**
* Get the state of all the button channels.
*
* @return The state of the 6 button channels in the 6 lsb of the returned byte.
*/
UINT8 DriverStationEnhancedIO::GetButtons()
{
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0;
}
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, 0, nUsageReporting::kDriverStationEIO_Button);
Synchronized sync(m_inputDataSemaphore);
return m_inputData.data.buttons;
}
/**
* Set the state of an LED on the IO board.
*
* @param channel The LED channel to set. [1,8]
* @param value True to turn the LED on.
*/
void DriverStationEnhancedIO::SetLED(UINT32 channel, bool value)
{
if (channel < 1 || channel > 8)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 8");
return;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
static UINT16 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_LED);
reported_mask |= (1 >> channel);
}
UINT8 leds;
Synchronized sync(m_outputDataSemaphore);
leds = m_outputData.data.leds;
leds &= ~(1 << (channel-1));
if (value) leds |= 1 << (channel-1);
m_outputData.data.leds = leds;
}
/**
* Set the state of all 8 LEDs on the IO board.
*
* @param value The state of each LED. LED1 is lsb and LED8 is msb.
*/
void DriverStationEnhancedIO::SetLEDs(UINT8 value)
{
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, 0, nUsageReporting::kDriverStationEIO_LED);
Synchronized sync(m_outputDataSemaphore);
m_outputData.data.leds = value;
}
/**
* Get the current state of a DIO channel regardless of mode.
*
* @param channel The DIO channel to read. [1,16]
* @return The state of the selected digital line.
*/
bool DriverStationEnhancedIO::GetDigital(UINT32 channel)
{
if (channel < 1 || channel > 16)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 16");
return false;
}
static UINT32 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_DigitalIn);
reported_mask |= (1 >> channel);
}
return ((GetDigitals() >> (channel-1)) & 1) != 0;
}
/**
* Get the state of all 16 DIO lines regardless of mode.
*
* @return The state of all DIO lines. DIO1 is lsb and DIO16 is msb.
*/
UINT16 DriverStationEnhancedIO::GetDigitals()
{
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0;
}
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, 0, nUsageReporting::kDriverStationEIO_DigitalIn);
Synchronized sync(m_inputDataSemaphore);
return m_inputData.data.digital;
}
/**
* Set the state of a DIO line that is configured for digital output.
*
* @param channel The DIO channel to set. [1,16]
* @param value The state to set the selected channel to.
*/
void DriverStationEnhancedIO::SetDigitalOutput(UINT32 channel, bool value)
{
if (channel < 1 || channel > 16)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 16");
return;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
static UINT32 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_DigitalOut);
reported_mask |= (1 >> channel);
}
UINT16 digital;
Synchronized sync(m_outputDataSemaphore);
if (m_outputData.data.digital_oe & (1 << (channel-1)))
{
digital = m_outputData.data.digital;
digital &= ~(1 << (channel-1));
if (value) digital |= 1 << (channel-1);
m_outputData.data.digital = digital;
}
else
{
wpi_setWPIError(LineNotOutput);
}
}
/**
* Get the current configuration for a DIO line.
*
* This has the side effect of forcing the Driver Station to switch to Enhanced mode if it's not when called.
* If Enhanced mode is not enabled when this is called, it will return kUnknown.
*
* @param channel The DIO channel config to get. [1,16]
* @return The configured mode for the DIO line.
*/
DriverStationEnhancedIO::tDigitalConfig DriverStationEnhancedIO::GetDigitalConfig(UINT32 channel)
{
if (channel < 1 || channel > 16)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 16");
return kUnknown;
}
if (!m_outputValid)
{
m_requestEnhancedEnable = true;
wpi_setWPIError(EnhancedIOMissing);
return kUnknown;
}
Synchronized sync(m_outputDataSemaphore);
if ((channel >= 1) && (channel <= 4))
{
if (m_outputData.data.pwm_enable & (1 << (channel - 1)))
{
return kPWM;
}
}
if ((channel >= 15) && (channel <= 16))
{
if (m_outputData.data.comparator_enable & (1 << (channel - 15)))
{
return kAnalogComparator;
}
}
if (m_outputData.data.digital_oe & (1 << (channel - 1)))
{
return kOutput;
}
if (!(m_outputData.data.digital_pe & (1 << (channel - 1))))
{
return kInputFloating;
}
if (m_outputData.data.digital & (1 << (channel - 1)))
{
return kInputPullUp;
}
else
{
return kInputPullDown;
}
}
/**
* Override the DS's configuration of a DIO line.
*
* If configured to kInputFloating, the selected DIO line will be tri-stated with no internal pull resistor.
*
* If configured to kInputPullUp, the selected DIO line will be tri-stated with a 5k-Ohm internal pull-up resistor enabled.
*
* If configured to kInputPullDown, the selected DIO line will be tri-stated with a 5k-Ohm internal pull-down resistor enabled.
*
* If configured to kOutput, the selected DIO line will actively drive to 0V or Vddio (specified by J1 and J4).
* DIO1 through DIO12, DIO15, and DIO16 can source 4mA and can sink 8mA.
* DIO12 and DIO13 can source 4mA and can sink 25mA.
*
* In addition to the common configurations, DIO1 through DIO4 can be configured to kPWM to enable PWM output.
*
* In addition to the common configurations, DIO15 and DIO16 can be configured to kAnalogComparator to enable
* analog comparators on those 2 DIO lines. When enabled, the lines are tri-stated and will accept analog voltages
* between 0V and 3.3V. If the input voltage is greater than the voltage output by AO1, the DIO will read as true,
* if less then false.
*
* @param channel The DIO line to configure. [1,16]
* @param config The mode to put the DIO line in.
*/
void DriverStationEnhancedIO::SetDigitalConfig(UINT32 channel, tDigitalConfig config)
{
if (channel < 1 || channel > 16)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 16");
return;
}
if (config == kPWM && (channel < 1 || channel > 4))
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel in PWM mode must be between 1 and 4");
return;
}
if (config == kAnalogComparator && (channel < 15 || channel > 16))
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel in Analog Comparator mode must be between 15 and 16");
return;
}
Synchronized sync(m_outputDataSemaphore);
m_configChanged = true;
if ((channel >= 1) && (channel <= 4))
{
if (config == kPWM)
{
m_outputData.data.pwm_enable |= 1 << (channel - 1);
m_outputData.data.digital &= ~(1 << (channel - 1));
m_outputData.data.digital_oe |= 1 << (channel - 1);
m_outputData.data.digital_pe &= ~(1 << (channel - 1));
return;
}
else
{
m_outputData.data.pwm_enable &= ~(1 << (channel - 1));
}
}
else if ((channel >= 15) && (channel <= 16))
{
if (config == kAnalogComparator)
{
m_outputData.data.comparator_enable |= 1 << (channel - 15);
m_outputData.data.digital &= ~(1 << (channel - 1));
m_outputData.data.digital_oe &= ~(1 << (channel - 1));
m_outputData.data.digital_pe &= ~(1 << (channel - 1));
return;
}
else
{
m_outputData.data.comparator_enable &= ~(1 << (channel - 15));
}
}
if (config == kInputFloating)
{
m_outputData.data.digital &= ~(1 << (channel - 1));
m_outputData.data.digital_oe &= ~(1 << (channel - 1));
m_outputData.data.digital_pe &= ~(1 << (channel - 1));
}
else if (config == kInputPullUp)
{
m_outputData.data.digital |= 1 << (channel - 1);
m_outputData.data.digital_oe &= ~(1 << (channel - 1));
m_outputData.data.digital_pe |= 1 << (channel - 1);
}
else if (config == kInputPullDown)
{
m_outputData.data.digital &= ~(1 << (channel - 1));
m_outputData.data.digital_oe &= ~(1 << (channel - 1));
m_outputData.data.digital_pe |= 1 << (channel - 1);
}
else if (config == kOutput)
{
m_outputData.data.digital_oe |= 1 << (channel - 1);
m_outputData.data.digital_pe &= ~(1 << (channel - 1));
}
else
{
// Something went wrong.
}
}
/**
* Get the period of a PWM generator.
*
* This has the side effect of forcing the Driver Station to switch to Enhanced mode if it's not when called.
* If Enhanced mode is not enabled when this is called, it will return 0.
*
* @param channels Select the generator by specifying the two channels to which it is connected.
* @return The period of the PWM generator in seconds.
*/
double DriverStationEnhancedIO::GetPWMPeriod(tPWMPeriodChannels channels)
{
if (channels < kPWMChannels1and2 || channels > kPWMChannels3and4)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channels must be kPWMChannels1and2 or kPWMChannels3and4");
return 0.0;
}
if (!m_outputValid)
{
m_requestEnhancedEnable = true;
wpi_setWPIError(EnhancedIOMissing);
return 0.0;
}
Synchronized sync(m_outputDataSemaphore);
return m_outputData.data.pwm_period[channels] / 24000000.0;
}
/**
* Set the period of a PWM generator.
*
* There are 2 PWM generators on the IO board. One can generate PWM signals on DIO1 and DIO2,
* the other on DIO3 and DIO4. Each generator has one counter and two compare registers. As such,
* each pair of PWM outputs share the output period but have independent duty cycles.
*
* @param channels Select the generator by specifying the two channels to which it is connected.
* @param period The period of the PWM generator in seconds. [0.0,0.002731]
*/
void DriverStationEnhancedIO::SetPWMPeriod(tPWMPeriodChannels channels, double period)
{
if (channels < kPWMChannels1and2 || channels > kPWMChannels3and4)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channels must be kPWMChannels1and2 or kPWMChannels3and4");
return;
}
// Convert to ticks based on the IO board's 24MHz clock
double ticks = period * 24000000.0;
// Limit the range of the ticks... warn if too big.
if (ticks > 65534.0)
{
wpi_setWPIError(EnhancedIOPWMPeriodOutOfRange);
ticks = 65534.0;
}
else if (ticks < 0.0) ticks = 0.0;
// Preserve the duty cycles.
double dutyCycles[2];
dutyCycles[0] = GetPWMOutput((channels << 1) + 1);
dutyCycles[1] = GetPWMOutput((channels << 1) + 2);
{
Synchronized sync(m_outputDataSemaphore);
// Update the period
m_outputData.data.pwm_period[channels] = (UINT16)ticks;
m_configChanged = true;
}
// Restore the duty cycles
SetPWMOutput((channels << 1) + 1, dutyCycles[0]);
SetPWMOutput((channels << 1) + 2, dutyCycles[1]);
}
/**
* Get the state being output on a fixed digital output.
*
* @param channel The FixedDO line to get. [1,2]
* @return The state of the FixedDO line.
*/
bool DriverStationEnhancedIO::GetFixedDigitalOutput(UINT32 channel)
{
if (channel < 1 || channel > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 2");
return 0;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0;
}
Synchronized sync(m_outputDataSemaphore);
return ((m_outputData.data.fixed_digital_out >> (channel-1)) & 1) != 0;
}
/**
* Set the state to output on a Fixed High Current Digital Output line.
*
* FixedDO1 is pin 5 on the top connector (P2).
* FixedDO2 is pin 3 on the top connector (P2).
*
* The FixedDO lines always output 0V and 3.3V regardless of J1 and J4.
* They can source 4mA and can sink 25mA. Because of this, they are expected to be used
* in an active low configuration, such as connecting to the cathode of a bright LED.
* Because they are expected to be active low, they default to true.
*
* @param channel The FixedDO channel to set.
* @param value The state to set the FixedDO.
*/
void DriverStationEnhancedIO::SetFixedDigitalOutput(UINT32 channel, bool value)
{
if (channel < 1 || channel > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 2");
return;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_FixedDigitalOut);
reported_mask |= (1 >> channel);
}
UINT8 digital;
Synchronized sync(m_outputDataSemaphore);
digital = m_outputData.data.fixed_digital_out;
digital &= ~(1 << (channel-1));
if (value) digital |= 1 << (channel-1);
m_outputData.data.fixed_digital_out = digital;
}
/**
* Get the position of a quadrature encoder.
*
* There are two signed 16-bit 4X quadrature decoders on the IO board. These decoders are always monitoring
* the state of the lines assigned to them, but these lines do not have to be used for encoders.
*
* Encoder1 uses DIO4 for "A", DIO6 for "B", and DIO8 for "Index".
* Encoder2 uses DIO5 for "A", DIO7 for "B", and DIO9 for "Index".
*
* The index functionality can be enabled or disabled using SetEncoderIndexEnable().
*
* @param encoderNumber The quadrature encoder to access. [1,2]
* @return The current position of the quadrature encoder.
*/
INT16 DriverStationEnhancedIO::GetEncoder(UINT32 encoderNumber)
{
if (encoderNumber < 1 || encoderNumber > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "encoderNumber must be between 1 and 2");
return 0;
}
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0;
}
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> encoderNumber)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, encoderNumber, nUsageReporting::kDriverStationEIO_Encoder);
reported_mask |= (1 >> encoderNumber);
}
Synchronized sync(m_inputDataSemaphore);
return m_inputData.data.quad[encoderNumber - 1] - m_encoderOffsets[encoderNumber - 1];
}
/**
* Reset the position of an encoder to 0.
*
* This simply stores an offset locally. It does not reset the hardware counter on the IO board.
* If you use this method with Index enabled, you may get unexpected results.
*
* @param encoderNumber The quadrature encoder to reset. [1,2]
*/
void DriverStationEnhancedIO::ResetEncoder(UINT32 encoderNumber)
{
if (encoderNumber < 1 || encoderNumber > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "encoderNumber must be between 1 and 2");
return;
}
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
Synchronized sync(m_inputDataSemaphore);
m_encoderOffsets[encoderNumber - 1] = m_inputData.data.quad[encoderNumber - 1];
}
/**
* Get the current configuration of a quadrature encoder index channel.
*
* This has the side effect of forcing the Driver Station to switch to Enhanced mode if it's not when called.
* If Enhanced mode is not enabled when this is called, it will return false.
*
* @param encoderNumber The quadrature encoder. [1,2]
* @return Is the index channel of the encoder enabled.
*/
bool DriverStationEnhancedIO::GetEncoderIndexEnable(UINT32 encoderNumber)
{
if (encoderNumber < 1 || encoderNumber > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "encoderNumber must be between 1 and 2");
return false;
}
if (!m_outputValid)
{
m_requestEnhancedEnable = true;
wpi_setWPIError(EnhancedIOMissing);
return false;
}
Synchronized sync(m_outputDataSemaphore);
return ((m_outputData.data.quad_index_enable >> (encoderNumber - 1)) & 1) != 0;
}
/**
* Enable or disable the index channel of a quadrature encoder.
*
* The quadrature decoders on the IO board support an active-low index input.
*
* Encoder1 uses DIO8 for "Index".
* Encoder2 uses DIO9 for "Index".
*
* When enabled, the decoder's counter will be reset to 0 when A, B, and Index are all low.
*
* @param encoderNumber The quadrature encoder. [1,2]
* @param enable If true, reset the encoder in an index condition.
*/
void DriverStationEnhancedIO::SetEncoderIndexEnable(UINT32 encoderNumber, bool enable)
{
if (encoderNumber < 1 || encoderNumber > 2)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "encoderNumber must be between 1 and 2");
return;
}
Synchronized sync(m_outputDataSemaphore);
m_outputData.data.quad_index_enable &= ~(1 << (encoderNumber - 1));
if (enable) m_outputData.data.quad_index_enable |= 1 << (encoderNumber - 1);
m_configChanged = true;
}
/**
* Get the value of the Capacitive Sense touch slider.
*
* @return Value between 0.0 (toward center of board) and 1.0 (toward edge of board). -1.0 means no touch detected.
*/
double DriverStationEnhancedIO::GetTouchSlider()
{
if (!m_inputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0.0;
}
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, 1, nUsageReporting::kDriverStationEIO_TouchSlider);
Synchronized sync(m_inputDataSemaphore);
UINT8 value = m_inputData.data.capsense_slider;
return value == 255 ? -1.0 : value / 254.0;
}
/**
* Get the percent duty-cycle that the PWM generator channel is configured to output.
*
* @param channel The DIO line's PWM generator to get the duty-cycle from. [1,4]
* @return The percent duty-cycle being output (if the DIO line is configured for PWM). [0.0,1.0]
*/
double DriverStationEnhancedIO::GetPWMOutput(UINT32 channel)
{
if (channel < 1 || channel > 4)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 4");
return 0.0;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return 0.0;
}
Synchronized sync(m_outputDataSemaphore);
return (double)m_outputData.data.pwm_compare[channel - 1] / (double)m_outputData.data.pwm_period[(channel - 1) >> 1];
}
/**
* Set the percent duty-cycle to output on a PWM enabled DIO line.
*
* DIO1 through DIO4 have the ability to output a PWM signal. The period of the
* signal can be configured in pairs using SetPWMPeriod().
*
* @param channel The DIO line's PWM generator to set. [1,4]
* @param value The percent duty-cycle to output from the PWM generator. [0.0,1.0]
*/
void DriverStationEnhancedIO::SetPWMOutput(UINT32 channel, double value)
{
if (channel < 1 || channel > 4)
{
wpi_setWPIErrorWithContext(ParameterOutOfRange, "channel must be between 1 and 4");
return;
}
if (!m_outputValid)
{
wpi_setWPIError(EnhancedIOMissing);
return;
}
static UINT8 reported_mask = 0;
if (!(reported_mask & (1 >> channel)))
{
nUsageReporting::report(nUsageReporting::kResourceType_DriverStationEIO, channel, nUsageReporting::kDriverStationEIO_PWM);
reported_mask |= (1 >> channel);
}
if (value > 1.0) value = 1.0;
else if (value < 0.0) value = 0.0;
Synchronized sync(m_outputDataSemaphore);
m_outputData.data.pwm_compare[channel - 1] = (UINT16)(value * (double)m_outputData.data.pwm_period[(channel - 1) >> 1]);
}
/**
* Get the firmware version running on the IO board.
*
* This also has the side effect of forcing the driver station to switch to Enhanced mode if it is not.
* If you plan to switch between Driver Stations with unknown IO configurations, you can call this
* until it returns a non-0 version to ensure that this API is accessible before proceeding.
*
* @return The version of the firmware running on the IO board. 0 if the board is not attached or not in Enhanced mode.
*/
UINT8 DriverStationEnhancedIO::GetFirmwareVersion()
{
if (!m_inputValid)
{
m_requestEnhancedEnable = true;
wpi_setWPIError(EnhancedIOMissing);
return 0;
}
Synchronized sync(m_inputDataSemaphore);
return m_inputData.data.fw_version;
}