[hal, wpilib] Remove interrupt (#7724)

This commit is contained in:
Thad House
2025-01-23 21:45:18 -08:00
committed by GitHub
parent e2b6beb28a
commit 5898cdd5c3
18 changed files with 0 additions and 1970 deletions

View File

@@ -1,120 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.hal;
/**
* Interrupt HAL JNI functions.
*
* @see "hal/Interrupts.h"
*/
public class InterruptJNI extends JNIWrapper {
/** Invalid handle value. */
public static final int HalInvalidHandle = 0;
/**
* Initializes an interrupt.
*
* @return the created interrupt handle
* @see "HAL_InitializeInterrupts"
*/
public static native int initializeInterrupts();
/**
* Frees an interrupt.
*
* @param interruptHandle the interrupt handle
* @see "HAL_CleanInterrupts"
*/
public static native void cleanInterrupts(int interruptHandle);
/**
* Waits for the defined interrupt to occur.
*
* @param interruptHandle the interrupt handle
* @param timeout timeout in seconds
* @param ignorePrevious if true, ignore interrupts that happened before waitForInterrupt was
* called
* @return the mask of interrupts that fired
* @see "HAL_WaitForInterrupt"
*/
public static native long waitForInterrupt(
int interruptHandle, double timeout, boolean ignorePrevious);
/**
* Waits for any interrupt covered by the mask to occur.
*
* @param interruptHandle the interrupt handle to use for the context
* @param mask the mask of interrupts to wait for
* @param timeout timeout in seconds
* @param ignorePrevious if true, ignore interrupts that happened before waitForInterrupt was
* called
* @return the mask of interrupts that fired
* @see "HAL_WaitForMultipleInterrupts"
*/
public static native long waitForMultipleInterrupts(
int interruptHandle, long mask, double timeout, boolean ignorePrevious);
/**
* Returns the timestamp for the rising interrupt that occurred most recently.
*
* <p>This is in the same time domain as getFPGATime(). It only contains the bottom 32 bits of the
* timestamp. If your robot has been running for over 1 hour, you will need to fill in the upper
* 32 bits yourself.
*
* @param interruptHandle the interrupt handle
* @return timestamp in microseconds since FPGA Initialization
*/
public static native long readInterruptRisingTimestamp(int interruptHandle);
/**
* Returns the timestamp for the falling interrupt that occurred most recently.
*
* <p>This is in the same time domain as getFPGATime(). It only contains the bottom 32 bits of the
* timestamp. If your robot has been running for over 1 hour, you will need to fill in the upper
* 32 bits yourself.
*
* @param interruptHandle the interrupt handle
* @return timestamp in microseconds since FPGA Initialization
*/
public static native long readInterruptFallingTimestamp(int interruptHandle);
/**
* Requests interrupts on a specific digital source.
*
* @param interruptHandle the interrupt handle
* @param digitalSourceHandle the digital source handle (either a HAL_AnalogTriggerHandle or a
* HAL_DigitalHandle)
* @param analogTriggerType the trigger type if the source is an AnalogTrigger
* @see "HAL_RequestInterrupts"
*/
public static native void requestInterrupts(
int interruptHandle, int digitalSourceHandle, int analogTriggerType);
/**
* Sets the edges to trigger the interrupt on.
*
* <p>Note that both edges triggered is a valid configuration.
*
* @param interruptHandle the interrupt handle
* @param risingEdge true for triggering on rising edge
* @param fallingEdge true for triggering on falling edge
* @see "HAL_SetInterruptUpSourceEdge"
*/
public static native void setInterruptUpSourceEdge(
int interruptHandle, boolean risingEdge, boolean fallingEdge);
/**
* Releases a waiting interrupt.
*
* <p>This will release both rising and falling waiters.
*
* @param interruptHandle the interrupt handle to release
* @see "HAL_ReleaseWaitingInterrupt"
*/
public static native void releaseWaitingInterrupt(int interruptHandle);
/** Utility class. */
private InterruptJNI() {}
}

View File

@@ -1,173 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <jni.h>
#include <atomic>
#include <cassert>
#include <thread>
#include <wpi/SafeThread.h>
#include <wpi/mutex.h>
#include "HALUtil.h"
#include "edu_wpi_first_hal_InterruptJNI.h"
#include "hal/Interrupts.h"
using namespace hal;
extern "C" {
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: initializeInterrupts
* Signature: ()I
*/
JNIEXPORT jint JNICALL
Java_edu_wpi_first_hal_InterruptJNI_initializeInterrupts
(JNIEnv* env, jclass)
{
int32_t status = 0;
HAL_InterruptHandle interrupt = HAL_InitializeInterrupts(&status);
CheckStatusForceThrow(env, status);
return (jint)interrupt;
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: cleanInterrupts
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_InterruptJNI_cleanInterrupts
(JNIEnv* env, jclass, jint interruptHandle)
{
if (interruptHandle != HAL_kInvalidHandle) {
HAL_CleanInterrupts((HAL_InterruptHandle)interruptHandle);
}
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: waitForInterrupt
* Signature: (IDZ)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_hal_InterruptJNI_waitForInterrupt
(JNIEnv* env, jclass, jint interruptHandle, jdouble timeout,
jboolean ignorePrevious)
{
int32_t status = 0;
int64_t result = HAL_WaitForInterrupt((HAL_InterruptHandle)interruptHandle,
timeout, ignorePrevious, &status);
CheckStatus(env, status);
return result;
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: waitForMultipleInterrupts
* Signature: (IJDZ)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_hal_InterruptJNI_waitForMultipleInterrupts
(JNIEnv* env, jclass, jint interruptHandle, jlong mask, jdouble timeout,
jboolean ignorePrevious)
{
int32_t status = 0;
int64_t result =
HAL_WaitForMultipleInterrupts((HAL_InterruptHandle)interruptHandle, mask,
timeout, ignorePrevious, &status);
CheckStatus(env, status);
return result;
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: readInterruptRisingTimestamp
* Signature: (I)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_hal_InterruptJNI_readInterruptRisingTimestamp
(JNIEnv* env, jclass, jint interruptHandle)
{
int32_t status = 0;
jlong timeStamp = HAL_ReadInterruptRisingTimestamp(
(HAL_InterruptHandle)interruptHandle, &status);
CheckStatus(env, status);
return timeStamp;
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: readInterruptFallingTimestamp
* Signature: (I)J
*/
JNIEXPORT jlong JNICALL
Java_edu_wpi_first_hal_InterruptJNI_readInterruptFallingTimestamp
(JNIEnv* env, jclass, jint interruptHandle)
{
int32_t status = 0;
jlong timeStamp = HAL_ReadInterruptFallingTimestamp(
(HAL_InterruptHandle)interruptHandle, &status);
CheckStatus(env, status);
return timeStamp;
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: requestInterrupts
* Signature: (III)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_InterruptJNI_requestInterrupts
(JNIEnv* env, jclass, jint interruptHandle, jint digitalSourceHandle,
jint analogTriggerType)
{
int32_t status = 0;
HAL_RequestInterrupts((HAL_InterruptHandle)interruptHandle,
(HAL_Handle)digitalSourceHandle,
(HAL_AnalogTriggerType)analogTriggerType, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: setInterruptUpSourceEdge
* Signature: (IZZ)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_InterruptJNI_setInterruptUpSourceEdge
(JNIEnv* env, jclass, jint interruptHandle, jboolean risingEdge,
jboolean fallingEdge)
{
int32_t status = 0;
HAL_SetInterruptUpSourceEdge((HAL_InterruptHandle)interruptHandle, risingEdge,
fallingEdge, &status);
CheckStatus(env, status);
}
/*
* Class: edu_wpi_first_hal_InterruptJNI
* Method: releaseWaitingInterrupt
* Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_edu_wpi_first_hal_InterruptJNI_releaseWaitingInterrupt
(JNIEnv* env, jclass, jint interruptHandle)
{
int32_t status = 0;
HAL_ReleaseWaitingInterrupt((HAL_InterruptHandle)interruptHandle, &status);
CheckStatus(env, status);
}
} // extern "C"

View File

@@ -20,7 +20,6 @@
#include "hal/FRCUsageReporting.h"
#include "hal/HALBase.h"
#include "hal/I2C.h"
#include "hal/Interrupts.h"
#include "hal/LEDs.h"
#include "hal/Main.h"
#include "hal/Notifier.h"

View File

@@ -1,137 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <stdint.h>
#include "hal/AnalogTrigger.h"
#include "hal/Types.h"
/**
* @defgroup hal_interrupts Interrupts Functions
* @ingroup hal_capi
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* Initializes an interrupt.
*
* @param[out] status Error status variable. 0 on success.
* @return the created interrupt handle
*/
HAL_InterruptHandle HAL_InitializeInterrupts(int32_t* status);
/**
* Frees an interrupt.
*
* @param interruptHandle the interrupt handle
*/
void HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle);
/**
* Waits for the defined interrupt to occur.
*
* @param[in] interruptHandle the interrupt handle
* @param[in] timeout timeout in seconds
* @param[in] ignorePrevious if true, ignore interrupts that happened before
* waitForInterrupt was called
* @param[out] status Error status variable. 0 on success.
* @return the mask of interrupts that fired
*/
int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
double timeout, HAL_Bool ignorePrevious,
int32_t* status);
/**
* Waits for any interrupt covered by the mask to occur.
*
* @param[in] interruptHandle the interrupt handle to use for the context
* @param[in] mask the mask of interrupts to wait for
* @param[in] timeout timeout in seconds
* @param[in] ignorePrevious if true, ignore interrupts that happened before
* waitForInterrupt was called
* @param[out] status Error status variable. 0 on success.
* @return the mask of interrupts that fired
*/
int64_t HAL_WaitForMultipleInterrupts(HAL_InterruptHandle interruptHandle,
int64_t mask, double timeout,
HAL_Bool ignorePrevious, int32_t* status);
/**
* Returns the timestamp for the rising interrupt that occurred most recently.
*
* This is in the same time domain as HAL_GetFPGATime(). It only contains the
* bottom 32 bits of the timestamp. If your robot has been running for over 1
* hour, you will need to fill in the upper 32 bits yourself.
*
* @param[in] interruptHandle the interrupt handle
* @param[out] status Error status variable. 0 on success.
* @return timestamp in microseconds since FPGA Initialization
*/
int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
int32_t* status);
/**
* Returns the timestamp for the falling interrupt that occurred most recently.
*
* This is in the same time domain as HAL_GetFPGATime(). It only contains the
* bottom 32 bits of the timestamp. If your robot has been running for over 1
* hour, you will need to fill in the upper 32 bits yourself.
*
* @param[in] interruptHandle the interrupt handle
* @param[out] status Error status variable. 0 on success.
* @return timestamp in microseconds since FPGA Initialization
*/
int64_t HAL_ReadInterruptFallingTimestamp(HAL_InterruptHandle interruptHandle,
int32_t* status);
/**
* Requests interrupts on a specific digital source.
*
* @param[in] interruptHandle the interrupt handle
* @param[in] digitalSourceHandle the digital source handle (either a
* HAL_AnalogTriggerHandle or a
* HAL_DigitalHandle)
* @param[in] analogTriggerType the trigger type if the source is an
* AnalogTrigger
* @param[out] status Error status variable. 0 on success.
*/
void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
HAL_Handle digitalSourceHandle,
HAL_AnalogTriggerType analogTriggerType,
int32_t* status);
/**
* Sets the edges to trigger the interrupt on.
*
* Note that both edges triggered is a valid configuration.
*
* @param[in] interruptHandle the interrupt handle
* @param[in] risingEdge true for triggering on rising edge
* @param[in] fallingEdge true for triggering on falling edge
* @param[out] status Error status variable. 0 on success.
*/
void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
HAL_Bool risingEdge, HAL_Bool fallingEdge,
int32_t* status);
/**
* Releases a waiting interrupt.
*
* This will release both rising and falling waiters.
*
* @param[in] interruptHandle the interrupt handle to release
* @param[out] status Error status variable. 0 on success.
*/
void HAL_ReleaseWaitingInterrupt(HAL_InterruptHandle interruptHandle,
int32_t* status);
#ifdef __cplusplus
} // extern "C"
#endif
/** @} */

View File

@@ -97,7 +97,6 @@ void InitializeHAL() {
InitializeEncoder();
InitializeExtensions();
InitializeI2C();
InitializeInterrupts();
InitializeMain();
InitializeMockHooks();
InitializeNotifier();

View File

@@ -48,7 +48,6 @@ extern void InitializeEncoder();
extern void InitializeExtensions();
extern void InitializeHAL();
extern void InitializeI2C();
extern void InitializeInterrupts();
extern void InitializeMain();
extern void InitializeMockHooks();
extern void InitializeNotifier();

View File

@@ -1,454 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "hal/Interrupts.h"
#include <memory>
#include <wpi/condition_variable.h>
#include "AnalogInternal.h"
#include "DigitalInternal.h"
#include "ErrorsInternal.h"
#include "HALInitializer.h"
#include "MockHooksInternal.h"
#include "PortsInternal.h"
#include "hal/AnalogTrigger.h"
#include "hal/Errors.h"
#include "hal/Value.h"
#include "hal/handles/HandlesInternal.h"
#include "hal/handles/LimitedHandleResource.h"
#include "hal/handles/UnlimitedHandleResource.h"
#include "mockdata/AnalogInDataInternal.h"
#include "mockdata/DIODataInternal.h"
#ifdef _WIN32
#pragma warning(disable : 4996 4018 6297 26451 4334)
#endif
using namespace hal;
enum WaitResult {
Timeout = 0x0,
RisingEdge = 0x1,
FallingEdge = 0x100,
Both = 0x101,
};
namespace {
struct Interrupt {
bool isAnalog;
HAL_Handle portHandle;
uint8_t index;
HAL_AnalogTriggerType trigType;
int64_t risingTimestamp;
int64_t fallingTimestamp;
bool currentState;
bool fireOnUp;
bool fireOnDown;
int32_t callbackId;
};
struct SynchronousWaitData {
HAL_InterruptHandle interruptHandle{HAL_kInvalidHandle};
wpi::condition_variable waitCond;
HAL_Bool waitPredicate{false};
};
} // namespace
static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
HAL_HandleEnum::Interrupt>* interruptHandles;
using SynchronousWaitDataHandle = HAL_Handle;
static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
HAL_HandleEnum::Vendor>*
synchronousInterruptHandles;
namespace hal::init {
void InitializeInterrupts() {
static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
HAL_HandleEnum::Interrupt>
iH;
interruptHandles = &iH;
static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
HAL_HandleEnum::Vendor>
siH;
synchronousInterruptHandles = &siH;
}
} // namespace hal::init
extern "C" {
HAL_InterruptHandle HAL_InitializeInterrupts(int32_t* status) {
hal::init::CheckInit();
HAL_InterruptHandle handle = interruptHandles->Allocate();
if (handle == HAL_kInvalidHandle) {
*status = NO_AVAILABLE_RESOURCES;
return HAL_kInvalidHandle;
}
auto anInterrupt = interruptHandles->Get(handle);
if (anInterrupt == nullptr) { // would only occur on thread issue.
*status = HAL_HANDLE_ERROR;
return HAL_kInvalidHandle;
}
anInterrupt->index = getHandleIndex(handle);
anInterrupt->callbackId = -1;
return handle;
}
void HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle) {
interruptHandles->Free(interruptHandle);
}
static void ProcessInterruptDigitalSynchronous(const char* name, void* param,
const struct HAL_Value* value) {
// void* is a SynchronousWaitDataHandle.
// convert to uintptr_t first, then to handle
uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
SynchronousWaitDataHandle handle =
static_cast<SynchronousWaitDataHandle>(handleTmp);
auto interruptData = synchronousInterruptHandles->Get(handle);
if (interruptData == nullptr) {
return;
}
auto interrupt = interruptHandles->Get(interruptData->interruptHandle);
if (interrupt == nullptr) {
return;
}
// Have a valid interrupt
if (value->type != HAL_Type::HAL_BOOLEAN) {
return;
}
bool retVal = value->data.v_boolean;
auto previousState = interrupt->currentState;
interrupt->currentState = retVal;
// If no change in interrupt, return;
if (retVal == previousState) {
return;
}
// If its a falling change, and we dont fire on falling return
if (previousState && !interrupt->fireOnDown) {
return;
}
// If its a rising change, and we dont fire on rising return.
if (!previousState && !interrupt->fireOnUp) {
return;
}
interruptData->waitPredicate = true;
// Pulse interrupt
interruptData->waitCond.notify_all();
}
static double GetAnalogTriggerValue(HAL_Handle triggerHandle,
HAL_AnalogTriggerType type,
int32_t* status) {
return HAL_GetAnalogTriggerOutput(triggerHandle, type, status);
}
static void ProcessInterruptAnalogSynchronous(const char* name, void* param,
const struct HAL_Value* value) {
// void* is a SynchronousWaitDataHandle.
// convert to uintptr_t first, then to handle
uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
SynchronousWaitDataHandle handle =
static_cast<SynchronousWaitDataHandle>(handleTmp);
auto interruptData = synchronousInterruptHandles->Get(handle);
if (interruptData == nullptr) {
return;
}
auto interrupt = interruptHandles->Get(interruptData->interruptHandle);
if (interrupt == nullptr) {
return;
}
// Have a valid interrupt
if (value->type != HAL_Type::HAL_DOUBLE) {
return;
}
int32_t status = 0;
bool retVal = GetAnalogTriggerValue(interrupt->portHandle,
interrupt->trigType, &status);
if (status != 0) {
// Interrupt and Cancel
interruptData->waitPredicate = true;
// Pulse interrupt
interruptData->waitCond.notify_all();
}
auto previousState = interrupt->currentState;
interrupt->currentState = retVal;
// If no change in interrupt, return;
if (retVal == previousState) {
return;
}
// If its a falling change, and we dont fire on falling return
if (previousState && !interrupt->fireOnDown) {
return;
}
// If its a rising change, and we dont fire on rising return.
if (!previousState && !interrupt->fireOnUp) {
return;
}
interruptData->waitPredicate = true;
// Pulse interrupt
interruptData->waitCond.notify_all();
}
static int64_t WaitForInterruptDigital(HAL_InterruptHandle handle,
Interrupt* interrupt, double timeout,
bool ignorePrevious) {
auto data = std::make_shared<SynchronousWaitData>();
auto dataHandle = synchronousInterruptHandles->Allocate(data);
if (dataHandle == HAL_kInvalidHandle) {
// Error allocating data
return WaitResult::Timeout;
}
// auto data = synchronousInterruptHandles->Get(dataHandle);
data->waitPredicate = false;
data->interruptHandle = handle;
int32_t status = 0;
int32_t digitalIndex = GetDigitalInputChannel(interrupt->portHandle, &status);
if (status != 0) {
return WaitResult::Timeout;
}
interrupt->currentState = SimDIOData[digitalIndex].value;
int32_t uid = SimDIOData[digitalIndex].value.RegisterCallback(
&ProcessInterruptDigitalSynchronous,
reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
bool timedOut = false;
wpi::mutex waitMutex;
auto timeoutTime =
std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
{
std::unique_lock lock(waitMutex);
while (!data->waitPredicate) {
if (data->waitCond.wait_until(lock, timeoutTime) ==
std::cv_status::timeout) {
timedOut = true;
break;
}
}
}
// Cancel our callback
SimDIOData[digitalIndex].value.CancelCallback(uid);
(void)synchronousInterruptHandles->Free(dataHandle);
// Check for what to return
if (timedOut) {
return WaitResult::Timeout;
}
// We know the value has changed because we would've timed out otherwise.
// If the current state is true, the previous state was false, so this is a
// rising edge. Otherwise, it's a falling edge.
if (interrupt->currentState) {
// Set our return value and our timestamps
interrupt->risingTimestamp = hal::GetFPGATime();
return 1 << (interrupt->index);
} else {
interrupt->fallingTimestamp = hal::GetFPGATime();
return 1 << (8 + interrupt->index);
}
}
static int64_t WaitForInterruptAnalog(HAL_InterruptHandle handle,
Interrupt* interrupt, double timeout,
bool ignorePrevious) {
auto data = std::make_shared<SynchronousWaitData>();
auto dataHandle = synchronousInterruptHandles->Allocate(data);
if (dataHandle == HAL_kInvalidHandle) {
// Error allocating data
return WaitResult::Timeout;
}
data->waitPredicate = false;
data->interruptHandle = handle;
int32_t status = 0;
interrupt->currentState = GetAnalogTriggerValue(interrupt->portHandle,
interrupt->trigType, &status);
if (status != 0) {
return WaitResult::Timeout;
}
int32_t analogIndex =
GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
if (status != 0) {
return WaitResult::Timeout;
}
int32_t uid = SimAnalogInData[analogIndex].voltage.RegisterCallback(
&ProcessInterruptAnalogSynchronous,
reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
bool timedOut = false;
wpi::mutex waitMutex;
auto timeoutTime =
std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
{
std::unique_lock lock(waitMutex);
while (!data->waitPredicate) {
if (data->waitCond.wait_until(lock, timeoutTime) ==
std::cv_status::timeout) {
timedOut = true;
break;
}
}
}
// Cancel our callback
SimAnalogInData[analogIndex].voltage.CancelCallback(uid);
(void)synchronousInterruptHandles->Free(dataHandle);
// Check for what to return
if (timedOut) {
return WaitResult::Timeout;
}
// We know the value has changed because we would've timed out otherwise.
// If the current state is true, the previous state was false, so this is a
// rising edge. Otherwise, it's a falling edge.
if (interrupt->currentState) {
// Set our return value and our timestamps
interrupt->risingTimestamp = hal::GetFPGATime();
return 1 << (interrupt->index);
} else {
interrupt->fallingTimestamp = hal::GetFPGATime();
return 1 << (8 + interrupt->index);
}
}
int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
double timeout, HAL_Bool ignorePrevious,
int32_t* status) {
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return WaitResult::Timeout;
}
if (interrupt->isAnalog) {
return WaitForInterruptAnalog(interruptHandle, interrupt.get(), timeout,
ignorePrevious);
} else {
return WaitForInterruptDigital(interruptHandle, interrupt.get(), timeout,
ignorePrevious);
}
}
int64_t HAL_WaitForMultipleInterrupts(HAL_InterruptHandle interruptHandle,
int64_t mask, double timeout,
HAL_Bool ignorePrevious,
int32_t* status) {
// TODO make this properly work, will require a decent rewrite
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return WaitResult::Timeout;
}
if (interrupt->isAnalog) {
return WaitForInterruptAnalog(interruptHandle, interrupt.get(), timeout,
ignorePrevious);
} else {
return WaitForInterruptDigital(interruptHandle, interrupt.get(), timeout,
ignorePrevious);
}
}
int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
int32_t* status) {
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return 0;
}
return interrupt->risingTimestamp;
}
int64_t HAL_ReadInterruptFallingTimestamp(HAL_InterruptHandle interruptHandle,
int32_t* status) {
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return 0;
}
return interrupt->fallingTimestamp;
}
void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
HAL_Handle digitalSourceHandle,
HAL_AnalogTriggerType analogTriggerType,
int32_t* status) {
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
bool routingAnalogTrigger = false;
uint8_t routingChannel = 0;
uint8_t routingModule = 0;
bool success =
remapDigitalSource(digitalSourceHandle, analogTriggerType, routingChannel,
routingModule, routingAnalogTrigger);
if (!success) {
*status = HAL_HANDLE_ERROR;
return;
}
interrupt->isAnalog = routingAnalogTrigger;
interrupt->trigType = analogTriggerType;
interrupt->portHandle = digitalSourceHandle;
}
void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
HAL_Bool risingEdge, HAL_Bool fallingEdge,
int32_t* status) {
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
interrupt->fireOnDown = fallingEdge;
interrupt->fireOnUp = risingEdge;
}
void HAL_ReleaseWaitingInterrupt(HAL_InterruptHandle interruptHandle,
int32_t* status) {
auto interrupt = interruptHandles->Get(interruptHandle);
if (interrupt == nullptr) {
*status = HAL_HANDLE_ERROR;
return;
}
synchronousInterruptHandles->ForEach(
[interruptHandle](SynchronousWaitDataHandle handle,
SynchronousWaitData* data) {
if (data->interruptHandle == interruptHandle) {
data->waitPredicate = true;
data->waitCond.notify_all();
}
});
}
} // extern "C"

