cancel
Showing results for 
Search instead for 
Did you mean: 

STSW-ST25R-LIB using SPI while in IRQ context

afr_e2
Associate II

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

This discussion has been locked for participation. If you have a question, please start a new topic in order to ask your question
1 ACCEPTED SOLUTION

Accepted Solutions

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 

View solution in original post

10 REPLIES 10
Ulysses HERNIOSUS
ST Employee

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

 

 

TDK
Guru

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
}

 

If you feel a post has answered your question, please click "Accept as Solution".

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

afr_e2
Associate II

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

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

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?

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

Hi Ulysses,

this ist my setup:

ST25.jpg

The test is to read cyclic the UID of the chip card. I have made 2 tests:

  1. st25r3916Isr() is called in the EXTI context (good case)
  2. st25r3916Isr() is called in the mainloop context (called in st25r3916WaitForInterruptsTimed() = bad case)

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

 

 

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