Adjust SPI Frequency in IMX7D

Dear Community,
I am using spidev2.0 on Colibri iMX7D. My Application works on 20Mhz. I want to control my SPI output frequency to 20Mhz. I am unable to adjust my frequency. Kindly provide details.

hi @narayanamohan

Could you provide the version of software of your module?
How did you try to change the frequency? Could you provide a snippet of code you are using for the SPI Transfer?

Thanks and best regards, Jaski

Dear jaski, Kindly find the code snippet attached. I have flashed Embedded Linux 2.7 Stable Version using TORADEX Easy installer

File 1:

/***************************************************************************/
int main(int argc, char **argv)
{
  //  int opt;
    spiData *data = malloc(sizeof(spiData));

 
    data->mode =0;
    data->bits = 8;
    data->speed = 23000000;           
    data->delay = 0;
    argv[1]="/dev/spidev2.0";
    sprintf(data->device, "%s", argv[1]);


    if(spiInit(data))
    {
	perror("SPI Init failed");
	exit(-1);
    }


    writeDac(data);

    free(data);
    close(data->fileDescriptor);

    return 0;             
}

File 2:

int spiInit(spiData *data){
	int retVal = 0;

	/* open file */
	data->fileDescriptor = open(data->device, O_RDWR);
	if(data->fileDescriptor < 0){
		perror("Can't open device");
		return retVal;
	}

	/* set SPI Mode */
	retVal = ioctl(data->fileDescriptor, SPI_IOC_WR_MODE, &(data->mode));   //set mode via I/O-Control,ioctrl(fileDescriptor, Action, *value)
	if(retVal < 0){								//if device cannot set mode
		perror("Can't set spi mode");
		return retVal;
	}

	/* check if SPI Mode was set successfully */
	retVal = ioctl(data->fileDescriptor, SPI_IOC_RD_MODE, &(data->mode));
	if(retVal < 0){
		perror("Can't get spi mode");
		return retVal;
	}

	/* set Bits per word */
	retVal = ioctl(data->fileDescriptor, SPI_IOC_WR_BITS_PER_WORD, &(data->bits));
	if(retVal < 0){
		perror("Can't set bits per word");
		return retVal;
	}

	/* check if Bits per word was set successfully */
	retVal = ioctl(data->fileDescriptor, SPI_IOC_RD_BITS_PER_WORD, &(data->bits));
	if(retVal < 0){
		perror("Can't get bits per word");
		return retVal;
	}

	/* set max speed */
	retVal = ioctl(data->fileDescriptor, SPI_IOC_WR_MAX_SPEED_HZ, &(data->speed));
	if (retVal < 0){
		perror("Can't set max speed hz");
		return retVal;
	}

	/* check if max speed was set successfully */
	retVal = ioctl(data->fileDescriptor, SPI_IOC_RD_MAX_SPEED_HZ, &(data->speed));
	if(retVal < 0){
		perror("Can't get max speed hz");
		return retVal;
	}
//#ifdef DEBUG
	/* print the set configurations */
	printf("SPI mode: %d\n", data->mode);
	printf("Bits per word: %d\n", data->bits);
	printf("Max speed: %d Hz (%d MHz)\n", data->speed, data->speed/1000000);
//#endif /* DEBUG */
	return 0;
}

/******************************************************************************/
int spiTransfer(spiData *data){
	int ret = 0;

	/* Prepare */
	struct spi_ioc_transfer tr =                       
	{
		.tx_buf = (unsigned long)data->tx,
		.rx_buf = (unsigned long)data->rx,
		.len = ARRAY_SIZE(data->tx),
		.delay_usecs = data->delay,
		.speed_hz = data->speed,
		.bits_per_word = data->bits,
		.cs_change=0,
	};
	
	/* Send data */
	ret = ioctl(data->fileDescriptor, SPI_IOC_MESSAGE(1), &tr);

	
	return ret;
}

Hi @narayanamohan

Thank you for the snippet.
What kind of error do you get or what is not working as expected? Did you change the used device tree in order to have the spidev2.0 ready? SPI (Linux) What does ls /dev/ show? Is there a spidev2.0?

What do you want to connect? Is it maybe possible that the connected device can’t handle 20 MHz? Do you have a datasheet? Did you check what kind of settings are needed to have a proper connection?

Thanks and best regards, Diego

Hi Diego,
The device is SPI ready. I have already compiled and enabled spi through device tree and
Upon giving ls /dev/ | grep spi command spidev 2.0 is showing.
I am using Digital to Analog Converter(DAC) and the frequency output is not varying.
My application is running on 20Mhz. Control Signals from Toradex spidev2.0 are converted from Serial to parallel and sent to DAC.
I have rechecked the connections and no problem with the connection.

How do we match the frequency. We have tried using the program and unable to obtain Analog output. How do we obtain 20Mhz signal at toradex spi pins.

Hi @narayanamohan

  1. What kind of DAC do you have? Can you send me a datasheet?
  2. Did you make some measurements on the spi output? Can you share these measurements?
  3. In the code snippet you shared with us, you configured 23 MHz on the SPI clock. Are you aware of that?