View File

@@ -60,7 +60,6 @@ void InitializeHAL() {
InitializeEncoder();
InitializeFRCDriverStation();
InitializeI2C();
InitializeInterrupts();
InitializeLEDs();
InitializeMain();
InitializeNotifier();

View File

@@ -30,11 +30,9 @@ extern void InitializeDigitalInternal();
extern void InitializeDIO();
extern void InitializeDutyCycle();
extern void InitializeEncoder();
extern void InitializeFPGAEncoder();
extern void InitializeFRCDriverStation();
extern void InitializeHAL();
extern void InitializeI2C();
extern void InitializeInterrupts();
extern void InitializeLEDs();
extern void InitializeMain();
extern void InitializeNotifier();

View File

@@ -1,83 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "hal/Interrupts.h"
#include <memory>
#include <wpi/SafeThread.h>
#include "HALInitializer.h"
#include "HALInternal.h"
#include "PortsInternal.h"
#include "hal/Errors.h"
#include "hal/HALBase.h"
#include "hal/handles/HandlesInternal.h"
#include "hal/handles/LimitedHandleResource.h"
using namespace hal;
namespace hal::init {
void InitializeInterrupts() {}
} // namespace hal::init
extern "C" {
HAL_InterruptHandle HAL_InitializeInterrupts(int32_t* status) {
hal::init::CheckInit();
*status = HAL_HANDLE_ERROR;
return HAL_kInvalidHandle;
}
void HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle) {}
int64_t HAL_WaitForInterrupt(HAL_InterruptHandle interruptHandle,
double timeout, HAL_Bool ignorePrevious,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return 0;
}
int64_t HAL_WaitForMultipleInterrupts(HAL_InterruptHandle interruptHandle,
int64_t mask, double timeout,
HAL_Bool ignorePrevious,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return 0;
}
int64_t HAL_ReadInterruptRisingTimestamp(HAL_InterruptHandle interruptHandle,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return 0;
}
int64_t HAL_ReadInterruptFallingTimestamp(HAL_InterruptHandle interruptHandle,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return 0;
}
void HAL_RequestInterrupts(HAL_InterruptHandle interruptHandle,
HAL_Handle digitalSourceHandle,
HAL_AnalogTriggerType analogTriggerType,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
void HAL_SetInterruptUpSourceEdge(HAL_InterruptHandle interruptHandle,
HAL_Bool risingEdge, HAL_Bool fallingEdge,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
void HAL_ReleaseWaitingInterrupt(HAL_InterruptHandle interruptHandle,
int32_t* status) {
*status = HAL_HANDLE_ERROR;
return;
}
} // extern "C"

View File

@@ -1,70 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/AsynchronousInterrupt.h"
#include <frc/DigitalSource.h>
#include <memory>
#include <utility>
using namespace frc;
AsynchronousInterrupt::AsynchronousInterrupt(
DigitalSource& source, std::function<void(bool, bool)> callback)
: m_interrupt{source}, m_callback{std::move(callback)} {}
AsynchronousInterrupt::AsynchronousInterrupt(
DigitalSource* source, std::function<void(bool, bool)> callback)
: m_interrupt{source}, m_callback{std::move(callback)} {}
AsynchronousInterrupt::AsynchronousInterrupt(
std::shared_ptr<DigitalSource> source,
std::function<void(bool, bool)> callback)
: m_interrupt{source}, m_callback{std::move(callback)} {}
AsynchronousInterrupt::~AsynchronousInterrupt() {
Disable();
}
void AsynchronousInterrupt::ThreadMain() {
while (m_keepRunning) {
auto result = m_interrupt.WaitForInterrupt(10_s, false);
if (!m_keepRunning) {
break;
}
if (result == SynchronousInterrupt::WaitResult::kTimeout) {
continue;
}
m_callback((result & SynchronousInterrupt::WaitResult::kRisingEdge) != 0,
(result & SynchronousInterrupt::WaitResult::kFallingEdge) != 0);
}
}
void AsynchronousInterrupt::Enable() {
if (m_keepRunning) {
return;
}
m_keepRunning = true;
m_thread = std::thread([this] { this->ThreadMain(); });
}
void AsynchronousInterrupt::Disable() {
m_keepRunning = false;
m_interrupt.WakeupWaitingInterrupt();
if (m_thread.joinable()) {
m_thread.join();
}
}
void AsynchronousInterrupt::SetInterruptEdges(bool risingEdge,
bool fallingEdge) {
m_interrupt.SetInterruptEdges(risingEdge, fallingEdge);
}
units::second_t AsynchronousInterrupt::GetRisingTimestamp() {
return m_interrupt.GetRisingTimestamp();
}
units::second_t AsynchronousInterrupt::GetFallingTimestamp() {
return m_interrupt.GetFallingTimestamp();
}

View File

@@ -1,103 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include "frc/SynchronousInterrupt.h"
#include <memory>
#include <type_traits>
#include <utility>
#include <hal/Interrupts.h>
#include <wpi/NullDeleter.h>
#include "frc/DigitalSource.h"
#include "frc/Errors.h"
using namespace frc;
SynchronousInterrupt::SynchronousInterrupt(DigitalSource& source)
: m_source{&source, wpi::NullDeleter<DigitalSource>()} {
InitSynchronousInterrupt();
}
SynchronousInterrupt::SynchronousInterrupt(DigitalSource* source)
: m_source{source, wpi::NullDeleter<DigitalSource>()} {
if (m_source == nullptr) {
FRC_CheckErrorStatus(frc::err::NullParameter, "Source is null");
} else {
InitSynchronousInterrupt();
}
}
SynchronousInterrupt::SynchronousInterrupt(
std::shared_ptr<DigitalSource> source)
: m_source{std::move(source)} {
if (m_source == nullptr) {
FRC_CheckErrorStatus(frc::err::NullParameter, "Source is null");
} else {
InitSynchronousInterrupt();
}
}
void SynchronousInterrupt::InitSynchronousInterrupt() {
int32_t status = 0;
m_handle = HAL_InitializeInterrupts(&status);
FRC_CheckErrorStatus(status, "Interrupt failed to initialize");
HAL_RequestInterrupts(m_handle, m_source->GetPortHandleForRouting(),
static_cast<HAL_AnalogTriggerType>(
m_source->GetAnalogTriggerTypeForRouting()),
&status);
FRC_CheckErrorStatus(status, "Interrupt request failed");
HAL_SetInterruptUpSourceEdge(m_handle, true, false, &status);
FRC_CheckErrorStatus(status, "Interrupt setting up source edge failed");
}
inline SynchronousInterrupt::WaitResult operator|(
SynchronousInterrupt::WaitResult lhs,
SynchronousInterrupt::WaitResult rhs) {
using T = std::underlying_type_t<SynchronousInterrupt::WaitResult>;
return static_cast<SynchronousInterrupt::WaitResult>(static_cast<T>(lhs) |
static_cast<T>(rhs));
}
SynchronousInterrupt::WaitResult SynchronousInterrupt::WaitForInterrupt(
units::second_t timeout, bool ignorePrevious) {
int32_t status = 0;
auto result =
HAL_WaitForInterrupt(m_handle, timeout.value(), ignorePrevious, &status);
auto rising =
((result & 0xFF) != 0) ? WaitResult::kRisingEdge : WaitResult::kTimeout;
auto falling = ((result & 0xFF00) != 0) ? WaitResult::kFallingEdge
: WaitResult::kTimeout;
return rising | falling;
}
void SynchronousInterrupt::SetInterruptEdges(bool risingEdge,
bool fallingEdge) {
int32_t status = 0;
HAL_SetInterruptUpSourceEdge(m_handle, risingEdge, fallingEdge, &status);
FRC_CheckErrorStatus(status, "Interrupt setting edges failed");
}
void SynchronousInterrupt::WakeupWaitingInterrupt() {
int32_t status = 0;
HAL_ReleaseWaitingInterrupt(m_handle, &status);
FRC_CheckErrorStatus(status, "Interrupt wakeup failed");
}
units::second_t SynchronousInterrupt::GetRisingTimestamp() {
int32_t status = 0;
auto ts = HAL_ReadInterruptRisingTimestamp(m_handle, &status);
FRC_CheckErrorStatus(status, "Interrupt rising timestamp failed");
units::microsecond_t ms{static_cast<double>(ts)};
return ms;
}
units::second_t SynchronousInterrupt::GetFallingTimestamp() {
int32_t status = 0;
auto ts = HAL_ReadInterruptFallingTimestamp(m_handle, &status);
FRC_CheckErrorStatus(status, "Interrupt falling timestamp failed");
units::microsecond_t ms{static_cast<double>(ts)};
return ms;
}

View File

@@ -1,180 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <frc/SynchronousInterrupt.h>
#include <atomic>
#include <functional>
#include <memory>
#include <thread>
#include <utility>
#include <units/time.h>
namespace frc {
/**
* Class for handling asynchronous interrupts using a callback thread.
*
* <p> By default, interrupts will occur on rising edge. Callbacks are disabled
* by default, and Enable() must be called before they will occur.
*
* <p> Both rising and falling edges can be indicated in one callback if both a
* rising and falling edge occurred since the previous callback.
*
* <p>Synchronous (blocking) interrupts are handled by the SynchronousInterrupt
* class.
*/
class AsynchronousInterrupt {
public:
/**
* Construct an Asynchronous Interrupt from a Digital Source.
*
* <p> At construction, the interrupt will trigger on the rising edge.
*
* <p> The first bool in the callback indicates the rising edge triggered the
* interrupt, the second bool is falling edge.
*
* @param source the DigitalSource the interrupts are triggered from
* @param callback the callback function to call when the interrupt is
* triggered
*/
AsynchronousInterrupt(DigitalSource& source,
std::function<void(bool, bool)> callback);
/**
* Construct an Asynchronous Interrupt from a Digital Source.
*
* <p> At construction, the interrupt will trigger on the rising edge.
*
* <p> The first bool in the callback indicates the rising edge triggered the
* interrupt, the second bool is falling edge.
*
* @param source the DigitalSource the interrupts are triggered from
* @param callback the callback function to call when the interrupt is
* triggered
*/
AsynchronousInterrupt(DigitalSource* source,
std::function<void(bool, bool)> callback);
/**
* Construct an Asynchronous Interrupt from a Digital Source.
*
* <p> At construction, the interrupt will trigger on the rising edge.
*
* <p> The first bool in the callback indicates the rising edge triggered the
* interrupt, the second bool is falling edge.
*
* @param source the DigitalSource the interrupts are triggered from
* @param callback the callback function to call when the interrupt is
* triggered
*/
AsynchronousInterrupt(std::shared_ptr<DigitalSource> source,
std::function<void(bool, bool)> callback);
/**
* Construct an Asynchronous Interrupt from a Digital Source.
*
* <p> At construction, the interrupt will trigger on the rising edge.
*
* @param source the DigitalSource the interrupts are triggered from
* @param f the callback function to call when the interrupt is triggered
* @param arg the first argument, interrupt was triggered on rising edge
* @param args the remaining arguments, interrupt was triggered on falling
* edge
*/
template <typename Callable, typename Arg, typename... Args>
AsynchronousInterrupt(DigitalSource& source, Callable&& f, Arg&& arg,
Args&&... args)
: AsynchronousInterrupt(
source, std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
std::forward<Args>(args)...)) {}
/**
* Construct an Asynchronous Interrupt from a Digital Source.
*
* <p> At construction, the interrupt will trigger on the rising edge.
*
* @param source the DigitalSource the interrupts are triggered from
* @param f the callback function to call when the interrupt is triggered
* @param arg the first argument, interrupt was triggered on rising edge
* @param args the remaining arguments, interrupt was triggered on falling
* edge
*/
template <typename Callable, typename Arg, typename... Args>
AsynchronousInterrupt(DigitalSource* source, Callable&& f, Arg&& arg,
Args&&... args)
: AsynchronousInterrupt(
source, std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
std::forward<Args>(args)...)) {}
/**
* Construct an Asynchronous Interrupt from a Digital Source.
*
* <p> At construction, the interrupt will trigger on the rising edge.
*
* @param source the DigitalSource the interrupts are triggered from
* @param f the callback function to call when the interrupt is triggered
* @param arg the first argument, interrupt was triggered on rising edge
* @param args the remaining arguments, interrupt was triggered on falling
* edge
*/
template <typename Callable, typename Arg, typename... Args>
AsynchronousInterrupt(std::shared_ptr<DigitalSource> source, Callable&& f,
Arg&& arg, Args&&... args)
: AsynchronousInterrupt(
source, std::bind(std::forward<Callable>(f), std::forward<Arg>(arg),
std::forward<Args>(args)...)) {}
~AsynchronousInterrupt();
/**
* Enables interrupt callbacks. Before this, callbacks will not occur. Does
* nothing if already enabled.
*/
void Enable();
/**
* Disables interrupt callbacks. Does nothing if already disabled.
*/
void Disable();
/**
* Set which edges to trigger the interrupt on.
*
* @param risingEdge %Trigger on rising edge
* @param fallingEdge %Trigger on falling edge
*/
void SetInterruptEdges(bool risingEdge, bool fallingEdge);
/**
* Get the timestamp of the last rising edge.
*
* <p>This function does not require the interrupt to be enabled to work.
*
* <p>This only works if rising edge was configured using SetInterruptEdges.
* @return the timestamp in seconds relative to GetFPGATime
*/
units::second_t GetRisingTimestamp();
/**
* Get the timestamp of the last falling edge.
*
* <p>This function does not require the interrupt to be enabled to work.
*
* <p>This only works if falling edge was configured using SetInterruptEdges.
* @return the timestamp in seconds relative to GetFPGATime
*/
units::second_t GetFallingTimestamp();
private:
void ThreadMain();
std::atomic_bool m_keepRunning{false};
std::thread m_thread;
SynchronousInterrupt m_interrupt;
std::function<void(bool, bool)> m_callback;
};
} // namespace frc

