<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://devzone.nordicsemi.com/cfs-file/__key/system/syndication/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>How to properly configure periodic SPI transfers with PPI and TIMER?</title><link>https://devzone.nordicsemi.com/f/nordic-q-a/88962/how-to-properly-configure-periodic-spi-transfers-with-ppi-and-timer</link><description>I&amp;#39;m working on a BLE application based on the Zephyr RTOS. I need to read a SPI device at fixed time intervals and, as already suggested me here at point 3, I&amp;#39;m trying to implement a PPI channel that connects the TIMER0 event to the SPI task. I wrote</description><dc:language>en-US</dc:language><generator>Telligent Community 13</generator><lastBuildDate>Fri, 17 Jun 2022 10:27:30 GMT</lastBuildDate><atom:link rel="self" type="application/rss+xml" href="https://devzone.nordicsemi.com/f/nordic-q-a/88962/how-to-properly-configure-periodic-spi-transfers-with-ppi-and-timer" /><item><title>RE: How to properly configure periodic SPI transfers with PPI and TIMER?</title><link>https://devzone.nordicsemi.com/thread/372983?ContentTypeID=1</link><pubDate>Fri, 17 Jun 2022 10:27:30 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:a0e4dee9-0570-47cb-9600-73158b2cd48c</guid><dc:creator>J&amp;#248;rgen Holmefjord</dc:creator><description>[quote user="Thoraz"]I found the problem:&amp;nbsp;NRFX_SPIM_FLAG_RX_POSTINC made an overflow in my size-defined rx buffer.[/quote]
&lt;p&gt;Good that you found the problem, that makes sense!&lt;/p&gt;
[quote user="Thoraz"]1) With the TIMER0 the code didn&amp;#39;t work. I read somewhere that the TIMER0 is supposed to be used by the radio module. I shifted to the TIMER1 and now it works. Is it correct that the TIMER0 belongs to the radio module?[/quote]
&lt;p&gt;TIMER0 does not&amp;nbsp;&lt;em&gt;belong&lt;/em&gt; to the RADIO, but it is used by many radio protocols for short and precise timing interval, including the &lt;a href="https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfxlib/mpsl/doc/mpsl.html"&gt;Multiprotocol Service Layer&lt;/a&gt;&amp;nbsp;(MPSL), which is used by the Softdevice controller for BLE, OpenThread, and Zigbee protocols.&lt;/p&gt;
[quote user="Thoraz"]2) SPI with PPI doesn&amp;#39;t support anyway the managening of chip select signal. As&amp;nbsp;explained &lt;a href="https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfx/drivers/spim/driver.html#c.nrfx_spim_xfer"&gt;here&lt;/a&gt; if the transfer is configured as&amp;nbsp;NRFX_SPIM_FLAG_HOLD_XFER the chip select needs to be&amp;nbsp;&lt;span&gt;managed&amp;nbsp;necessarily outside the driver. Am I right? For this reason I created other two PPI channels to toggle&amp;nbsp;the chip select&amp;nbsp;using the SPI&amp;nbsp;START and END events.&lt;/span&gt;[/quote]
&lt;p&gt;Yes, that is correct.&lt;/p&gt;
[quote user="Thoraz"]And last: how can I configure just one PPI channel for the timer compare event since it triggers two tasks, the SPI transfer and the chip select toggle?[/quote]
&lt;p&gt;You can use the &lt;a href="https://infocenter.nordicsemi.com/topic/ps_nrf52833/ppi.html#register.FORK-0-31.TEP"&gt;FORK feature&lt;/a&gt; of the PPI peripheral to trigger two separate tasks from one event. See the &lt;a href="https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfx/drivers/ppi/driver.html#c.nrfx_ppi_channel_fork_assign"&gt;nrfx_ppi_channel_fork_assign&lt;/a&gt;&lt;span&gt;() API documentation.&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: How to properly configure periodic SPI transfers with PPI and TIMER?</title><link>https://devzone.nordicsemi.com/thread/372943?ContentTypeID=1</link><pubDate>Fri, 17 Jun 2022 07:58:49 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:fb05fdcb-c541-4e3b-8c95-fb086515d1a3</guid><dc:creator>Andrea Verdecchia</dc:creator><description>[quote userid="112991" url="~/f/nordic-q-a/88962/how-to-properly-configure-periodic-spi-transfers-with-ppi-and-timer/372862#372862"]But now I have a little issue again. If I run the code above on the nRF52833 &lt;strong&gt;the sistem reboot every fixed time (about 25 seconds) without errors in the nRF terminal&lt;/strong&gt;. Why?[/quote]
&lt;p&gt;I found the problem:&amp;nbsp;NRFX_SPIM_FLAG_RX_POSTINC made an overflow in my size-defined rx buffer. I disabled it and now the code works.&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: How to properly configure periodic SPI transfers with PPI and TIMER?</title><link>https://devzone.nordicsemi.com/thread/372862?ContentTypeID=1</link><pubDate>Thu, 16 Jun 2022 14:48:24 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:1aff702b-ea03-43ea-94b9-c3a5f6195923</guid><dc:creator>Andrea Verdecchia</dc:creator><description>[quote userid="14926" url="~/f/nordic-q-a/88962/how-to-properly-configure-periodic-spi-transfers-with-ppi-and-timer/372596#372596"]The second argument to&amp;nbsp;&lt;span&gt;nrfx_timer_compare_event_address_get() should be the TIMER COMPARE channel, not the PPI channel (e.g. 0 for COMPARE0). Most likely, the variable&amp;nbsp;readingEventAddr does not contain the desired address after the function call (&lt;a href="https://infocenter.nordicsemi.com/topic/ps_nrf52833/timer.html#register.EVENTS_COMPARE-0-5"&gt;0x40008140&lt;/a&gt;), and the PPI channel will connect to something else.&lt;/span&gt;[/quote]
&lt;p&gt;Yes, you are right. Thanks.&lt;/p&gt;
&lt;p&gt;After that I wrote a semi-working code&amp;nbsp;with some modifications. And I found out:&lt;/p&gt;
&lt;p&gt;1) With the TIMER0 the code didn&amp;#39;t work. I read somewhere that the TIMER0 is supposed to be used by the radio module. I shifted to the TIMER1 and now it works. Is it correct that the TIMER0 belongs to the radio module?&lt;/p&gt;
&lt;p&gt;2) SPI with PPI doesn&amp;#39;t support anyway the managening of chip select signal. As&amp;nbsp;explained &lt;a href="https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfx/drivers/spim/driver.html#c.nrfx_spim_xfer"&gt;here&lt;/a&gt; if the transfer is configured as&amp;nbsp;NRFX_SPIM_FLAG_HOLD_XFER the chip select needs to be&amp;nbsp;&lt;span&gt;managed&amp;nbsp;necessarily outside the driver. Am I right? For this reason I created other two PPI channels to toggle&amp;nbsp;the chip select&amp;nbsp;using the SPI&amp;nbsp;START and END events.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;My code now is:&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;pre class="ui-code" data-mode="text"&gt;#define LOG_MODULE_NAME main
LOG_MODULE_REGISTER( LOG_MODULE_NAME );