Thanks and best regards, Diego

Dear Diego,
I am using DAC0800 link text
While using PIC Microntroller 16F877A and connecting with DAC0800. I am able to obtain output from DAC. alt text

But while using Toradex with Shift Register 74HC595link text I am not able to obtain output at DAC.

I did not make any measurements from spi output.

Yes I am aware of the frequency set as 23 Mhz. Because I tried frequency range from 15 to 23Mhz. I had also tried 20Mhz.

Hi @narayanamohan

Thank you for your drawing. It seems that the connection between the Toradex module and the 74HC595 doesn’t work properly. Could you please

  1. Give us the exact scheme how you connected each pin between the Toradex module and the 74HC595? This is very important.
  2. Make a measurement of the signals on these pins and provide it to us?
  3. Provide us the function writeDac(spiData *data) you called in the first code snippet on line 23?

Thank you very much. While doing so you could also have a look at this article, which explains the functionality of the 74HC595 very well. Indeed it is very important to send the data in the proper way. What is a serial-to-parallel device?

Thank you and best regards, Diego

Dear Diego, Thank you for the help.

  1. Connection between toradex and shift register with DAC0800 alt text
  2. I am able to measure voltage from output of shift register pins 2.5 Volts. What kind of measurement do you suggest on toradex spi pins.
  3. Kindly find the snippet

.

int writeDac(spiData *data)
{
//  uint16_t ret;
    uint16_t loopCount =10000;
    uint16_t count = 0;


//  uint8_t sineWave1[1] = {85};

    uint8_t sineWave[256] ={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,
	0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,
	0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,
	0x5F,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
	0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA0,
	0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,0xC0,0xC1,
	0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,
	0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF};


    while(loopCount > 0)

//while(1)
    {

	for (count=0; count <256; count=count+1)
	{

	    data->tx[0] = sineWave[count];
	    printf("%X \n  ",data->tx[0]);
	    spiTransfer(data);
        usleep(13);
//	    data->tx[0]=0x00;
//	    spiTransfer(data);
//	    usleep(13);
//	    printf("%X \n  ",data->tx[0]);
//	    sleep(3);
	}
    loopCount--;
}
data->tx[0]=0x00;
spiTransfer(data);
return 0;
}

Hi @narayanamohan

Thank you for the response. The pins you have connected yet seems good. But you have to connect more than that. Output Enable OE and Shift Register Clear SRCLR need to be handled as well. You may can connect OE to ground and SRCLR to VCC directly. If you connect them directly, you can work with the RCLK pin to enable the sent bits to the output (see the arduino example below).

Second thing is, you have to change your method of sending the data. You should send bit by bit, and not a full byte at once. Because everytime you make a data transaction, the bits already sent will be shifted internally to the next register in the 74HC595. Like this, you are able to set the full byte by one by one bit. I found an article of an arduino tutorial which may helps you: https://www.arduino.cc/en/Tutorial/ShftOut13 While trying to understand the code, also keep open the logic diagram of the datasheet on page 12. http://www.ti.com/lit/ds/symlink/sn74hc595.pdf There you see that you need to send each bit one by one and how the shift of the bits is working.

Please try this. If it’s still not working, you can still measure the clock-, data- and CS-pin of the Toradex board with the KO and share results with us.

Thank you and best regards, Diego

Dear Diego,
Thanks for the help.Your suggestion on sending data bit by bit seems to work. The DAC output has improved but still working to achieve expected output.
Also referred your Arduino tutorial . It is very helpful.

  1. Regarding the hardware is it ok to connect 5V from Toradex to shift register. To both “VCC” and “SRCLR” of shift register. Because all SPI pins operate at 3.3V.

  2. How to handle the spi clk in program for data transfer. The code below is a snippet from Arduino code provided by you in the link.

    //for each bit in the byte myDataOut
    //NOTICE THAT WE ARE COUNTING DOWN in our for loop
    //This means that %00000001 or “1” will go through such
    //that it will be pin Q0 that lights.
    for (i=7; i>=0; i–) {
    digitalWrite(myClockPin, 0);

How do i implement this in my code.
Kindly help.

Dear Diego,
Kindly provide your answers for above mentioned make it quick. How do we use clock and synchronize our data with it.
How to switch on and off the clock function in spi.

Dear @narayanamohan

  1. Could you please explain what is not working as expected? Because working with the SPI output of the iMX7 to generate a sinus-wave is not that precise. The Linux is not realtime and you will never get a proper sinus wave exactly.
  2. You better connect VCC and SRCLR to 3.3V as well. With this, you prevent to have imprecise values on the SPI.
  3. We tried the code in our office and did some measurements with the KO. The code worked and we were able to measure 8 bits in one chip select sequence. Please find the code below.

.

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define FILE_DESCRIPTOR "/dev/spidev2.0"
#define VERBOSE 0

int main(int argc, char *argv[])
{
  int ret = 0;
  int fd;
  uint8_t tx;
  uint8_t rx;
  uint32_t data = 0;
  uint8_t verbose = VERBOSE;

  const char *device = FILE_DESCRIPTOR;
  uint32_t mode = 0;
  uint8_t bits = 8;
  uint32_t speed = 20000000;
  uint16_t delay = 0;
  uint8_t len_msg = sizeof(uint8_t); 
 
  struct spi_ioc_transfer tr = {
       .tx_buf = (unsigned long)&tx,
       .rx_buf = (unsigned long)&rx,
       .len = len_msg,
       .delay_usecs = delay,
       .speed_hz = speed,
       .bits_per_word = bits,
  };
  
  printf("Message length = %d\n", len_msg);
  fd = open(device, O_RDWR);
  if (fd < 0)
       printf("can't open device");
 
  /*
  * spi mode
  */
  ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
  if (ret == -1)
    printf("can't set spi mode");
 
  ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
  if (ret == -1)
    printf("can't get spi mode");
 
  /*
  * bits per word
  */
  ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
  if (ret == -1)
    printf("can't set bits per word");
 
  ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
  if (ret == -1)
    printf("can't get bits per word");
 
  /*
  * max speed hz
  */
  ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
  if (ret == -1)
    printf("can't set max speed hz");
 
  ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
  if (ret == -1)
    printf("can't get max speed hz");
 
    printf("spi mode: 0x%x\n", mode);
    printf("bits per word: %d\n", bits);
    printf("max speed: %d Hz (%d MHz)\n", speed, speed/1000000);
 
  tx  = 0xFF;
 
  while(1) {
    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 1) {
         printf("can't send spi message");
     }
 
    if (verbose) {
      data = (uint32_t) (0x0000FFFF & rx);
      data |= (uint32_t) (0xFFFF0000uL & (rx << 16));
      printf("RX = 0x%08x\n", data);
    }
  }
 
  close(fd);
 
  return ret;
}

