SPI CS delay

Hi,
We are using Colibri iMX6 DualLite 512MB V1.1A in Colibri Evaluation Board V3.2B with Custom Linux Image developed using OE/YOCTO from Linux Image V2.8. We have interfaced ADC IC - ADS8684A with the standard spi interface available with the Colibri iMX6 SoM.
Our requirement is to measure the input signals upto 20kHz per channel. But we couldn’t achieve the speed.
We are using libsoc library for accessing the SPI. We have used a GPIO pin as Chip Select pin. I measured the switching speed the gpio pin using a scope and I’ve attached the images.
time delay b/w CS falling to first SPI clock = 400 micro seconds
time delay b/w SPI last clock to CS rising = 110 micro seconds
I’ve checked with both 1MHz and 10MHz as SPI clocks. The time delay remains the same.
How to reduce this time delay?
If I use the chip select pin of the SPI, whether I could achieve high speed? For that whether I should modify anything in the device tree?

Is there any other way possibilities that could result in SPI communication delay?
Thank You for your time.

Image Attached

Hi @goofy

We are using libsoc library for accessing the SPI. We have used a GPIO pin as Chip Select pin. I measured the switching speed the gpio pin using a scope and I’ve attached the images.

Could you share a sample code you are using to send and receive data?

Best regards,
Jaski

Hi,
I have once again checked the same today. But surprisingly the delay got reduced.

time delay b/w CS falling to first SPI clock = 14 micro seconds

time delay b/w SPI last clock to CS rising = 24 micro seconds

If the delay is reduced further, the data acquisition speed can be increased.

The code:

#include "libsoc_gpio.h"
#include "libsoc_debug.h"
#include"libsoc_file.h"
#include "libsoc_spi.h"

#define CS  146
#define RST  147
#define SPI_DEVICE   3


void  error()
{
	printf("error\n");
}

int main(void)
{
	 uint8_t wdata[4],rdata[4];
	 gpio  *cs,*rst;		//chip select and reset

	 spi* spi_dev = libsoc_spi_init(SPI_DEVICE, 0);
	 if (!spi_dev)
	 {
		 printf("Failed to get spidev device!\n");
		 return EXIT_FAILURE;
	 }
	 //For SPI
	 libsoc_spi_set_mode(spi_dev, MODE_0);
	 libsoc_spi_get_mode(spi_dev);

	 libsoc_spi_set_speed(spi_dev, 10000000);		//10MHz
	 libsoc_spi_get_speed(spi_dev);

	 libsoc_spi_set_bits_per_word(spi_dev,BITS_8);

	 // Request gpios
	  cs = libsoc_gpio_request(CS, LS_GPIO_SHARED);
	  rst = libsoc_gpio_request(RST, LS_GPIO_SHARED);
	  if (cs == NULL || rst == NULL)
	  {
	    error();
	  }

	  // Set direction to OUTPUT
	  libsoc_gpio_set_direction(cs, OUTPUT);
	  if (libsoc_gpio_get_direction(cs) != OUTPUT)
	  {
	    printf("Failed to set cs direction to OUTPUT\n");
	    error();
	  }

	  libsoc_gpio_set_direction(rst, OUTPUT);
	  if (libsoc_gpio_get_direction(rst) != OUTPUT)
	  {
	    printf("Failed to set rst direction to OUTPUT\n");
	    error();
	  }


	  // Set reset always HIGH. It goes to the active low reset pin of ADC. 
	  libsoc_gpio_set_level(rst, LOW);
	  usleep(250);
	  libsoc_gpio_set_level(rst, HIGH);

	  //softwre rst of adc
	  wdata[0]=0x85; wdata[1]=0x00;
	  libsoc_gpio_set_level(cs, HIGH);
	  usleep(1);
	  libsoc_gpio_set_level(cs, LOW);
	  libsoc_spi_rw(spi_dev,wdata,rdata,2);
	  libsoc_gpio_set_level(cs, HIGH);
	  usleep(1);


	  //feature select of adc - ADC Configuration
	  wdata[0]=0x03;  wdata[1]=0x03; wdata[2]=0x00;
	  libsoc_gpio_set_level(cs, LOW);
	  libsoc_spi_rw(spi_dev,wdata,rdata,3);
	  libsoc_gpio_set_level(cs, HIGH);
	  usleep(1);

	  //to set range for all channel - ADC Configuration
	  for(int i=0;i<4;i++)
	  {
		  libsoc_gpio_set_level(cs, LOW);
		  wdata[0]=(((0x05+i)<<1) | 1);  wdata[1]=0x05; wdata[2]=0x00;
		  libsoc_spi_rw(spi_dev,wdata,rdata,3);
		  libsoc_gpio_set_level(cs, HIGH);
		  usleep(1);
	  }

	  // to get back to auto mode in adc - ADC Configuration
	  libsoc_gpio_set_level(cs, LOW);
	  wdata[0]=(0x01<<1)|1;  wdata[1]=0x0f; wdata[2]=0x00;
	  libsoc_spi_rw(spi_dev,wdata,rdata,4);
	  libsoc_gpio_set_level(cs, HIGH);
	  usleep(1);

	  // to auto rst of adc - ADC Configuration
	  libsoc_gpio_set_level(cs, LOW);
	  wdata[0]=0xA0;  wdata[1]=0x00; wdata[2]=0x00; wdata[3]=0x00;
	  libsoc_spi_rw(spi_dev,wdata,rdata,4);
	  libsoc_gpio_set_level(cs, HIGH);
	  usleep(1);


	   //data acquistion loop
	   while(1)
	   {
		  time_t start, end;
		  double *arr;
		  int n=1;
		  arr=(double*)malloc(n*sizeof(double));
		  start=time(NULL);
		  end=start+5;		//to acquire data for 5 sec
		  while(start<=end)
		  {
			  for(int i=0;i<4;i++) 	//to take data from 4 ch of adc in auto sequence
			  {
	  			libsoc_gpio_set_level(cs, LOW);
				libsoc_spi_read(spi_dev,rdata,4);
			  	libsoc_gpio_set_level(cs, HIGH);

				arr[n-1]=(10.24/65536)*((rdata[2]<<8) | rdata[3]);
				n=n+1;
				arr=(double*)realloc(arr,n*sizeof(double));
			  }
			  start=time(NULL);
		  }
		  for(int i=0;i<n;i++)
		  {
			  printf("arr[%d] is  %f\n",i,arr[i]);
		  }
		  printf("The size of arr for 5 sec is %d\n",n);
		  free(arr);
		  break;
	   }
	   return 0;
}