#define SCK_PIN                    0x04
#define MISO_PIN                   0x1D
#define MOSI_PIN                   0x1C
#define CS_PIN                     0x03

static const nrfx_timer_t readingTimerInst = NRFX_TIMER_INSTANCE( 1 );
static const nrfx_spim_t spiInst = NRFX_SPIM_INSTANCE( 0 );
static const nrfx_spim_config_t spiConfig = NRFX_SPIM_DEFAULT_CONFIG( SCK_PIN, MISO_PIN, MOSI_PIN, NRFX_SPIM_PIN_NOT_USED );
nrf_ppi_channel_t readingPpiChan;
nrf_ppi_channel_t csPpiChan;
nrf_ppi_channel_t spiEndPpiChan;
static uint32_t readingTaskAddr;
static uint32_t csTaskAddr;
static uint32_t readingEventAddr;
static uint32_t spiEndEventAddr;
static uint8_t tempBuff[ 2 ];
static uint8_t tempReg;

void Timer_Handler( nrf_timer_event_t event_type, void *p_context )
{
    LOG_INF(&amp;quot;Timer completed.&amp;quot;);
}

void Spi_Handler( nrfx_spim_evt_t const *p_event, void *p_context )
{
    switch ( p_event-&amp;gt;type )
    {
        case NRFX_SPIM_EVENT_DONE:
        {
            LOG_INF(&amp;quot;Spi transfer completed. Received %02X&amp;quot;, tempBuff[ 1 ] );
            break;
        }

        default:
        {
            break;
        }
    }
}

