This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SPI doesn't work when started by Timer

Short: Working Timer. Working SPI. SPI does not work when triggered by timer_handler.

Hello,

I am currently trying to replace delays with timer in my code...

I set up a working timer that goes off every 4 seconds. In the handler that is called I would like to call the function that initiates a measurement and then gets the values afterwards. It does not work in combination. Debugger stops at if (p_spi_instance->disable_all_irq) in the SPI routine. (Flag is not set btw).

If I log via UART instead of any value it only outputs 0.

main.c

//Edit:

static void test_timer_handler(void * p_context)
{
    UNUSED_PARAMETER(p_context);
		printf("Hallo\n");
		hole_temperatur();
}

When I call this function from within the timer handler:

        static int32_t hole_temperatur(void)
    {
    				
    					if (m_transfer_completed) 
    				{	// ADC Temperatur 256 Aufloesung
    					m_transfer_completed = false;	
    					m_tx_data_1B[0] = 0x50; 
    					uint32_t err_code = spi_master_send_recv(SPI_MASTER_0, m_tx_data_1B, 1, m_rx_data_1B, 1);
APP_ERROR_CHECK(err_code);	
    					printf("%d %X %d \n",err_code, m_rx_data_1B[0], m_transfer_completed);
    					nrf_delay_ms(1);
    				}
    				
    					if (m_transfer_completed)
            { //Hole und berechne Temperatur
              m_transfer_completed = false;					
    					m_tx_data_4B[0] = 0x00;
    					m_tx_data_4B[1] = 0xFF;
    					m_tx_data_4B[2] = 0xFF;
    					m_tx_data_4B[3] = 0xFF;
    					uint32_t err_code = spi_master_send_recv(SPI_MASTER_0, m_tx_data_4B, 4, m_rx_data_4B, 4);	
    					APP_ERROR_CHECK(err_code);	
    					printf("%d \n",err_code);
    					nrf_delay_ms(1);
    					printf("%X \n",m_rx_data_4B[1]);
    					//printf("%X \n",m_rx_data_4B[2]);
    					//printf("%X \n",m_rx_data_4B[3]);
    					D2 = m_rx_data_4B[1]<<16 | m_rx_data_4B[2]<<8 | m_rx_data_4B[3];
    					//printf("%d \n",D2);
    					//printf("%s \n", m_transfer_completed ? "true" : "false");
    					dT = (int32_t)D2 - ( (int32_t)Koeff[5] * 256 );				
    					T = 2000 + ((int64_t)dT * Koeff[6]) / 8388608LL;					
    				} return T = (int32_t)T;
    }

It outputs "0 FE 0" So no error. Expected Read Result but transfer not completed! I removed the static from the variable declaration but that didn't help. The flag is meant to be set inside the spi_master_event_handler.

When I remove the if(transfer_completed) from the //Hole und berechne Part I only receive a capital H every 4 seconds. (The "Hallo" is also gone missing)

Well it looks like it is the H from Hallo. With the hallo-printf statement excluded I aint got nothin'.

How else could I realize this? All I could think of is just setting a flag in the timer_handler and check for this in the main()-loop and then execute the code from here. But that wouldn't help me with current consumption, would it? This would also keep the chip awake or would this be better than using delay?

timer_handler {test_flag=true}

    for (;;)
    {
      power_manage();       
            if(test_flag) { printf("%d \n",hole_temperatur()); test_flag=false;}
}
  • after you use spi_master_send_recv you have wait for it complete like RK said.

    spi_master_send_recv
    while(!m_transfer_completed){}
    m_transfer_completed = false;
    
  • This part is already implemented for the basic function.. just not for the printf. while(!m_transfer_completed){} or if (m_transfer_completed) afterwards does pretty much the same, doesn't it? But I added it now. Still works perfectly when called in main context and still does not work when called from timer.

    why should I set it to false afterwards? I thought of it more like this:

    if (m_transfer_completed) 
    				{	// ADC Temperatur 256 Aufloesung
    					m_transfer_completed = false;	
    					m_tx_data_1B[0] = 0x50; 
    					uint32_t err_code = spi_master_send_recv(SPI_MASTER_0, m_tx_data_1B, 1, m_rx_data_1B, 1);
    					APP_ERROR_CHECK(err_code);
    				while(!m_transfer_completed){}	
    					//printf("%d %X %d \n",err_code, m_rx_data_1B[0], m_transfer_completed);
    					nrf_delay_ms(1);
    				}
    					if (m_transfer_completed)
    

    The Flag is intialized with true and set by the spi_event_handler to true. And before every transfer I set it to false.

  • Have you by the way tried putting a breakpoint in your error_handler routine. You have random APP_ERROR_CHECK() all over the place (in some odd places like why after the while loop in the code above instead of before it?). It's always possible you are catching an error, since interrupts keep firing and the default error handler just loops, you may not know. That would make a right mess. I think it's quite possible you're trying to call spi_master_send_recv() while a send is already in-progress from one of your timer callbacks, it's impossible to tell as the code has become more and more messy.

    And using m_transfer_completed to re-synchronise an asynchronous method really misses the point. If you're going to do that use a non-interrupt-driven SPI driver which just loops until completion. What you're supposed to do is put the code in the completion event handler to do the next piece.

  • loop before error_check makes no sense and after I posted the comment I changed it in the code but forget to change it here aswell. Its now

    APP_ERROR_CHECK(err_code);
    while(!m_transfer_completed){}	
    

    everywhere.

    Besides the BLE stuff I only got 1 Timer and no other interrupts besides SPI. The timer only calls every 4second and therefore it's not likely that a send is already in-progress. The idea with the m_transfer_completed is taken from the spi_master_example..

  • And you should still put a breakpoint in the error handler. I keep a breakpoint in there at all times as long as I have a spare one, you never know when you're going to get a bad return from a library call. It's saved me hours, hours and more hours of debugging issues than I can remember.

Related