Author Topic: STM32 HAL using multipe SPI peripherals with interrupt driven callback  (Read 5771 times)

0 Members and 1 Guest are viewing this topic.

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
So, I'm slowly advancing my experience with STM32 HAL, and have got to the point where I am writing an embedded application that uses two SPI ports, and I need interrupt handling on end of transfer for each port....

Except it seems like there's only one SPI interrupt callback function in HAL? (kind of like there's the one SPI command function too, though that is fine to just call from multiple places! a callback that is tied into the internal operation of a driver for an external IC, not so much...)

in my ideal dreamland fantasy world I could link whatever callback function I wanted when I do the SPI command to set the peripheral off, which will eventually generate the interrupt that calls the callback... But it seems we don't all live in my happy dreamland unfortunately... so.. back to the reality of STM32 HAL...

I guess I'm going to have to move my callback function somewhere else, so it can deal with two separate SPI peripherals, and write the function so it does totally different things depending on which SPI interrupt event has called it.  :--

Am I reading this situation right? this seems pretty blah.

(PS, I don't want to be too down on STM32cube.. I'm learning it more, the more I use it, and finding that *overall* it's useful, and once a peripheral is mastered it cecomes very fast to use again on another project, which is nice... bugs haven't really been a problem so far either - though have to admit the learning curve without having an existing expert on hand I can just ask for tips from is inhumane....  I am still really feeling the lack of a "so you want to use STM32HAL to develop something useful" guide that just explains the overall approach and what everything is meant to do, for someone with pre existing embedded code experience...)


anyway.. sorry for the ramble...

Anyone who knows - is what I'm planning to do for this callback the correct approach when using STM32 HAL with multiple SPI peripherals that need callbacks to implement different things?

thanks in advance!!
 

Online Kjelt

  • Super Contributor
  • ***
  • Posts: 6460
  • Country: nl
You shouldn't be doing "much" anyway in an ISR.
So check source of interrupt and copy the received data to the buffer of your own software.
Then return and your RTOS will schedule the handlerfunction for the data.
 

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
Yeah not doing much, but actually in both cases I'm already doing multi byte DMA. The interrupt handler is to clean up after the DMA burst.

In the case of the slave control interface it's launching the SPI slave ti receive next command, and for the DAC, it's driving the nSYNC pin of the DAC, which is pretty much like slave select, but stays down for all 3 bytes to make 24 bits, instead of popping up every 8, like the hw slave select pin that the peripheral has...
 

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
And yes I plan to move the SPI slave stuff out of the callback entirely, once it grows beyond 4 states is currently got... Though right now it's working great just making the next call from the callback.
 

Offline SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14466
  • Country: fr
There is a different interrupt handler for each SPIx peripheral: SPI1_IRQHandler(), SPI2_IRQHandler()...

Just put whatever code needs to be executed inside each one of those.

HAL has an additional HAL_SPI_IRQHandler() that you can call inside the SPI interrupt handlers (which you can see in many of their examples), but it's just unnecessary sugar coating that calls your own callback in turn. Note that if you don't use HAL_SPI_IRQHandler(), you have to deal with the interrupt flags and clear them yourself. It's very straightforward though.

If you still insist on using HAL_SPI_IRQHandler(), you have to pass them the SPIx handle corresponding to each different SPI peripheral.
The SPIx handles are the variables defined as SPI_HandleTypeDef that you use to initialize each SPIx.
« Last Edit: April 24, 2018, 01:19:39 pm by SiliconWizard »
 
The following users thanked this post: julianhigginson

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
There is a different interrupt handler for each SPIx peripheral: SPI1_IRQHandler(), SPI2_IRQHandler()...

Just put whatever code needs to be executed inside each one of those.

HAL has an additional HAL_SPI_IRQHandler() that you can call inside the SPI interrupt handlers (which you can see in many of their examples), but it's just unnecessary sugar coating that calls your own callback in turn.
QFT.

To further address OP's doubt, the HAL callbacks function are indeed unique, called inside the generic, single,  HAL_PPP_IRQHandlers.
Both the ISRs and the callbacks expect the HAL peripheral handle as argument, to differentiate among the various PPP instances.
As an example, this is the generated code for SPI1 and SPI2:
Code: [Select]
/**
* @brief This function handles SPI1 global interrupt.
*/
void SPI1_IRQHandler(void)
{
  /* USER CODE BEGIN SPI1_IRQn 0 */

  /* USER CODE END SPI1_IRQn 0 */
  HAL_SPI_IRQHandler(&hspi1);
  /* USER CODE BEGIN SPI1_IRQn 1 */

  /* USER CODE END SPI1_IRQn 1 */
}

/**
* @brief This function handles SPI2 global interrupt.
*/
void SPI2_IRQHandler(void)
{
  /* USER CODE BEGIN SPI2_IRQn 0 */

  /* USER CODE END SPI2_IRQn 0 */
  HAL_SPI_IRQHandler(&hspi2);
  /* USER CODE BEGIN SPI2_IRQn 1 */

  /* USER CODE END SPI2_IRQn 1 */
}
Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: julianhigginson

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
haha! wow! that's excellent. so glad I asked rather than just hacked this.

the only thing I found that seemed useful to use when I setup my first interrupt driven response was HAL_SPI_IRQHandler() so I used that...

