Intan RHD 2216 SPI communication with nRF52840

I'm trying to setup an SPI communication between an EVK of the Intan RHD2216 and the nRF 52840.

Browsing similar issues, I read the following topics already
Intan RHD2164 with NRF52840 (SPI) - Double Data Rate Issue 

 Intan RHD2216 SPI communication with Nordic 52840  NCK:Intan RHD2216 SPI communication with nRF 52840-- my spi_transieve working at high sample rate bacomes very slow. 

I apparently got the SPI to work since on my oscilloscope I can see CS and SCK behaving. But the I/O data doesn't correspond with the docs 
https://intantech.com/files/Intan_RHD2000_series_datasheet.pdf 

In particular, when I perform a WRITE(R,D) (page 18) and wait the 3-messages delay, I would expect to have the LSB portion of the 16-bits message to match the one that I sent; but that's not the case.

Here is my main.c code

/*
 * Copyright (c) 2012-2014 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */
/*
 * include
 */
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>

typedef unsigned char u8_t;
typedef unsigned short u16_t;
/*
 * marco define and struct define
 */
// marco define
#define SLEEP_TIME_MS 2000

static u16_t T_result[19];
// RHD command
// CONVERT command  LSB set to 0(option)TODO
static u16_t RHD_CONVERT[19] = {0x0000, 0x0100, 0x0200, 0x0300, 0x0400, 0x0500, 0x0600, 0x0700,
                                0x0800, 0x0900, 0x0a00, 0x0b00, 0x0c00, 0x0d00, 0x0e00, 0x0f00, 0xFF00, 0xFF00, 0xFF00};
// 16 channels + 3 dummy command(read ROM 63)(user define)
// 0x3f00 -> CONVERT(63)
// ADC self-calibration command ,this command needs nine dummy command to generate the necessary clock cycles to run.
#define CALIBRATE 0x5500
static const u16_t NINE_DUMMPY[9] = {0xbf00, 0xbf00, 0xbf00, 0xbf00, 0xbf00, 0xbf00, 0xbf00, 0xbf00, 0xbf00};
#define CLEAR 0x6A00     // not necessary to use this command
                         // Registers configuration using write command
#define Register0 0x80DE // amp fast settle is 0 ,20-150HZ filter ,enable ADC AND amp(disable is OK)
#define Register1 0x8120 // VDD sense disable ,using 16 * 1KS/s ADC
#define Register2 0x8228 // MUX bias current, configuration as above
#define Register3 0x8302 // diable tempS and digout
#define Register4 0x84B0 // absmode enable + unsigned ADC + weak MISO + DSP high-pass filter enable(differentiator)
// Impedance check
#define Register5 0x8500 // Impedance check control ,which is disable
#define Register6 0x8600 // DAC output voltage ,there is 0
#define Register7 0x8700 // Impedance check electrode select, this is 0
// on-chip Amplifier bandwidth Select
#define Register8 0x882C //  using 20-150 Hz bandwidth
#define Register9 0x8911
#define Register10 0x8a08
#define Register11 0x8b15
#define Register12 0x8c36
#define Register13 0x8d00
// indicidual Amplifier Power ,all set to one for using all channels' Amplifier
#define Register14 0x8eff
#define Register15 0x8fff
#define Register16 0x90ff
#define Register17 0x91ff

// spi struct
static u16_t tx_buffer[1];
static u16_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};
// LED
/* The devicetree node identifier for the "led0" alias. */
#define LED0_NODE DT_ALIAS(led0) // macro function of devicetree

#if DT_NODE_HAS_STATUS(LED0_NODE, okay)
#define LED0 DT_GPIO_CTLR(LED0_NODE, gpios)
#define PIN DT_GPIO_PIN(LED0_NODE, gpios)
#define FLAGS DT_GPIO_FLAGS(LED0_NODE, gpios)
#else
/* A build error here means your board isn't set up to blink an LED. */
// #error "Unsupported board: led0 devicetree alias is not defined"
#define LED0 ""
#define PIN 0
#define FLAGS 0
#endif
// SPI RHD cs
#if DT_SPI_HAS_CS_GPIOS(DT_NODELABEL(my_spi_master))
#define RHD2216 DT_SPI_DEV_CS_GPIOS_LABEL(DT_NODELABEL(reg_my_spi_master))   // get CS label a is RHD2216
#define RHD_PIN DT_SPI_DEV_CS_GPIOS_PIN(DT_NODELABEL(reg_my_spi_master))     // get RHD cs pin
#define RHD_FLAGS DT_SPI_DEV_CS_GPIOS_FLAGS(DT_NODELABEL(reg_my_spi_master)) // get RHD FLAGS
#else
#define RHD2216 ""
#define RHD_PIN 0
#define RHD_FLAGS 0
#endif

