Trouble getting SPI to work together with BLE

Hi,

I have been working on a setup where I connect a sensor (TI ADS1299) to the nRF5340-DK 2.0.0 using an SPI connection and transmit the recorded data using BLE. I have been able to get the SPI connection and the BLE connection working separately, but when combining the functionalities a problem arises. The program builds and programs fine, but when running it, the SPI connection does not seem to be working properly. All registers that store the data are left empty, instead of containing the measured data. The BLE connection does still work but only transmits zeros, probably as the result of the SPI connection not working as it should. 

What could be the problem here? Do the nrfx_SPIM and BLE functionalities interfere with each other? 

My setup:

PC: HP Zbook G4 Studio 

OS: Windows 10

nRF Connect for Desktop v3.10.0

Does anyone have any suggestions on how to fix this issue?

Kind regards,

C

Parents Reply
  • I do not see any obvious reasons for why this works fine without BLE, but not when it's enabled. You haven't changed the pinout by any chance? I notice you are using P0.02 for the chip select line and that you have set the CONFIG_NFCT_PINS_AS_GPIOS option, but have you also remembered to short R42 and R45 and remove the 0R resitor on R43 and R46 on the board?

    NFC antenna interface

    A scope or logic analyzer capture of the SPI transfer would be helpful if we need to troubleshoot this further.