nrfx_err_t Spi_Timer_Ppi_Init( void )
{
    nrfx_err_t err;

    /********************************************************************
     *     SPI
     * *****************************************************************/
    IRQ_CONNECT( DT_IRQN( DT_NODELABEL( spi0 ) ), DT_IRQ( DT_NODELABEL( spi0 ), priority ), nrfx_isr, nrfx_spim_0_irq_handler, 0 );

    err = nrfx_spim_init( &amp;amp;spiInst, &amp;amp;spiConfig, Spi_Handler, tempBuff );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_spim_init error: %08X&amp;quot;, err );
        return err;
    }

    tempReg = 0x80;
    nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_TRX( &amp;amp;tempReg, 1, tempBuff, 2 );
    uint32_t flags = NRFX_SPIM_FLAG_HOLD_XFER | NRFX_SPIM_FLAG_RX_POSTINC | NRFX_SPIM_FLAG_REPEATED_XFER;

    err = nrfx_spim_xfer( &amp;amp;spiInst, &amp;amp;xfer, flags );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_spim_xfer error: %08X&amp;quot;, err );
        return err;
    }

    readingTaskAddr = nrfx_spim_start_task_get( &amp;amp;spiInst );
    spiEndEventAddr = nrfx_spim_end_event_get( &amp;amp;spiInst );

    /********************************************************************
     *     TIMER
     * *****************************************************************/
    nrfx_timer_config_t timerConfig = NRFX_TIMER_DEFAULT_CONFIG;
    timerConfig.bit_width = NRF_TIMER_BIT_WIDTH_32;

    err = nrfx_timer_init( &amp;amp;readingTimerInst, &amp;amp;timerConfig, Timer_Handler );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_timer_init error: %08X&amp;quot;, err );
        return err;
    }

    uint32_t ticks = nrfx_timer_ms_to_ticks( &amp;amp;readingTimerInst, 1000 );
    nrfx_timer_extended_compare( &amp;amp;readingTimerInst, NRF_TIMER_CC_CHANNEL0, ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false );
    readingEventAddr = nrfx_timer_compare_event_address_get( &amp;amp;readingTimerInst, NRF_TIMER_CC_CHANNEL0 );
    nrfx_timer_enable( &amp;amp;readingTimerInst );

    /********************************************************************
     *     CS
     * *****************************************************************/
    nrfx_gpiote_out_config_t const out_config = {
        .action = NRF_GPIOTE_POLARITY_TOGGLE,
        .init_state = 1,
        .task_pin = true,
    };

    err = nrfx_gpiote_out_init( CS_PIN, &amp;amp;out_config );
    if (err != NRFX_SUCCESS)
    {
        LOG_ERR(&amp;quot;nrfx_gpiote_out_init error: %08x&amp;quot;, err );
        return err;
    }

    nrfx_gpiote_out_task_enable( CS_PIN );
    csTaskAddr = nrfx_gpiote_out_task_addr_get( CS_PIN );


    /********************************************************************
     *     PPI
     * *****************************************************************/
    err = nrfx_ppi_channel_alloc( &amp;amp;readingPpiChan );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_alloc error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_assign( readingPpiChan, readingEventAddr, readingTaskAddr );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_assign error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_enable( readingPpiChan );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_enable error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_alloc( &amp;amp;csPpiChan );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_alloc error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_assign( csPpiChan, readingEventAddr, csTaskAddr );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_assign error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_enable( csPpiChan );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_enable error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_alloc( &amp;amp;spiEndPpiChan );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_alloc error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_assign( spiEndPpiChan, spiEndEventAddr, csTaskAddr );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_assign error: %08X&amp;quot;, err );
        return err;
    }

    err = nrfx_ppi_channel_enable( spiEndPpiChan );
    if ( err != NRFX_SUCCESS )
    {
        LOG_ERR( &amp;quot;nrfx_ppi_channel_enable error: %08X&amp;quot;, err );
        return err;
    }

    return NRFX_SUCCESS;
}