Another issue that we are facing is that the Data Acquisition is not happening at constant speed. As in the code we are continuously capturing data for 5 sec. But if I run the code each time the size of the array is different. The size of the array is like 10925, 18402, 5589 etc. How to control the speed of the data acquisition? Whether the loop interval between each iteration varies based upon the OS background operations? Or is it the problem of the time() function that we have used to figure out the time interval?

Thank You for your time.

How many channel do you want to read?
What do you want to do with the data?
Could you explain your application,
please?

Four channels. The main scope of the application is to collect data from four channels of ADC (says x1, y1, x2, y2) and draw two xy graphs - x1 vs y1 and x2 vs y2.
The SoM waits for a trigger in one of the GPIOs. Once it is triggered, data acquisition should start in all 4 channels of the ADC. In another one of the GPIOs is triggered the data acquisition should stop and plot graph with the acquired data. Then some processing on the acquired data will be done and results are displayed. GUI is developed using gtk. Ethernet communication thread runs in the background.

I just enabled SPI in the device tree and the SPI CS pin is working. And with that CS pin I could reduce the delay time.

Whether the speed can be compromised from 20kHz to 5kHz per channel to improve the delay between iterations?

Whether the variability can be reduced by making the process a real-time process by calling sched_setscheduler() with SCHED_FIFO?

For you application, I would recommend
you to use the M4 on iMX7 for data
acquisition and data handling. Please
have a look at the Documentation here.

We have already made hardware schematic and major software code for Colibri iMX6. I know the SoM under Colibri family are pin compatible. But since iMX7 has a M4 processor in built, whether it’s pins are multiplexed with the SoM pins?
How much modification we have to do if we choose to switch from iMX6 to iMX7?

For programming the M4 processor, toradex suggest to use ARM DS-5 v5.24.0. Whether it is a free version or paid?
Can we program the M4 processor using any other free IDEs such as Eclipse?

Thank You for your time.

Hi Goofy

Our requirement is to measure the input signals upto 20kHz per channel. But we couldn’t achieve the speed.

How many channel do you want to read? What do you want to do with the data? Could you explain your application, please?

We have used a GPIO pin as Chip Select pin. I measured the switching speed the gpio pin using a scope and I’ve attached the images.

Why are you using a GPIO pin and not define this in the devicetree? Using an adequate chip select pin will reduce the delay between clock and chip select.

How to reduce this time delay? If I use the chip select pin of the SPI, whether I could achieve high speed? For that whether I should modify anything in the device tree?

Yes, use the chip select of SPI Driver by changing this in the device tree.

But if I run the code each time the size of the array is different. The size of the array is like 10925, 18402, 5589 etc. How to control the speed of the data acquisition? Whether the loop interval between each iteration varies based upon the OS background operations? Or is it the problem of the time() function that we have used to figure out the time interval?

Depending on how you are reading out the data, the size of the array will change. You have to understand that the Linux is not a real-time OS. With the Fully Preemptibile patch for the Kernel you can reduce the jitter you are having on the time base to a certain value (some 100microseconds).

For you application, I would recommend you to use the M4 on iMX7 for data acquisition and data handling. Please have a look at the Documentation here.

Best regards,
Jaski

HI @goofy

I just enabled SPI in the device tree and the SPI CS pin is working. And with that CS pin I could reduce the delay time.

That’s perfect.

Whether the speed can be compromised from 20kHz to 5kHz per channel to improve the delay between iterations?

You need to try out different clock speeds and different size of data to get the maximum data throughput.

Whether the variability can be reduced by making the process a real-time process by calling sched_setscheduler() with SCHED_FIFO?

I think, it depends which jitter are you expecting. The best would be to use free RTOS on M4. You can also give a try to use PREEMPT_RT kernel.

We have already made hardware schematic and major software code for Colibri iMX6. I know the SoM under Colibri family are pin compatible. But since iMX7 has a M4 processor in built, whether it’s pins are multiplexed with the SoM pins?

The standard SPI interface is on same pins for all the Colibri Modules. The software should also run on iMX7. The only thing is iMX7 does not feature a powerful GPU as iMX6. You need to assign the Pins/Interfaces either to M4 or the ARM Cpu.

How much modification we have to do if we choose to switch from iMX6 to iMX7

This depends on your application.

For programming the M4 processor, toradex suggest to use ARM DS-5 v5.24.0. Whether it is a free version or paid?

ARM DS is paid version, but you can use an IDE of your choice.

Can we program the M4 processor using any other free IDEs such as Eclipse?

Yes, you can use Eclipse, however Visual Studio Code is a good alternative.

Thank You for your time.

You are welcome.

Best regards,
Jaski