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.