void main( void )
{
    Spi_Timer_Ppi_Init();

    while ( 1 )
    {
        
    }
}&lt;/pre&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;But now I have a little issue again. If I run the code above on the nRF52833 &lt;strong&gt;the sistem reboot every fixed time (about 25 seconds) without errors in the nRF terminal&lt;/strong&gt;. Why?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;And last: how can I configure just one PPI channel for the timer compare event since it triggers two tasks, the SPI transfer and the chip select toggle?&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item><item><title>RE: How to properly configure periodic SPI transfers with PPI and TIMER?</title><link>https://devzone.nordicsemi.com/thread/372596?ContentTypeID=1</link><pubDate>Wed, 15 Jun 2022 13:28:09 GMT</pubDate><guid isPermaLink="false">137ad170-7792-4731-bb38-c0d22fbe4515:3a839484-da54-42a4-a7c0-0417dd87ca27</guid><dc:creator>J&amp;#248;rgen Holmefjord</dc:creator><description>&lt;p&gt;Hi,&lt;/p&gt;
&lt;p&gt;The second argument to&amp;nbsp;&lt;span&gt;nrfx_timer_compare_event_address_get() should be the TIMER COMPARE channel, not the PPI channel (e.g. 0 for COMPARE0). Most likely, the variable&amp;nbsp;readingEventAddr does not contain the desired address after the function call (&lt;a href="https://infocenter.nordicsemi.com/topic/ps_nrf52833/timer.html#register.EVENTS_COMPARE-0-5"&gt;0x40008140&lt;/a&gt;), and the PPI channel will connect to something else.&lt;/span&gt;&lt;/p&gt;
[quote user=""]1) &lt;a href="https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfx/drivers/spim/driver.html#c.nrfx_spim_xfer"&gt;Here&lt;/a&gt;&amp;nbsp;it describes that if I wanto to use the&amp;nbsp;NRFX_SPIM_FLAG_HOLD_XFER flag I must to set the chip select pin as&amp;nbsp;NRFX_SPIM_PIN_NOT_USED&amp;nbsp;&lt;span&gt;and manage it outside the driver. How I should do it if the tranfers are supposed to be autonomous?&lt;/span&gt;[/quote]
&lt;p&gt;&lt;span&gt;If you have multiple SPI devices on the same bus, or need to control the CS line for power reasons, you can setup multiple COMPARE events to trigger after each other in the same timer, and use a second PPI channel together with GPIOTE to toggle the CS line before starting the SPI transfer. Similarly, you can toggle the GPIOTE channel when SPI END event is generated.&lt;/span&gt;&lt;/p&gt;
[quote user=""]&lt;span&gt;2) To generate the TIMER0 event in the PPI&amp;nbsp;can I set false the &lt;em&gt;enable_int&lt;/em&gt; parameter in the &lt;a href="https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/nrfx/drivers/timer/driver.html#c.nrfx_timer_compare"&gt;nrfx_timer_compare&lt;/a&gt;&lt;/span&gt;&amp;nbsp;function or I have to set it as true? Because if I enable the interrupt, the Zephyr OS crash and restart the chip.[/quote]
&lt;p&gt;Yes, interrupt is not needed when using PPI. This is only used when you need the event to trigger an interrupt in SW.&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Best regards,&lt;br /&gt;Jørgen&lt;/span&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;</description></item></channel></rss>