This is the latest WPILib src, VisionSample2013, cRIO image, ... pulled down from firstforge.wpi.edu.
There might be risks in using the top of tree rather than an official release, but the commit messages do mention fixes for some deadlocks and race conditions.
git-svn-id: https://robotics.mvla.net/svn/frc971/2013/trunk/src@4066 f308d9b7-e957-4cde-b6ac-9a88185e7312
diff --git a/azaleasource/WPILibCProgramming/trunk/WPILib/DigitalModule.cpp b/azaleasource/WPILibCProgramming/trunk/WPILib/DigitalModule.cpp
new file mode 100644
index 0000000..0f0bf80
--- /dev/null
+++ b/azaleasource/WPILibCProgramming/trunk/WPILib/DigitalModule.cpp
@@ -0,0 +1,530 @@
+/*----------------------------------------------------------------------------*/
+/* 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 "DigitalModule.h"
+#include "I2C.h"
+#include "PWM.h"
+#include "Resource.h"
+#include "Synchronized.h"
+#include "WPIErrors.h"
+#include <math.h>
+#include <taskLib.h>
+
+static Resource *DIOChannels = NULL;
+static Resource *DO_PWMGenerators[tDIO::kNumSystems] = {NULL};
+
+/**
+ * Get an instance of an Digital Module.
+ * Singleton digital module creation where a module is allocated on the first use
+ * and the same module is returned on subsequent uses.
+ *
+ * @param moduleNumber The digital module to get (1 or 2).
+ */
+DigitalModule* DigitalModule::GetInstance(UINT8 moduleNumber)
+{
+ if (CheckDigitalModule(moduleNumber))
+ {
+ return (DigitalModule *)GetModule(nLoadOut::kModuleType_Digital, moduleNumber);
+ }
+
+ // If this wasn't caught before now, make sure we say what's wrong before we crash
+ char buf[64];
+ snprintf(buf, 64, "Digital Module %d", moduleNumber);
+ wpi_setGlobalWPIErrorWithContext(ModuleIndexOutOfRange, buf);
+
+ return NULL;
+}
+
+/**
+ * Create a new instance of an digital module.
+ * Create an instance of the digital module object. Initialize all the parameters
+ * to reasonable values on start.
+ * Setting a global value on an digital module can be done only once unless subsequent
+ * values are set the previously set value.
+ * Digital modules are a singleton, so the constructor is never called outside of this class.
+ *
+ * @param moduleNumber The digital module to create (1 or 2).
+ */
+DigitalModule::DigitalModule(UINT8 moduleNumber)
+ : Module(nLoadOut::kModuleType_Digital, moduleNumber)
+ , m_fpgaDIO (NULL)
+{
+ Resource::CreateResourceObject(&DIOChannels, tDIO::kNumSystems * kDigitalChannels);
+ Resource::CreateResourceObject(&DO_PWMGenerators[m_moduleNumber - 1], tDIO::kNumDO_PWMDutyCycleElements);
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ m_fpgaDIO = tDIO::create(m_moduleNumber - 1, &localStatus);
+ wpi_setError(localStatus);
+
+ // Make sure that the 9403 IONode has had a chance to initialize before continuing.
+ while(m_fpgaDIO->readLoopTiming(&localStatus) == 0) taskDelay(1);
+ if (m_fpgaDIO->readLoopTiming(&localStatus) != kExpectedLoopTiming)
+ {
+ char err[128];
+ sprintf(err, "DIO LoopTiming: %d, expecting: %d\n", m_fpgaDIO->readLoopTiming(&localStatus), kExpectedLoopTiming);
+ wpi_setWPIErrorWithContext(LoopTimingError, err);
+ }
+ m_fpgaDIO->writePWMConfig_Period(PWM::kDefaultPwmPeriod, &localStatus);
+ m_fpgaDIO->writePWMConfig_MinHigh(PWM::kDefaultMinPwmHigh, &localStatus);
+
+ // Ensure that PWM output values are set to OFF
+ for (UINT32 pwm_index = 1; pwm_index <= kPwmChannels; pwm_index++)
+ {
+ SetPWM(pwm_index, PWM::kPwmDisabled);
+ SetPWMPeriodScale(pwm_index, 3); // Set all to 4x by default.
+ }
+
+ // Turn off all relay outputs.
+ m_fpgaDIO->writeSlowValue_RelayFwd(0, &localStatus);
+ m_fpgaDIO->writeSlowValue_RelayRev(0, &localStatus);
+ wpi_setError(localStatus);
+
+ // Create a semaphore to protect changes to the digital output values
+ m_digitalSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
+
+ // Create a semaphore to protect changes to the relay values
+ m_relaySemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
+
+ // Create a semaphore to protect changes to the DO PWM config
+ m_doPwmSemaphore = semMCreate(SEM_Q_PRIORITY | SEM_DELETE_SAFE | SEM_INVERSION_SAFE);
+
+ AddToSingletonList();
+}
+
+DigitalModule::~DigitalModule()
+{
+ semDelete(m_doPwmSemaphore);
+ m_doPwmSemaphore = NULL;
+ semDelete(m_relaySemaphore);
+ m_relaySemaphore = NULL;
+ semDelete(m_digitalSemaphore);
+ m_digitalSemaphore = NULL;
+ delete m_fpgaDIO;
+}
+
+/**
+ * Set a PWM channel to the desired value. The values range from 0 to 255 and the period is controlled
+ * by the PWM Period and MinHigh registers.
+ *
+ * @param channel The PWM channel to set.
+ * @param value The PWM value to set.
+ */
+void DigitalModule::SetPWM(UINT32 channel, UINT8 value)
+{
+ CheckPWMChannel(channel);
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ m_fpgaDIO->writePWMValue(channel - 1, value, &localStatus);
+ wpi_setError(localStatus);
+}
+
+/**
+ * Get a value from a PWM channel. The values range from 0 to 255.
+ *
+ * @param channel The PWM channel to read from.
+ * @return The raw PWM value.
+ */
+UINT8 DigitalModule::GetPWM(UINT32 channel)
+{
+ CheckPWMChannel(channel);
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ return m_fpgaDIO->readPWMValue(channel - 1, &localStatus);
+ wpi_setError(localStatus);
+}
+
+/**
+ * Set how how often the PWM signal is squelched, thus scaling the period.
+ *
+ * @param channel The PWM channel to configure.
+ * @param squelchMask The 2-bit mask of outputs to squelch.
+ */
+void DigitalModule::SetPWMPeriodScale(UINT32 channel, UINT32 squelchMask)
+{
+ CheckPWMChannel(channel);
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ m_fpgaDIO->writePWMPeriodScale(channel - 1, squelchMask, &localStatus);
+ wpi_setError(localStatus);
+}
+
+/**
+ * Set the state of a relay.
+ * Set the state of a relay output to be forward. Relays have two outputs and each is
+ * independently set to 0v or 12v.
+ */
+void DigitalModule::SetRelayForward(UINT32 channel, bool on)
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ CheckRelayChannel(channel);
+ {
+ Synchronized sync(m_relaySemaphore);
+ UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
+ if (on)
+ forwardRelays |= 1 << (channel - 1);
+ else
+ forwardRelays &= ~(1 << (channel - 1));
+ m_fpgaDIO->writeSlowValue_RelayFwd(forwardRelays, &localStatus);
+ }
+ wpi_setError(localStatus);
+}
+
+/**
+ * Set the state of a relay.
+ * Set the state of a relay output to be reverse. Relays have two outputs and each is
+ * independently set to 0v or 12v.
+ */
+void DigitalModule::SetRelayReverse(UINT32 channel, bool on)
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ CheckRelayChannel(channel);
+ {
+ Synchronized sync(m_relaySemaphore);
+ UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
+ if (on)
+ reverseRelays |= 1 << (channel - 1);
+ else
+ reverseRelays &= ~(1 << (channel - 1));
+ m_fpgaDIO->writeSlowValue_RelayRev(reverseRelays, &localStatus);
+ }
+ wpi_setError(localStatus);
+}
+
+/**
+ * Get the current state of the forward relay channel
+ */
+bool DigitalModule::GetRelayForward(UINT32 channel)
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
+ wpi_setError(localStatus);
+ return (forwardRelays & (1 << (channel - 1))) != 0;
+}
+
+/**
+ * Get the current state of all of the forward relay channels on this module.
+ */
+UINT8 DigitalModule::GetRelayForward()
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT8 forwardRelays = m_fpgaDIO->readSlowValue_RelayFwd(&localStatus);
+ wpi_setError(localStatus);
+ return forwardRelays;
+}
+
+/**
+ * Get the current state of the reverse relay channel
+ */
+bool DigitalModule::GetRelayReverse(UINT32 channel)
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
+ wpi_setError(localStatus);
+ return (reverseRelays & (1 << (channel - 1))) != 0;
+
+}
+
+/**
+ * Get the current state of all of the reverse relay channels on this module.
+ */
+UINT8 DigitalModule::GetRelayReverse()
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT8 reverseRelays = m_fpgaDIO->readSlowValue_RelayRev(&localStatus);
+ wpi_setError(localStatus);
+ return reverseRelays;
+}
+
+
+/**
+ * Allocate Digital I/O channels.
+ * Allocate channels so that they are not accidently reused. Also the direction is set at the
+ * time of the allocation.
+ *
+ * @param channel The Digital I/O channel
+ * @param input If true open as input; if false open as output
+ * @return Was successfully allocated
+ */
+bool DigitalModule::AllocateDIO(UINT32 channel, bool input)
+{
+ char buf[64];
+ snprintf(buf, 64, "DIO %d (Module %d)", channel, m_moduleNumber);
+ if (DIOChannels->Allocate(kDigitalChannels * (m_moduleNumber - 1) + channel - 1, buf) == ~0ul) return false;
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ {
+ Synchronized sync(m_digitalSemaphore);
+ UINT32 bitToSet = 1 << (RemapDigitalChannel(channel - 1));
+ UINT32 outputEnable = m_fpgaDIO->readOutputEnable(&localStatus);
+ UINT32 outputEnableValue;
+ if (input)
+ {
+ outputEnableValue = outputEnable & (~bitToSet); // clear the bit for read
+ }
+ else
+ {
+ outputEnableValue = outputEnable | bitToSet; // set the bit for write
+ }
+ m_fpgaDIO->writeOutputEnable(outputEnableValue, &localStatus);
+ }
+ wpi_setError(localStatus);
+ return true;
+}
+
+/**
+ * Free the resource associated with a digital I/O channel.
+ *
+ * @param channel The Digital I/O channel to free
+ */
+void DigitalModule::FreeDIO(UINT32 channel)
+{
+ DIOChannels->Free(kDigitalChannels * (m_moduleNumber - 1) + channel - 1);
+}
+
+/**
+ * Write a digital I/O bit to the FPGA.
+ * Set a single value on a digital I/O channel.
+ *
+ * @param channel The Digital I/O channel
+ * @param value The state to set the digital channel (if it is configured as an output)
+ */
+void DigitalModule::SetDIO(UINT32 channel, short value)
+{
+ if (value != 0 && value != 1)
+ {
+ wpi_setWPIError(NonBinaryDigitalValue);
+ if (value != 0)
+ value = 1;
+ }
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ {
+ Synchronized sync(m_digitalSemaphore);
+ UINT16 currentDIO = m_fpgaDIO->readDO(&localStatus);
+ if(value == 0)
+ {
+ currentDIO = currentDIO & ~(1 << RemapDigitalChannel(channel - 1));
+ }
+ else if (value == 1)
+ {
+ currentDIO = currentDIO | (1 << RemapDigitalChannel(channel - 1));
+ }
+ m_fpgaDIO->writeDO(currentDIO, &localStatus);
+ }
+ wpi_setError(localStatus);
+}
+
+/**
+ * Read a digital I/O bit from the FPGA.
+ * Get a single value from a digital I/O channel.
+ *
+ * @param channel The digital I/O channel
+ * @return The state of the specified channel
+ */
+bool DigitalModule::GetDIO(UINT32 channel)
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT32 currentDIO = m_fpgaDIO->readDI(&localStatus);
+ wpi_setError(localStatus);
+
+ //Shift 00000001 over channel-1 places.
+ //AND it against the currentDIO
+ //if it == 0, then return false
+ //else return true
+ return ((currentDIO >> RemapDigitalChannel(channel - 1)) & 1) != 0;
+}
+
+/**
+ * Read the state of all the Digital I/O lines from the FPGA
+ * These are not remapped to logical order. They are still in hardware order.
+ */
+UINT16 DigitalModule::GetDIO()
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT32 currentDIO = m_fpgaDIO->readDI(&localStatus);
+ wpi_setError(localStatus);
+ return currentDIO;
+}
+
+/**
+ * Read the direction of a the Digital I/O lines
+ * A 1 bit means output and a 0 bit means input.
+ *
+ * @param channel The digital I/O channel
+ * @return The direction of the specified channel
+ */
+bool DigitalModule::GetDIODirection(UINT32 channel)
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT32 currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus);
+ wpi_setError(localStatus);
+
+ //Shift 00000001 over channel-1 places.
+ //AND it against the currentOutputEnable
+ //if it == 0, then return false
+ //else return true
+ return ((currentOutputEnable >> RemapDigitalChannel(channel - 1)) & 1) != 0;
+}
+
+/**
+ * Read the direction of all the Digital I/O lines from the FPGA
+ * A 1 bit means output and a 0 bit means input.
+ * These are not remapped to logical order. They are still in hardware order.
+ */
+UINT16 DigitalModule::GetDIODirection()
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT32 currentOutputEnable = m_fpgaDIO->readOutputEnable(&localStatus);
+ wpi_setError(localStatus);
+ return currentOutputEnable;
+}
+
+/**
+ * Generate a single pulse.
+ * Write a pulse to the specified digital output channel. There can only be a single pulse going at any time.
+ *
+ * @param channel The Digital Output channel that the pulse should be output on
+ * @param pulseLength The active length of the pulse (in seconds)
+ */
+void DigitalModule::Pulse(UINT32 channel, float pulseLength)
+{
+ UINT16 mask = 1 << RemapDigitalChannel(channel - 1);
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ m_fpgaDIO->writePulseLength((UINT8)(1.0e9 * pulseLength / (m_fpgaDIO->readLoopTiming(&localStatus) * 25)), &localStatus);
+ m_fpgaDIO->writePulse(mask, &localStatus);
+ wpi_setError(localStatus);
+}
+
+/**
+ * Check a DIO line to see if it is currently generating a pulse.
+ *
+ * @return A pulse is in progress
+ */
+bool DigitalModule::IsPulsing(UINT32 channel)
+{
+ UINT16 mask = 1 << RemapDigitalChannel(channel - 1);
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT16 pulseRegister = m_fpgaDIO->readPulse(&localStatus);
+ wpi_setError(localStatus);
+ return (pulseRegister & mask) != 0;
+}
+
+/**
+ * Check if any DIO line is currently generating a pulse.
+ *
+ * @return A pulse on some line is in progress
+ */
+bool DigitalModule::IsPulsing()
+{
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT16 pulseRegister = m_fpgaDIO->readPulse(&localStatus);
+ wpi_setError(localStatus);
+ return pulseRegister != 0;
+}
+
+/**
+ * Allocate a DO PWM Generator.
+ * Allocate PWM generators so that they are not accidently reused.
+ *
+ * @return PWM Generator refnum
+ */
+UINT32 DigitalModule::AllocateDO_PWM()
+{
+ char buf[64];
+ snprintf(buf, 64, "DO_PWM (Module: %d)", m_moduleNumber);
+ return DO_PWMGenerators[(m_moduleNumber - 1)]->Allocate(buf);
+}
+
+/**
+ * Free the resource associated with a DO PWM generator.
+ *
+ * @param pwmGenerator The pwmGen to free that was allocated with AllocateDO_PWM()
+ */
+void DigitalModule::FreeDO_PWM(UINT32 pwmGenerator)
+{
+ if (pwmGenerator == ~0ul) return;
+ DO_PWMGenerators[(m_moduleNumber - 1)]->Free(pwmGenerator);
+}
+
+/**
+ * Change the frequency of the DO PWM generator.
+ *
+ * The valid range is from 0.6 Hz to 19 kHz. The frequency resolution is logarithmic.
+ *
+ * @param rate The frequency to output all digital output PWM signals on this module.
+ */
+void DigitalModule::SetDO_PWMRate(float rate)
+{
+ // Currently rounding in the log rate domain... heavy weight toward picking a higher freq.
+ // TODO: Round in the linear rate domain.
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ UINT8 pwmPeriodPower = (UINT8)(log(1.0 / (m_fpgaDIO->readLoopTiming(&localStatus) * 0.25E-6 * rate))/log(2.0) + 0.5);
+ m_fpgaDIO->writeDO_PWMConfig_PeriodPower(pwmPeriodPower, &localStatus);
+ wpi_setError(localStatus);
+}
+
+/**
+ * Configure which DO channel the PWM siganl is output on
+ *
+ * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
+ * @param channel The Digital Output channel to output on
+ */
+void DigitalModule::SetDO_PWMOutputChannel(UINT32 pwmGenerator, UINT32 channel)
+{
+ if (pwmGenerator == ~0ul) return;
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ switch(pwmGenerator)
+ {
+ case 0:
+ m_fpgaDIO->writeDO_PWMConfig_OutputSelect_0(RemapDigitalChannel(channel - 1), &localStatus);
+ break;
+ case 1:
+ m_fpgaDIO->writeDO_PWMConfig_OutputSelect_1(RemapDigitalChannel(channel - 1), &localStatus);
+ break;
+ case 2:
+ m_fpgaDIO->writeDO_PWMConfig_OutputSelect_2(RemapDigitalChannel(channel - 1), &localStatus);
+ break;
+ case 3:
+ m_fpgaDIO->writeDO_PWMConfig_OutputSelect_3(RemapDigitalChannel(channel - 1), &localStatus);
+ break;
+ }
+ wpi_setError(localStatus);
+}
+
+/**
+ * Configure the duty-cycle of the PWM generator
+ *
+ * @param pwmGenerator The generator index reserved by AllocateDO_PWM()
+ * @param dutyCycle The percent duty cycle to output [0..1].
+ */
+void DigitalModule::SetDO_PWMDutyCycle(UINT32 pwmGenerator, float dutyCycle)
+{
+ if (pwmGenerator == ~0ul) return;
+ if (dutyCycle > 1.0) dutyCycle = 1.0;
+ if (dutyCycle < 0.0) dutyCycle = 0.0;
+ float rawDutyCycle = 256.0 * dutyCycle;
+ if (rawDutyCycle > 255.5) rawDutyCycle = 255.5;
+ tRioStatusCode localStatus = NiFpga_Status_Success;
+ {
+ Synchronized sync(m_doPwmSemaphore);
+ UINT8 pwmPeriodPower = m_fpgaDIO->readDO_PWMConfig_PeriodPower(&localStatus);
+ if (pwmPeriodPower < 4)
+ {
+ // The resolution of the duty cycle drops close to the highest frequencies.
+ rawDutyCycle = rawDutyCycle / pow(2.0, 4 - pwmPeriodPower);
+ }
+ m_fpgaDIO->writeDO_PWMDutyCycle(pwmGenerator, (UINT8)rawDutyCycle, &localStatus);
+ }
+ wpi_setError(localStatus);
+}
+
+/**
+ * Return a pointer to an I2C object for this digital module
+ * The caller is responsible for deleting the pointer.
+ *
+ * @param address The address of the device on the I2C bus
+ * @return A pointer to an I2C object to talk to the device at address
+ */
+I2C* DigitalModule::GetI2C(UINT32 address)
+{
+ return new I2C(this, address);
+}
+
+