// timer
struct k_timer RHD_timer;
extern void my_timer_handler(struct k_timer *timer_id); // cb function call

// SPI
#define SPI_NODE DT_NODELABEL(my_spi_master) // macro function of devicetree
#define MY_SPI_MASTER_CS_DT_SPEC SPI_CS_GPIOS_DT_SPEC_GET(DT_NODELABEL(reg_my_spi_master))
const struct device *spi_dev = DEVICE_DT_GET(SPI_NODE);
// spi config

static const struct spi_config spi_cfg = {
    .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_MODE_CPOL | SPI_MODE_CPHA ,
    .frequency = 16000000,
    //.slave = 1,
    //.cs = {.gpio = MY_SPI_MASTER_CS_DT_SPEC, .delay = 0},
};

// RHD_CS
bool RHD_SAMPLE = true;
int ret;
int RHD_err;
// LED Blinky
static const struct gpio_dt_spec LED_dev = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
bool led_is_on = true;
int ret_LED;

/*
 * function define
 */
// timer function

// SPI
static void spi_init(void)
{
        if (!device_is_ready(spi_dev))
        {
                printk("Could not get %s device\n", spi_dev->name);
                return;
        }
        printk("Device %s Correctly configured!\n", spi_dev->name);
}

u16_t spi_trans(u16_t command) // SPI trans in the timer period
{
        int err;
        tx_buffer[0] = command;
        err = spi_transceive(spi_dev, &spi_cfg, &tx, &rx);
        RHD_SAMPLE = !RHD_SAMPLE;
        if (err)
        {
                printk("SPI error: %d\n", err);
        }

        return rx_buffer[0];
}
// RHD init function
static int RHD2216_init(void)
{
        static const u16_t Register_config[20] = {Register0, Register1, Register2, Register3, Register4, Register5, Register6, Register7,
                                                  Register8, Register9, Register10, Register11, Register12, Register13, Register14, Register15,
                                                  Register16, Register17, 0xbf00, 0xbf00};
        static u16_t result[20];
        int err;
        u16_t calibrate_err;

        for (int i = 0; i < sizeof(Register_config) / 2; i++)
        {
                result[i] = spi_trans(Register_config[i]);
        }

        // loopback to test the correct result (write command result)
        for (int j = 0; j < (sizeof(Register_config) / 2) - 2; j++)
        {
                printk("SPI I/O: %02X %02X \n", (0xFF & Register_config[j]), (0xFF & result[2+j]));

                if ((0xFF & result[2 + j]) != (0xFF & Register_config[j]))
                {
                        err = 1;
                }
        }

        if(err) {
                printk("loopback test failed! \n");
                return err;
        }

        k_sleep(K_USEC(100)); // 100us delay before ADC calibrate
        calibrate_err = spi_trans(CALIBRATE);
        for (int j = 0; j < 9; j++)
        { // using generate nine SCLK to calibrate ADC
                calibrate_err = spi_trans(NINE_DUMMPY[j]);
        }
        if (calibrate_err == 0x8000)
        { // only check last calibrate result this is a loopback test ,true value is 0x8000 TODO
                printk("calibrate success! \n");
        }
        else
        {
                printk("calibrate failed! \n");
                err = 1;
                return err;
        }

        return err;
}
/*
 * cb function define
 */
// timer cb
void RHD_handler(struct k_work *work) // using round-robin fashion ,a timer callback to sample all needed channels
{
        // static u16_t T_result[19];
        uint64_t stamp;
        int64_t delta;
        stamp = k_uptime_get_32();
        /* do the processing that needs to be done periodically */
        for (int j = 0; j < 10; j++)
        {
                for (int i = 0; i < 19; i++)
                {
                        T_result[i] = spi_trans(RHD_CONVERT[i]);
                }
        }
        delta = k_uptime_delta(&stamp);
        printk("SPI Use time is:%lld ms \n", delta);
}
K_WORK_DEFINE(my_work, RHD_handler); // define RHD_handler as my_work
void my_timer_handler(struct k_timer *dummy)
{
        k_work_submit(&my_work);
}

// #include "hal/nrf_gpio.h"
// #define YOUR_PIN NRF_GPIO_PIN_MAP(1, 10) // Example connection - P1.03

/*
 * main function loop
 */
