From ed7d2d6aa6a493e3ab2ea17242b5c2afe51f42f0 Mon Sep 17 00:00:00 2001 From: Thad House Date: Thu, 26 May 2016 22:14:25 -0700 Subject: [PATCH] Splits the HAL Analog Implementation Files (#82) --- hal/lib/athena/Analog.cpp | 799 --------------------------- hal/lib/athena/AnalogAccumulator.cpp | 145 +++++ hal/lib/athena/AnalogInput.cpp | 354 ++++++++++++ hal/lib/athena/AnalogInternal.cpp | 76 +++ hal/lib/athena/AnalogInternal.h | 41 ++ hal/lib/athena/AnalogOutput.cpp | 70 +++ hal/lib/athena/AnalogTrigger.cpp | 159 ++++++ 7 files changed, 845 insertions(+), 799 deletions(-) delete mode 100644 hal/lib/athena/Analog.cpp create mode 100644 hal/lib/athena/AnalogAccumulator.cpp create mode 100644 hal/lib/athena/AnalogInput.cpp create mode 100644 hal/lib/athena/AnalogInternal.cpp create mode 100644 hal/lib/athena/AnalogInternal.h create mode 100644 hal/lib/athena/AnalogOutput.cpp create mode 100644 hal/lib/athena/AnalogTrigger.cpp diff --git a/hal/lib/athena/Analog.cpp b/hal/lib/athena/Analog.cpp deleted file mode 100644 index 9986c4b75b..0000000000 --- a/hal/lib/athena/Analog.cpp +++ /dev/null @@ -1,799 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) FIRST 2016. 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 the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -#include "HAL/AnalogAccumulator.h" -#include "HAL/AnalogInput.h" -#include "HAL/AnalogOutput.h" -#include "HAL/AnalogTrigger.h" - -#include "ChipObject.h" -#include "FRC_NetworkCommunication/AICalibration.h" -#include "FRC_NetworkCommunication/LoadOut.h" -#include "HAL/HAL.h" -#include "HAL/Port.h" -#include "HAL/cpp/Resource.h" -#include "HAL/cpp/priority_mutex.h" - -static const long kTimebase = 40000000; ///< 40 MHz clock -static const long kDefaultOversampleBits = 0; -static const long kDefaultAverageBits = 7; -static const float kDefaultSampleRate = 50000.0; -static const uint32_t kAnalogInputPins = 8; -static const uint32_t kAnalogOutputPins = 2; - -static const uint32_t kAccumulatorNumChannels = 2; -static const uint32_t kAccumulatorChannels[] = {0, 1}; - -struct AnalogPort { - Port port; - tAccumulator* accumulator; -}; - -static bool analogSampleRateSet = false; -static priority_recursive_mutex analogRegisterWindowMutex; -static tAI* analogInputSystem = nullptr; -static tAO* analogOutputSystem = nullptr; -static uint32_t analogNumChannelsToActivate = 0; - -extern "C" { - -// Utility methods defined below. -static uint32_t getAnalogNumActiveChannels(int32_t* status); -static uint32_t getAnalogNumChannelsToActivate(int32_t* status); -static void setAnalogNumChannelsToActivate(uint32_t channels); - -static bool analogSystemInitialized = false; - -/** - * Initialize the analog System. - */ -void initializeAnalog(int32_t* status) { - std::lock_guard sync(analogRegisterWindowMutex); - if (analogSystemInitialized) return; - analogInputSystem = tAI::create(status); - analogOutputSystem = tAO::create(status); - setAnalogNumChannelsToActivate(kAnalogInputPins); - setAnalogSampleRate(kDefaultSampleRate, status); - analogSystemInitialized = true; -} - -/** - * Initialize the analog input port using the given port object. - */ -void* initializeAnalogInputPort(void* port_pointer, int32_t* status) { - initializeAnalog(status); - Port* port = (Port*)port_pointer; - - // Initialize port structure - AnalogPort* analog_port = new AnalogPort(); - analog_port->port = *port; - if (isAccumulatorChannel(analog_port, status)) { - analog_port->accumulator = tAccumulator::create(port->pin, status); - } else - analog_port->accumulator = nullptr; - - // Set default configuration - analogInputSystem->writeScanList(port->pin, port->pin, status); - setAnalogAverageBits(analog_port, kDefaultAverageBits, status); - setAnalogOversampleBits(analog_port, kDefaultOversampleBits, status); - return analog_port; -} - -void freeAnalogInputPort(void* analog_port_pointer) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (!port) return; - delete port->accumulator; - delete port; -} - -/** - * Initialize the analog output port using the given port object. - */ -void* initializeAnalogOutputPort(void* port_pointer, int32_t* status) { - initializeAnalog(status); - Port* port = (Port*)port_pointer; - - // Initialize port structure - AnalogPort* analog_port = new AnalogPort(); - analog_port->port = *port; - analog_port->accumulator = nullptr; - return analog_port; -} - -void freeAnalogOutputPort(void* analog_port_pointer) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (!port) return; - delete port->accumulator; - delete port; -} - -/** - * Check that the analog module number is valid. - * - * @return Analog module is valid and present - */ -bool checkAnalogModule(uint8_t module) { return module == 1; } - -/** - * Check that the analog output channel number is value. - * Verify that the analog channel number is one of the legal channel numbers. - * Channel numbers are 0-based. - * - * @return Analog channel is valid - */ -bool checkAnalogInputChannel(uint32_t pin) { - if (pin < kAnalogInputPins) return true; - return false; -} - -/** - * Check that the analog output channel number is value. - * Verify that the analog channel number is one of the legal channel numbers. - * Channel numbers are 0-based. - * - * @return Analog channel is valid - */ -bool checkAnalogOutputChannel(uint32_t pin) { - if (pin < kAnalogOutputPins) return true; - return false; -} - -void setAnalogOutput(void* analog_port_pointer, double voltage, - int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - - uint16_t rawValue = (uint16_t)(voltage / 5.0 * 0x1000); - - if (voltage < 0.0) - rawValue = 0; - else if (voltage > 5.0) - rawValue = 0x1000; - - analogOutputSystem->writeMXP(port->port.pin, rawValue, status); -} - -double getAnalogOutput(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - - uint16_t rawValue = analogOutputSystem->readMXP(port->port.pin, status); - - return rawValue * 5.0 / 0x1000; -} - -/** - * Set the sample rate. - * - * This is a global setting for the Athena and effects all channels. - * - * @param samplesPerSecond The number of samples per channel per second. - */ -void setAnalogSampleRate(double samplesPerSecond, int32_t* status) { - // TODO: This will change when variable size scan lists are implemented. - // TODO: Need float comparison with epsilon. - // wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond); - analogSampleRateSet = true; - - // Compute the convert rate - uint32_t ticksPerSample = (uint32_t)((float)kTimebase / samplesPerSecond); - uint32_t ticksPerConversion = - ticksPerSample / getAnalogNumChannelsToActivate(status); - // ticksPerConversion must be at least 80 - if (ticksPerConversion < 80) { - if ((*status) >= 0) *status = SAMPLE_RATE_TOO_HIGH; - ticksPerConversion = 80; - } - - // Atomically set the scan size and the convert rate so that the sample rate - // is constant - tAI::tConfig config; - config.ScanSize = getAnalogNumChannelsToActivate(status); - config.ConvertRate = ticksPerConversion; - analogInputSystem->writeConfig(config, status); - - // Indicate that the scan size has been commited to hardware. - setAnalogNumChannelsToActivate(0); -} - -/** - * Get the current sample rate. - * - * This assumes one entry in the scan list. - * This is a global setting for the Athena and effects all channels. - * - * @return Sample rate. - */ -float getAnalogSampleRate(int32_t* status) { - uint32_t ticksPerConversion = analogInputSystem->readLoopTiming(status); - uint32_t ticksPerSample = - ticksPerConversion * getAnalogNumActiveChannels(status); - return (float)kTimebase / (float)ticksPerSample; -} - -/** - * Set the number of averaging bits. - * - * This sets the number of averaging bits. The actual number of averaged samples - * is 2**bits. Use averaging to improve the stability of your measurement at the - * expense of sampling rate. The averaging is done automatically in the FPGA. - * - * @param analog_port_pointer Pointer to the analog port to configure. - * @param bits Number of bits to average. - */ -void setAnalogAverageBits(void* analog_port_pointer, uint32_t bits, - int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - analogInputSystem->writeAverageBits(port->port.pin, bits, status); -} - -/** - * Get the number of averaging bits. - * - * This gets the number of averaging bits from the FPGA. The actual number of - * averaged samples is 2**bits. The averaging is done automatically in the FPGA. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return Bits to average. - */ -uint32_t getAnalogAverageBits(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - uint32_t result = analogInputSystem->readAverageBits(port->port.pin, status); - return result; -} - -/** - * Set the number of oversample bits. - * - * This sets the number of oversample bits. The actual number of oversampled - * values is 2**bits. Use oversampling to improve the resolution of your - * measurements at the expense of sampling rate. The oversampling is done - * automatically in the FPGA. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @param bits Number of bits to oversample. - */ -void setAnalogOversampleBits(void* analog_port_pointer, uint32_t bits, - int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - analogInputSystem->writeOversampleBits(port->port.pin, bits, status); -} - -/** - * Get the number of oversample bits. - * - * This gets the number of oversample bits from the FPGA. The actual number of - * oversampled values is 2**bits. The oversampling is done automatically in the - * FPGA. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return Bits to oversample. - */ -uint32_t getAnalogOversampleBits(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - uint32_t result = - analogInputSystem->readOversampleBits(port->port.pin, status); - return result; -} - -/** - * Get a sample straight from the channel on this module. - * - * The sample is a 12-bit value representing the 0V to 5V range of the A/D - * converter in the module. The units are in A/D converter codes. Use - * GetVoltage() to get the analog value in calibrated units. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return A sample straight from the channel on this module. - */ -int16_t getAnalogValue(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - int16_t value; - if (!checkAnalogInputChannel(port->port.pin)) { - return 0; - } - - tAI::tReadSelect readSelect; - readSelect.Channel = port->port.pin; - readSelect.Averaged = false; - - { - std::lock_guard sync(analogRegisterWindowMutex); - analogInputSystem->writeReadSelect(readSelect, status); - analogInputSystem->strobeLatchOutput(status); - value = (int16_t)analogInputSystem->readOutput(status); - } - - return value; -} - -/** - * Get a sample from the output of the oversample and average engine for the - * channel. - * - * The sample is 12-bit + the value configured in SetOversampleBits(). - * The value configured in SetAverageBits() will cause this value to be averaged - * 2**bits number of samples. This is not a sliding window. The sample will not - * change until 2**(OversamplBits + AverageBits) samples have been acquired from - * the module on this channel. Use GetAverageVoltage() to get the analog value - * in calibrated units. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return A sample from the oversample and average engine for the channel. - */ -int32_t getAnalogAverageValue(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - int32_t value; - if (!checkAnalogInputChannel(port->port.pin)) { - return 0; - } - - tAI::tReadSelect readSelect; - readSelect.Channel = port->port.pin; - readSelect.Averaged = true; - - { - std::lock_guard sync(analogRegisterWindowMutex); - analogInputSystem->writeReadSelect(readSelect, status); - analogInputSystem->strobeLatchOutput(status); - value = (int32_t)analogInputSystem->readOutput(status); - } - - return value; -} - -/** - * Get a scaled sample straight from the channel on this module. - * - * The value is scaled to units of Volts using the calibrated scaling data from - * GetLSBWeight() and GetOffset(). - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return A scaled sample straight from the channel on this module. - */ -float getAnalogVoltage(void* analog_port_pointer, int32_t* status) { - int16_t value = getAnalogValue(analog_port_pointer, status); - uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status); - int32_t offset = getAnalogOffset(analog_port_pointer, status); - float voltage = LSBWeight * 1.0e-9 * value - offset * 1.0e-9; - return voltage; -} - -/** - * Get a scaled sample from the output of the oversample and average engine for - * the channel. - * - * The value is scaled to units of Volts using the calibrated scaling data from - * GetLSBWeight() and GetOffset(). Using oversampling will cause this value to - * be higher resolution, but it will update more slowly. Using averaging will - * cause this value to be more stable, but it will update more slowly. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return A scaled sample from the output of the oversample and average engine - * for the channel. - */ -float getAnalogAverageVoltage(void* analog_port_pointer, int32_t* status) { - int32_t value = getAnalogAverageValue(analog_port_pointer, status); - uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status); - int32_t offset = getAnalogOffset(analog_port_pointer, status); - uint32_t oversampleBits = - getAnalogOversampleBits(analog_port_pointer, status); - float voltage = - ((LSBWeight * 1.0e-9 * value) / (float)(1 << oversampleBits)) - - offset * 1.0e-9; - return voltage; -} - -/** - * Convert a voltage to a raw value for a specified channel. - * - * This process depends on the calibration of each channel, so the channel - * must be specified. - * - * @todo This assumes raw values. Oversampling not supported as is. - * - * @param analog_port_pointer Pointer to the analog port to use. - * @param voltage The voltage to convert. - * @return The raw value for the channel. - */ -int32_t getAnalogVoltsToValue(void* analog_port_pointer, double voltage, - int32_t* status) { - if (voltage > 5.0) { - voltage = 5.0; - *status = VOLTAGE_OUT_OF_RANGE; - } - if (voltage < 0.0) { - voltage = 0.0; - *status = VOLTAGE_OUT_OF_RANGE; - } - uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status); - int32_t offset = getAnalogOffset(analog_port_pointer, status); - int32_t value = (int32_t)((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9)); - return value; -} - -/** - * Get the factory scaling least significant bit weight constant. - * The least significant bit weight constant for the channel that was calibrated - * in manufacturing and stored in an eeprom in the module. - * - * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9) - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return Least significant bit weight. - */ -uint32_t getAnalogLSBWeight(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - uint32_t lsbWeight = FRC_NetworkCommunication_nAICalibration_getLSBWeight( - 0, port->port.pin, status); // XXX: aiSystemIndex == 0? - return lsbWeight; -} - -/** - * Get the factory scaling offset constant. - * The offset constant for the channel that was calibrated in manufacturing and - * stored in an eeprom in the module. - * - * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9) - * - * @param analog_port_pointer Pointer to the analog port to use. - * @return Offset constant. - */ -int32_t getAnalogOffset(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - int32_t offset = FRC_NetworkCommunication_nAICalibration_getOffset( - 0, port->port.pin, status); // XXX: aiSystemIndex == 0? - return offset; -} - -/** - * Return the number of channels on the module in use. - * - * @return Active channels. - */ -static uint32_t getAnalogNumActiveChannels(int32_t* status) { - uint32_t scanSize = analogInputSystem->readConfig_ScanSize(status); - if (scanSize == 0) return 8; - return scanSize; -} - -/** - * Get the number of active channels. - * - * This is an internal function to allow the atomic update of both the - * number of active channels and the sample rate. - * - * When the number of channels changes, use the new value. Otherwise, - * return the curent value. - * - * @return Value to write to the active channels field. - */ -static uint32_t getAnalogNumChannelsToActivate(int32_t* status) { - if (analogNumChannelsToActivate == 0) - return getAnalogNumActiveChannels(status); - return analogNumChannelsToActivate; -} - -/** - * Set the number of active channels. - * - * Store the number of active channels to set. Don't actually commit to - * hardware - * until SetSampleRate(). - * - * @param channels Number of active channels. - */ -static void setAnalogNumChannelsToActivate(uint32_t channels) { - analogNumChannelsToActivate = channels; -} - -//// Accumulator Stuff - -/** - * Is the channel attached to an accumulator. - * - * @return The analog channel is attached to an accumulator. - */ -bool isAccumulatorChannel(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - for (uint32_t i = 0; i < kAccumulatorNumChannels; i++) { - if (port->port.pin == kAccumulatorChannels[i]) return true; - } - return false; -} - -/** - * Initialize the accumulator. - */ -void initAccumulator(void* analog_port_pointer, int32_t* status) { - setAccumulatorCenter(analog_port_pointer, 0, status); - resetAccumulator(analog_port_pointer, status); -} - -/** - * Resets the accumulator to the initial value. - */ -void resetAccumulator(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (port->accumulator == nullptr) { - *status = NULL_PARAMETER; - return; - } - port->accumulator->strobeReset(status); -} - -/** - * Set the center value of the accumulator. - * - * The center value is subtracted from each A/D value before it is added to the - * accumulator. This is used for the center value of devices like gyros and - * accelerometers to make integration work and to take the device offset into - * account when integrating. - * - * This center value is based on the output of the oversampled and averaged - * source from channel 1. Because of this, any non-zero oversample bits will - * affect the size of the value for this field. - */ -void setAccumulatorCenter(void* analog_port_pointer, int32_t center, - int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (port->accumulator == nullptr) { - *status = NULL_PARAMETER; - return; - } - port->accumulator->writeCenter(center, status); -} - -/** - * Set the accumulator's deadband. - */ -void setAccumulatorDeadband(void* analog_port_pointer, int32_t deadband, - int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (port->accumulator == nullptr) { - *status = NULL_PARAMETER; - return; - } - port->accumulator->writeDeadband(deadband, status); -} - -/** - * Read the accumulated value. - * - * Read the value that has been accumulating on channel 1. - * The accumulator is attached after the oversample and average engine. - * - * @return The 64-bit value accumulated since the last Reset(). - */ -int64_t getAccumulatorValue(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (port->accumulator == nullptr) { - *status = NULL_PARAMETER; - return 0; - } - int64_t value = port->accumulator->readOutput_Value(status); - return value; -} - -/** - * Read the number of accumulated values. - * - * Read the count of the accumulated values since the accumulator was last - * Reset(). - * - * @return The number of times samples from the channel were accumulated. - */ -uint32_t getAccumulatorCount(void* analog_port_pointer, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (port->accumulator == nullptr) { - *status = NULL_PARAMETER; - return 0; - } - return port->accumulator->readOutput_Count(status); -} - -/** - * Read the accumulated value and the number of accumulated values atomically. - * - * This function reads the value and count from the FPGA atomically. - * This can be used for averaging. - * - * @param value Pointer to the 64-bit accumulated output. - * @param count Pointer to the number of accumulation cycles. - */ -void getAccumulatorOutput(void* analog_port_pointer, int64_t* value, - uint32_t* count, int32_t* status) { - AnalogPort* port = (AnalogPort*)analog_port_pointer; - if (port->accumulator == nullptr) { - *status = NULL_PARAMETER; - return; - } - if (value == nullptr || count == nullptr) { - *status = NULL_PARAMETER; - return; - } - - tAccumulator::tOutput output = port->accumulator->readOutput(status); - - *value = output.Value; - *count = output.Count; -} - -struct trigger_t { - tAnalogTrigger* trigger; - AnalogPort* port; - uint32_t index; -}; -typedef struct trigger_t AnalogTrigger; - -static hal::Resource* triggers = nullptr; - -void* initializeAnalogTrigger(void* port_pointer, uint32_t* index, - int32_t* status) { - Port* port = (Port*)port_pointer; - hal::Resource::CreateResourceObject(&triggers, tAnalogTrigger::kNumSystems); - - AnalogTrigger* trigger = new AnalogTrigger(); - trigger->port = (AnalogPort*)initializeAnalogInputPort(port, status); - trigger->index = triggers->Allocate("Analog Trigger"); - *index = trigger->index; - // TODO: if (index == ~0ul) { CloneError(triggers); return; } - - trigger->trigger = tAnalogTrigger::create(trigger->index, status); - trigger->trigger->writeSourceSelect_Channel(port->pin, status); - - return trigger; -} - -void cleanAnalogTrigger(void* analog_trigger_pointer, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - if (!trigger) return; - triggers->Free(trigger->index); - delete trigger->trigger; - freeAnalogInputPort(trigger->port); - delete trigger; -} - -void setAnalogTriggerLimitsRaw(void* analog_trigger_pointer, int32_t lower, - int32_t upper, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - if (lower > upper) { - *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR; - } - trigger->trigger->writeLowerLimit(lower, status); - trigger->trigger->writeUpperLimit(upper, status); -} - -/** - * Set the upper and lower limits of the analog trigger. - * The limits are given as floating point voltage values. - */ -void setAnalogTriggerLimitsVoltage(void* analog_trigger_pointer, double lower, - double upper, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - if (lower > upper) { - *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR; - } - // TODO: This depends on the averaged setting. Only raw values will work as - // is. - trigger->trigger->writeLowerLimit( - getAnalogVoltsToValue(trigger->port, lower, status), status); - trigger->trigger->writeUpperLimit( - getAnalogVoltsToValue(trigger->port, upper, status), status); -} - -/** - * Configure the analog trigger to use the averaged vs. raw values. - * If the value is true, then the averaged value is selected for the analog - * trigger, otherwise the immediate value is used. - */ -void setAnalogTriggerAveraged(void* analog_trigger_pointer, - bool useAveragedValue, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - if (trigger->trigger->readSourceSelect_Filter(status) != 0) { - *status = INCOMPATIBLE_STATE; - // TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not - // support average and filtering at the same time."); - } - trigger->trigger->writeSourceSelect_Averaged(useAveragedValue, status); -} - -/** - * Configure the analog trigger to use a filtered value. - * The analog trigger will operate with a 3 point average rejection filter. This - * is designed to help with 360 degree pot applications for the period where the - * pot crosses through zero. - */ -void setAnalogTriggerFiltered(void* analog_trigger_pointer, - bool useFilteredValue, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - if (trigger->trigger->readSourceSelect_Averaged(status) != 0) { - *status = INCOMPATIBLE_STATE; - // TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not " - // "support average and filtering at the same time."); - } - trigger->trigger->writeSourceSelect_Filter(useFilteredValue, status); -} - -/** - * Return the InWindow output of the analog trigger. - * True if the analog input is between the upper and lower limits. - * @return The InWindow output of the analog trigger. - */ -bool getAnalogTriggerInWindow(void* analog_trigger_pointer, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - return trigger->trigger->readOutput_InHysteresis(trigger->index, status) != 0; -} - -/** - * Return the TriggerState output of the analog trigger. - * True if above upper limit. - * False if below lower limit. - * If in Hysteresis, maintain previous state. - * @return The TriggerState output of the analog trigger. - */ -bool getAnalogTriggerTriggerState(void* analog_trigger_pointer, - int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - return trigger->trigger->readOutput_OverLimit(trigger->index, status) != 0; -} - -/** - * Get the state of the analog trigger output. - * @return The state of the analog trigger output. - */ -bool getAnalogTriggerOutput(void* analog_trigger_pointer, - AnalogTriggerType type, int32_t* status) { - AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; - bool result = false; - switch (type) { - case kInWindow: - result = - trigger->trigger->readOutput_InHysteresis(trigger->index, status); - break; // XXX: Backport - case kState: - result = trigger->trigger->readOutput_OverLimit(trigger->index, status); - break; // XXX: Backport - case kRisingPulse: - case kFallingPulse: - *status = ANALOG_TRIGGER_PULSE_OUTPUT_ERROR; - return false; - } - return result; -} - -//// Float JNA Hack -// Float -int getAnalogSampleRateIntHack(int32_t* status) { - return floatToInt(getAnalogSampleRate(status)); -} - -int getAnalogVoltageIntHack(void* analog_port_pointer, int32_t* status) { - return floatToInt(getAnalogVoltage(analog_port_pointer, status)); -} - -int getAnalogAverageVoltageIntHack(void* analog_port_pointer, int32_t* status) { - return floatToInt(getAnalogAverageVoltage(analog_port_pointer, status)); -} - -// Doubles -void setAnalogSampleRateIntHack(int samplesPerSecond, int32_t* status) { - setAnalogSampleRate(intToFloat(samplesPerSecond), status); -} - -int32_t getAnalogVoltsToValueIntHack(void* analog_port_pointer, int voltage, - int32_t* status) { - return getAnalogVoltsToValue(analog_port_pointer, intToFloat(voltage), - status); -} - -void setAnalogTriggerLimitsVoltageIntHack(void* analog_trigger_pointer, - int lower, int upper, - int32_t* status) { - setAnalogTriggerLimitsVoltage(analog_trigger_pointer, intToFloat(lower), - intToFloat(upper), status); -} - -} // extern "C" diff --git a/hal/lib/athena/AnalogAccumulator.cpp b/hal/lib/athena/AnalogAccumulator.cpp new file mode 100644 index 0000000000..a758e7380d --- /dev/null +++ b/hal/lib/athena/AnalogAccumulator.cpp @@ -0,0 +1,145 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "HAL/AnalogAccumulator.h" + +#include "AnalogInternal.h" +#include "HAL/HAL.h" + +using namespace hal; + +extern "C" { +/** +*Is the channel attached to an accumulator. +* +*@return The analog channel is attached to an accumulator. +*/ +bool isAccumulatorChannel(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + for (uint32_t i = 0; i < kAccumulatorNumChannels; i++) { + if (port->port.pin == kAccumulatorChannels[i]) return true; + } + return false; +} + +/** + * Initialize the accumulator. + */ +void initAccumulator(void* analog_port_pointer, int32_t* status) { + setAccumulatorCenter(analog_port_pointer, 0, status); + resetAccumulator(analog_port_pointer, status); +} + +/** + * Resets the accumulator to the initial value. + */ +void resetAccumulator(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (port->accumulator == nullptr) { + *status = NULL_PARAMETER; + return; + } + port->accumulator->strobeReset(status); +} + +/** + * Set the center value of the accumulator. + * + * The center value is subtracted from each A/D value before it is added to the + * accumulator. This is used for the center value of devices like gyros and + * accelerometers to make integration work and to take the device offset into + * account when integrating. + * + * This center value is based on the output of the oversampled and averaged + * source from channel 1. Because of this, any non-zero oversample bits will + * affect the size of the value for this field. + */ +void setAccumulatorCenter(void* analog_port_pointer, int32_t center, + int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (port->accumulator == nullptr) { + *status = NULL_PARAMETER; + return; + } + port->accumulator->writeCenter(center, status); +} + +/** + * Set the accumulator's deadband. + */ +void setAccumulatorDeadband(void* analog_port_pointer, int32_t deadband, + int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (port->accumulator == nullptr) { + *status = NULL_PARAMETER; + return; + } + port->accumulator->writeDeadband(deadband, status); +} + +/** + * Read the accumulated value. + * + * Read the value that has been accumulating on channel 1. + * The accumulator is attached after the oversample and average engine. + * + * @return The 64-bit value accumulated since the last Reset(). + */ +int64_t getAccumulatorValue(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (port->accumulator == nullptr) { + *status = NULL_PARAMETER; + return 0; + } + int64_t value = port->accumulator->readOutput_Value(status); + return value; +} + +/** + * Read the number of accumulated values. + * + * Read the count of the accumulated values since the accumulator was last + * Reset(). + * + * @return The number of times samples from the channel were accumulated. + */ +uint32_t getAccumulatorCount(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (port->accumulator == nullptr) { + *status = NULL_PARAMETER; + return 0; + } + return port->accumulator->readOutput_Count(status); +} + +/** + * Read the accumulated value and the number of accumulated values atomically. + * + * This function reads the value and count from the FPGA atomically. + * This can be used for averaging. + * + * @param value Pointer to the 64-bit accumulated output. + * @param count Pointer to the number of accumulation cycles. + */ +void getAccumulatorOutput(void* analog_port_pointer, int64_t* value, + uint32_t* count, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (port->accumulator == nullptr) { + *status = NULL_PARAMETER; + return; + } + if (value == nullptr || count == nullptr) { + *status = NULL_PARAMETER; + return; + } + + tAccumulator::tOutput output = port->accumulator->readOutput(status); + + *value = output.Value; + *count = output.Count; +} +} diff --git a/hal/lib/athena/AnalogInput.cpp b/hal/lib/athena/AnalogInput.cpp new file mode 100644 index 0000000000..c2ab7c85b3 --- /dev/null +++ b/hal/lib/athena/AnalogInput.cpp @@ -0,0 +1,354 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "HAL/AnalogInput.h" + +#include + +#include "AnalogInternal.h" +#include "FRC_NetworkCommunication/AICalibration.h" +#include "HAL/AnalogAccumulator.h" +#include "HAL/HAL.h" +#include "HAL/cpp/priority_mutex.h" + +using namespace hal; + +static bool analogSampleRateSet = false; + +extern "C" { +/** + * Initialize the analog input port using the given port object. + */ +void* initializeAnalogInputPort(void* port_pointer, int32_t* status) { + initializeAnalog(status); + Port* port = (Port*)port_pointer; + + // Initialize port structure + AnalogPort* analog_port = new AnalogPort(); + analog_port->port = *port; + if (isAccumulatorChannel(analog_port, status)) { + analog_port->accumulator = tAccumulator::create(port->pin, status); + } else + analog_port->accumulator = nullptr; + + // Set default configuration + analogInputSystem->writeScanList(port->pin, port->pin, status); + setAnalogAverageBits(analog_port, kDefaultAverageBits, status); + setAnalogOversampleBits(analog_port, kDefaultOversampleBits, status); + return analog_port; +} + +void freeAnalogInputPort(void* analog_port_pointer) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (!port) return; + delete port->accumulator; + delete port; +} + +/** + * Check that the analog module number is valid. + * + * @return Analog module is valid and present + */ +bool checkAnalogModule(uint8_t module) { return module == 1; } + +/** + * Check that the analog output channel number is value. + * Verify that the analog channel number is one of the legal channel numbers. + * Channel numbers are 0-based. + * + * @return Analog channel is valid + */ +bool checkAnalogInputChannel(uint32_t pin) { + if (pin < kAnalogInputPins) return true; + return false; +} + +/** + * Set the sample rate. + * + * This is a global setting for the Athena and effects all channels. + * + * @param samplesPerSecond The number of samples per channel per second. + */ +void setAnalogSampleRate(double samplesPerSecond, int32_t* status) { + // TODO: This will change when variable size scan lists are implemented. + // TODO: Need float comparison with epsilon. + // wpi_assert(!sampleRateSet || GetSampleRate() == samplesPerSecond); + analogSampleRateSet = true; + + // Compute the convert rate + uint32_t ticksPerSample = (uint32_t)((float)kTimebase / samplesPerSecond); + uint32_t ticksPerConversion = + ticksPerSample / getAnalogNumChannelsToActivate(status); + // ticksPerConversion must be at least 80 + if (ticksPerConversion < 80) { + if ((*status) >= 0) *status = SAMPLE_RATE_TOO_HIGH; + ticksPerConversion = 80; + } + + // Atomically set the scan size and the convert rate so that the sample rate + // is constant + tAI::tConfig config; + config.ScanSize = getAnalogNumChannelsToActivate(status); + config.ConvertRate = ticksPerConversion; + analogInputSystem->writeConfig(config, status); + + // Indicate that the scan size has been commited to hardware. + setAnalogNumChannelsToActivate(0); +} + +/** + * Get the current sample rate. + * + * This assumes one entry in the scan list. + * This is a global setting for the Athena and effects all channels. + * + * @return Sample rate. + */ +float getAnalogSampleRate(int32_t* status) { + uint32_t ticksPerConversion = analogInputSystem->readLoopTiming(status); + uint32_t ticksPerSample = + ticksPerConversion * getAnalogNumActiveChannels(status); + return (float)kTimebase / (float)ticksPerSample; +} + +/** + * Set the number of averaging bits. + * + * This sets the number of averaging bits. The actual number of averaged samples + * is 2**bits. Use averaging to improve the stability of your measurement at the + * expense of sampling rate. The averaging is done automatically in the FPGA. + * + * @param analog_port_pointer Pointer to the analog port to configure. + * @param bits Number of bits to average. + */ +void setAnalogAverageBits(void* analog_port_pointer, uint32_t bits, + int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + analogInputSystem->writeAverageBits(port->port.pin, bits, status); +} + +/** + * Get the number of averaging bits. + * + * This gets the number of averaging bits from the FPGA. The actual number of + * averaged samples is 2**bits. The averaging is done automatically in the FPGA. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return Bits to average. + */ +uint32_t getAnalogAverageBits(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + uint32_t result = analogInputSystem->readAverageBits(port->port.pin, status); + return result; +} + +/** + * Set the number of oversample bits. + * + * This sets the number of oversample bits. The actual number of oversampled + * values is 2**bits. Use oversampling to improve the resolution of your + * measurements at the expense of sampling rate. The oversampling is done + * automatically in the FPGA. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @param bits Number of bits to oversample. + */ +void setAnalogOversampleBits(void* analog_port_pointer, uint32_t bits, + int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + analogInputSystem->writeOversampleBits(port->port.pin, bits, status); +} + +/** + * Get the number of oversample bits. + * + * This gets the number of oversample bits from the FPGA. The actual number of + * oversampled values is 2**bits. The oversampling is done automatically in the + * FPGA. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return Bits to oversample. + */ +uint32_t getAnalogOversampleBits(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + uint32_t result = + analogInputSystem->readOversampleBits(port->port.pin, status); + return result; +} + +/** + * Get a sample straight from the channel on this module. + * + * The sample is a 12-bit value representing the 0V to 5V range of the A/D + * converter in the module. The units are in A/D converter codes. Use + * GetVoltage() to get the analog value in calibrated units. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return A sample straight from the channel on this module. + */ +int16_t getAnalogValue(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + int16_t value; + if (!checkAnalogInputChannel(port->port.pin)) { + return 0; + } + + tAI::tReadSelect readSelect; + readSelect.Channel = port->port.pin; + readSelect.Averaged = false; + + { + std::lock_guard sync(analogRegisterWindowMutex); + analogInputSystem->writeReadSelect(readSelect, status); + analogInputSystem->strobeLatchOutput(status); + value = (int16_t)analogInputSystem->readOutput(status); + } + + return value; +} + +/** + * Get a sample from the output of the oversample and average engine for the + * channel. + * + * The sample is 12-bit + the value configured in SetOversampleBits(). + * The value configured in SetAverageBits() will cause this value to be averaged + * 2**bits number of samples. This is not a sliding window. The sample will not + * change until 2**(OversamplBits + AverageBits) samples have been acquired from + * the module on this channel. Use GetAverageVoltage() to get the analog value + * in calibrated units. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return A sample from the oversample and average engine for the channel. + */ +int32_t getAnalogAverageValue(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + int32_t value; + if (!checkAnalogInputChannel(port->port.pin)) { + return 0; + } + + tAI::tReadSelect readSelect; + readSelect.Channel = port->port.pin; + readSelect.Averaged = true; + + { + std::lock_guard sync(analogRegisterWindowMutex); + analogInputSystem->writeReadSelect(readSelect, status); + analogInputSystem->strobeLatchOutput(status); + value = (int32_t)analogInputSystem->readOutput(status); + } + + return value; +} + +/** + * Get a scaled sample straight from the channel on this module. + * + * The value is scaled to units of Volts using the calibrated scaling data from + * GetLSBWeight() and GetOffset(). + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return A scaled sample straight from the channel on this module. + */ +float getAnalogVoltage(void* analog_port_pointer, int32_t* status) { + int16_t value = getAnalogValue(analog_port_pointer, status); + uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status); + int32_t offset = getAnalogOffset(analog_port_pointer, status); + float voltage = LSBWeight * 1.0e-9 * value - offset * 1.0e-9; + return voltage; +} + +/** + * Get a scaled sample from the output of the oversample and average engine for + * the channel. + * + * The value is scaled to units of Volts using the calibrated scaling data from + * GetLSBWeight() and GetOffset(). Using oversampling will cause this value to + * be higher resolution, but it will update more slowly. Using averaging will + * cause this value to be more stable, but it will update more slowly. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return A scaled sample from the output of the oversample and average engine + * for the channel. + */ +float getAnalogAverageVoltage(void* analog_port_pointer, int32_t* status) { + int32_t value = getAnalogAverageValue(analog_port_pointer, status); + uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status); + int32_t offset = getAnalogOffset(analog_port_pointer, status); + uint32_t oversampleBits = + getAnalogOversampleBits(analog_port_pointer, status); + float voltage = + ((LSBWeight * 1.0e-9 * value) / (float)(1 << oversampleBits)) - + offset * 1.0e-9; + return voltage; +} + +/** + * Convert a voltage to a raw value for a specified channel. + * + * This process depends on the calibration of each channel, so the channel + * must be specified. + * + * @todo This assumes raw values. Oversampling not supported as is. + * + * @param analog_port_pointer Pointer to the analog port to use. + * @param voltage The voltage to convert. + * @return The raw value for the channel. + */ +int32_t getAnalogVoltsToValue(void* analog_port_pointer, double voltage, + int32_t* status) { + if (voltage > 5.0) { + voltage = 5.0; + *status = VOLTAGE_OUT_OF_RANGE; + } + if (voltage < 0.0) { + voltage = 0.0; + *status = VOLTAGE_OUT_OF_RANGE; + } + uint32_t LSBWeight = getAnalogLSBWeight(analog_port_pointer, status); + int32_t offset = getAnalogOffset(analog_port_pointer, status); + int32_t value = (int32_t)((voltage + offset * 1.0e-9) / (LSBWeight * 1.0e-9)); + return value; +} + +/** + * Get the factory scaling least significant bit weight constant. + * The least significant bit weight constant for the channel that was calibrated + * in manufacturing and stored in an eeprom in the module. + * + * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9) + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return Least significant bit weight. + */ +uint32_t getAnalogLSBWeight(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + uint32_t lsbWeight = FRC_NetworkCommunication_nAICalibration_getLSBWeight( + 0, port->port.pin, status); // XXX: aiSystemIndex == 0? + return lsbWeight; +} + +/** + * Get the factory scaling offset constant. + * The offset constant for the channel that was calibrated in manufacturing and + * stored in an eeprom in the module. + * + * Volts = ((LSB_Weight * 1e-9) * raw) - (Offset * 1e-9) + * + * @param analog_port_pointer Pointer to the analog port to use. + * @return Offset constant. + */ +int32_t getAnalogOffset(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + int32_t offset = FRC_NetworkCommunication_nAICalibration_getOffset( + 0, port->port.pin, status); // XXX: aiSystemIndex == 0? + return offset; +} +} diff --git a/hal/lib/athena/AnalogInternal.cpp b/hal/lib/athena/AnalogInternal.cpp new file mode 100644 index 0000000000..2ca5bc2045 --- /dev/null +++ b/hal/lib/athena/AnalogInternal.cpp @@ -0,0 +1,76 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "AnalogInternal.h" + +#include "ChipObject.h" +#include "HAL/AnalogInput.h" +#include "HAL/Port.h" +#include "HAL/cpp/priority_mutex.h" + +namespace hal { +priority_recursive_mutex analogRegisterWindowMutex; +tAI* analogInputSystem = nullptr; +tAO* analogOutputSystem = nullptr; +static uint32_t analogNumChannelsToActivate = 0; + +bool analogSystemInitialized = false; + +/** + * Initialize the analog System. + */ +void initializeAnalog(int32_t* status) { + std::lock_guard sync(analogRegisterWindowMutex); + if (analogSystemInitialized) return; + analogInputSystem = tAI::create(status); + analogOutputSystem = tAO::create(status); + setAnalogNumChannelsToActivate(kAnalogInputPins); + setAnalogSampleRate(kDefaultSampleRate, status); + analogSystemInitialized = true; +} + +/** + * Return the number of channels on the module in use. + * + * @return Active channels. + */ +uint32_t getAnalogNumActiveChannels(int32_t* status) { + uint32_t scanSize = analogInputSystem->readConfig_ScanSize(status); + if (scanSize == 0) return 8; + return scanSize; +} + +/** + * Get the number of active channels. + * + * This is an internal function to allow the atomic update of both the + * number of active channels and the sample rate. + * + * When the number of channels changes, use the new value. Otherwise, + * return the curent value. + * + * @return Value to write to the active channels field. + */ +uint32_t getAnalogNumChannelsToActivate(int32_t* status) { + if (analogNumChannelsToActivate == 0) + return getAnalogNumActiveChannels(status); + return analogNumChannelsToActivate; +} + +/** + * Set the number of active channels. + * + * Store the number of active channels to set. Don't actually commit to + * hardware + * until SetSampleRate(). + * + * @param channels Number of active channels. + */ +void setAnalogNumChannelsToActivate(uint32_t channels) { + analogNumChannelsToActivate = channels; +} +} diff --git a/hal/lib/athena/AnalogInternal.h b/hal/lib/athena/AnalogInternal.h new file mode 100644 index 0000000000..a9ae1e00fe --- /dev/null +++ b/hal/lib/athena/AnalogInternal.h @@ -0,0 +1,41 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include "ChipObject.h" +#include "HAL/Port.h" +#include "HAL/cpp/priority_mutex.h" + +namespace hal { +constexpr long kTimebase = 40000000; ///< 40 MHz clock +constexpr long kDefaultOversampleBits = 0; +constexpr long kDefaultAverageBits = 7; +constexpr float kDefaultSampleRate = 50000.0; +constexpr uint32_t kAnalogInputPins = 8; +constexpr uint32_t kAnalogOutputPins = 2; +constexpr uint32_t kAccumulatorNumChannels = 2; +static const uint32_t kAccumulatorChannels[] = {0, 1}; + +extern tAI* analogInputSystem; +extern tAO* analogOutputSystem; +extern priority_recursive_mutex analogRegisterWindowMutex; + +struct AnalogPort { + Port port; + tAccumulator* accumulator; +}; + +uint32_t getAnalogNumActiveChannels(int32_t* status); +uint32_t getAnalogNumChannelsToActivate(int32_t* status); +void setAnalogNumChannelsToActivate(uint32_t channels); +void initializeAnalog(int32_t* status); + +extern bool analogSystemInitialized; +} diff --git a/hal/lib/athena/AnalogOutput.cpp b/hal/lib/athena/AnalogOutput.cpp new file mode 100644 index 0000000000..d50392cd2c --- /dev/null +++ b/hal/lib/athena/AnalogOutput.cpp @@ -0,0 +1,70 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "HAL/AnalogOutput.h" + +#include "AnalogInternal.h" + +using namespace hal; + +extern "C" { + +/** + * Initialize the analog output port using the given port object. + */ +void* initializeAnalogOutputPort(void* port_pointer, int32_t* status) { + initializeAnalog(status); + Port* port = (Port*)port_pointer; + + // Initialize port structure + AnalogPort* analog_port = new AnalogPort(); + analog_port->port = *port; + analog_port->accumulator = nullptr; + return analog_port; +} + +void freeAnalogOutputPort(void* analog_port_pointer) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + if (!port) return; + delete port->accumulator; + delete port; +} + +/** + * Check that the analog output channel number is value. + * Verify that the analog channel number is one of the legal channel numbers. + * Channel numbers are 0-based. + * + * @return Analog channel is valid + */ +bool checkAnalogOutputChannel(uint32_t pin) { + if (pin < kAnalogOutputPins) return true; + return false; +} + +void setAnalogOutput(void* analog_port_pointer, double voltage, + int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + + uint16_t rawValue = (uint16_t)(voltage / 5.0 * 0x1000); + + if (voltage < 0.0) + rawValue = 0; + else if (voltage > 5.0) + rawValue = 0x1000; + + analogOutputSystem->writeMXP(port->port.pin, rawValue, status); +} + +double getAnalogOutput(void* analog_port_pointer, int32_t* status) { + AnalogPort* port = (AnalogPort*)analog_port_pointer; + + uint16_t rawValue = analogOutputSystem->readMXP(port->port.pin, status); + + return rawValue * 5.0 / 0x1000; +} +} diff --git a/hal/lib/athena/AnalogTrigger.cpp b/hal/lib/athena/AnalogTrigger.cpp new file mode 100644 index 0000000000..a3d6239625 --- /dev/null +++ b/hal/lib/athena/AnalogTrigger.cpp @@ -0,0 +1,159 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. 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 the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "HAL/AnalogTrigger.h" + +#include "AnalogInternal.h" +#include "HAL/AnalogInput.h" +#include "HAL/cpp/Resource.h" + +using namespace hal; + +extern "C" { +struct trigger_t { + tAnalogTrigger* trigger; + AnalogPort* port; + uint32_t index; +}; +typedef struct trigger_t AnalogTrigger; + +static hal::Resource* triggers = nullptr; + +void* initializeAnalogTrigger(void* port_pointer, uint32_t* index, + int32_t* status) { + Port* port = (Port*)port_pointer; + hal::Resource::CreateResourceObject(&triggers, tAnalogTrigger::kNumSystems); + + AnalogTrigger* trigger = new AnalogTrigger(); + trigger->port = (AnalogPort*)initializeAnalogInputPort(port, status); + trigger->index = triggers->Allocate("Analog Trigger"); + *index = trigger->index; + // TODO: if (index == ~0ul) { CloneError(triggers); return; } + + trigger->trigger = tAnalogTrigger::create(trigger->index, status); + trigger->trigger->writeSourceSelect_Channel(port->pin, status); + + return trigger; +} + +void cleanAnalogTrigger(void* analog_trigger_pointer, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + if (!trigger) return; + triggers->Free(trigger->index); + delete trigger->trigger; + freeAnalogInputPort(trigger->port); + delete trigger; +} + +void setAnalogTriggerLimitsRaw(void* analog_trigger_pointer, int32_t lower, + int32_t upper, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + if (lower > upper) { + *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR; + } + trigger->trigger->writeLowerLimit(lower, status); + trigger->trigger->writeUpperLimit(upper, status); +} + +/** + * Set the upper and lower limits of the analog trigger. + * The limits are given as floating point voltage values. + */ +void setAnalogTriggerLimitsVoltage(void* analog_trigger_pointer, double lower, + double upper, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + if (lower > upper) { + *status = ANALOG_TRIGGER_LIMIT_ORDER_ERROR; + } + // TODO: This depends on the averaged setting. Only raw values will work as + // is. + trigger->trigger->writeLowerLimit( + getAnalogVoltsToValue(trigger->port, lower, status), status); + trigger->trigger->writeUpperLimit( + getAnalogVoltsToValue(trigger->port, upper, status), status); +} + +/** + * Configure the analog trigger to use the averaged vs. raw values. + * If the value is true, then the averaged value is selected for the analog + * trigger, otherwise the immediate value is used. + */ +void setAnalogTriggerAveraged(void* analog_trigger_pointer, + bool useAveragedValue, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + if (trigger->trigger->readSourceSelect_Filter(status) != 0) { + *status = INCOMPATIBLE_STATE; + // TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not + // support average and filtering at the same time."); + } + trigger->trigger->writeSourceSelect_Averaged(useAveragedValue, status); +} + +/** + * Configure the analog trigger to use a filtered value. + * The analog trigger will operate with a 3 point average rejection filter. This + * is designed to help with 360 degree pot applications for the period where the + * pot crosses through zero. + */ +void setAnalogTriggerFiltered(void* analog_trigger_pointer, + bool useFilteredValue, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + if (trigger->trigger->readSourceSelect_Averaged(status) != 0) { + *status = INCOMPATIBLE_STATE; + // TODO: wpi_setWPIErrorWithContext(IncompatibleMode, "Hardware does not " + // "support average and filtering at the same time."); + } + trigger->trigger->writeSourceSelect_Filter(useFilteredValue, status); +} + +/** + * Return the InWindow output of the analog trigger. + * True if the analog input is between the upper and lower limits. + * @return The InWindow output of the analog trigger. + */ +bool getAnalogTriggerInWindow(void* analog_trigger_pointer, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + return trigger->trigger->readOutput_InHysteresis(trigger->index, status) != 0; +} + +/** + * Return the TriggerState output of the analog trigger. + * True if above upper limit. + * False if below lower limit. + * If in Hysteresis, maintain previous state. + * @return The TriggerState output of the analog trigger. + */ +bool getAnalogTriggerTriggerState(void* analog_trigger_pointer, + int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + return trigger->trigger->readOutput_OverLimit(trigger->index, status) != 0; +} + +/** + * Get the state of the analog trigger output. + * @return The state of the analog trigger output. + */ +bool getAnalogTriggerOutput(void* analog_trigger_pointer, + AnalogTriggerType type, int32_t* status) { + AnalogTrigger* trigger = (AnalogTrigger*)analog_trigger_pointer; + bool result = false; + switch (type) { + case kInWindow: + result = + trigger->trigger->readOutput_InHysteresis(trigger->index, status); + break; // XXX: Backport + case kState: + result = trigger->trigger->readOutput_OverLimit(trigger->index, status); + break; // XXX: Backport + case kRisingPulse: + case kFallingPulse: + *status = ANALOG_TRIGGER_PULSE_OUTPUT_ERROR; + return false; + } + return result; +} +}