Children
  • Thank you for your response,

    In the original SPI code, I used P1.01 for the chip select. After combining the SPI code and BLE code I got the same issue I described in this post. I found somewhere that P1.01 might be used by the BLE connection, so I tried another pin (P0.02) for the chip select. I did not know about the need to short/remove the resistors from the board to use P0.02. Perhaps using another pin is more convenient in this case. I will try some others.

    I sadly do not own a scope or a logic analyzer, but I might be able to borrow one if using another pin for the chip select does not help. 

    Thanks for your help,

    C

  • I have tried using several different pins from the GPIO_0 and GPIO_1 for the chip select, but none of them seem to be working. Using a logic analyzer does show that the chip select seems to be malfunctioning (see picture). In this test, P1.01 was used as the chip select pin, as that was the pin that was working in the original code. I have attached the original code used to set up the SPI connection.

      

    #include "ADS1299.h"
    #include <zephyr.h>
    #include <sys/printk.h>
    #include <nrfx_spim.h>
    #include <device.h>
    #include <devicetree.h>
    #include <drivers/gpio.h>
    
    #ifndef POLL_DREADY
        #include <nrfx_gpiote.h>
    #endif
    
    #define SPI_INSTANCE 2
    
    #define PIN_SCK NRF_GPIO_PIN_MAP(0, 8)
    #define PIN_MOSI NRF_GPIO_PIN_MAP(0, 9)
    #define PIN_MISO NRF_GPIO_PIN_MAP(0, 10)
    #define PIN_CS NRF_GPIO_PIN_MAP(0, 2) //1.1
    
    #define PIN_DRDY NRF_GPIO_PIN_MAP(0, 7)
    
    #define COMMAND_WAKEUP 0x02
    #define COMMAND_STANDBY 0x04
    #define COMMAND_RESET 0x06
    #define COMMAND_START 0x08
    #define COMMAND_STOP 0x0A
    #define COMMAND_RDATA 0x12
    #define COMMAND_RDATAC 0x10
    #define COMMAND_SDATAC 0x11
    #define COMMAND_RREG 0b00100000
    #define COMMAND_WREG 0b01000000
    
    volatile enum state_t {idle, send_command, receive_data, processing_data} state = idle;
    bool new_data_available = false;
    
    static const nrfx_spim_t m_spi = NRFX_SPIM_INSTANCE(SPI_INSTANCE);
    uint8_t rx_buffer[27], tx_buffer[8];
    uint8_t sample_number = 0;
    
    const struct device *gpio_dev_0, *gpio_dev_1;
    
    inline int32_t buffer_to_channel(uint8_t id);
    bool ADS1299_receive_data();
    ADS1299_data_t ADS1299_convert_data();
    
    #ifndef POLL_DREADY
        void data_ready_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action);
    #endif
    
    void spim_event_handler(nrfx_spim_evt_t const *p_event, void *p_context) {
        switch (p_event->type)
        {
        case NRFX_SPIM_EVENT_DONE:
            if(state == receive_data) {
                new_data_available = true;
            }
            gpio_pin_set(gpio_dev_1, 1, 1);
            state = idle;
            break;
        
        default:
            break;
        }
        return;
    }
    
    #ifndef POLL_DREADY
    void data_ready_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
        if(pin == PIN_DRDY && action == NRF_GPIOTE_POLARITY_HITOLO) {
            ADS1299_receive_data();
        }
    }
    #endif
    
    bool ADS1299_init() {
        nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG(PIN_SCK, PIN_MOSI, PIN_MISO, NRFX_SPIM_PIN_NOT_USED);
        config.frequency = NRF_SPIM_FREQ_125K;
        config.ss_active_high = false;
        config.bit_order = SPIM_CONFIG_ORDER_MsbFirst;
        config.mode = NRF_SPIM_MODE_1;
        
        nrfx_spim_uninit(&m_spi);
        int err = nrfx_spim_init(&m_spi, &config, spim_event_handler, NULL);
    
        if(err == NRFX_SUCCESS) {
            printk("SPI init success\r\n");
        } else {
            printk("FAIL: SPI init failed: %d\r\n", err);
            return false;
        }
        
        gpio_dev_0 = device_get_binding("GPIO_0");
        gpio_dev_1 = device_get_binding("GPIO_1");
    
        if (gpio_dev_0 == NULL) {
    		return false;
    	}
    
        if (gpio_dev_1 == NULL) {
    		return false;
    	}
    
        #ifdef POLL_DREADY
            printk("Configure DREADY pin\r\n");
            err = gpio_pin_configure(gpio_dev_0, 7, GPIO_INPUT);
            printk("Return config DREADY pin: %d\r\n", err);
        #else
            // Interrupt
            if(!nrfx_gpiote_is_init()) {
                err = nrfx_gpiote_init(6);
    
                if(err == NRFX_SUCCESS) {
                    printk("Pin IRQ init success\r\n");
                } else {
                    printk("FAIL: pin IRQ init failed: %d\r\n", err);
                    return false;
                }
            }
    
            nrfx_gpiote_in_config_t irq_config = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(false); // TODO: what is argument?
            err = nrfx_gpiote_in_init(PIN_DRDY, &irq_config, data_ready_handler);
    
            if(err == NRFX_SUCCESS) {
                printk("Pin IRQ config success\r\n");
            } else {
                printk("FAIL: pin IRQ config failed: %d\r\n", err);
                return false;
            }
        #endif
        
    
    	gpio_pin_configure(gpio_dev_1, 1, GPIO_OUTPUT);
        gpio_pin_set(gpio_dev_1, 1, 1);
    
        #ifndef POLL_DREADY
            nrfx_gpiote_in_event_enable(PIN_DRDY, true);
        #endif
    
        return true;
    }
    
    void poll_dready() {
        if(gpio_pin_get(gpio_dev_0, 7) == 0) {
            ADS1299_receive_data();
        }
    }
    
    int spi_transfer(const nrfx_spim_xfer_desc_t *desc) {
        gpio_pin_set(gpio_dev_1, 1, 0);
        return nrfx_spim_xfer(&m_spi, desc, 0);
    }
    
    bool ADS1299_send_command(uint8_t command) {
        if(state == idle) {
            nrfx_spim_xfer_desc_t spim_xfer_desc = NRFX_SPIM_XFER_TX(tx_buffer, 1);
            spim_xfer_desc.p_rx_buffer = NULL;
            spim_xfer_desc.rx_length = 0;
            tx_buffer[0] = command;
            state = send_command;
            int err = spi_transfer(&spim_xfer_desc);
            if(err != NRFX_SUCCESS) {
                printk("send command err: %d\r\n", err);
            }
            return true;
        } else {
            return false;
        }
    }
    
    bool ADS1299_write_register(uint8_t address, uint8_t value) {
        if(state == idle) {
            nrfx_spim_xfer_desc_t spim_xfer_desc = NRFX_SPIM_XFER_TX(tx_buffer, 2);
            spim_xfer_desc.p_rx_buffer = NULL;
            spim_xfer_desc.rx_length = 0;
            tx_buffer[0] = (address & 0b00011111) | COMMAND_WREG;
            tx_buffer[1] = value;
            state = send_command;
            int err = spi_transfer(&spim_xfer_desc);
            if(err != NRFX_SUCCESS) {
                printk("send command err: %d\r\n", err);
            }
            return true;
        } else {
            return false;
        }
    }
    
    bool ADS1299_send_start() {
        return ADS1299_send_command(COMMAND_START);
    }
    
    bool ADS1299_send_read_continuous() {
        return ADS1299_send_command(COMMAND_RDATAC);
    }
    
    bool ADS1299_send_reset() {
        return ADS1299_send_command(COMMAND_RESET);
    }
    
    bool ADS1299_receive_data() {
        if(state == idle) {
            state = receive_data;
            new_data_available = false;
            nrfx_spim_xfer_desc_t spim_xfer_desc = NRFX_SPIM_XFER_RX(rx_buffer, 27);
            spi_transfer(&spim_xfer_desc);
            return true;
        } else {
            return false;
        }
    }
    
    bool ADS1299_poll_new_data(ADS1299_data_t *data) {
        if(new_data_available) {
            new_data_available = false;
            state = processing_data;
    
            *data = ADS1299_convert_data();
    
            state = idle;
    
            return true;
        } else {
            return false;
        }
    }
    
    ADS1299_data_t ADS1299_convert_data() {
        /* Data structure: */
        /* 24 bit status */
        /*  - 1100 + LOFF_STATP(8 bit) + LOFF_STATN(8 bit) + GPIO(4 bit) */
        /* 8ch * 24 bits data */
    
        ADS1299_data_t data;
        
        data.lead_off_positive = rx_buffer[0] << 4;
        data.lead_off_positive |= (rx_buffer[1] & 0b11110000) >> 4;
        data.lead_off_negative = rx_buffer[1] << 4;
        data.lead_off_negative |= (rx_buffer[2] & 0b11110000) >> 4;
    
        data.gpio_0 = rx_buffer[2] & 0b00001000;
        data.gpio_1 = rx_buffer[2] & 0b00000100;
        data.gpio_2 = rx_buffer[2] & 0b00000010;
        data.gpio_3 = rx_buffer[2] & 0b00000001;
    
        data.channgel_0 = buffer_to_channel(0);
        data.channgel_1 = buffer_to_channel(1);
        data.channgel_2 = buffer_to_channel(2);
        data.channgel_3 = buffer_to_channel(3);
        data.channgel_4 = buffer_to_channel(4);
        data.channgel_5 = buffer_to_channel(5);
        data.channgel_6 = buffer_to_channel(6);
        data.channgel_7 = buffer_to_channel(7);
    
        return data;
    }
    
    inline int32_t buffer_to_channel(uint8_t id) {
        return rx_buffer[3 + id] << 16 | (rx_buffer[3 + id + 1] << 8) | rx_buffer[3 + id + 2];
    }
    
    bool get_spi_busy() {
        return state != idle;
    }
    
    void send_new_available_data() {
        if(new_data_available) {
            new_data_available = false;
    
            // OpenBCI communication protocol
            printk("%c", 0xA0);
            printk("%d", sample_number++);
            for(uint8_t i = 0; i < 27; i++) {
                printk("%c", (char)rx_buffer[i]);
            }
            printk("%c%c%c%c%c%c%c", 0, 0, 0, 0, 0, 0, 0xC2);
        }
    }

  • Hi,

    Can you try use P1.04 for CS instead? P1.01 is assigned to the network core for UART0 TX by default and therefore not accessible for the app core.

    	gpio_pin_configure(gpio_dev_1, 4, GPIO_OUTPUT);
        gpio_pin_set(gpio_dev_1, 4, 1);

    Edit: P1.01 ("33") gets assigned to the network core in the startup file here: https://github.com/nrfconnect/sdk-zephyr/blob/c3208e7ff49d22d8271f305344382e9306fdde99/boards/arm/nrf5340dk_nrf5340/nrf5340_cpunet_reset.c#L35

  • I have changed it to P1.04, but still no luck. For some reason, the rx_buffer seems to remain empty. The function ADS1200_poll_new_data does return 'true', as the data channels are printed (bottom of main.c). That indicates that 'new_data_available' (in ADS1299.c) must also become true. It seems like the code thinks data is received successfully, but either only zero values are received, or the data is not stored properly.

  • Did the CS signal start working now when you changed to P1.04?  And what is the difference between the non-working BLE example and the old SPI example if you look at the logic trace?

    I don't have this sensor to test with, but I can at least check what gets sent. Here are a few screenshots from when I ran your code on a DK:

Related