int main(void)
{
        printf("Hello World! %s\n", CONFIG_BOARD_TARGET);
        k_msleep(SLEEP_TIME_MS);
        // nrf_gpio_cfg_output(YOUR_PIN); /* Initialize with default config of pin */

        /*
         * setup
         */
        // timer
        k_timer_init(&RHD_timer, my_timer_handler, NULL); // init timer
        // Extract the device driver implementation
        // LED_dev = device_get_binding("led0"); // combine the driver and devicetree node

        // LED_dev = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
        if (!gpio_is_ready_dt(&LED_dev))
        {
                printf("Issue on device_get_binding\n");
                return 1;
        }

        // Configure the GPIO pin
        ret_LED = gpio_pin_configure_dt(&LED_dev, GPIO_OUTPUT_ACTIVE);
        if (ret_LED < 0)
        {
                printf("Issue on gpio_pin_configure\n");

                return 1;
        }
        // SPI
        printk("SPIM Example\n");
        spi_init();
        printk("RHD_FLAGS %x\n", RHD_FLAGS);
        RHD_err = RHD2216_init();
        if (RHD_err)
        {
                printk("RHD2216 init fail \n");
        }
        else
        {
                RHD_err = spi_trans(RHD_CONVERT[0]);
                /* start periodic timer that expires once at 1kHz */
                k_timer_start(&RHD_timer, K_SECONDS(3), K_USEC(5000)); // first param is timer duration(initial timer duration)
        }
        /*
         * Loop function : other data process code
         */
        printk("Going to main loop! \n");
        bool led_state = true;

        while (true)
        {
                // nrf_gpio_pin_toggle(YOUR_PIN);

                ret = gpio_pin_toggle_dt(&LED_dev);
                if (ret < 0)
                {
                        return 0;
                }

                led_state = !led_state;
                // printf("LED state: %s\n", led_state ? "ON" : "OFF");
                k_msleep(SLEEP_TIME_MS);
                // printk("This is main loop! \n");
        }

        return 0;
}

my .overlay file

// To get started, press Ctrl+Space to bring up the completion menu and view the available nodes.

// You can also use the buttons in the sidebar to perform actions on nodes.
// Actions currently available include:

// * Enabling / disabling the node
// * Adding the bus to a bus
// * Removing the node
// * Connecting ADC channels

// For more help, browse the DeviceTree documentation at https://docs.zephyrproject.org/latest/guides/dts/index.html
// You can also visit the nRF DeviceTree extension documentation at https://docs.nordicsemi.com/bundle/nrf-connect-vscode/page/guides/ncs_configure_app.html#devicetree-support-in-the-extension
/ {
	aliases {
		spi0 = &spi0;
	};
};

&pinctrl {
	spi0_default {
		group1 {
            psels = <NRF_PSEL(SPIM_SCK, 1, 15)>,
                    <NRF_PSEL(SPIM_MOSI, 1, 13)>,
                    <NRF_PSEL(SPIM_MISO, 1, 14)>;
		};
	};

	spi0_sleep {
		group1 {
            psels = <NRF_PSEL(SPIM_SCK, 1, 15)>,
                    <NRF_PSEL(SPIM_MOSI, 1, 13)>,
                    <NRF_PSEL(SPIM_MISO, 1, 14)>;
			low-power-enable;
		};
	};	
};

my_spi_master: &spi0 {
	compatible = "nordic,nrf-spim";
	status = "okay";
	pinctrl-0 = <&spi0_default>;
	pinctrl-1 = <&spi0_sleep>;
	pinctrl-names = "default", "sleep";
	cs-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
	reg_my_spi_master: spi-dev-a@0 {
		reg = <0>;
	};

	max-frequency = <DT_FREQ_M(16)>;
};

&spi1 {
	compatible = "nordic,nrf-spi";
	status = "disabled";
};

&spi3 {
	status = "disabled";
};

and my prj.conf file

CONFIG_GPIO=y
CONFIG_SPI=y


I'm still a bit confused about 2 things:

  • the definition of the SPI, even though I copied from the other forum posts, does not match the RHD docs. I would expect something like the code here below, also because the SPI on nRF side I understood cannot work at more than 8 mega
    static const struct spi_config spi_cfg = {
        .operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB, 
        .frequency = 8000000,
        .slave = 1,
        .cs = {.gpio = MY_SPI_MASTER_CS_DT_SPEC, .delay = 0},
    };

  • the .overlay file keeps complaining with the message "Only spi nodes accepted in /soc/spi@40003000/.". but everything compiles.

Any suggestion is really appreciated.

Related