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

nRF51822 SPI master

Hi!

I have custom board with nRF51822 and I need to communicate with serial flash W25X40 using SPI master example with SoftDevice enabled (S110 is used). I worked with serial flashes before but didn't encountered the problems described below.

During debugging I figured out that after first execution of the spi_master_send_recv function all subsequent transfers use original values of the TX buffer.

Here's example that explains what I mean:

TX and RX buffers are declared as global arrays:

uint8_t tx_data[5] = {0x00, 0x00, 0x00, 0x00, 0x00}  // Transmit buffer
uint8_t rx_data[4]; // Receive buffer

Then after standard procedures I initialize SPI interface:

leds_init();
buttons_init();
ble_stack_init();

timers_init();
device_manager_init();
gap_params_init();
advertising_init();
services_init();
sensor_sim_init();
conn_params_init();

// Start execution.
application_timers_start();
advertising_start();	
spi_master_init(SPI_MASTER_0, spi_master_event_handler, false);

After that two transfers are being executed in endless loop, the first one reads chip info and the second should read one byte located at address 0x000000:

for (;; )
	{	
		tx_data[0] = 0x9F;
		spi_master_send_recv(SPI_MASTER_0, tx_data, 1, rx_data, 4);
		tx_data[0] = 0x03;
		spi_master_send_recv(SPI_MASTER_0, tx_data, 4, rx_data, 1);
		power_manage();			
	}

The first transfer returns correct values according to the datasheet. The problem I mentioned above is that the second transfer uses value 0x9F rather than 0x03 as if SPI master instance references to the saved copy of original array - this was revealed when I stepped in the function and watched actual TX buffer values.

So far I have two questions:

  1. How should I modify values of the TX buffer after first using of spi_master_send_recv function?
  2. Do I use spi_master_send_recv function correctly for reading data from serial flash?

Thanks!

  • Dmitry,

    The spi_master_send_recv function is non-blocking, it just kicks off the SPI hardware to start the transaction. When the transaction is completed, an interrupt will fire and call the event handler you passed to the spi_master_init function (in your case, spi_master_event_handler). You need to wait for the event hander to be called with the SPI_MASTER_EVT_TRANSFER_COMPLETED event type before calling spi_master_send_recv again with the new data.

    Alternatively, you can use the uint32_t return value of spi_master_send_recv to determine when data has been sent successfully. The function will return NRF_SUCCESS if the data was added to the SPI output correctly, or it will return NRF_ERROR_BUSY if a transaction is currently in progress and the data will not be sent.

  • Hi Nathan,

    Thanks for the reply.

    I added the feature you talk about but I guess the problem lies elsewhere. I've found a mistake but it doesn't solves the problem: when calling

    spi_master_send_recv(SPI_MASTER_0, tx_data, 4, rx_data, 1);
    

    I should use value 5 instead 4 because master latches transfer from slave during 5th byte. Accordingly RX buffer should have 5 elements instead 4 in my case.

    The issue which is still there is that spi_master_send_recv function changes TX buffer contents to the original value although new value is passed as an argument: in my case if step in the function during second call I observe that tx_data[0] assigned by 0x9F. This occurs when the following instruction has been called:

    volatile spi_master_instance_t * p_spi_instance = spi_master_get_instance(
        spi_master_hw_instance);
    

    Any thoughts?

  • Hi. I have experimented for a while now and using your code I see the same behaviour as you do. Although I haven't been able to pinpoint exactly what is happening @Nathan is correct. Calling spi_master_send_recv() consecutively without waiting for the previous transmission to end will fail.

    A simple solution that works for me is to set a flag in the spi event handler like this:

    void spi_master_event_handler(spi_master_evt_t spi_master_evt)
    {
        if(spi_master_evt.evt_type == SPI_MASTER_EVT_TRANSFER_COMPLETED)
            m_transfer_completed = true;
    }
    

    and then wait with a while loop for transmission 1 to complete before you start transmission 2:

    while(!m_transfer_completed);
    m_transfer_completed = false;
    

    It sounds like you have tried to implement something like this though? Do you mind sharing you code?

  • Hi,

    Thanks to all for the answers.

    So far I couldn't resolve the issue but I've found another library which I used to implement all I need (erasing flash, reading and writing the data). The library is attached: spi_master.zip

    If someone is going to use the library do the following:

    1. Change pins definition in header file (CLK, MISO, MOSI, CS) according to your schematic design.

    2. Call spi_master_init from main file or from the library which is based on the library I've attached. For example, if SPI0 is used in the mode 0 with MSB first call the function:

      spi_base_address = spi_master_init(SPI0, SPI_MODE0, (bool)0);

    3. Use function spi_master_tx_rx to send/receive data. For example, in my case mentioned in the first post the code

      tx_data[0] = 0x9F;

      spi_master_tx_rx(spi_base_address, 4, (const uint8_t *)tx_data, rx_data);

    will return SPI flash ID in the rx_data array.

    Hope this helps.

    Regards, Dmitry.

Related