View File

@@ -1,112 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#include <memory>
#include <hal/Interrupts.h>
#include <hal/Types.h>
#include <units/time.h>
namespace frc {
class DigitalSource;
/**
* Class for handling synchronous (blocking) interrupts.
*
* <p> By default, interrupts will occur on rising edge.
*
* <p> Asynchronous interrupts are handled by the AsynchronousInterrupt class.
*/
class SynchronousInterrupt {
public:
/**
* Event trigger combinations for a synchronous interrupt.
*/
enum WaitResult {
/// Timeout event.
kTimeout = 0x0,
/// Rising edge event.
kRisingEdge = 0x1,
/// Falling edge event.
kFallingEdge = 0x100,
/// Both rising and falling edge events.
kBoth = 0x101,
};
/**
* Construct a Synchronous Interrupt from a Digital Source.
*
* @param source the DigitalSource the interrupts are triggered from
*/
explicit SynchronousInterrupt(DigitalSource& source);
/**
* Construct a Synchronous Interrupt from a Digital Source.
*
* @param source the DigitalSource the interrupts are triggered from
*/
explicit SynchronousInterrupt(DigitalSource* source);
/**
* Construct a Synchronous Interrupt from a Digital Source.
*
* @param source the DigitalSource the interrupts are triggered from
*/
explicit SynchronousInterrupt(std::shared_ptr<DigitalSource> source);
SynchronousInterrupt(SynchronousInterrupt&&) = default;
SynchronousInterrupt& operator=(SynchronousInterrupt&&) = default;
/**
* Wait for an interrupt to occur.
*
* <p> Both rising and falling edge can be returned if both a rising and
* falling happened between calls, and ignorePrevious is false.
*
* @param timeout The timeout to wait for. 0s or less will return immediately.
* @param ignorePrevious True to ignore any previous interrupts, false to
* return interrupt value if an interrupt has occurred since last call.
* @return The edge(s) that were triggered, or timeout.
*/
WaitResult WaitForInterrupt(units::second_t timeout,
bool ignorePrevious = true);
/**
* Set which edges cause an interrupt to occur.
*
* @param risingEdge true to trigger on rising edge, false otherwise.
* @param fallingEdge true to trigger on falling edge, false otherwise
*/
void SetInterruptEdges(bool risingEdge, bool fallingEdge);
/**
* Get the timestamp (relative to FPGA Time) of the last rising edge.
*
* @return the timestamp in seconds relative to getFPGATime
*/
units::second_t GetRisingTimestamp();
/**
* Get the timestamp of the last falling edge.
*
* <p>This function does not require the interrupt to be enabled to work.
*
* <p>This only works if falling edge was configured using setInterruptEdges.
* @return the timestamp in seconds relative to getFPGATime
*/
units::second_t GetFallingTimestamp();
/**
* Wake up an existing wait call. Can be called from any thread.
*/
void WakeupWaitingInterrupt();
private:
void InitSynchronousInterrupt();
std::shared_ptr<DigitalSource> m_source;
hal::Handle<HAL_InterruptHandle, HAL_CleanInterrupts> m_handle;
};
} // namespace frc

