2024-10-07 03:11 AM
Hello everyone,
in a project we are using the ST25R3916 chip to read RFID cards. To do this we are using the sources of the STSW-ST25R-LIB library. If the ST25R3916 sets its IRQ output, the routine st25r3916isr() is called. In this ISR the interrupt registers are read via SPI.
void st25r3916Isr( void )
{
st25r3916CheckForReceivedInterrupts();
...
}
void st25r3916CheckForReceivedInterrupts( void )
{
...
/* In case the IRQ is Edge (not Level) triggered read IRQs until done */
while( platformGpioIsHigh( ST25R_INT_PORT, ST25R_INT_PIN ) )
{
st25r3916ReadMultipleRegisters( ST25R3916_REG_IRQ_MAIN, iregs, ST25R3916_INT_REGS_LEN );
...
}
...
}
As other components can be connected to this SPI bus, no SPI communication is alowed in the context of an interrupt, as otherwise 2 SPI operations can overlap and this can lead to undesirable effects.
How can I get around this problem? Is there some kind of polling mode or how can I move the SPI operation into the context of the main loop?
Thank you for your support!
Best regards,
Andy
Solved! Go to Solution.
2024-10-09 01:32 AM
Hi Andy,
just look at the ST25_IRQ line in the failing case. The IRQ line stays high for a very long time. Lookin at the IRQ just before there is an I_txe which is read in around 60us. 70us after the starts the next IRQ which goes un-handled. So something wrong with your timeouts maybe or your flag not working properly. With the GetInterrupt() it should repeatedly be called and see the pin being high. But apparently that doesn't happen.
Did you also adapt the GetInterrupt() function or only the WaitFor..()?
Ulysses
2024-10-07 06:26 AM
Hi Andy,
IMO the freertos demo of the STSW-ST25R-LIB shows a deferred interrupt, also the Linux port (stsw-st25r013) defers the interrupt reading.
BR, Ulysses
2024-10-07 06:29 PM
The standard way of solving this problem is to set a flag in the interrupt which you check for in the main loop to do the thing you want.
// global
volatile int flag = 0;
// in interrupt
void IRQHandler() {
flag = 1;
}
// in main loop
if (flag == 1) {
flag = 0;
// SPI operations here
}
2024-10-07 11:17 PM
Hi Ulysses,
thank you for the reply. I checked both implementations and have seen, that both are using a mulithreading OS and spend a seperate thread to handle the ISR. In that case you can use a semaphore or mutex.
In my project I'm not using a RTOS, its running completelly baremetal, so I cannot synchronize the ISR with a semaphore.
Regards,
Andy
2024-10-07 11:20 PM
Hi TDK,
thank you for your reply. I already tried to do so, but then I'm not able to read the card anymore. Maybe it takes too much time between irq rising and handling?!? So, is there any other possibility to synchronize the SPI access with the main loop?
Greetz, Andy
2024-10-07 11:37 PM
Hi Andy,
not sure how you have actually done this.
I recommend for your case to change functions st25r3916WaitForInterruptsTimed() and st25r3916GetInterrupt() to add at the beginning and inside the while a check for the interrupt pin or another interrupt flag as described by TDK to execute then the st25r3916Isr().
It should also work without the flag but I don't recommend it as it will create lots of traffic on the SPI bus and makes debugging using Logic Analyzer difficult.
BR, Ulysses
2024-10-08 06:05 AM
In my current implementation I registered the st25r3916Isr() to the EXTI9_5_IRQHandler(). If the rising edge is detected, the st25r3916Isr() is called immediatelly - in EXTI9_5_IRQHandler context. Since the function reads the IRQ registers via SPI, there will be problems in future...
void EXTI9_5_IRQHandler(void)
{
st25r3916Isr();
}
When calling the st25r3916Isr() immediatelly, most the time just one single IRQ flag is set. While changing to your or TDK's suggestion (degenerate ISR by global flag), multiple IRQ bits are set, when reading the IRQ registers, but I cannot read the chip card.
uint32_t st25r3916WaitForInterruptsTimed( uint32_t mask, uint16_t tmo )
{
uint32_t tmrDelay;
uint32_t status;
tmrDelay = platformTimerCreate( tmo );
/* Run until specific interrupt has happen or the timer has expired */
do
{
/* start my modification */
if (platformGpioIsHigh( ST25R_INT_PORT, ST25R_INT_PIN ))
{
st25r3916Isr();
}
/* end my modification */
status = (st25r3916interrupt.status & mask);
} while( ( !platformTimerIsExpired( tmrDelay ) || (tmo == 0U)) && (status == 0U) );
platformTimerDestroy( tmrDelay );
status = st25r3916interrupt.status & mask;
platformProtectST25RIrqStatus();
st25r3916interrupt.status &= ~status;
platformUnprotectST25RIrqStatus();
return status;
}
I'm not sure understanding you suggestion with changing the functions st25r3916WaitForInterruptsTimed() and st25r3916GetInterrupt(). Is my modification as you mentioned? If "yes": I cannot read chip cards. Any other ideas?
2024-10-08 08:07 AM
Hi,
looks correct. Fastest way forward is if you can do a logic analyzer trace (SPI + IRQ) of the failing case and post it here.
BR, Ulysses
2024-10-08 11:54 PM
Hi Ulysses,
this ist my setup:
The test is to read cyclic the UID of the chip card. I have made 2 tests:
Please see zip file with both logic analyzer traces. These traces are monitored with saleae logic analyzer. You can download the software at https://www.saleae.com/ for free.
Regards, Andy
2024-10-09 01:32 AM
Hi Andy,
just look at the ST25_IRQ line in the failing case. The IRQ line stays high for a very long time. Lookin at the IRQ just before there is an I_txe which is read in around 60us. 70us after the starts the next IRQ which goes un-handled. So something wrong with your timeouts maybe or your flag not working properly. With the GetInterrupt() it should repeatedly be called and see the pin being high. But apparently that doesn't happen.
Did you also adapt the GetInterrupt() function or only the WaitFor..()?
Ulysses