Hello Nordic,
I am using nRF 52840 (SPI Master) with RHD2216(SPI Slave) to get sample data through SPI.
I have used the nRF connect SDK v1.9.0 SPI example to build my application as a reference.
This application is working fine, but when the timer period is set to 1 MSEC, the main loop will be blocked, and the main problem I think is the low operating speed of the spi_transieve function.
As a reference, when using spi_transieve API to send a data array in one spi_transieve calling, the speed is same as the spi_config setting. However, using the round-robin fashion to send the same size data with many spi_transieve calling, the speed is going to slow down, and the gap is huge, like ten times.
But using the round-robin fashion is the recommended way by Intan to configure the RHD2216 chip.
Please let me know if this spi_transieve API can be faster in the round-robin fashion to meet the Intan chips' demand.
I have attached my code along with a link of the data sheet for Intan RHD2216.
/* * Copyright (c) 2012-2014 Wind River Systems, Inc. * * SPDX-License-Identifier: Apache-2.0 */ /* * include */ #include <zephyr.h> #include <sys/printk.h> #include <drivers/spi.h> #include <drivers/gpio.h> #include <device.h> #include <devicetree.h> #include <kernel.h> typedef unsigned char u8_t; typedef unsigned short u16_t; /* * marco define and struct define */ // marco define #define SLEEP_TIME_MS 2000 #define BLE_BUFFER_SIZE 190 // 10 sample 1 BLE packet 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 // BLE buffer database // there is a test BLE buffer static u16_t BLE_buffer[BLE_BUFFER_SIZE]; static int BLE_buffer_inds = 0; // 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_LABEL(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(spi1)) #define RHD2216 DT_SPI_DEV_CS_GPIOS_LABEL(DT_NODELABEL(a)) // get CS label a is RHD2216 #define RHD_PIN DT_SPI_DEV_CS_GPIOS_PIN(DT_NODELABEL(a)) // get RHD cs pin #define RHD_FLAGS DT_SPI_DEV_CS_GPIOS_FLAGS(DT_NODELABEL(a)) // 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 struct device * spi_dev; static struct spi_cs_control spi_cs; struct device * RHD_dev; // Define a pointer to the device structure // 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 = 0, }; // RHD_CS // const struct device *RHD_dev; // Define a pointer to the device structure bool RHD_SAMPLE = true; int ret; int RHD_err; // LED Blinky const struct device *LED_dev; bool led_is_on = true; int ret_LED; static struct k_poll_signal cs_signal; /* * function define */ // timer function // SPI static void spi_init(void) { const char* const spiName = "SPI_1"; //SPI1 label spi_dev = device_get_binding(spiName); cs_signal.signaled = 0; if (spi_dev == NULL) { printk("Could not get %s device\n", spiName); return; } } 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 -1; } else { return rx_buffer[0]; } } // RHD init function static int RHD2216_init(void){ static const u16_t Register_config[18] = {Register0 ,Register1 ,Register2 ,Register3 ,Register4 ,Register5 ,Register6 ,Register7 , Register8 ,Register9 ,Register10 ,Register11 ,Register12 ,Register13 ,Register14 ,Register15 , Register16 ,Register17}; static u16_t result[18]; int err; u16_t calibrate_err; for(int i=0 ;i <sizeof(Register_config) / 2;i++){ result[i] = spi_trans(Register_config[i]); } 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 == 0xbf00){ // only check last calibrate result this is a loopback test ,true value is 0x8000 TODO printk("calibrate success! \n"); }else{ err = 1; return err; } // loopback test ,to test the correct result (write command result) TODO for(int j=0 ;j <sizeof(Register_config) / 2;j++){ if(result[j] == Register_config[j]){ err = 0; }else{ 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); } /* * main function loop */ void main(void) { /* * setup */ // timer k_timer_init(&RHD_timer, my_timer_handler, NULL); // init timer // blinky LED setup // Extract the device driver implementation LED_dev = device_get_binding(LED0); // combine the driver and devicetree node if (LED_dev == NULL) { return; } // Configure the GPIO pin ret_LED = gpio_pin_configure(LED_dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); if (ret_LED < 0) { return; } // 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 */ while(true){ printk("This is main loop! \n"); if(BLE_buffer_inds == BLE_BUFFER_SIZE){ // start BLE trans TODO printk("BLE trans start! \n"); BLE_buffer_inds = 0; } } }
http://intantech.com/files/Intan_RHD2000_series_datasheet.pdf