This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Issue in SPI trensfer inside timer handler function

I need to read a SPI device every fixed milliseconds so I thought to create a TIMER and read SPI slave inside its handler. But there are some problems cause I get all zero values and in some cases I get the "SPI timeout transfer" error. I think that is a problem related to the ineraction between the SPI and TIMER interrupts.

The TIMER definition:

K_TIMER_DEFINE( fetchlDataTimer, Timer_Handler, NULL );

...

k_timer_start( &fetchDataTimer, K_SECONDS( 1 ), K_SECONDS( 1 ) );

The SPI read function inside the TIMER handler:

void ICM20948_Read_Reg( const struct spi_dt_spec *spec, uint8_t reg, uint8_t *data, uint8_t dataLen )
{
    reg |= 0x80;

	const struct spi_buf spiBufTx = {
		.buf = &reg,
		.len = 1
	};

	struct spi_buf_set tx = {
		.buffers = &spiBufTx,
		.count = 1
	};

	struct spi_buf spiBufRx[] = {
		{
			.buf = NULL,
			.len = 1
		},
		{
			.buf = data,
			.len = dataLen
		}
	};

	struct spi_buf_set rx = {
		.buffers = spiBufRx,
		.count = 2
	};

	spi_transceive_dt( spec, &tx, &rx );
}

1) What's the problem?

2) When I create a timer with the K_TIMER_DEFINE macro am I going to use a TIMERx peripheral? If so, how can I choose one in particular?

3) OUT TOPIC: is the above SPI read routine a good choice in terms of time performance? If not, have you some suggestions?

  • Hi,

    1) What's the problem?

    Most likely, the K_TIMER event handler runs at the same or lower priority as the SPI interrupt handler, which prevents the SPI interrupt handler from being executed as long as you stay in the timer handler. You can try to use spi_transceive_async() and exit the timer handler to allow the SPI interrupt to complete the transfer, see this post for more info. You will then be notified in the provided handler when the transfer is completed.

    2) When I create a timer with the K_TIMER_DEFINE macro am I going to use a TIMERx peripheral? If so, how can I choose one in particular?

    No, the system timer is by default using the 32.768 kHz system clock/RTC peripheral. If you need better resolution for the timers, you can use the nrfx_timer driver, see this post.

    3) OUT TOPIC: is the above SPI read routine a good choice in terms of time performance? If not, have you some suggestions?

    This depends on your accuracy requirements. If you need the SPI readings to happen at very precise timing intervals, you could get better accuracy by using NRFX drivers and connect the TIMER/RTC event directly to trigger a predefined SPI transfer over a PPI channel (see for instance the Advanced usage in nRF5 SDK driver description documentation). This will give exact timing regardless of interrupts and BLE activity in your application. When handling the transfers in software, you may be blocked by higher priority tasks (BLE, etc), so the transfers may be a bit delayed.

    Best regards,
    Jørgen

  • This depends on your accuracy requirements. If you need the SPI readings to happen at very precise timing intervals, you could get better accuracy by using NRFX drivers and connect the TIMER/RTC event directly to trigger a predefined SPI transfer over a PPI channel (see for instance the Advanced usage in nRF5 SDK driver description documentation). This will give exact timing regardless of interrupts and BLE activity in your application. When handling the transfers in software, you may be blocked by higher priority tasks (BLE, etc), so the transfers may be a bit delayed.

    Actually I need to read the SPI device every precise time intervals, so I think that I'll follow your suggestion about the use of NRFX drivers and PPI. But since I'm newbie with Nordic devices I have two last questions:

    1) Until now I've used the Zephyr APIs. There is something fundamental to know about the integration between the NRFX and Zephyr's API?

    2) About the PPI, do you have a good starting point example (maybe with SPI and TIMERS)?

    Thanks very much.

  • Andrea Verdecchia said:
    1) Until now I've used the Zephyr APIs. There is something fundamental to know about the integration between the NRFX and Zephyr's API?

    You should not use both APIs for the same peripheral/instance at the same time, to prevent compatibility issues. Apart from that, most of the Zephyr APIs should already use nrfx drivers (e.g https://github.com/nrfconnect/sdk-zephyr/blob/v3.0.99-ncs1/drivers/spi/spi_nrfx_spim.c), so you would get runtime errors in case of overlapping usage.

    Andrea Verdecchia said:
    2) About the PPI, do you have a good starting point example (maybe with SPI and TIMERS)?

    I'm not aware of any nRF Connect SDK/Zephyr examples showing this, but you can find a nrfx_spim example in this post. This example shows how to use nrfx_ppi. Usage of nrfx_timer should be very similar, set "CONFIG_NRFX_TIMER=y" and "CONFIG_NRFX_TIMERx=y" (where x is the TIMER instance you want to use), and include "nrfx_timer.h" in your application.

  • You should not use both APIs for the same peripheral/instance at the same time, to prevent compatibility issues. Apart from that, most of the Zephyr APIs should already use nrfx drivers (e.g https://github.com/nrfconnect/sdk-zephyr/blob/v3.0.99-ncs1/drivers/spi/spi_nrfx_spim.c), so you would get runtime errors in case of overlapping usage.

    Ok I got it. Thanks.

    I'm not aware of any nRF Connect SDK/Zephyr examples showing this, but you can find a nrfx_spim example in this post. This example shows how to use nrfx_ppi. Usage of nrfx_timer should be very similar, set "CONFIG_NRFX_TIMER=y" and "CONFIG_NRFX_TIMERx=y" (where x is the TIMER instance you want to use), and include "nrfx_timer.h" in your application.

    I tryed to set the SPI and TIMER through PPI but I have some issue. I opened a different post here.

Related