diff --git a/hal/lib/athena/AnalogInput.cpp b/hal/lib/athena/AnalogInput.cpp index 8717e2afbc..f2edba7b15 100644 --- a/hal/lib/athena/AnalogInput.cpp +++ b/hal/lib/athena/AnalogInput.cpp @@ -19,8 +19,6 @@ using namespace hal; -static bool analogSampleRateSet = false; - extern "C" { /** * Initialize the analog input port using the given port object. @@ -103,28 +101,9 @@ void HAL_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 = - static_cast(static_cast(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); + initializeAnalog(status); + if (*status != 0) return; + setAnalogSampleRate(samplesPerSecond, status); } /** @@ -136,6 +115,8 @@ void HAL_SetAnalogSampleRate(double samplesPerSecond, int32_t* status) { * @return Sample rate. */ double HAL_GetAnalogSampleRate(int32_t* status) { + initializeAnalog(status); + if (*status != 0) return 0; uint32_t ticksPerConversion = analogInputSystem->readLoopTiming(status); uint32_t ticksPerSample = ticksPerConversion * getAnalogNumActiveChannels(status); diff --git a/hal/lib/athena/AnalogInternal.cpp b/hal/lib/athena/AnalogInternal.cpp index f734f26549..82a68d92a5 100644 --- a/hal/lib/athena/AnalogInternal.cpp +++ b/hal/lib/athena/AnalogInternal.cpp @@ -7,6 +7,8 @@ #include "AnalogInternal.h" +#include + #include "ChipObject.h" #include "HAL/AnalogInput.h" #include "HAL/cpp/priority_mutex.h" @@ -23,18 +25,21 @@ IndexedHandleResource analogSystemInitialized{false}; + +bool analogSampleRateSet = false; /** * Initialize the analog System. */ void initializeAnalog(int32_t* status) { + if (analogSystemInitialized) return; std::lock_guard sync(analogRegisterWindowMutex); if (analogSystemInitialized) return; analogInputSystem.reset(tAI::create(status)); analogOutputSystem.reset(tAO::create(status)); setAnalogNumChannelsToActivate(kNumAnalogInputs); - HAL_SetAnalogSampleRate(kDefaultSampleRate, status); + setAnalogSampleRate(kDefaultSampleRate, status); analogSystemInitialized = true; } @@ -66,6 +71,41 @@ int32_t getAnalogNumChannelsToActivate(int32_t* status) { return analogNumChannelsToActivate; } +/** + * 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 = + static_cast(static_cast(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); +} + /** * Set the number of active channels. * diff --git a/hal/lib/athena/AnalogInternal.h b/hal/lib/athena/AnalogInternal.h index faad09c3d0..baff302eab 100644 --- a/hal/lib/athena/AnalogInternal.h +++ b/hal/lib/athena/AnalogInternal.h @@ -27,6 +27,7 @@ static const uint32_t kAccumulatorChannels[] = {0, 1}; extern std::unique_ptr analogInputSystem; extern std::unique_ptr analogOutputSystem; extern priority_recursive_mutex analogRegisterWindowMutex; +extern bool analogSampleRateSet; struct AnalogPort { uint8_t channel; @@ -40,7 +41,6 @@ extern IndexedHandleResource( std::log(1.0 / (pwmSystem->readLoopTiming(status) * 0.25E-6 * rate)) / std::log(2.0) + @@ -371,6 +373,8 @@ HAL_Bool HAL_IsPulsing(HAL_DigitalHandle dioPortHandle, int32_t* status) { * @return A pulse on some line is in progress */ HAL_Bool HAL_IsAnyPulsing(int32_t* status) { + initializeDigital(status); + if (*status != 0) return false; tDIO::tPulse pulseRegister = digitalSystem->readPulse(status); return pulseRegister.Headers != 0 && pulseRegister.MXP != 0; } @@ -436,6 +440,8 @@ int32_t HAL_GetFilterSelect(HAL_DigitalHandle dioPortHandle, int32_t* status) { * counted as a transition. */ void HAL_SetFilterPeriod(int32_t filterIndex, int64_t value, int32_t* status) { + initializeDigital(status); + if (*status != 0) return; std::lock_guard sync(digitalDIOMutex); digitalSystem->writeFilterPeriodHdr(filterIndex, value, status); if (*status == 0) { @@ -456,6 +462,8 @@ void HAL_SetFilterPeriod(int32_t filterIndex, int64_t value, int32_t* status) { * counted as a transition. */ int64_t HAL_GetFilterPeriod(int32_t filterIndex, int32_t* status) { + initializeDigital(status); + if (*status != 0) return 0; uint32_t hdrPeriod = 0; uint32_t mxpPeriod = 0; { diff --git a/hal/lib/athena/DigitalInternal.cpp b/hal/lib/athena/DigitalInternal.cpp index df4be28112..fbad7eef98 100644 --- a/hal/lib/athena/DigitalInternal.cpp +++ b/hal/lib/athena/DigitalInternal.cpp @@ -7,6 +7,7 @@ #include "DigitalInternal.h" +#include #include #include @@ -27,7 +28,8 @@ std::unique_ptr digitalSystem; std::unique_ptr relaySystem; std::unique_ptr pwmSystem; -bool digitalSystemsInitialized = false; +static std::atomic digitalSystemsInitialized{false}; +static priority_mutex initializeMutex; DigitalHandleResource @@ -37,6 +39,11 @@ DigitalHandleResource lock(initializeMutex); + // Second check in case another thread was waiting if (digitalSystemsInitialized) return; digitalSystem.reset(tDIO::create(status)); diff --git a/hal/lib/athena/DigitalInternal.h b/hal/lib/athena/DigitalInternal.h index 2adecdf833..a432ddffcd 100644 --- a/hal/lib/athena/DigitalInternal.h +++ b/hal/lib/athena/DigitalInternal.h @@ -63,8 +63,6 @@ extern std::unique_ptr digitalSystem; extern std::unique_ptr relaySystem; extern std::unique_ptr pwmSystem; -extern bool digitalSystemsInitialized; - struct DigitalPort { uint8_t channel; bool configSet = false; diff --git a/hal/lib/athena/PWM.cpp b/hal/lib/athena/PWM.cpp index 0ec47eec95..a72b684d53 100644 --- a/hal/lib/athena/PWM.cpp +++ b/hal/lib/athena/PWM.cpp @@ -441,6 +441,8 @@ void HAL_SetPWMPeriodScale(HAL_DigitalHandle pwmPortHandle, int32_t squelchMask, * @return The loop time */ int32_t HAL_GetLoopTiming(int32_t* status) { + initializeDigital(status); + if (*status != 0) return 0; return pwmSystem->readLoopTiming(status); } }