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

NRF9160 SPI loopback return zero values

I build project without problems, but this simple sample return zero results for read buffer. I return 5 zero from 5 zeros.
I have right names for overlays files. nrf9160_pca10090ns.overlay  AND nrf9160_pca10090.overlay But it not correct work for me.

Main.c

#include <zephyr.h>
#include <sys/printk.h>
#include <drivers/spi.h>

static const struct spi_config spi_cfg = {
 .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
 SPI_MODE_CPOL | SPI_MODE_CPHA,
 .frequency = 4000000,
 .slave = 0,
};

struct device * spi_dev;

static void spi_init(void)
{
 const char* const spiName = "SPI_3";
 spi_dev = device_get_binding(spiName);

if (spi_dev == NULL) {
 printk("Could not get %s device\n", spiName);
 return;
 }
}

void spi_test_send(void)
{
 int err_write;
 int err_read;
 u8_t tx_buffer[1]={'A'};
 u8_t rx_buffer[1];

const struct spi_buf tx_buf = {
 .buf = tx_buffer,
 .len = sizeof(tx_buffer)
 };
 const struct spi_buf_set tx = {
 .buffers = &tx_buf,
 .count = 1
 };

struct spi_buf rx_buf = {
 .buf = rx_buffer,
 .len = sizeof(rx_buffer),
 };
 const struct spi_buf_set rx = {
 .buffers = &rx_buf,
 .count = 1
 };
 
 printk("new iteration\n");
 err_write = spi_write(spi_dev, &spi_cfg,&tx);//spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
 err_read = spi_read(spi_dev, &spi_cfg,&rx);
 if (err_write||err_read) {
 printk("SPI error_write: %d\n", err_write);
 printk("SPI error_read: %d\n", err_read);
 } else {
 /* Connect MISO to MOSI for loopback */
 printk("TX sent: %x\n", tx_buffer[0]);
 printk("RX recv: %x\n", rx_buffer[0]);
 //tx_buffer[0]++;
 } 
}

void main(void)
{
 printk("SPIM Example\n");
 //spi_init();
 int counter= 0;
 while (counter < 5) {
 spi_test_send();
 k_sleep(1000);
 counter++;
 }
}


overlay file

&spi3 {
status = "ok";
sck-pin = <10>;
mosi-pin = <11>;
miso-pin = <12>;
ss-pin = <13>;
spi-max-frequency = <4000000>;
};

prj file

CONFIG_SERIAL=y
CONFIG_TRUSTED_EXECUTION_NONSECURE=y
CONFIG_UART_INTERRUPT_DRIVEN=y
#CONFIG_UART_0_NRF_UARTE=y

# LTE link control
CONFIG_LTE_LINK_CONTROL=n
CONFIG_LTE_AUTO_INIT_AND_CONNECT=n

# BSD library
CONFIG_BSD_LIBRARY=y
CONFIG_BSD_LIBRARY_TRACE_ENABLED=n

# network
CONFIG_TEST_RANDOM_GENERATOR=y
CONFIG_NETWORKING=y
CONFIG_NET_BUF_USER_DATA_SIZE=1
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y
#CONFIG_NET_RAW_MODE=y
CONFIG_HEAP_MEM_POOL_SIZE=1024

# SPI
CONFIG_SPI=y
CONFIG_SPI_3=y
#CONFIG_SPI_3_NRF_SPIM=y
#CONFIG_SPI_3=y 
CONFIG_SPI_NRFX=y
CONFIG_MAIN_STACK_SIZE=4096

CONFIG_LEGACY_TIMEOUT_API=y

