Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions src/st25r3916_interrupt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,21 @@
/*! Length of the interrupt registers */
#define ST25R3916_INT_REGS_LEN ( (ST25R3916_REG_IRQ_TARGET - ST25R3916_REG_IRQ_MAIN) + 1U )

#ifdef ST25R3916_SHARED_IRQ_LINE
/*! Upper bound on IRQ-register reads per st25r3916Isr() call. Only reached
* when another device on the shared IRQ line is holding it asserted, which
* this IC cannot clear. Rationale in st25r3916CheckForReceivedInterrupts(). */
#ifndef ST25R3916_ISR_MAX_REG_READS
#define ST25R3916_ISR_MAX_REG_READS 8U
#endif

/*! Interval at which st25r3916WaitForInterruptsTimed() polls the IRQ registers
* instead of trusting a pin edge to have filled in the interrupt status. */
#ifndef ST25R3916_IRQ_POLL_INTERVAL_US
#define ST25R3916_IRQ_POLL_INTERVAL_US 200U
#endif
#endif /* ST25R3916_SHARED_IRQ_LINE */

/*
******************************************************************************
* LOCAL DATA TYPES
Expand Down Expand Up @@ -99,6 +114,34 @@ void RfalRfST25R3916Class::st25r3916CheckForReceivedInterrupts(void)
ST_MEMSET(iregs, (int32_t)(ST25R3916_IRQ_MASK_ALL & 0xFFU), ST25R3916_INT_REGS_LEN);


#ifdef ST25R3916_SHARED_IRQ_LINE
/* Read this IC's IRQ registers at least once, then repeat while the pin is
* still asserted, up to a bounded number of reads.
*
* The default path below uses the pin level as the sole entry condition, which
* assumes the pin belongs to exactly one IC. When the line is shared, its
* level describes the bus rather than the selected IC, and it fails in both
* directions: held de-asserted, nothing is ever collected and every wait times
* out; held asserted by another IC, the while() cannot terminate because this
* IC has nothing left to clear. Reading unconditionally makes collection
* depend on the IC rather than on the wire, and the bound makes a foreign
* assertion cost a fixed number of SPI transactions instead of hanging.
*
* Undefine ST25R3916_SHARED_IRQ_LINE to get the upstream behaviour back; that
* is the correct choice as soon as the pin can represent per-IC state, i.e.
* one IRQ line per IC, or a true wired-OR of active-high outputs. */
uint8_t reads = 0U;
do {
st25r3916ReadMultipleRegisters(ST25R3916_REG_IRQ_MAIN, iregs, ST25R3916_INT_REGS_LEN);

irqStatus |= (uint32_t)iregs[0];
irqStatus |= (uint32_t)iregs[1] << 8;
irqStatus |= (uint32_t)iregs[2] << 16;
irqStatus |= (uint32_t)iregs[3] << 24;

reads++;
} while ((digitalRead(int_pin) == HIGH) && (reads < ST25R3916_ISR_MAX_REG_READS));
#else
/* In case the IRQ is Edge (not Level) triggered read IRQs until done */
while (digitalRead(int_pin) == HIGH) {
st25r3916ReadMultipleRegisters(ST25R3916_REG_IRQ_MAIN, iregs, ST25R3916_INT_REGS_LEN);
Expand All @@ -108,6 +151,7 @@ void RfalRfST25R3916Class::st25r3916CheckForReceivedInterrupts(void)
irqStatus |= (uint32_t)iregs[2] << 16;
irqStatus |= (uint32_t)iregs[3] << 24;
}
#endif /* ST25R3916_SHARED_IRQ_LINE */

/* Forward all interrupts, even masked ones to application */
st25r3916interrupt.status |= irqStatus;
Expand Down Expand Up @@ -146,10 +190,36 @@ uint32_t RfalRfST25R3916Class::st25r3916WaitForInterruptsTimed(uint32_t mask, ui

tmrDelay = timerCalculateTimer(tmo);

#ifdef ST25R3916_SHARED_IRQ_LINE
/* Poll this IC's IRQ registers while waiting, rather than only spinning on
* st25r3916interrupt.status.
*
* The default path below assumes a pin-edge ISR is filling in .status
* concurrently. On a shared IRQ line that edge is not dependable (see
* st25r3916CheckForReceivedInterrupts()), so .status can stay empty and the
* loop always runs to its timeout -- which is every transceive. Calling
* st25r3916CheckForReceivedInterrupts() on an interval fetches this IC's
* own registers over SPI, so progress no longer depends on the pin.
*
* Timing uses micros() rather than the RFAL timer, since timerIsExpired() is
* the coarse ms timer being used for the outer bound. The unsigned delta is
* wraparound-safe at this interval. */
uint32_t last_us = micros();

do {
uint32_t now_us = micros();
if ((now_us - last_us) >= ST25R3916_IRQ_POLL_INTERVAL_US) {
st25r3916CheckForReceivedInterrupts();
last_us = now_us;
}
status = (st25r3916interrupt.status & mask);
} while ((!timerIsExpired(tmrDelay) || (tmo == 0U)) && (status == 0U));
#else
/* Run until specific interrupt has happen or the timer has expired */
do {
status = (st25r3916interrupt.status & mask);
} while ((!timerIsExpired(tmrDelay) || (tmo == 0U)) && (status == 0U));
#endif /* ST25R3916_SHARED_IRQ_LINE */

status = st25r3916interrupt.status & mask;

Expand Down