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

SPI Master transfer not completing

Hi,

I am using the SPI Master 1 to control an EEPROM. I used the spi_example project to help get the code working. It all works fine if the code that initiates the SPI transfer is in main() or simply in a function called by main(). But if I attempt an SPI transfer in an "application timer" timeout handler function, it doesn't seem to work properly. It just seems like the transfer never completes. As shown in the oscilloscope trace below, the Chip Select (top yellow waveform) goes low fine, the SPI clock (green waveform) is generated fine, the data to the slave (blue waveform) is fine (the value is 00000110). Ignore the pink waveform. But for some reason the Chip Select doesn't go high, when it does if the SPI initiation is in main().

image description

So when I next try to start a transfer I am stuck at:

while (!m_transfer_completed) { //wait for current transfer to complete }

Is there any reason why it would work in main but not in an application timer timeout handler?

Kind regards

Jack

edit:

I am setting the Chip Select high at the end of the transaction, where m_transfer_completed is also set as true. Then when I try to initiate another spi transfer, the code gets to the while loop. So essentially, the Chip Select pin should go high before the while loop occurs.

Here is some extracts from my code:

In the function timers_init() that is called in main, I have:

err_code = app_timer_create(&m_del_timer_id,
                            APP_TIMER_MODE_REPEATED,
                            del_timer_handler);

Here is the implementation of del_timer_handler

void del_timer_handler(void * p_context) { eeprom_write(m_tx_eeprom,m_rx_eeprom,m_eeprom_last_write_addr,m_eeprom_last_write_addr + EEPROM_PAGE_SIZE);

}

eeprom_write is defined as:

//To write to EEPROM, first send a write enable

static void eeprom_write(uint8_t * const tx_buffer, uint8_t * const rx_buffer, uint32_t from_addr, uint32_t to_addr) {

execute_eeprom_command(EEPROM_WREN,tx_buffer,rx_buffer,NULL,NULL); execute_eeprom_command(EEPROM_WRITE,tx_buffer,rx_buffer,from_addr,to_addr);

}

execute_eeprom_command is defined below:

void execute_eeprom_command(eeprom_command_t command, uint8_t * const tx_buffer, uint8_t * const rx_buffer, uint32_t from_addr, uint32_t to_addr)

{

uint16_t  rx_len = 0;
uint16_t  tx_len = 0;	

while (!m_eeprom_transfer_completed)
{
	//wait for current transfer to complete
}
	m_eeprom_transfer_completed = false;
			
	//Initalize buffers.
	eeprom_buf_init(command,tx_buffer, &tx_len, rx_buffer, &rx_len, from_addr, to_addr);			
			
	 //initialise SPI Master				
	spi1_master_init();
							
	//Start transfer.
			
	uint32_t err_code = spi_master_send_recv(SPI_MASTER_1, tx_buffer, tx_len, rx_buffer, rx_len);
	APP_ERROR_CHECK(err_code);

}

Note, I am using the SS pin of the spi_master for the Chip Select. So the spi_master should automatically toggle the pin as required. I have also tried implementing the Chip Select independently of the spi_master and I still have the same problem. When doing this, I set the Chip Select to high in the spi master event handler:

void spi_master_1_event_handler(spi_master_evt_t spi_master_evt) {

switch (spi_master_evt.evt_type)

{
    case SPI_MASTER_EVT_TRANSFER_COMPLETED:
        //Check if received data is correct.
    
        //Close SPI master.
        spi_master_close(SPI_MASTER_1);
			        
        m_eeprom_transfer_completed = true;
 // nrf_gpio_pin_set(EEPROM_CS_PIN);				

        break;
    
    default:
        //No implementation needed.
        break;
}

}

Parents
  • Without seeing the code when you are running this in the timer callback I'm going to have to make a couple of guesses. First, the code that pulls the chip select high after the SPI transaction is complete is after your while loop waiting for m_transfer_completed to become true? Second, that m_transfer_completed is set to true in a SPI IRQ handler?

    I believe the app timer callbacks execute in an interrupt context which then could be blocking the SPI interrupt from being handled and you would end up in this situation.

    If that is not the case, perhaps you could post your code so we could take a look at it.

    One solution would be to just have your timer callback set a flag that indicates it is time to perform the SPI transaction and still do the transaction in the context of main() when the flag is set. There is also an app_scheduler that could be used but I am not familiar with it.

Reply
  • Without seeing the code when you are running this in the timer callback I'm going to have to make a couple of guesses. First, the code that pulls the chip select high after the SPI transaction is complete is after your while loop waiting for m_transfer_completed to become true? Second, that m_transfer_completed is set to true in a SPI IRQ handler?

    I believe the app timer callbacks execute in an interrupt context which then could be blocking the SPI interrupt from being handled and you would end up in this situation.

    If that is not the case, perhaps you could post your code so we could take a look at it.

    One solution would be to just have your timer callback set a flag that indicates it is time to perform the SPI transaction and still do the transaction in the context of main() when the flag is set. There is also an app_scheduler that could be used but I am not familiar with it.

Children
No Data
Related