Author Topic: SPI1 interrupt won't fire (STM32U5)  (Read 917 times)

0 Members and 1 Guest are viewing this topic.

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
SPI1 interrupt won't fire (STM32U5)
« on: January 26, 2022, 06:07:25 am »
I'm trying to port my fairly minimal project that's HAL code generated by CubeMX to bare metal. I've stepped through the HAL code and reproduced all the register changes manually.

In my bare metal code the SPI transfer completes without ever firing an interrupt.  I also have an EXTI7 interrupt which fires reliably (and often, about once per 38us). I discovered that disabling the EXTI7 interrupt allows the SPI ISR to be reached.

In the HAL implementation my EXTI7 ISR (which only initiates the SPI xfer) is much more bloated, and there I don't have this issue. I suspect there is some other kind of conflict happening between the interrupts rather than EXTI7 hogging clock cycles. I took a look at EXTI mux, but I don't see any HAL calls to manage this so I don't think that could be it. How should I debug this?

PS yes, I know such frequent interrupts aren't a good method for controlling SPI. I will be looking at DMA shortly, but I just wanted to get my working HAL code into bare-metal form to learn with.

« Last Edit: January 26, 2022, 06:37:16 am by davegravy »
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1193
  • Country: ca
    • VE7XEN Blog
Re: SPI1 interrupt won't fire (STM32U5)
« Reply #1 on: January 26, 2022, 09:10:48 am »
First the obvious:

Is the desired SPI1 interrupt enabled in NVIC?

Are the desired SPI interrupts unmasked in the SPI peripheral?

Have you verified this with a debugger?

Which SPI interrupt(s) have you enabled, and are you certain their conditions are being met?

You're sure your handler has the correct 'magic' name? Not sure about CubeMX but often if the name is wrong, it will just compile it as a normal function and you get no warning that the linker has stubbed out the ISR. Remember that C is case sensitive.

The less obvious:

What are you actually doing in EXTI7, and what is triggering it? If it 'initiates the SPI xfer', is it possible it is clearing the enabled interrupt flags in the SPI peripheral e.g. by adding data to the FIFO? By default EXTI has higher priority than SPI1 and will be serviced first. If it's running frequently / has a long run time, or even worse, if its timing gets synchronized with the SPI peripheral, it's could be very likely it will be running when the SPI interrupt fires. If its behaviour clears the SPI interrupt flags, then I believe the pending SPI interrupt doesn't get serviced (or any flag checks you do in the ISR may fail unexpectedly).

As to how to approach this, I would disable EXTI7 and do something in main() with the SPI peripheral that you think should trigger the interrupt. If it's not getting triggered, get out the debugger and interrogate the SPI registers and interrupt flags as the transfer gets executed, probably you've misconfigured the peripheral.
73 de VE7XEN
He/Him
 
The following users thanked this post: davegravy

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: SPI1 interrupt won't fire (STM32U5)
« Reply #2 on: January 26, 2022, 01:49:08 pm »
First the obvious:

Is the desired SPI1 interrupt enabled in NVIC?

Are the desired SPI interrupts unmasked in the SPI peripheral?

Have you verified this with a debugger?

Which SPI interrupt(s) have you enabled, and are you certain their conditions are being met?

You're sure your handler has the correct 'magic' name? Not sure about CubeMX but often if the name is wrong, it will just compile it as a normal function and you get no warning that the linker has stubbed out the ISR. Remember that C is case sensitive.


I think I can eliminate these because the SPI interrupt fires successfully when I disable the EXTI7 interrupt, right?

Just to be sure I'm doing:

Code: [Select]
WRITE_REG(SPI1->IER, (SPI_IER_EOTIE | SPI_IER_DXPIE | SPI_IER_TXPIE | SPI_IER_RXPIE | SPI_IER_TXTFIE | SPI_IER_OVRIE | SPI_IER_UDRIE | SPI_IER_CRCEIE | SPI_IER_TIFREIE | SPI_IER_MODFIE));

prioritygroup = NVIC_GetPriorityGrouping();
NVIC_SetPriority(SPI1_IRQn, NVIC_EncodePriority(prioritygroup, 0, 0));
NVIC_EnableIRQ(SPI1_IRQn);

and then for my ISR function:

Code: [Select]
void SPI1_IRQHandler(void)
{
}


The less obvious:

What are you actually doing in EXTI7, and what is triggering it? If it 'initiates the SPI xfer', is it possible it is clearing the enabled interrupt flags in the SPI peripheral e.g. by adding data to the FIFO? By default EXTI has higher priority than SPI1 and will be serviced first. If it's running frequently / has a long run time, or even worse, if its timing gets synchronized with the SPI peripheral, it's could be very likely it will be running when the SPI interrupt fires. If its behaviour clears the SPI interrupt flags, then I believe the pending SPI interrupt doesn't get serviced (or any flag checks you do in the ISR may fail unexpectedly).

