mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-22 01:11:42 +00:00
During shared library loading, a different libLLVM can be pulled in, causing llvm symbols from dependent libraries to resolve to that library instead of this one. This has been seen in the wild with the Mesa OpenGL implementation in JavaFX applications (see wpilibsuite/shuffleboard#361). This is clearly a very breaking change. For some level of backwards compatibility, a namespace alias from llvm to wpi is performed in the "llvm" headers. Unfortunately, forward declarations of llvm classes will still break, but compilers seem to generate clear error messages in those cases ("namespace alias 'llvm' not allowed here, assuming 'wpi'"). This change also moves all the wpiutil headers to a single "wpi" subdirectory from the previously split "llvm", "support", "tcpsockets", and "udpsockets". Shim headers will be added for backwards compatibility in a later commit.
571 lines
19 KiB
C++
571 lines
19 KiB
C++
/*----------------------------------------------------------------------------*/
|
|
/* Copyright (c) 2017-2018 FIRST. All Rights Reserved. */
|
|
/* Open Source Software - may be modified and shared by FRC teams. The code */
|
|
/* must be accompanied by the FIRST BSD license file in the root directory of */
|
|
/* the project. */
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
#include "HAL/Interrupts.h"
|
|
|
|
#include <memory>
|
|
|
|
#include <wpi/condition_variable.h>
|
|
|
|
#include "AnalogInternal.h"
|
|
#include "DigitalInternal.h"
|
|
#include "ErrorsInternal.h"
|
|
#include "HAL/AnalogTrigger.h"
|
|
#include "HAL/Errors.h"
|
|
#include "HAL/handles/HandlesInternal.h"
|
|
#include "HAL/handles/LimitedHandleResource.h"
|
|
#include "HAL/handles/UnlimitedHandleResource.h"
|
|
#include "MockData/AnalogInDataInternal.h"
|
|
#include "MockData/DIODataInternal.h"
|
|
#include "MockData/HAL_Value.h"
|
|
#include "MockHooksInternal.h"
|
|
#include "PortsInternal.h"
|
|
|
|
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;
|
|
bool watcher;
|
|
double risingTimestamp;
|
|
double fallingTimestamp;
|
|
bool previousState;
|
|
bool fireOnUp;
|
|
bool fireOnDown;
|
|
int32_t callbackId;
|
|
|
|
void* callbackParam;
|
|
HAL_InterruptHandlerFunction callbackFunction;
|
|
};
|
|
|
|
struct SynchronousWaitData {
|
|
HAL_InterruptHandle interruptHandle;
|
|
wpi::condition_variable waitCond;
|
|
HAL_Bool waitPredicate;
|
|
};
|
|
} // namespace
|
|
|
|
static LimitedHandleResource<HAL_InterruptHandle, Interrupt, kNumInterrupts,
|
|
HAL_HandleEnum::Interrupt>* interruptHandles;
|
|
|
|
typedef HAL_Handle SynchronousWaitDataHandle;
|
|
static UnlimitedHandleResource<SynchronousWaitDataHandle, SynchronousWaitData,
|
|
HAL_HandleEnum::Vendor>*
|
|
synchronousInterruptHandles;
|
|
|
|
namespace hal {
|
|
namespace 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 init
|
|
} // namespace hal
|
|
|
|
extern "C" {
|
|
HAL_InterruptHandle HAL_InitializeInterrupts(HAL_Bool watcher,
|
|
int32_t* status) {
|
|
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;
|
|
|
|
anInterrupt->watcher = watcher;
|
|
|
|
return handle;
|
|
}
|
|
void HAL_CleanInterrupts(HAL_InterruptHandle interruptHandle, int32_t* status) {
|
|
HAL_DisableInterrupts(interruptHandle, status);
|
|
auto interrupt = interruptHandles->Get(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;
|
|
// If no change in interrupt, return;
|
|
if (retVal == interrupt->previousState) return;
|
|
// If its a falling change, and we dont fire on falling return
|
|
if (interrupt->previousState && !interrupt->fireOnDown) return;
|
|
// If its a rising change, and we dont fire on rising return.
|
|
if (!interrupt->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();
|
|
}
|
|
// If no change in interrupt, return;
|
|
if (retVal == interrupt->previousState) return;
|
|
// If its a falling change, and we dont fire on falling return
|
|
if (interrupt->previousState && !interrupt->fireOnDown) return;
|
|
// If its a rising change, and we dont fire on rising return.
|
|
if (!interrupt->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->previousState = SimDIOData[digitalIndex].GetValue();
|
|
|
|
int32_t uid = SimDIOData[digitalIndex].RegisterValueCallback(
|
|
&ProcessInterruptDigitalSynchronous,
|
|
reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
|
|
|
|
bool timedOut = false;
|
|
|
|
wpi::mutex waitMutex;
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
auto timeoutTime = std::chrono::steady_clock::now() +
|
|
std::chrono::duration<int64_t, std::nano>(
|
|
static_cast<int64_t>(timeout * 1e9));
|
|
#else
|
|
auto timeoutTime =
|
|
std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
|
|
#endif
|
|
|
|
{
|
|
std::unique_lock<wpi::mutex> lock(waitMutex);
|
|
while (!data->waitPredicate) {
|
|
if (data->waitCond.wait_until(lock, timeoutTime) ==
|
|
std::cv_status::timeout) {
|
|
timedOut = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cancel our callback
|
|
SimDIOData[digitalIndex].CancelValueCallback(uid);
|
|
synchronousInterruptHandles->Free(dataHandle);
|
|
|
|
// Check for what to return
|
|
if (timedOut) return WaitResult::Timeout;
|
|
// True => false, Falling
|
|
if (interrupt->previousState) {
|
|
// Set our return value and our timestamps
|
|
interrupt->fallingTimestamp = hal::GetFPGATimestamp();
|
|
return 1 << (8 + interrupt->index);
|
|
} else {
|
|
interrupt->risingTimestamp = hal::GetFPGATimestamp();
|
|
return 1 << (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->previousState = 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].RegisterVoltageCallback(
|
|
&ProcessInterruptAnalogSynchronous,
|
|
reinterpret_cast<void*>(static_cast<uintptr_t>(dataHandle)), false);
|
|
|
|
bool timedOut = false;
|
|
|
|
wpi::mutex waitMutex;
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
auto timeoutTime = std::chrono::steady_clock::now() +
|
|
std::chrono::duration<int64_t, std::nano>(
|
|
static_cast<int64_t>(timeout * 1e9));
|
|
#else
|
|
auto timeoutTime =
|
|
std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
|
|
#endif
|
|
|
|
{
|
|
std::unique_lock<wpi::mutex> lock(waitMutex);
|
|
while (!data->waitPredicate) {
|
|
if (data->waitCond.wait_until(lock, timeoutTime) ==
|
|
std::cv_status::timeout) {
|
|
timedOut = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cancel our callback
|
|
SimAnalogInData[analogIndex].CancelVoltageCallback(uid);
|
|
synchronousInterruptHandles->Free(dataHandle);
|
|
|
|
// Check for what to return
|
|
if (timedOut) return WaitResult::Timeout;
|
|
// True => false, Falling
|
|
if (interrupt->previousState) {
|
|
// Set our return value and our timestamps
|
|
interrupt->fallingTimestamp = hal::GetFPGATimestamp();
|
|
return 1 << (8 + interrupt->index);
|
|
} else {
|
|
interrupt->risingTimestamp = hal::GetFPGATimestamp();
|
|
return 1 << (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;
|
|
}
|
|
|
|
// Check to make sure we are actually an interrupt in synchronous mode
|
|
if (!interrupt->watcher) {
|
|
*status = NiFpga_Status_InvalidParameter;
|
|
return WaitResult::Timeout;
|
|
}
|
|
|
|
if (interrupt->isAnalog) {
|
|
return WaitForInterruptAnalog(interruptHandle, interrupt.get(), timeout,
|
|
ignorePrevious);
|
|
} else {
|
|
return WaitForInterruptDigital(interruptHandle, interrupt.get(), timeout,
|
|
ignorePrevious);
|
|
}
|
|
}
|
|
|
|
static void ProcessInterruptDigitalAsynchronous(const char* name, void* param,
|
|
const struct HAL_Value* value) {
|
|
// void* is a HAL handle
|
|
// convert to uintptr_t first, then to handle
|
|
uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
|
|
HAL_InterruptHandle handle = static_cast<HAL_InterruptHandle>(handleTmp);
|
|
auto interrupt = interruptHandles->Get(handle);
|
|
if (interrupt == nullptr) return;
|
|
// Have a valid interrupt
|
|
if (value->type != HAL_Type::HAL_BOOLEAN) return;
|
|
bool retVal = value->data.v_boolean;
|
|
// If no change in interrupt, return;
|
|
if (retVal == interrupt->previousState) return;
|
|
int32_t mask = 0;
|
|
if (interrupt->previousState) {
|
|
interrupt->previousState = retVal;
|
|
interrupt->fallingTimestamp = hal::GetFPGATimestamp();
|
|
mask = 1 << (8 + interrupt->index);
|
|
if (!interrupt->fireOnDown) return;
|
|
} else {
|
|
interrupt->previousState = retVal;
|
|
interrupt->risingTimestamp = hal::GetFPGATimestamp();
|
|
mask = 1 << (interrupt->index);
|
|
if (!interrupt->fireOnUp) return;
|
|
}
|
|
|
|
// run callback
|
|
auto callback = interrupt->callbackFunction;
|
|
if (callback == nullptr) return;
|
|
callback(mask, interrupt->callbackParam);
|
|
}
|
|
|
|
static void ProcessInterruptAnalogAsynchronous(const char* name, void* param,
|
|
const struct HAL_Value* value) {
|
|
// void* is a HAL handle
|
|
// convert to intptr_t first, then to handle
|
|
uintptr_t handleTmp = reinterpret_cast<uintptr_t>(param);
|
|
HAL_InterruptHandle handle = static_cast<HAL_InterruptHandle>(handleTmp);
|
|
auto interrupt = interruptHandles->Get(handle);
|
|
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) return;
|
|
// If no change in interrupt, return;
|
|
if (retVal == interrupt->previousState) return;
|
|
int mask = 0;
|
|
if (interrupt->previousState) {
|
|
interrupt->previousState = retVal;
|
|
interrupt->fallingTimestamp = hal::GetFPGATimestamp();
|
|
if (!interrupt->fireOnDown) return;
|
|
mask = 1 << (8 + interrupt->index);
|
|
} else {
|
|
interrupt->previousState = retVal;
|
|
interrupt->risingTimestamp = hal::GetFPGATimestamp();
|
|
if (!interrupt->fireOnUp) return;
|
|
mask = 1 << (interrupt->index);
|
|
}
|
|
|
|
// run callback
|
|
auto callback = interrupt->callbackFunction;
|
|
if (callback == nullptr) return;
|
|
callback(mask, interrupt->callbackParam);
|
|
}
|
|
|
|
static void EnableInterruptsDigital(HAL_InterruptHandle handle,
|
|
Interrupt* interrupt) {
|
|
int32_t status = 0;
|
|
int32_t digitalIndex = GetDigitalInputChannel(interrupt->portHandle, &status);
|
|
if (status != 0) return;
|
|
|
|
interrupt->previousState = SimDIOData[digitalIndex].GetValue();
|
|
|
|
int32_t uid = SimDIOData[digitalIndex].RegisterValueCallback(
|
|
&ProcessInterruptDigitalAsynchronous,
|
|
reinterpret_cast<void*>(static_cast<uintptr_t>(handle)), false);
|
|
interrupt->callbackId = uid;
|
|
}
|
|
|
|
static void EnableInterruptsAnalog(HAL_InterruptHandle handle,
|
|
Interrupt* interrupt) {
|
|
int32_t status = 0;
|
|
int32_t analogIndex =
|
|
GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
|
|
if (status != 0) return;
|
|
|
|
status = 0;
|
|
interrupt->previousState = GetAnalogTriggerValue(
|
|
interrupt->portHandle, interrupt->trigType, &status);
|
|
if (status != 0) return;
|
|
|
|
int32_t uid = SimAnalogInData[analogIndex].RegisterVoltageCallback(
|
|
&ProcessInterruptAnalogAsynchronous,
|
|
reinterpret_cast<void*>(static_cast<uintptr_t>(handle)), false);
|
|
interrupt->callbackId = uid;
|
|
}
|
|
|
|
void HAL_EnableInterrupts(HAL_InterruptHandle interruptHandle,
|
|
int32_t* status) {
|
|
auto interrupt = interruptHandles->Get(interruptHandle);
|
|
if (interrupt == nullptr) {
|
|
*status = HAL_HANDLE_ERROR;
|
|
return;
|
|
}
|
|
|
|
// If we have not had a callback set, error out
|
|
if (interrupt->callbackFunction == nullptr) {
|
|
*status = INCOMPATIBLE_STATE;
|
|
return;
|
|
}
|
|
|
|
// EnableInterrupts has already been called
|
|
if (interrupt->callbackId >= 0) {
|
|
// We can double enable safely.
|
|
return;
|
|
}
|
|
|
|
if (interrupt->isAnalog) {
|
|
EnableInterruptsAnalog(interruptHandle, interrupt.get());
|
|
} else {
|
|
EnableInterruptsDigital(interruptHandle, interrupt.get());
|
|
}
|
|
}
|
|
void HAL_DisableInterrupts(HAL_InterruptHandle interruptHandle,
|
|
int32_t* status) {
|
|
auto interrupt = interruptHandles->Get(interruptHandle);
|
|
if (interrupt == nullptr) {
|
|
*status = HAL_HANDLE_ERROR;
|
|
return;
|
|
}
|
|
|
|
// No need to disable if we are already disabled
|
|
if (interrupt->callbackId < 0) return;
|
|
|
|
if (interrupt->isAnalog) {
|
|
// Do analog
|
|
int32_t status = 0;
|
|
int32_t analogIndex =
|
|
GetAnalogTriggerInputIndex(interrupt->portHandle, &status);
|
|
if (status != 0) return;
|
|
SimAnalogInData[analogIndex].CancelVoltageCallback(interrupt->callbackId);
|
|
} else {
|
|
int32_t status = 0;
|
|
int32_t digitalIndex =
|
|
GetDigitalInputChannel(interrupt->portHandle, &status);
|
|
if (status != 0) return;
|
|
SimDIOData[digitalIndex].CancelValueCallback(interrupt->callbackId);
|
|
}
|
|
interrupt->callbackId = -1;
|
|
}
|
|
double 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;
|
|
}
|
|
double 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_AttachInterruptHandler(HAL_InterruptHandle interruptHandle,
|
|
HAL_InterruptHandlerFunction handler,
|
|
void* param, int32_t* status) {
|
|
auto interrupt = interruptHandles->Get(interruptHandle);
|
|
if (interrupt == nullptr) {
|
|
*status = HAL_HANDLE_ERROR;
|
|
return;
|
|
}
|
|
|
|
interrupt->callbackFunction = handler;
|
|
interrupt->callbackParam = param;
|
|
}
|
|
|
|
void HAL_AttachInterruptHandlerThreaded(HAL_InterruptHandle interruptHandle,
|
|
HAL_InterruptHandlerFunction handler,
|
|
void* param, int32_t* status) {
|
|
HAL_AttachInterruptHandler(interruptHandle, handler, param, status);
|
|
}
|
|
|
|
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;
|
|
}
|
|
} // extern "C"
|