Can I move the definition for SPI2_IRQHandler(), SPI1_IRQHandler() in my driver files so that they are where the rest of the code they need to work with lives?
 

Offline newbrain

  • Super Contributor
  • ***
  • Posts: 1719
  • Country: se
Can I move the definition for SPI2_IRQHandler(), SPI1_IRQHandler() in my driver files so that they are where the rest of the code they need to work with lives?
The code I shown is the one the CubeMx generates when you ask it to use two SPIs and enable their interrupts. In the startup code provided with the CMSIS/HAL those two ISR functions are weakly* defined with a default handler (an infinite loop).
It is, in fact, up to the user to provide useful implementation of the various ISRs; they definitely belong to your code.

*weakly means that the linker will use the default __weak definition (in the startup source), unless a user provided one is found. Most of the ISRs are defined that way, as are the callbacks in the HAL. Less flexible than a function pointer, a smidgen more efficient.

Nandemo wa shiranai wa yo, shitteru koto dake.
 
The following users thanked this post: julianhigginson

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
interesting...

the function I was originally using happily with one SPI device at a time was actually HAL_SPI_TxRxCpltCallback()
which was linked with __weak and could be overriden..

but when I try to use SPI1_IRQHandler() to do the job, I get a "Multiple definition" error, and when I go looking in the source code I see that the provided SPI1_IRQHandler() isn't linked with __weak

So perhaps I wasn't 100% right in understanding what I was doing when i asked the question originally.....
HAL_SPI_TxRxCpltCallback() obviously isn't the same thing as HAL_SPI_IRQHandler( ) though I was sure that enabling interrupts for the SPI device is what allowed this function I was using to get called at the end of an SPI run... now I'm chasing the call heirachy back, maybe it's not.

either way... looks like I can't have a nice simple function just defined in each of my drivers code to deal with the automatic housekeeping at the end of a multi byte DMA transfer to/from SPI peripheral.

Best option now as I understand it, is to make a new file with my single HAL_SPI_TxRxCpltCallback( )that includes the headers of each of my driver files, so it can directly operate on the relevant state variables, etc in there... and then have a check of the hspi value passed when it's called, to select which chunk of code inside it to run each time!! aargh!

This sounds a bit horrible.

Has anyone had multiple SPI peripherals running DMA, with an end of transfer function used to do basic things? if so, what's the function(s) to use?
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Hey, the SPI peripheral is trivial. If the library makes it so difficult to use, just don't use the library.

The reason why it sucks is probably because it's hard to write a library that suits all the needs. So just do directly what's best for your case, and you'll get rid of a lot of unwanted code (and unnecessary learning curve).

Depending on the case, you may need either of the following:
1) DMA end of transfer interrupt (OKish for fixed length transmissions, although unreliable without extra watchdogs around it), or
2) EXTI interrupt for the rising nCS pin (specifically needed for variable length messages)

Be aware that at least on some STM32 SPI implementations, the only way to flush TXFIFO and restart the SPI for a fresh transfer after a variable length message is to reset the SPI through RCC reset registers(!).

Also note that EXTI interrupts mux on a weird way (i.e., for example, PORTA5 is orred with PORTB5, PORTC5, and so on...), and several EXTI sources are further orred together to same interrupt vectors, so it depends on your product pin mapping and luck whether you can get separate interrupt vectors at all for separate SPI devices. So, you may need to resolve the actual source in the beginning of your handler anyway.

Getting rid of the library will expose you to the internals and it will became apparent what's possible and what's not.
 
The following users thanked this post: julianhigginson

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
Re: STM32 HAL using multipe SPI peripherals with interrupt driven callback
« Reply #10 on: April 25, 2018, 11:46:12 pm »
I have done my own peripheral code for years (though not with STM32) but I want to move away from that.

I'm trying to use the HAL, so that I have the ability to work much faster on new projects with the STM32 once I understand the HAL's way of working.  I like the look of the range of STM32 chips, and think that it's worth learning to be able to use all this stuff as one platform. (especially now they have announced what looks like it will be a very solid "IoT" chip)

I feel like everything I need is there, but the issue is there's so much other stuff, and no real obvious "this is how we do things with HAL" guide.. Even though from what I've seen so far, the HAL seems pretty consistent across peripheral types.

 

Offline julianhigginsonTopic starter

  • Frequent Contributor
  • **
  • Posts: 783
  • Country: au
Re: STM32 HAL using multipe SPI peripherals with interrupt driven callback
« Reply #11 on: April 26, 2018, 03:26:22 am »

So, I made a new c file in the project to hold my override of HAL_SPI_TxRxCpltCallback() and included the headers for my driver code, then pasted the original two function contents onto this function, with a simple if/then to determine which SPI it was dealing with.

To be honest I'm not happy with it, because it's ugly as all hell - It splits driver functionality across files, while at the same time merges some different driver functionality into one file, and required pushing a bunch of defines/variables only relevant to the drivers operation into the drivers include file... (well I could make 2 include files for each driver, I guess....)

But it does work... And now I have my two (soon to be 3) SPI ports both auto-doing the things I need them to at the end of a DMA transfer...

I can't help but think there's a more sensible approach to this whole thing that I can jump to later, but I guess if there is no better option, then I'll just document the hell out of each driver NEEDING this other random c file to behave right, and hope it doesn't ruin anyone's day sometime in the future.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf