mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[hal, wpilib] Remove interrupt (#7724)
This commit is contained in:
@@ -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() {}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
/** @} */
|
||||
@@ -97,7 +97,6 @@ void InitializeHAL() {
|
||||
InitializeEncoder();
|
||||
InitializeExtensions();
|
||||
InitializeI2C();
|
||||
InitializeInterrupts();
|
||||
InitializeMain();
|
||||
InitializeMockHooks();
|
||||
InitializeNotifier();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
@@ -60,7 +60,6 @@ void InitializeHAL() {
|
||||
InitializeEncoder();
|
||||
InitializeFRCDriverStation();
|
||||
InitializeI2C();
|
||||
InitializeInterrupts();
|
||||
InitializeLEDs();
|
||||
InitializeMain();
|
||||
InitializeNotifier();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user