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

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

Parents Reply Children
No Data
Related