Parents
  • Hi.

    SPI works in full-duplex meaning that you can send and receive at the same time.

    Therefore, the "normal" SPI "send" function is called spi_transceive, which both sends and reads at the same time.

    However, many devices support sending commands over SPI, and then typically uses a protocol similar to this (from the master's perspective):

    1. Send a "read" command

    2. Send the address you want to read

    3. Read the data

    However, as a write and read operation happens at the same time, what really happens is this:

    1. Send a "read" command, while receiving garbage

    2. Send the address you want to read, while receiving garbage

    3. Write garbage, while reading the data

    (the garbage will typically be 0x00 or 0xff)

    Therefore, when you call spi_send, you are sending 'A', and reading it. But you throw away the read value.

    Then, you read '0x00', because that is the garbage value sent when you try to read.

    Try to use the spi_transceive function instead.

    Best regards,

    Didrik

  • Okey, i try use like this. But i don't understand how i can send address for this function? Maybe you can provide me simple code example>>???

            printk("new iteration\n");
            spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
    
    	if (err) {
    		printk("SPI error: %d\n", err);
    	} else {
    		/* Connect MISO to MOSI for loopback */
    		printk("TX sent: %x\n", tx_buffer[0]);
    		printk("RX recv: %x\n", rx_buffer[0]);
    		tx_buffer[0]++;
    	}	

  • That was just an example, assuming the SPI was connected to an imagined device.

    But to flesh out the example, let's imagine we have an external ADC, which takes commands on the following format:

    command address data

    Where each field is 1 byte, which requires a separate SPI transaction.

    Our hypothetical ADC supports the following commands:

    0x1 Write

    0x2 Read

    And has the following registers (on the format address, read and/or write purpose):

    0x0 R/W Configuration register. This register configures the ADC based on the bit pattern

    0x1 R Channel 1 value

    0x2 R Channel 2 value

    0x3 R Status register

    0x4 W Start conversion. If you write 0x1, it will start a conversion on channel 1. If you write 0x2, channel 2.

    The code for getting the voltage on channel 1 would then be something like this:

    // spi_dev and spi_config are global variables 
    // defined and initialized elsewhere
    
    // Helper write function
    void spi_write_byte(u8_t byte)
    {
        u8_t tx_buffer[1];
        tx_buffer[0] = byte;
        struct spi_buf tx_buf = {
            .buf = tx_buffer,
            .len = sizeof(tx_buffer)
        };
        const struct spi_buf_set tx = {
            .buffers = &tx_buf,
            .count = 1
        };
        spi_write(&spi_dev, &spi_config, &tx);
    }
    
    // Helper read function
    u8_t spi_read_byte()
    {
        u8_t rx_buffer[1];
        struct spi_buf rx_buf = {
            .buf = rx_buffer,
            .len = sizeof(rx_buffer)
        };
        const struct spi_buf_set rx = {
            .buffers = &rx_buf,
            .count = 1
        };
        spi_write(&spi_dev, &spi_config, &rx);
        return rx.rx_buf.buf[0];
    }
    
    u8_t read_adc_channel_1()
    {
        // We assume the ADC is already configured correctly
        
        // Start conversion
        spi_write_byte(1); // write command
        spi_write_byte(0x4); // register address
        spi_write_byte(1); // start conversion on channel 1
        
        // wait until conversion is done
        // ...
        
        // Read value
        spi_write_byte(2); // read command
        spi_write_byte(1); // channel 1
        u8_t channel_1_value = spi_read_byte(); // recieve the value
        return channel_1_value;
    }

Reply
  • That was just an example, assuming the SPI was connected to an imagined device.

    But to flesh out the example, let's imagine we have an external ADC, which takes commands on the following format:

    command address data

    Where each field is 1 byte, which requires a separate SPI transaction.

    Our hypothetical ADC supports the following commands:

    0x1 Write

    0x2 Read

    And has the following registers (on the format address, read and/or write purpose):

    0x0 R/W Configuration register. This register configures the ADC based on the bit pattern

    0x1 R Channel 1 value

    0x2 R Channel 2 value

    0x3 R Status register

    0x4 W Start conversion. If you write 0x1, it will start a conversion on channel 1. If you write 0x2, channel 2.

    The code for getting the voltage on channel 1 would then be something like this:

    // spi_dev and spi_config are global variables 
    // defined and initialized elsewhere
    
    // Helper write function
    void spi_write_byte(u8_t byte)
    {
        u8_t tx_buffer[1];
        tx_buffer[0] = byte;
        struct spi_buf tx_buf = {
            .buf = tx_buffer,
            .len = sizeof(tx_buffer)
        };
        const struct spi_buf_set tx = {
            .buffers = &tx_buf,
            .count = 1
        };
        spi_write(&spi_dev, &spi_config, &tx);
    }
    
    // Helper read function
    u8_t spi_read_byte()
    {
        u8_t rx_buffer[1];
        struct spi_buf rx_buf = {
            .buf = rx_buffer,
            .len = sizeof(rx_buffer)
        };
        const struct spi_buf_set rx = {
            .buffers = &rx_buf,
            .count = 1
        };
        spi_write(&spi_dev, &spi_config, &rx);
        return rx.rx_buf.buf[0];
    }
    
    u8_t read_adc_channel_1()
    {
        // We assume the ADC is already configured correctly
        
        // Start conversion
        spi_write_byte(1); // write command
        spi_write_byte(0x4); // register address
        spi_write_byte(1); // start conversion on channel 1
        
        // wait until conversion is done
        // ...
        
        // Read value
        spi_write_byte(2); // read command
        spi_write_byte(1); // channel 1
        u8_t channel_1_value = spi_read_byte(); // recieve the value
        return channel_1_value;
    }

Children
  • I rewrited spi_read_byte method, because i think you can mistaked with call spi_write(), if i change this on spi_read() method, so return without errors 0xff byte. Unlikely to be a voltage)) Any ideas?

    u8_t spi_read_byte()
    {
        u8_t rx_buffer[1]={0};
        struct spi_buf rx_buf = {
            .buf = rx_buffer,
            .len = sizeof(rx_buffer)
        };
        const struct spi_buf_set rx = {
            .buffers = &rx_buf,
            .count = 1
        };
        int err = spi_read(spi_dev, &spi_cfg, &rx);
        if (err) {
    	printk("SPI error: %d\n", err);
        } 
        else {
            printk("Good read!");
        }
        return rx_buffer[0];
    }

  • mikhail.maryankou said:
    because i think you can mistaked with call spi_write()

     That might have been the case. The point of that code was just to illustrate my point.

    However, to get loopback to work, you should replace your calls to spi_write and spi_read in your original code with spi_transceive, not use the code I posted (which assumes you are using a hypothetical ADC).

Related