Can't get SPI communication to work on the nrf5340-dk

Hi,

For a project I am currently trying to do a simple test to check the SPI communication between the nrf5340 en ADS1299(ADC for EEG application) before we order our pcbs.

So far I haven't been able to get it to work, to narrow down the source of the problem I connected a logic analyzer and the signals look different than I would expect (see the following pictures).

  

As can be seen the clock signal doesn't have a constant frequency and the duty cycle of the clock signal is not ~50% as I would expect.

To test the MOSI signal I tried sending another command (at first all commands had just one bit which was a 1), I tried sending 0b10101010 which resulted in the following picture.

On the positive side the command send is present in the MOSI signal, however the different periods of the bits don't seem correct.

Given these results I expect there might be something wrong in the way I handle the SPIM on the nrf5340, does anyone have any idea what the problem might be?

Thanks in advance

Info

Code

Remark: Interrupt part for the DRDY pin is not enabled at the moment, part of the code is not working at the moment and I want to test sending commands first. Since there is no pulse visible on the DRDY signal on the Logic analyzer it is clear that the ADS1299 is not sending data yet.

main.c

#include <zephyr.h>
#include <sys/printk.h>
#include "ADS1299.h"
#include <nrfx_systick.h>

void main(void)
{

	printk("\r\nHello World! %s\n", CONFIG_BOARD);

	ADS1299_init();

	ADS1299_send_reset();
	k_msleep(500);

	ADS1299_send_start();
	k_msleep(500);

	ADS1299_send_read_continuous();

	// Interrupt is not working yet so no data received. First checking CLK, MOSI and interrupt
	// ADS1299_data_t data;

	uint32_t count = 0;

	while(1) {
		// if(ADS1299_poll_new_data(&data)) {
		// 	printk("%d %d %d %d\n", data.gpio_0, data.gpio_1, data.gpio_2, data.gpio_3);
		// } else {
			if(++count > 4294967) {
				printk(".");
				count = 0;
			}
		// }
	}
}

ads1299.c (init at line 66)

#include "ADS1299.h"
#include <zephyr.h>
#include <sys/printk.h>
#include <nrfx_spim.h>
#include <nrfx_gpiote.h>
#include <device.h>
#include <devicetree.h>
#include <drivers/gpio.h>

#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)

#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];

const struct device *gpio_dev;

inline int32_t buffer_to_channel(uint8_t id);
bool ADS1299_receive_data();
ADS1299_data_t ADS1299_convert_data();
void data_ready_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action);

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);
        state = idle;
        break;
    
    default:
        break;
    }
    return;
}

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();
    }
}

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;
    }
    

    // Interrupt not working at the moment, first testing CLK and MOSI after that switch to GPIO sense
    // nrfx_gpiote_in_config_t irq_config = NRFX_GPIOTE_CONFIG_IN_SENSE_TOGGLE(false); // TODO: what is argument?
    // nrfx_gpiote_init(3);
    // nrfx_gpiote_in_uninit(PIN_DRDY);
    // err = nrfx_gpiote_in_init(PIN_DRDY, &irq_config, data_ready_handler);

    // if(err == NRFX_SUCCESS) {
    //     printk("Pin IRQ init success\r\n");
    // } else {
    //     printk("FAIL: pin IRQ init failed: %d\r\n", err);
    //     return false;
    // }

	gpio_dev = device_get_binding("GPIO_1");

	if (gpio_dev == NULL) {
		return false;
	}
	gpio_pin_configure(gpio_dev, 1, GPIO_OUTPUT);
    gpio_pin_set(gpio_dev, 1, 1);

    // nrfx_gpiote_in_event_enable(PIN_DRDY, true);

    return true;
}

