1
\$\begingroup\$

I've been trying to use spi.write() in a Ticker function, but the following error is thrown out onto the serial line:

Code: 324 Module: 255
Error Message: Assertion failed: _id
Location: 0xCE97
File: /extras/mbed-os.lib/rtos/Mutex.cpp+51
Error Value: 0x0
Current Thread: rtx_idle  Id: 0x1000037C Entry: 0xF117 StackSize: 0x200 StackMem: 0x10000408 SP: 0x10007F30
For more info, visit: https://armmbed.github.io/mbedos-error/?error=0x80FF0144
-- MbedOS Error Info --

I'm now starting to wonder whether mbed's SPI is non-reentrant.

If so, what are my options? Not using Ticker would involve a lot of code being re-written, which I really don't want to do. If I use the internal LPC1768 SPI registers instead of the mbed SPI class, would the problem go away?

\$\endgroup\$
2
  • \$\begingroup\$ Note that if you do visit that URL, it says that this particular error code is undefined. \$\endgroup\$ – Dave Tweed Jun 2 '19 at 20:01
  • \$\begingroup\$ @DaveTweed, yes I noticed that. \$\endgroup\$ – 19172281 Jun 2 '19 at 20:26
2
\$\begingroup\$

Mutexes cannot be acquired in an ISR context, and the SPI write function requires one. An easy way around would be to use an EventQueue to defer the operation to the main thread. Barely any code change required.

\$\endgroup\$
5
  • \$\begingroup\$ The problem is that I have TCP recv calls in main which block. Would this get in the way? I really need the SPI to write every tick of the Ticker. \$\endgroup\$ – 19172281 Jun 3 '19 at 17:31
  • \$\begingroup\$ Start a second thread with priority osPriorityRealtime. Then defer from ISR to that thread. Then you can keep your main thread code unchanged. \$\endgroup\$ – Jan Jongboom Jun 4 '19 at 9:37
  • \$\begingroup\$ Would I start the thread in main? \$\endgroup\$ – 19172281 Jun 4 '19 at 16:48
  • \$\begingroup\$ Could you please provide a code example? \$\endgroup\$ – 19172281 Jun 4 '19 at 18:28
  • \$\begingroup\$ I've asked a new question with some example code: electronics.stackexchange.com/questions/441943/… \$\endgroup\$ – 19172281 Jun 8 '19 at 12:27
0
\$\begingroup\$

I'm now starting to wonder whether mbed's SPI is non-reentrant.

Browsing the source code, it definitely is not. spi.write() ends up calling spi_master_write() in the file mbed-os/targets/TARGET_NXP/TARGET_LPC176X/spi_api.c. Nowhere along the way that I can see is there any attempt to serialize accesses to the hardware.

The documentation says that the API is thread-safe, and it appears to be true. In embed-os/drivers/SPI.ccp, the code is essentially

int SPI::write(...)
{
    select();
    int ret = spi_master_[block_]write(...);
    deselect();
    return ret;
}

In order for this to be thread-safe, there would have to be a mutex around the first three statements. The select() function calls lock() as its first statement and the deselect() function calls unlock() as its final statement. But that still doesn't mean that it can be called from both interrupt and non-interrupt code.

To be safe, you'll have to add such protection at the application level. Specifically, disable interrupts when accessing the SPI controller in non-interrupt code.

How many different threads do you have, and how many SPI slaves do have connected?

If I use the internal LPC1768 SPI registers instead of the mbed SPI class, would the problem go away?

No, you'd still need to add protection among the various users of the SPI hardware.

\$\endgroup\$
3
  • \$\begingroup\$ I'm only using one thread, and one SPI slave. \$\endgroup\$ – 19172281 Jun 2 '19 at 21:08
  • \$\begingroup\$ could you please clarify what you mean by "add protection"? \$\endgroup\$ – 19172281 Jun 17 '19 at 21:05
  • \$\begingroup\$ How about using EventQueue as Jan suggested, and defer the SPI_write call to another thread? \$\endgroup\$ – 19172281 Jun 17 '19 at 21:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.