Dear Diego,

We are developing cij printer application.
Example Link https://www.youtube.com/watch?v=-WB1aelMLJM

For the control of drop deflection i am using pic microcontroller 16f877A (20Mhz) and able to produce perfect drop deflection at hex value of 0x3C, 0x46… and so on.

But want use latest technology with Toradex as our processor.
This control signal is going to DAC0800 which is sending the analog signal to Nozzle deflector plate.

While using Toradex with shift register for serial data using SPI. Drop is not deflected as expected. When connecting to led circuit exact value is appearing (our hex value is exact).

the following problems are noticed

  1. Improper Frequency
  2. Clock On/Off for data transfer.

I am linking successful print of pic controller program link text

Kindly provide any solution.

Dear @narayanamohan

Did you manage to run the code like we described it? Do you have a proper signal on the SPI?
Do you have the possibility to check the signal on the SPI with a KO? Please follow the instructions we suggested with the code and the connection to the 3.3V pins.

The Linux operating system is not ideal to control something time sensitive and I think you can’t achieve the accuracy you want to. But you have an iMX7 with an integrated Cortex-M4, why don’t you try this one with the RTOS? In one of our developer articles we dive into this topic: Real-Time Linux and the best result has the M4 with RTOS on it.

Thank you and best regards, Diego

how to use chip select pin and clock pin in spi …?

  struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)&tx,
        .rx_buf = (unsigned long)&rx,
        .len = len_msg,
        .delay_usecs = delay,
        .speed_hz = speed,
        .bits_per_word = bits, 

In the above structure not use clock pin and chip select pin …?

Dear @narayanamohan

You are able to configure the chip select in the device tree. For this, see the device tree file imx7-colibri.dtsi

/* Colibri SPI */
&ecspi3 {
        fsl,spi-num-chipselects = <1>;
        cs-gpios = <&gpio4 11 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_ecspi3 &pinctrl_ecspi3_cs>;
};

But you already have one chip select pin in your default configuration, described in this article: https://developer.toradex.com/knowledge-base/spi-(linux).
The configuration of the clock is made in your code-snippet on line 6. You actually already made that properly.

Thank you and best regards, Diego

ok …But how to handle clock and chip select in spi code …?

digitalWrite(latchPin, 0);

shiftOut(dataPin, clockPin, data);

digitalWrite(latchPin, 1);

}
}

void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {

int i=0;
int pinState;

pinMode(myClockPin, OUTPUT);

pinMode(myDataPin, OUTPUT);

digitalWrite(myDataPin, 0);

digitalWrite(myClockPin, 0);

for (i=7; i>=0; i–) {
digitalWrite(myClockPin, 0);

if ( myDataOut & (1<<i) ) {
  pinState= 1;
}
else { 
  pinState= 0;
}


digitalWrite(myDataPin, pinState);

digitalWrite(myClockPin, 1);

digitalWrite(myDataPin, 0);

}

digitalWrite(myClockPin, 0);

}

This code provided was an example from Arduino to just give you an Idea, how you should send the data to the Shift Register . For the Toradex Module sending data out to the Shift register is handled by the SPI Driver, so you don’t have to to anything for clock and chip select.

Further the only thing which will help to solve you issue, is to measure the signals on the Pins SER (14), SRCLK (12) and RCLK (11) with an logic analyzer or oscilloscope. Could you please do it and share the results with us.