int spi_transfer(const nrfx_spim_xfer_desc_t *desc) {
    gpio_pin_set(gpio_dev, 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) {
        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) {
        state = processing_data;

        *data = ADS1299_convert_data();

        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;
}

ADS1299.h

#ifndef _ADS1299_H
#define _ADS1299_H

#include <stdint.h>
#include <stdbool.h>

bool ADS1299_init();
bool ADS1299_send_start();
bool ADS1299_send_reset();
bool ADS1299_send_read_continuous();
bool ADS1299_poll_new_data();
bool get_spi_busy();

typedef struct ADS1299_data
{
    uint8_t lead_off_positive;
    uint8_t lead_off_negative;
    bool gpio_0;
    bool gpio_1;
    bool gpio_2;
    bool gpio_3;
    int32_t channgel_0;
    int32_t channgel_1;
    int32_t channgel_2;
    int32_t channgel_3;
    int32_t channgel_4;
    int32_t channgel_5;
    int32_t channgel_6;
    int32_t channgel_7;
} ADS1299_data_t;


#endif /* _ADS1299_H */

proj.conf

CONFIG_SPI=y
CONFIG_NFCT_PINS_AS_GPIOS=y
CONFIG_OPENOCD_SUPPORT=y
CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_NRFX_SPIM=y
CONFIG_NRFX_SPIM2=y
CONFIG_GPIO=y
# CONFIG_NRFX_GPIOTE=y

Fixes tried:

  • Try other SPIM instance (0 and 1), resulted in a reboot loop
  • Try other pin for SCLK, same result
  • Slow done clock to 125kHz, same result
  • Remove connection to sensor (so only connected to LA), same result
Parents
  • If your clock and data line have inconsistent timings then your signal integrity is far too low. This is most likely a HW issue. Do you have access to an oscilloscope? An analog scope of the clock signal would be of great help. 

    Typical HW issues that affect signal integrity are poor grounding, parasitic/stray capacitance, and EMI. 

    Remove connection to sensor (so only connected to LA), same result

    This is strange, I would expect the DK to have sufficient signal integrity in the layout. How do you connect the DA to the DK? Did you remember to connect the DA's ground lead to DK's ground?

  • Thank you for your reply.
    I will have access to an oscilloscope next week, so I will check that and get back to you with that result.

    For now it is just a simple test setup with jumper wires and a breadboard. I already swapped the cable ones, but I will try that again to eliminate that option.

    The ground wire is connected between the 3 devices.

  • I have made some progress on this issue. It turned out that there was either another bad connection or I needed to disconnect the Arduino I used as an analog in (simple oscilloscope), which gave me the following result.

    So the signals look a lot cleaner and the sensor responds to commands, which is great.

    Unfortunately now I am having some troubles getting the interrupt for the DRDY pin to work. I looked at some examples online, which resulted in the following (related) code:

    #include <nrfx_gpiote.h>
    
    #define PIN_DRDY NRF_GPIO_PIN_MAP(0, 7)
    
    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();
        }
    }
    
    bool ADS1299_init() {
    .
    .
    .
        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;
        }
    .
        nrfx_gpiote_in_event_enable(PIN_DRDY, true);
    }

    When I put a breakpoint at the start of the `data_ready_handler` function and run in debug mode, I never trigger the breakpoint, while seeing multiple pulses on the input signal.

    Do you have any idea what this issue might be?

    Thanks again

    EDIT: Forgot to copy a couple of lines of code.

  • MickyT said:
    Do you have any idea what this issue might be?

    I don't know. 

    I think you should monitor the GPIOTE registers, specifically EVENTS_IN[n] and EVENTS_PORT during runtime(pause the debugger a few times after you've configured the GPIOTE channel).

    You should also verify that the CONFIG[n] and INTENSET register is configured as intended. If you print out the values I can help you parse them.

  • After a couple pauses I observed the following values:

    So the only event generated is on the EVENTS_PORT and all the configs are 0, which corresponds to the result I am seeing, but not what I would expect given that the two init functions return a NRFX_SUCCESS.

    Any ideas are appreciated, or if you know of a working example that would be great as well.

  • Sry for the late reply. 

    The PORT event has been fired, and the PORT event interrupt is enabled so that should also have fired. 

    Have you called gpiote_in_init() at all?

  • No worries, thank you for replying.

    I did call `nrfx_gpiote_in_init`, but I see now that I didn't copy it correctly in my previous post, sorry about that. I will edit that post and add my entire file below (see line 75 for the init function).

    #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)
    
    #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;
    }
    

Reply
  • No worries, thank you for replying.

    I did call `nrfx_gpiote_in_init`, but I see now that I didn't copy it correctly in my previous post, sorry about that. I will edit that post and add my entire file below (see line 75 for the init function).

    #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)
    
    #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;
    }
    

Children
No Data
Related