View File

@@ -1,103 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#include <atomic>
#include <gtest/gtest.h>
#include <hal/HAL.h>
#include "frc/AsynchronousInterrupt.h"
#include "frc/DigitalInput.h"
#include "frc/Timer.h"
#include "frc/simulation/DIOSim.h"
namespace frc {
using namespace frc::sim;
TEST(InterruptTest, AsynchronousInterrupt) {
HAL_Initialize(500, 0);
std::atomic_int counter{0};
std::atomic_bool hasFired{false};
DigitalInput di{0};
AsynchronousInterrupt interrupt{di, [&](bool rising, bool falling) {
counter++;
hasFired = true;
}};
interrupt.Enable();
frc::Wait(0.5_s);
DIOSim digitalSim{di};
digitalSim.SetValue(false);
frc::Wait(20_ms);
digitalSim.SetValue(true);
frc::Wait(20_ms);
int count = 0;
while (!hasFired) {
frc::Wait(5_ms);
count++;
ASSERT_TRUE(count < 1000);
}
ASSERT_EQ(1, counter.load());
}
TEST(InterruptTest, RisingEdge) {
HAL_Initialize(500, 0);
std::atomic_bool hasFiredFallingEdge{false};
std::atomic_bool hasFiredRisingEdge{false};
DigitalInput di{0};
AsynchronousInterrupt interrupt{di, [&](bool rising, bool falling) {
hasFiredFallingEdge = falling;
hasFiredRisingEdge = rising;
}};
interrupt.SetInterruptEdges(true, true);
DIOSim digitalSim{di};
digitalSim.SetValue(false);
interrupt.Enable();
frc::Wait(0.5_s);
digitalSim.SetValue(true);
frc::Wait(20_ms);
int count = 0;
while (!hasFiredRisingEdge) {
frc::Wait(5_ms);
count++;
ASSERT_TRUE(count < 1000);
}
EXPECT_FALSE(hasFiredFallingEdge);
EXPECT_TRUE(hasFiredRisingEdge);
}
TEST(InterruptTest, FallingEdge) {
HAL_Initialize(500, 0);
std::atomic_bool hasFiredFallingEdge{false};
std::atomic_bool hasFiredRisingEdge{false};
DigitalInput di{0};
AsynchronousInterrupt interrupt{di, [&](bool rising, bool falling) {
hasFiredFallingEdge = falling;
hasFiredRisingEdge = rising;
}};
interrupt.SetInterruptEdges(true, true);
DIOSim digitalSim{di};
digitalSim.SetValue(true);
interrupt.Enable();
frc::Wait(0.5_s);
digitalSim.SetValue(false);
frc::Wait(20_ms);
int count = 0;
while (!hasFiredFallingEdge) {
frc::Wait(5_ms);
count++;
ASSERT_TRUE(count < 1000);
}
EXPECT_TRUE(hasFiredFallingEdge);
EXPECT_FALSE(hasFiredRisingEdge);
}
} // namespace frc