Here's the entirety of my EXTI7 ISR:
Code: [Select]
void EXTI7_IRQHandler(void)
{
//Clear EXTI7 interrupt
EXTI->FPR1 = (0x08);

uint8_t Size = 4;

//Set xfer size
MODIFY_REG(SPI1->CR2, SPI_CR2_TSIZE, Size);

//Enable SPI1 peripheral
SET_BIT(SPI1->CR1, SPI_CR1_SPE);

//Enable SPI interrupts
WRITE_REG(SPI1->IER, (SPI_IER_EOTIE | SPI_IER_DXPIE | SPI_IER_TXPIE | SPI_IER_RXPIE | SPI_IER_TXTFIE | SPI_IER_OVRIE | SPI_IER_UDRIE | SPI_IER_CRCEIE | SPI_IER_TIFREIE | SPI_IER_MODFIE));

/* Master transfer start */
SET_BIT(SPI1->CR1, SPI_CR1_CSTART);

The priority thing you mention is interesting, is it possible the generated HAL code is somehow achieving a different priority balance between the two interrupts than my code? I looked through it with this in mind but didn't see anything. I am setting priority in main() for the EXTI7 interrupt similar to how I do it for SPI in the EXTI7 ISR:

Code: [Select]
uint32_t prioritygroup;
prioritygroup = NVIC_GetPriorityGrouping();
NVIC_SetPriority(EXTI7_IRQn, NVIC_EncodePriority(prioritygroup, 0, 0));
NVIC_EnableIRQ(EXTI7_IRQn);

 

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: SPI1 interrupt won't fire (STM32U5)
« Reply #3 on: January 26, 2022, 06:50:55 pm »
OK, so I've been trying to understand how the NVIC registers work. I can't seem to find any detailed documentation on these - they aren't covered at all  in the STM32U5 TRM and the Cortex M33 TRM only mentions that they're there, not how they actually function - like what does a 1 at a particular bit position do?

Moving on, I took the HAL code and replaced only the EXTI7 ISR call to HAL_SPI_Receive_IT() with my minimal bare metal version:

Code: [Select]
if (hspi1.State == HAL_SPI_STATE_READY) {
uint8_t Size = 4;

//Set xfer size
MODIFY_REG(SPI1->CR2, SPI_CR2_TSIZE, Size);

//Enable SPI1 peripheral
SET_BIT(SPI1->CR1, SPI_CR1_SPE);

//Enable SPI interrupts
WRITE_REG(SPI1->IER, (SPI_IER_EOTIE | SPI_IER_DXPIE | SPI_IER_TXPIE | SPI_IER_RXPIE | SPI_IER_TXTFIE | SPI_IER_OVRIE | SPI_IER_UDRIE | SPI_IER_CRCEIE | SPI_IER_TIFREIE | SPI_IER_MODFIE));

//Master transfer start
SET_BIT(SPI1->CR1, SPI_CR1_CSTART);
}

With this the SPI interrupt did fire, so I think I can rule out my EXTI7 ISR as the source of the problem. It must be related to init of the peripherals or interrupts somehow.
« Last Edit: January 26, 2022, 06:52:46 pm by davegravy »
 

Offline ve7xen

  • Super Contributor
  • ***
  • Posts: 1193
  • Country: ca
    • VE7XEN Blog
Re: SPI1 interrupt won't fire (STM32U5)
« Reply #4 on: January 26, 2022, 07:30:37 pm »
NVIC is part of the CPU core, so it's covered in the ARM documentation. Though honestly it's pretty shit documentation. ST might have some app notes or whatnot about it, but the canonical source is https://developer.arm.com/documentation/100230/0004/functional-description/nested-vectored-interrupt-controller/nvic-programmers-model/nvic-register-summary?lang=en

Quote
I think I can eliminate these because the SPI interrupt fires successfully when I disable the EXTI7 interrupt, right?

Sounds reasonable.

If you're setting both interrupts to the same priority, then if an ISR interrupt comes in while EXTI7 is being serviced, it will be queued until the ISR is complete. By default, each interrupt has a different priority (this is documented in the vector table in the STM32 reference manual). It's probably good practice (and likely what CubeMX does) to leave them unchanged unless you have reason to, the priorities generally 'make sense' for most applications. If you do change them though, set the SPI1 priority lower than EXTI7 if you want to guarantee it gets serviced (it will interrupt EXTI7), as long as you aren't turning off interrupts during EXTI7.

I think your EXTI7 ISR will at least clear TXC, but I'm not really sure what the peripheral does if you trigger a transfer without anything in the FIFO (writing to DR). I think unless you're operating it in RX-only mode, you need to stuff some data into the FIFO somewhere...

If it's handling setting up the next SPI transfer, what is triggering EXTI7? Shouldn't this be part of the SPI ISR, triggered when the transfer is complete or FIFOs have room for another packet?
73 de VE7XEN
He/Him
 
The following users thanked this post: davegravy

Offline davegravyTopic starter

  • Regular Contributor
  • *
  • Posts: 152
  • Country: ca
Re: SPI1 interrupt won't fire (STM32U5)
« Reply #5 on: January 26, 2022, 10:31:26 pm »
External ADC is pulsing the Data Ready Line (DRL) high every ~38us which is my trigger to do an SPI Rx on the STM32. After each DRL pulse I have a small time window to read and then SCK has to go quiet (else digital switching causes noise).  I know I probably need to do this with DMA and maybe timers but I had interrupts working pretty quickly in HAL so I thought this would be an easier step 1.

I believe I have a strong clue as to the problem. I physically disconnected the DRL line and the EXTI7 interrupt is still constantly firing. Not sure why yet since I tried to copy the HAL interrupt inits exactly.

EDIT:

I was clearing the EXTI7 interrupt flag incorrectly, writing 0x8 to EXTI->FPR1 instead of 0x80.  :palm:  :-DD
« Last Edit: January 26, 2022, 11:04:49 pm by davegravy »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf