rusefi-1/firmware/hw_layer/digital_input/digital_input_exti.cpp

267 lines
5.2 KiB
C++

/*
* digital_input_exti.cpp
*
* Created on: Dec 18, 2018
* @author Andrey Belomutskiy, (c) 2012-2021
*/
#include "pch.h"
#if HAL_USE_PAL && EFI_PROD_CODE
#include "digital_input_exti.h"
/**
* EXTI is a funny thing: you can only use same pin on one port. For example, you can use
* PA0 PB5 PE2 PD7
* but you cannot use
* PA0 PB0 PE2 PD7
* because pin '0' would be used on two different ports
*/
#ifdef STM32_I2C_I2C1_IRQ_PRIORITY
void efiExtiInit() {
nvicEnableVector(I2C1_EV_IRQn, STM32_I2C_I2C1_IRQ_PRIORITY);
}
struct ExtiChannel
{
ExtiCallback Callback = nullptr;
void* CallbackData;
// Name is also used as an enable bit
const char* Name = nullptr;
};
static ExtiChannel channels[16];
// EXT is not able to give you the front direction but you could read the pin in the callback.
void efiExtiEnablePin(const char *msg, brain_pin_e brainPin, uint32_t mode, ExtiCallback cb, void *cb_data) {
/* paranoid check, in case of GPIO_UNASSIGNED getHwPort will return NULL
* and we will fail on next check */
if (!isBrainPinValid(brainPin)) {
return;
}
efiAssertVoid(OBD_PCM_Processor_Fault, msg, "efiExtiEnablePin msg must not be null");
ioportid_t port = getHwPort(msg, brainPin);
if (port == NULL) {
return;
}
bool wasUsed = brain_pin_markUsed(brainPin, msg);
if (wasUsed) {
// error condition we shall bail
return;
}
int index = getHwPin(msg, brainPin);
auto& channel = channels[index];
/* is this index already used? */
if (channel.Callback) {
firmwareError(CUSTOM_ERR_PIN_ALREADY_USED_2, "%s: pin %s/index %d: exti index already used by %s",
msg,
hwPortname(brainPin),
index,
channel.Name);
return;
}
channel.Callback = cb;
channel.CallbackData = cb_data;
channel.Name = msg;
ioline_t line = PAL_LINE(port, index);
palEnableLineEvent(line, mode);
}
void efiExtiDisablePin(brain_pin_e brainPin)
{
/* paranoid check, in case of GPIO_UNASSIGNED getHwPort will return NULL
* and we will fail on next check */
if (!isBrainPinValid(brainPin))
return;
ioportid_t port = getHwPort("exti", brainPin);
if (port == NULL)
return;
brain_pin_markUnused(brainPin);
int index = getHwPin("exti", brainPin);
auto& channel = channels[index];
/* is this index was used? */
if (!channel.Callback) {
return;
}
ioline_t line = PAL_LINE(port, index);
palDisableLineEvent(line);
/* mark unused */
channel.Name = nullptr;
channel.Callback = nullptr;
channel.CallbackData = nullptr;
}
digital_input_s* startDigitalCaptureExti(const char * /*msg*/, brain_pin_e /*brainPin*/) {
return nullptr;
}
#if ! EFI_ICU_INPUTS
digital_input_s* startDigitalCapture(const char *msg, brain_pin_e brainPin) {
return startDigitalCaptureExti(msg, brainPin);
}
#endif // EFI_ICU_INPUTS
static inline void triggerInterrupt() {
// Manually fire the I2C1_EV interrupt, it will be queued after this interrupt returns
NVIC->STIR = I2C1_EV_IRQn;
}
struct ExtiQueueEntry {
efitick_t Timestamp;
uint8_t Channel;
};
template <typename T, size_t TSize>
class ExtiQueue {
public:
void push(const T& val) {
if ((m_write == m_read - 1) || (m_write == TSize - 1 && m_read == 0)) {
// queue full, drop
return;
}
arr[m_write] = val;
m_write++;
// wrap end of list
if (m_write == TSize) {
m_write = 0;
}
}
expected<T> pop() {
if (m_read == m_write) {
// Queue empty
return unexpected;
}
T value = arr[m_read];
m_read++;
// wrap end of list
if (m_read == TSize) {
m_read = 0;
}
return value;
}
private:
T arr[TSize];
uint8_t m_read = 0;
uint8_t m_write = 0;
};
static ExtiQueue<ExtiQueueEntry, 32> queue;
CH_IRQ_HANDLER(STM32_I2C1_EVENT_HANDLER) {
OSAL_IRQ_PROLOGUE();
while (true) {
// get the timestamp out under lock
// todo: lock freeeeee!
__disable_irq();
auto result = queue.pop();
__enable_irq();
// Queue empty, we're done here.
if (!result) {
break;
}
auto& entry = result.Value;
auto& timestamp = entry.Timestamp;
if (timestamp != 0) {
auto& channel = channels[entry.Channel];
if (channel.Name) {
channel.Callback(channel.CallbackData, timestamp);
}
}
}
OSAL_IRQ_EPILOGUE();
}
void handleExtiIsr(uint8_t index) {
// No need to lock anything, we're already the highest-pri interrupt!
uint32_t pr;
extiGetAndClearGroup1(1U << index, pr);
if (pr & (1 << index)) {
queue.push({getTimeNowNt(), index});
triggerInterrupt();
}
}
CH_FAST_IRQ_HANDLER(Vector58) {
handleExtiIsr(0);
}
CH_FAST_IRQ_HANDLER(Vector5C) {
handleExtiIsr(1);
}
CH_FAST_IRQ_HANDLER(Vector60) {
handleExtiIsr(2);
}
CH_FAST_IRQ_HANDLER(Vector64) {
handleExtiIsr(3);
}
CH_FAST_IRQ_HANDLER(Vector68) {
handleExtiIsr(4);
}
CH_FAST_IRQ_HANDLER(Vector9C) {
handleExtiIsr(5);
handleExtiIsr(6);
handleExtiIsr(7);
handleExtiIsr(8);
handleExtiIsr(9);
}
CH_FAST_IRQ_HANDLER(VectorE0) {
handleExtiIsr(10);
handleExtiIsr(11);
handleExtiIsr(12);
handleExtiIsr(13);
handleExtiIsr(14);
handleExtiIsr(15);
}
#else // not STM32
// TODO: non-stm32 exti
void efiExtiInit() {
firmwareError(OBD_PCM_Processor_Fault, "exti not supported");
}
void efiExtiEnablePin(const char *, brain_pin_e, uint32_t, ExtiCallback, void *) { }
void efiExtiDisablePin(brain_pin_e) { }
#endif
#endif /* HAL_USE_PAL && EFI_PROD_CODE */