View File

@@ -1,155 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj;
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
/**
* Class for handling asynchronous interrupts using a callback thread.
*
* <p>By default, interrupts will occur on rising edge. Callbacks are disabled by default, and
* enable() must be called before they will occur.
*
* <p>Both rising and falling edges can be indicated in one callback if both a rising and falling
* edge occurred since the previous callback.
*
* <p>Synchronous (blocking) interrupts are handled by the SynchronousInterrupt class.
*/
public class AsynchronousInterrupt implements AutoCloseable {
private final BiConsumer<Boolean, Boolean> m_callback;
private final SynchronousInterrupt m_interrupt;
private final AtomicBoolean m_keepRunning = new AtomicBoolean(false);
private Thread m_thread;
/**
* Construct a new asynchronous interrupt using a Digital Source.
*
* <p>At construction, the interrupt will trigger on the rising edge.
*
* <p>Callbacks will not be triggered until enable() is called.
*
* <p>The first bool in the callback indicates the rising edge triggered the interrupt, the second
* bool is falling edge.
*
* @param source The digital source to use.
* @param callback The callback to call on an interrupt
*/
public AsynchronousInterrupt(DigitalSource source, BiConsumer<Boolean, Boolean> callback) {
m_callback = requireNonNullParam(callback, "callback", "AsynchronousInterrupt");
m_interrupt = new SynchronousInterrupt(source);
}
/**
* Closes the interrupt.
*
* <p>This does not close the associated digital source.
*
* <p>This will disable the interrupt if it is enabled.
*/
@Override
public void close() {
disable();
m_interrupt.close();
}
/**
* Enables interrupt callbacks. Before this, callbacks will not occur. Does nothing if already
* enabled.
*/
public void enable() {
if (m_keepRunning.get()) {
return;
}
m_keepRunning.set(true);
m_thread = new Thread(this::threadMain);
m_thread.start();
}
/** Disables interrupt callbacks. Does nothing if already disabled. */
public void disable() {
m_keepRunning.set(false);
m_interrupt.wakeupWaitingInterrupt();
if (m_thread != null) {
if (m_thread.isAlive()) {
try {
m_thread.interrupt();
m_thread.join();
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
m_thread = null;
}
}
/**
* Set which edges to trigger the interrupt on.
*
* @param risingEdge Trigger on rising edge
* @param fallingEdge Trigger on falling edge
*/
public void setInterruptEdges(boolean risingEdge, boolean fallingEdge) {
m_interrupt.setInterruptEdges(risingEdge, fallingEdge);
}
/**
* Get the timestamp of the last rising edge.
*
* <p>This function does not require the interrupt to be enabled to work.
*
* <p>This only works if rising edge was configured using setInterruptEdges.
*
* @return the timestamp in seconds relative to getFPGATime
*/
public double getRisingTimestamp() {
return m_interrupt.getRisingTimestamp();
}
/**
* Get the timestamp of the last falling edge.
*
* <p>This function does not require the interrupt to be enabled to work.
*
* <p>This only works if falling edge was configured using setInterruptEdges.
*
* @return the timestamp in seconds relative to getFPGATime
*/
public double getFallingTimestamp() {
return m_interrupt.getFallingTimestamp();
}
private void threadMain() {
while (m_keepRunning.get()) {
var result = m_interrupt.waitForInterrupt(10, false);
if (!m_keepRunning.get()) {
break;
}
boolean rising = false;
boolean falling = false;
switch (result) {
case kBoth -> {
rising = true;
falling = true;
}
case kFallingEdge -> {
falling = true;
}
case kRisingEdge -> {
rising = true;
}
default -> {
continue;
}
}
m_callback.accept(rising, falling);
}
}
}

View File

@@ -1,154 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj;
import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
import edu.wpi.first.hal.InterruptJNI;
/**
* Class for handling synchronous (blocking) interrupts.
*
* <p>By default, interrupts will occur on rising edge.
*
* <p>Asynchronous interrupts are handled by the AsynchronousInterrupt class.
*/
public class SynchronousInterrupt implements AutoCloseable {
@SuppressWarnings("PMD.SingularField")
private final DigitalSource m_source;
private final int m_handle;
/** Event trigger combinations for a synchronous interrupt. */
public enum WaitResult {
/** Timeout event. */
kTimeout(0x0),
/** Rising edge event. */
kRisingEdge(0x1),
/** Falling edge event. */
kFallingEdge(0x100),
/** Both rising and falling edge events. */
kBoth(0x101);
/** WaitResult value. */
public final int value;
WaitResult(int value) {
this.value = value;
}
/**
* Create a wait result enum.
*
* @param rising True if a rising edge occurred.
* @param falling True if a falling edge occurred.
* @return A wait result enum.
*/
public static WaitResult getValue(boolean rising, boolean falling) {
if (rising && falling) {
return kBoth;
} else if (rising) {
return kRisingEdge;
} else if (falling) {
return kFallingEdge;
} else {
return kTimeout;
}
}
}
/**
* Constructs a new synchronous interrupt using a DigitalSource.
*
* <p>At construction, the interrupt will trigger on the rising edge.
*
* @param source The digital source to use.
*/
public SynchronousInterrupt(DigitalSource source) {
m_source = requireNonNullParam(source, "source", "SynchronousInterrupt");
m_handle = InterruptJNI.initializeInterrupts();
InterruptJNI.requestInterrupts(
m_handle, m_source.getPortHandleForRouting(), m_source.getAnalogTriggerTypeForRouting());
InterruptJNI.setInterruptUpSourceEdge(m_handle, true, false);
}
/**
* Closes the interrupt.
*
* <p>This does not close the associated digital source.
*/
@Override
public void close() {
InterruptJNI.cleanInterrupts(m_handle);
}
/**
* Wait for an interrupt.
*
* @param timeoutSeconds The timeout in seconds. 0 or less will return immediately.
* @param ignorePrevious True to ignore if a previous interrupt has occurred, and only wait for a
* new trigger. False will consider if an interrupt has occurred since the last time the
* interrupt was read.
* @return Result of which edges were triggered, or if a timeout occurred.
*/
public WaitResult waitForInterrupt(double timeoutSeconds, boolean ignorePrevious) {
long result = InterruptJNI.waitForInterrupt(m_handle, timeoutSeconds, ignorePrevious);
// Rising edge result is the interrupt bit set in the byte 0xFF
// Falling edge result is the interrupt bit set in the byte 0xFF00
// Set any bit set to be true for that edge, and then conduct a logical AND on the 2 results
// together to match the existing enum for all interrupts
boolean rising = (result & 0xFF) != 0;
boolean falling = (result & 0xFF00) != 0;
return WaitResult.getValue(rising, falling);
}
/**
* Wait for an interrupt, ignoring any previously occurring interrupts.
*
* @param timeoutSeconds The timeout in seconds. 0 or less will return immediately.
* @return Result of which edges were triggered, or if a timeout occurred.
*/
public WaitResult waitForInterrupt(double timeoutSeconds) {
return waitForInterrupt(timeoutSeconds, true);
}
/**
* Set which edges to trigger the interrupt on.
*
* @param risingEdge Trigger on rising edge
* @param fallingEdge Trigger on falling edge
*/
public void setInterruptEdges(boolean risingEdge, boolean fallingEdge) {
InterruptJNI.setInterruptUpSourceEdge(m_handle, risingEdge, fallingEdge);
}
/**
* Get the timestamp of the last rising edge.
*
* <p>This only works if rising edge was configured using setInterruptEdges.
*
* @return the timestamp in seconds relative to getFPGATime
*/
public double getRisingTimestamp() {
return InterruptJNI.readInterruptRisingTimestamp(m_handle) * 1e-6;
}
/**
* Get the timestamp of the last falling edge.
*
* <p>This only works if falling edge was configured using setInterruptEdges.
*
* @return the timestamp in seconds relative to getFPGATime
*/
public double getFallingTimestamp() {
return InterruptJNI.readInterruptFallingTimestamp(m_handle) * 1e-6;
}
/** Force triggering of any waiting interrupt, which will be seen as a timeout. */
public void wakeupWaitingInterrupt() {
InterruptJNI.releaseWaitingInterrupt(m_handle);
}
}

View File

@@ -1,120 +0,0 @@
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
package edu.wpi.first.wpilibj;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import edu.wpi.first.wpilibj.simulation.DIOSim;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.Test;
class InterruptTest {
@Test
void testAsynchronousInterrupt() {
AtomicBoolean hasFired = new AtomicBoolean(false);
AtomicInteger counter = new AtomicInteger(0);
try (DigitalInput di = new DigitalInput(0);
AsynchronousInterrupt interrupt =
new AsynchronousInterrupt(
di,
(rising, falling) -> {
counter.incrementAndGet();
hasFired.set(true);
})) {
interrupt.enable();
Timer.delay(0.5);
DIOSim digitalSim = new DIOSim(di);
digitalSim.setValue(false);
Timer.delay(0.02);
digitalSim.setValue(true);
Timer.delay(0.02);
int count = 0;
while (!hasFired.get()) {
Timer.delay(0.005);
count++;
assertTrue(count < 1000);
}
assertEquals(1, counter.get(), "The interrupt did not fire the expected number of times");
}
}
@Test
void testRisingEdge() {
AtomicBoolean hasFiredFallingEdge = new AtomicBoolean(false);
AtomicBoolean hasFiredRisingEdge = new AtomicBoolean(false);
try (DigitalInput di = new DigitalInput(0);
AsynchronousInterrupt interrupt =
new AsynchronousInterrupt(
di,
(rising, falling) -> {
hasFiredFallingEdge.set(falling);
hasFiredRisingEdge.set(rising);
})) {
interrupt.setInterruptEdges(true, true);
DIOSim digitalSim = new DIOSim(di);
digitalSim.setValue(false);
interrupt.enable();
Timer.delay(0.5);
digitalSim.setValue(true);
Timer.delay(0.02);
int count = 0;
while (!hasFiredRisingEdge.get()) {
Timer.delay(0.005);
count++;
assertTrue(count < 1000);
}
assertAll(
() ->
assertFalse(hasFiredFallingEdge.get(), "The interrupt triggered on the falling edge"),
() ->
assertTrue(
hasFiredRisingEdge.get(), "The interrupt did not trigger on the rising edge"));
}
}
@Test
void testFallingEdge() {
AtomicBoolean hasFiredFallingEdge = new AtomicBoolean(false);
AtomicBoolean hasFiredRisingEdge = new AtomicBoolean(false);
try (DigitalInput di = new DigitalInput(0);
AsynchronousInterrupt interrupt =
new AsynchronousInterrupt(
di,
(rising, falling) -> {
hasFiredFallingEdge.set(falling);
hasFiredRisingEdge.set(rising);
})) {
interrupt.setInterruptEdges(true, true);
DIOSim digitalSim = new DIOSim(di);
digitalSim.setValue(true);
interrupt.enable();
Timer.delay(0.5);
digitalSim.setValue(false);
Timer.delay(0.02);
int count = 0;
while (!hasFiredFallingEdge.get()) {
Timer.delay(0.005);
count++;
assertTrue(count < 1000);
}
assertAll(
() ->
assertTrue(
hasFiredFallingEdge.get(), "The interrupt did not trigger on the rising edge"),
() ->
assertFalse(hasFiredRisingEdge.get(), "The interrupt triggered on the rising edge"));
}
}
}