| /*----------------------------------------------------------------------------*/ |
| /* 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 "Counter.h" |
| #include "AnalogTrigger.h" |
| #include "DigitalInput.h" |
| #include "NetworkCommunication/UsageReporting.h" |
| #include "Resource.h" |
| #include "WPIErrors.h" |
| |
| static Resource *counters = NULL; |
| |
| /** |
| * Create an instance of a counter object. |
| * This creates a ChipObject counter and initializes status variables appropriately |
| */ |
| void Counter::InitCounter(Mode mode) |
| { |
| Resource::CreateResourceObject(&counters, tCounter::kNumSystems); |
| UINT32 index = counters->Allocate("Counter", this); |
| if (index == ~0ul) |
| { |
| return; |
| } |
| m_index = index; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter = tCounter::create(m_index, &localStatus); |
| m_counter->writeConfig_Mode(mode, &localStatus); |
| m_upSource = NULL; |
| m_downSource = NULL; |
| m_allocatedUpSource = false; |
| m_allocatedDownSource = false; |
| m_counter->writeTimerConfig_AverageSize(1, &localStatus); |
| wpi_setError(localStatus); |
| |
| nUsageReporting::report(nUsageReporting::kResourceType_Counter, index, mode); |
| } |
| |
| /** |
| * Create an instance of a counter where no sources are selected. |
| * Then they all must be selected by calling functions to specify the upsource and the downsource |
| * independently. |
| */ |
| Counter::Counter() : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| } |
| |
| /** |
| * Create an instance of a counter from a Digital Input. |
| * This is used if an existing digital input is to be shared by multiple other objects such |
| * as encoders. |
| */ |
| Counter::Counter(DigitalSource *source) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| SetUpSource(source); |
| ClearDownSource(); |
| } |
| |
| Counter::Counter(DigitalSource &source) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| SetUpSource(&source); |
| ClearDownSource(); |
| } |
| |
| /** |
| * Create an instance of a Counter object. |
| * Create an up-Counter instance given a channel. The default digital module is assumed. |
| */ |
| Counter::Counter(UINT32 channel) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| SetUpSource(channel); |
| ClearDownSource(); |
| } |
| |
| /** |
| * Create an instance of a Counter object. |
| * Create an instance of an up-Counter given a digital module and a channel. |
| * @param moduleNumber The digital module (1 or 2). |
| * @param channel The channel in the digital module |
| */ |
| Counter::Counter(UINT8 moduleNumber, UINT32 channel) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| SetUpSource(moduleNumber, channel); |
| ClearDownSource(); |
| } |
| |
| /** |
| * Create an instance of a Counter object. |
| * Create an instance of a simple up-Counter given an analog trigger. |
| * Use the trigger state output from the analog trigger. |
| */ |
| Counter::Counter(AnalogTrigger *trigger) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| SetUpSource(trigger->CreateOutput(AnalogTriggerOutput::kState)); |
| ClearDownSource(); |
| m_allocatedUpSource = true; |
| } |
| |
| Counter::Counter(AnalogTrigger &trigger) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| InitCounter(); |
| SetUpSource(trigger.CreateOutput(AnalogTriggerOutput::kState)); |
| ClearDownSource(); |
| m_allocatedUpSource = true; |
| } |
| |
| Counter::Counter(EncodingType encodingType, DigitalSource *upSource, DigitalSource *downSource, bool inverted) : |
| m_upSource(NULL), |
| m_downSource(NULL), |
| m_counter(NULL) |
| { |
| if (encodingType != k1X && encodingType != k2X) |
| { |
| wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports 1X and 2X quadrature decoding."); |
| return; |
| } |
| InitCounter(kExternalDirection); |
| SetUpSource(upSource); |
| SetDownSource(downSource); |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| |
| if (encodingType == k1X) |
| { |
| SetUpSourceEdge(true, false); |
| m_counter->writeTimerConfig_AverageSize(1, &localStatus); |
| } |
| else |
| { |
| SetUpSourceEdge(true, true); |
| m_counter->writeTimerConfig_AverageSize(2, &localStatus); |
| } |
| |
| wpi_setError(localStatus); |
| SetDownSourceEdge(inverted, true); |
| } |
| |
| /** |
| * Delete the Counter object. |
| */ |
| Counter::~Counter() |
| { |
| SetUpdateWhenEmpty(true); |
| if (m_allocatedUpSource) |
| { |
| delete m_upSource; |
| m_upSource = NULL; |
| } |
| if (m_allocatedDownSource) |
| { |
| delete m_downSource; |
| m_downSource = NULL; |
| } |
| delete m_counter; |
| m_counter = NULL; |
| counters->Free(m_index, this); |
| } |
| |
| /** |
| * Set the up source for the counter as digital input channel and slot. |
| * |
| * @param moduleNumber The digital module (1 or 2). |
| * @param channel The digital channel (1..14). |
| */ |
| void Counter::SetUpSource(UINT8 moduleNumber, UINT32 channel) |
| { |
| if (StatusIsFatal()) return; |
| SetUpSource(new DigitalInput(moduleNumber, channel)); |
| m_allocatedUpSource = true; |
| } |
| |
| /** |
| * Set the upsource for the counter as a digital input channel. |
| * The slot will be the default digital module slot. |
| */ |
| void Counter::SetUpSource(UINT32 channel) |
| { |
| if (StatusIsFatal()) return; |
| SetUpSource(GetDefaultDigitalModule(), channel); |
| m_allocatedUpSource = true; |
| } |
| |
| /** |
| * Set the up counting source to be an analog trigger. |
| * @param analogTrigger The analog trigger object that is used for the Up Source |
| * @param triggerType The analog trigger output that will trigger the counter. |
| */ |
| void Counter::SetUpSource(AnalogTrigger *analogTrigger, AnalogTriggerOutput::Type triggerType) |
| { |
| if (StatusIsFatal()) return; |
| SetUpSource(analogTrigger->CreateOutput(triggerType)); |
| m_allocatedUpSource = true; |
| } |
| |
| /** |
| * Set the up counting source to be an analog trigger. |
| * @param analogTrigger The analog trigger object that is used for the Up Source |
| * @param triggerType The analog trigger output that will trigger the counter. |
| */ |
| void Counter::SetUpSource(AnalogTrigger &analogTrigger, AnalogTriggerOutput::Type triggerType) |
| { |
| SetUpSource(&analogTrigger, triggerType); |
| } |
| |
| /** |
| * Set the source object that causes the counter to count up. |
| * Set the up counting DigitalSource. |
| */ |
| void Counter::SetUpSource(DigitalSource *source) |
| { |
| if (StatusIsFatal()) return; |
| if (m_allocatedUpSource) |
| { |
| delete m_upSource; |
| m_upSource = NULL; |
| m_allocatedUpSource = false; |
| } |
| m_upSource = source; |
| if (m_upSource->StatusIsFatal()) |
| { |
| CloneError(m_upSource); |
| } |
| else |
| { |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_UpSource_Module(source->GetModuleForRouting(), &localStatus); |
| m_counter->writeConfig_UpSource_Channel(source->GetChannelForRouting(), &localStatus); |
| m_counter->writeConfig_UpSource_AnalogTrigger(source->GetAnalogTriggerForRouting(), &localStatus); |
| |
| if(m_counter->readConfig_Mode(&localStatus) == kTwoPulse || |
| m_counter->readConfig_Mode(&localStatus) == kExternalDirection) |
| { |
| SetUpSourceEdge(true, false); |
| } |
| m_counter->strobeReset(&localStatus); |
| wpi_setError(localStatus); |
| } |
| } |
| |
| /** |
| * Set the source object that causes the counter to count up. |
| * Set the up counting DigitalSource. |
| */ |
| void Counter::SetUpSource(DigitalSource &source) |
| { |
| SetUpSource(&source); |
| } |
| |
| /** |
| * Set the edge sensitivity on an up counting source. |
| * Set the up source to either detect rising edges or falling edges. |
| */ |
| void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge) |
| { |
| if (StatusIsFatal()) return; |
| if (m_upSource == NULL) |
| { |
| wpi_setWPIErrorWithContext(NullParameter, "Must set non-NULL UpSource before setting UpSourceEdge"); |
| } |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_UpRisingEdge(risingEdge, &localStatus); |
| m_counter->writeConfig_UpFallingEdge(fallingEdge, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Disable the up counting source to the counter. |
| */ |
| void Counter::ClearUpSource() |
| { |
| if (StatusIsFatal()) return; |
| if (m_allocatedUpSource) |
| { |
| delete m_upSource; |
| m_upSource = NULL; |
| m_allocatedUpSource = false; |
| } |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| bool state = m_counter->readConfig_Enable(&localStatus); |
| m_counter->writeConfig_Enable(false, &localStatus); |
| m_counter->writeConfig_UpFallingEdge(false, &localStatus); |
| m_counter->writeConfig_UpRisingEdge(false, &localStatus); |
| // Index 0 of digital is always 0. |
| m_counter->writeConfig_UpSource_Channel(0, &localStatus); |
| m_counter->writeConfig_UpSource_AnalogTrigger(false, &localStatus); |
| m_counter->writeConfig_Enable(state, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Set the down counting source to be a digital input channel. |
| * The slot will be set to the default digital module slot. |
| */ |
| void Counter::SetDownSource(UINT32 channel) |
| { |
| if (StatusIsFatal()) return; |
| SetDownSource(new DigitalInput(channel)); |
| m_allocatedDownSource = true; |
| } |
| |
| /** |
| * Set the down counting source to be a digital input slot and channel. |
| * |
| * @param moduleNumber The digital module (1 or 2). |
| * @param channel The digital channel (1..14). |
| */ |
| void Counter::SetDownSource(UINT8 moduleNumber, UINT32 channel) |
| { |
| if (StatusIsFatal()) return; |
| SetDownSource(new DigitalInput(moduleNumber, channel)); |
| m_allocatedDownSource = true; |
| } |
| |
| /** |
| * Set the down counting source to be an analog trigger. |
| * @param analogTrigger The analog trigger object that is used for the Down Source |
| * @param triggerType The analog trigger output that will trigger the counter. |
| */ |
| void Counter::SetDownSource(AnalogTrigger *analogTrigger, AnalogTriggerOutput::Type triggerType) |
| { |
| if (StatusIsFatal()) return; |
| SetDownSource(analogTrigger->CreateOutput(triggerType)); |
| m_allocatedDownSource = true; |
| } |
| |
| /** |
| * Set the down counting source to be an analog trigger. |
| * @param analogTrigger The analog trigger object that is used for the Down Source |
| * @param triggerType The analog trigger output that will trigger the counter. |
| */ |
| void Counter::SetDownSource(AnalogTrigger &analogTrigger, AnalogTriggerOutput::Type triggerType) |
| { |
| SetDownSource(&analogTrigger, triggerType); |
| } |
| |
| /** |
| * Set the source object that causes the counter to count down. |
| * Set the down counting DigitalSource. |
| */ |
| void Counter::SetDownSource(DigitalSource *source) |
| { |
| if (StatusIsFatal()) return; |
| if (m_allocatedDownSource) |
| { |
| delete m_downSource; |
| m_downSource = NULL; |
| m_allocatedDownSource = false; |
| } |
| m_downSource = source; |
| if (m_downSource->StatusIsFatal()) |
| { |
| CloneError(m_downSource); |
| } |
| else |
| { |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| unsigned char mode = m_counter->readConfig_Mode(&localStatus); |
| if (mode != kTwoPulse && mode != kExternalDirection) |
| { |
| wpi_setWPIErrorWithContext(ParameterOutOfRange, "Counter only supports DownSource in TwoPulse and ExternalDirection modes."); |
| return; |
| } |
| m_counter->writeConfig_DownSource_Module(source->GetModuleForRouting(), &localStatus); |
| m_counter->writeConfig_DownSource_Channel(source->GetChannelForRouting(), &localStatus); |
| m_counter->writeConfig_DownSource_AnalogTrigger(source->GetAnalogTriggerForRouting(), &localStatus); |
| |
| SetDownSourceEdge(true, false); |
| m_counter->strobeReset(&localStatus); |
| wpi_setError(localStatus); |
| } |
| } |
| |
| /** |
| * Set the source object that causes the counter to count down. |
| * Set the down counting DigitalSource. |
| */ |
| void Counter::SetDownSource(DigitalSource &source) |
| { |
| SetDownSource(&source); |
| } |
| |
| /** |
| * Set the edge sensitivity on a down counting source. |
| * Set the down source to either detect rising edges or falling edges. |
| */ |
| void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge) |
| { |
| if (StatusIsFatal()) return; |
| if (m_downSource == NULL) |
| { |
| wpi_setWPIErrorWithContext(NullParameter, "Must set non-NULL DownSource before setting DownSourceEdge"); |
| } |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_DownRisingEdge(risingEdge, &localStatus); |
| m_counter->writeConfig_DownFallingEdge(fallingEdge, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Disable the down counting source to the counter. |
| */ |
| void Counter::ClearDownSource() |
| { |
| if (StatusIsFatal()) return; |
| if (m_allocatedDownSource) |
| { |
| delete m_downSource; |
| m_downSource = NULL; |
| m_allocatedDownSource = false; |
| } |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| bool state = m_counter->readConfig_Enable(&localStatus); |
| m_counter->writeConfig_Enable(false, &localStatus); |
| m_counter->writeConfig_DownFallingEdge(false, &localStatus); |
| m_counter->writeConfig_DownRisingEdge(false, &localStatus); |
| // Index 0 of digital is always 0. |
| m_counter->writeConfig_DownSource_Channel(0, &localStatus); |
| m_counter->writeConfig_DownSource_AnalogTrigger(false, &localStatus); |
| m_counter->writeConfig_Enable(state, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Set standard up / down counting mode on this counter. |
| * Up and down counts are sourced independently from two inputs. |
| */ |
| void Counter::SetUpDownCounterMode() |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_Mode(kTwoPulse, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Set external direction mode on this counter. |
| * Counts are sourced on the Up counter input. |
| * The Down counter input represents the direction to count. |
| */ |
| void Counter::SetExternalDirectionMode() |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_Mode(kExternalDirection, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Set Semi-period mode on this counter. |
| * Counts up on both rising and falling edges. |
| */ |
| void Counter::SetSemiPeriodMode(bool highSemiPeriod) |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_Mode(kSemiperiod, &localStatus); |
| m_counter->writeConfig_UpRisingEdge(highSemiPeriod, &localStatus); |
| SetUpdateWhenEmpty(false); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Configure the counter to count in up or down based on the length of the input pulse. |
| * This mode is most useful for direction sensitive gear tooth sensors. |
| * @param threshold The pulse length beyond which the counter counts the opposite direction. Units are seconds. |
| */ |
| void Counter::SetPulseLengthMode(float threshold) |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_Mode(kPulseLength, &localStatus); |
| m_counter->writeConfig_PulseLengthThreshold((UINT32)(threshold * 1.0e6) * kSystemClockTicksPerMicrosecond, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Start the Counter counting. |
| * This enables the counter and it starts accumulating counts from the associated |
| * input channel. The counter value is not reset on starting, and still has the previous value. |
| */ |
| void Counter::Start() |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_Enable(1, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Read the current counter value. |
| * Read the value at this instant. It may still be running, so it reflects the current value. Next |
| * time it is read, it might have a different value. |
| */ |
| INT32 Counter::Get() |
| { |
| if (StatusIsFatal()) return 0; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| INT32 value = m_counter->readOutput_Value(&localStatus); |
| wpi_setError(localStatus); |
| return value; |
| } |
| |
| /** |
| * Reset the Counter to zero. |
| * Set the counter value to zero. This doesn't effect the running state of the counter, just sets |
| * the current value to zero. |
| */ |
| void Counter::Reset() |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->strobeReset(&localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Stop the Counter. |
| * Stops the counting but doesn't effect the current value. |
| */ |
| void Counter::Stop() |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeConfig_Enable(0, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /* |
| * Get the Period of the most recent count. |
| * Returns the time interval of the most recent count. This can be used for velocity calculations |
| * to determine shaft speed. |
| * @returns The period of the last two pulses in units of seconds. |
| */ |
| double Counter::GetPeriod() |
| { |
| if (StatusIsFatal()) return 0.0; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| tCounter::tTimerOutput output = m_counter->readTimerOutput(&localStatus); |
| double period; |
| if (output.Stalled) |
| { |
| // Return infinity |
| double zero = 0.0; |
| period = 1.0 / zero; |
| } |
| else |
| { |
| // output.Period is a fixed point number that counts by 2 (24 bits, 25 integer bits) |
| period = (double)(output.Period << 1) / (double)output.Count; |
| } |
| wpi_setError(localStatus); |
| return period * 1.0e-6; |
| } |
| |
| /** |
| * Set the maximum period where the device is still considered "moving". |
| * Sets the maximum period where the device is considered moving. This value is used to determine |
| * the "stopped" state of the counter using the GetStopped method. |
| * @param maxPeriod The maximum period where the counted device is considered moving in |
| * seconds. |
| */ |
| void Counter::SetMaxPeriod(double maxPeriod) |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeTimerConfig_StallPeriod((UINT32)(maxPeriod * 1.0e6), &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Select whether you want to continue updating the event timer output when there are no samples captured. |
| * The output of the event timer has a buffer of periods that are averaged and posted to |
| * a register on the FPGA. When the timer detects that the event source has stopped |
| * (based on the MaxPeriod) the buffer of samples to be averaged is emptied. If you |
| * enable the update when empty, you will be notified of the stopped source and the event |
| * time will report 0 samples. If you disable update when empty, the most recent average |
| * will remain on the output until a new sample is acquired. You will never see 0 samples |
| * output (except when there have been no events since an FPGA reset) and you will likely not |
| * see the stopped bit become true (since it is updated at the end of an average and there are |
| * no samples to average). |
| */ |
| void Counter::SetUpdateWhenEmpty(bool enabled) |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| m_counter->writeTimerConfig_UpdateWhenEmpty(enabled, &localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * Determine if the clock is stopped. |
| * Determine if the clocked input is stopped based on the MaxPeriod value set using the |
| * SetMaxPeriod method. If the clock exceeds the MaxPeriod, then the device (and counter) are |
| * assumed to be stopped and it returns true. |
| * @return Returns true if the most recent counter period exceeds the MaxPeriod value set by |
| * SetMaxPeriod. |
| */ |
| bool Counter::GetStopped() |
| { |
| if (StatusIsFatal()) return false; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| return m_counter->readTimerOutput_Stalled(&localStatus); |
| wpi_setError(localStatus); |
| } |
| |
| /** |
| * The last direction the counter value changed. |
| * @return The last direction the counter value changed. |
| */ |
| bool Counter::GetDirection() |
| { |
| if (StatusIsFatal()) return false; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| bool value = m_counter->readOutput_Direction(&localStatus); |
| wpi_setError(localStatus); |
| return value; |
| } |
| |
| /** |
| * Set the Counter to return reversed sensing on the direction. |
| * This allows counters to change the direction they are counting in the case of 1X and 2X |
| * quadrature encoding only. Any other counter mode isn't supported. |
| * @param reverseDirection true if the value counted should be negated. |
| */ |
| void Counter::SetReverseDirection(bool reverseDirection) |
| { |
| if (StatusIsFatal()) return; |
| tRioStatusCode localStatus = NiFpga_Status_Success; |
| if (m_counter->readConfig_Mode(&localStatus) == kExternalDirection) |
| { |
| if (reverseDirection) |
| SetDownSourceEdge(true, true); |
| else |
| SetDownSourceEdge(false, true); |
| } |
| wpi_setError(localStatus); |
| } |
| |
| |
| void Counter::UpdateTable() { |
| if (m_table != NULL) { |
| m_table->PutNumber("Value", Get()); |
| } |
| } |
| |
| void Counter::StartLiveWindowMode() { |
| |
| } |
| |
| void Counter::StopLiveWindowMode() { |
| |
| } |
| |
| std::string Counter::GetSmartDashboardType() { |
| return "Counter"; |
| } |
| |
| void Counter::InitTable(ITable *subTable) { |
| m_table = subTable; |
| UpdateTable(); |
| } |
| |
| ITable * Counter::GetTable() { |
| return m_table; |
| } |
| |