Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to improve the speed of spi communication? #33

Closed
Hubuwenxin opened this issue Jul 9, 2024 · 10 comments
Closed

how to improve the speed of spi communication? #33

Hubuwenxin opened this issue Jul 9, 2024 · 10 comments
Assignees

Comments

@Hubuwenxin
Copy link

I ran the "spi_loop.c " to get data from ad7606, the frequency is 3khz. But i need more than 20khz, Is there other method to improve the spi communication speed?

here is my modified code of SPI_loop.c:
//gcc -Wall -o spi_loop spi_loop.c -ljetgpio
//sudo ./spi_loop
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <jetgpio.h>
#include <time.h>

struct timespec start, end;
double elapsed_time;
void ADC7606_Reset()
{
gpioWrite(16, 1);
gpioWrite(16, 0);
}

void Convert_Volt(uint16_t* input, float* output, int inputSize) {
const float lsb_voltage = 5.0f / 32768.0f;

for (int i = 0; i < inputSize; i++) {
    int16_t signedInput = (int16_t)input[i];
    if (input[i] > 32767) {
        signedInput = (int16_t)(input[i] - 65536);
    }
    output[i] = signedInput * lsb_voltage;
}

}

int main(int argc, char *argv[])
{
int Init;
int SPI_init;
int SPI_stat;
uint16_t tx[4] = {0,};
uint16_t rx[4] = {0,};
float voltages[4] = {0};

Init = gpioInitialise();
if (Init < 0)
{
    printf("Jetgpio initialisation failed. Error code: %d\n", Init);
    return EXIT_FAILURE;
}
else
{
    printf("Jetgpio initialisation OK. Return code: %d\n", Init);
}

gpioSetMode(15, JET_OUTPUT); 
gpioSetMode(16, JET_OUTPUT);  
gpioSetMode(18, JET_INPUT); 

SPI_init = spiOpen(0, 50000000, 0, 0, 16, 0, 1); 
if (SPI_init < 0)
{
    printf("Port SPI1 opening failed. Error code: %d\n", SPI_init);
    return EXIT_FAILURE;
}
else
{
    printf("Port SPI1 opened OK. Return code: %d\n", SPI_init);
}

ADC7606_Reset();

while (1)
{
    clock_gettime(CLOCK_MONOTONIC, &start);
    gpioWrite(15, 1);
    gpioWrite(15, 0);
    while(gpioRead(18) == 0)
    {}
    gpioWrite(24, 0);
    SPI_stat = spiXfer(SPI_init, (char*)tx, (char*)rx, 8 * sizeof(uint16_t));
    if (SPI_stat < 0)
    {
        printf("SPI port transfer failed. Error code: %d\n", SPI_stat);
        break;  
    }
    gpioWrite(24, 1);
    clock_gettime(CLOCK_MONOTONIC, &end);

    elapsed_time = (end.tv_sec - start.tv_sec) * 1e9;
    elapsed_time = (elapsed_time + (end.tv_nsec - start.tv_nsec)) * 1e-9;

    printf("Elapsed time: %f seconds\n", elapsed_time);

    printf("Data received:\n");
    for (int j = 0; j < 4; j++) { 
        printf("tx%d:%x --> rx%d:%x\n", j+1, tx[j], j+1, rx[j]);
    }

    Convert_Volt(rx, voltages, 4); 

    for (int i = 0; i < 4; i++) {
        printf("Channel %d: %f V\n", i+1, voltages[i]);
    }

    memset(rx, 0, sizeof(rx));
}   

spiClose(SPI_init);
gpioTerminate();
return EXIT_SUCCESS;

}

@SalmanKhaja
Copy link

From your code, I can see that you have configured SPI at 50MHz

SPI_init = spiOpen(0, 50000000, 0, 0, 16, 0, 1);

I think, you are confused about the sampling rate of your ADC with SPI speed.

@Rubberazer
Copy link
Owner

Rubberazer commented Jul 9, 2024

Cheers @SalmanKhaja, that's quite right.
OK @Hubuwenxin I will try to help a bit but remember that whatever I say is based only on the datasheet.

So the timing diagrams are:

timing_general

and for SPI:

timing spi

So looking at then we would need the following pins (aside from SPI):

  • RESET (you are using pin 16)
  • CS (looks like you are using 24)-> you don't need to do: gpioWrite(24, X) , you can remove both lines, the SPI function will take care of the CS pin, pin 24 is CS0 on the AGX, you still can use another pin but in that case you have to do a: gpioSetMode(XX, JET_OUTPUT) previously
  • CONVST A(you are using pin 15)
  • BUSY(you are using pin 18 as input)

You are waiting for BUSY I guess? -> while(gpioRead(18) == 0) {}
I see something weird here, BUSY is 1 during conversion but you are waiting while 0? it should be:
while(gpioRead(18)){} ?

You could try to change that? You could also could remove that while() altogether and just read all the time? is more or less what you are doing now. Also, I would remove the printfs() as well, dump the values on a file for 10 s or something and measure the time at the beginning and at the end and calculate the samples/ second there.

@Hubuwenxin
Copy link
Author

From your code, I can see that you have configured SPI at 50MHz

SPI_init = spiOpen(0, 50000000, 0, 0, 16, 0, 1);

I think, you are confused about the sampling rate of your ADC with SPI speed.


reply: yes, actually the max frequency of ADC is more than 20MHz, and agx orin is 50Mhz, but when using spi to transmit the data from adc to agx orin is very slow, just 3khz. And i tested adc with MCU(stm32) up to 40khz, but connect MCU to agx orin is also slow.
So i think maybe the agx part decrease the transmit frequency.

@Hubuwenxin
Copy link
Author

Cheers @SalmanKhaja, that's quite right. OK @Hubuwenxin I will try to help a bit but remember that whatever I say is based only on the datasheet.

So the timing diagrams are:

timing_general

and for SPI:

timing spi

So looking at then we would need the following pins (aside from SPI):

  • RESET (you are using pin 16)
  • CS (looks like you are using 24)-> you don't need to do: gpioWrite(24, X) , you can remove both lines, the SPI function will take care of the CS pin, pin 24 is CS0 on the AGX, you still can use another pin but in that case you have to do a: gpioSetMode(XX, JET_OUTPUT) previously
  • CONVST A(you are using pin 15)
  • BUSY(you are using pin 18 as input)

You are waiting for BUSY I guess? -> while(gpioRead(18) == 0) {} I see something weird here, BUSY is 1 during conversion but you are waiting while 0? it should be: while(gpioRead(18)){} ?

You could try to change that? You could also could remove that while() altogether and just read all the time? is more or less what you are doing now. Also, I would remove the printfs() as well, dump the values on a file for 10 s or something and measure the time at the beginning and at the end and calculate the samples/ second there.


I follow your suggestion and it help improve the speed, but still far from 20KHz.

@Rubberazer
Copy link
Owner

@Hubuwenxin

You are right, I tried on the Orin AGX and the same result with Orin Nano, the number of samples/transfers per seconds is just above 3000, really low. I tried in the old Nano and I am getting over 23000...

I really don't know the reason for this one, but there is some problem going on for the Orin family and the SPI I don't have a solution for this at this time.

@Rubberazer
Copy link
Owner

Rubberazer commented Jul 13, 2024

For documentation purposes only, the SPI driver is introducing a massive transfer delay for all Jetpacks from 5.1.2, this affects the whole Orin family, driver is the same. I verified this, latest 6 doesn't seem to solve the problem. Haven't tried to go back to 5.1.1 but that's a potential solution(https://developer.nvidia.com/embedded/jetpack-sdk-511)

(One) Reference: https://forums.developer.nvidia.com/t/spi-ioc-message-ioctl-for-spi-extremely-slow/295460

@Rubberazer Rubberazer self-assigned this Jul 13, 2024
@Rubberazer Rubberazer pinned this issue Jul 13, 2024
@Hubuwenxin
Copy link
Author

@Hubuwenxin

You are right, I tried on the Orin AGX and the same result with Orin Nano, the number of samples/transfers per seconds is just above 3000, really low. I tried in the old Nano and I am getting over 23000...

I really don't know the reason for this one, but there is some problem going on for the Orin family and the SPI I don't have a solution for this at this time.

I tried the connect the ad7606 with agx by parallel mode , but the problem is that 16 gpio pins of the agx orin don't change. I tested every pins, and many pins can get 0 and 1 as input. so i not sure that is the agx orin problem or else?

@Rubberazer
Copy link
Owner

@Hubuwenxin can you show me your code here please?

@Hubuwenxin
Copy link
Author

here is the parallel code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <jetgpio.h>

// Giving meaningful names to pins in the Orin
#define CS_RD 37
#define CONVST_AB 7
#define BUSY 38
#define RESET 36
#define DB0 10
#define DB1 11
#define DB2 12
#define DB3 13
#define DB4 15
#define DB5 16
#define DB6 18
#define DB7 19
#define DB8 21
#define DB9 22
#define DB10 23
#define DB11 24
#define DB12 26
#define DB13 29
#define DB14 31
#define DB15 32

#define BILLION 1000000000L
#define NUM_CHANNELS 4

uint64_t diff;
struct timespec start, end;

void ADC7606_Reset() {
gpioWrite(RESET, 1);
usleep(10); // Ensure the reset pulse is long enough
gpioWrite(RESET, 0);
}

void Convert_Volt(const uint16_t* input, float* output, int inputSize, float xferFactor) {
for (int i = 0; i < inputSize; i++) {
int16_t signedInput = (int16_t)input[i];
if (input[i] > 32767) {
signedInput = (int16_t)(input[i] - 65536);
}
output[i] = signedInput * xferFactor;
}
}

int main(void) {
uint16_t Vx[NUM_CHANNELS] = {0};
//int pin_level[16] = {0};
float voltages[NUM_CHANNELS] = {0};
int Init = 0;
const float inputRange = 5.0f; // Adjust based on your ADC configuration
const float xferFactor = inputRange / 32768.0f;
double elapsed_seconds = 0; // Changed to double

// List of AGX data pins DB0 to DB15
unsigned pin_number[16] = {DB0, DB1, DB2, DB3, DB4, DB5, DB6, DB7, DB8, DB9, DB10, DB11, DB12, DB13, DB14, DB15};
Init = gpioInitialise();
if (Init < 0)
{
    /* jetgpio initialisation failed */
    printf("Jetgpio initialisation failed. Error code: %d\n", Init);
    exit(Init);
}
else
{
    /* jetgpio initialised okay */
    printf("Jetgpio initialisation OK. Return code: %d\n", Init);
}

// Setting BUSY and data pins as inputs
for (unsigned i = 0; i < 16; i++) {
    gpioSetMode(pin_number[i], JET_INPUT);
}
gpioSetMode(BUSY, JET_INPUT);

// Setting CS/RD and CONVSTA & B as outputs
gpioSetMode(CS_RD, JET_OUTPUT);
gpioSetMode(CONVST_AB, JET_OUTPUT);
gpioSetMode(RESET, JET_OUTPUT);
gpioWrite(CS_RD, 1);
gpioWrite(CONVST_AB, 1);
gpioWrite(RESET, 0);

ADC7606_Reset();

while (1) {
    // Measure how long it takes in nanoseconds
    clock_gettime(CLOCK_MONOTONIC, &start);

    for (int channel = 0; channel < NUM_CHANNELS; channel++) {
        // Start conversion
        gpioWrite(CONVST_AB, 0);
        gpioWrite(CONVST_AB, 1);

        while (gpioRead(BUSY)) {
            // Wait for conversion to complete
        }

        // Enable data output
        gpioWrite(CS_RD, 0);

        // Read all the data input pins
        Vx[channel] = 0;
        for (unsigned i = 8; i < 16; i++) {
            int pin_value = gpioRead(pin_number[i]);
            Vx[channel] |= (pin_value << (i - 8 + 8)); // Read the higher bits first
        }

        gpioWrite(CS_RD, 1);
    }

    clock_gettime(CLOCK_MONOTONIC, &end);
    diff = (end.tv_sec * BILLION + end.tv_nsec) - (start.tv_sec * BILLION + start.tv_nsec);
    elapsed_seconds = (double)diff / BILLION;

    Convert_Volt(Vx, voltages, NUM_CHANNELS, xferFactor);

    for (int channel = 0; channel < NUM_CHANNELS; channel++) {
        printf("Channel %d Raw ADC Value: %d\n", channel, Vx[channel]);
        printf("Channel %d Converted Voltage: %.5f V\n", channel, voltages[channel]);
    }

    printf("Elapsed time = %.9f seconds\n", elapsed_seconds);

    usleep(1000); // Optional: add a delay to reduce the read frequency
}

gpioTerminate();

return 0;

}

@Rubberazer
Copy link
Owner

It is looking OK to me, the only thing I would make sure is:

-Set the AD7606 pin PAR/SER/BYTE SEL to GND
-Power the AD7606 from the +5 & GND on the Orin
-Also the pins OS0, OS1 & OS2 to GND just in case

It should work, it you see the stuff on the oscilloscope then it has to show up in the Orin.

@Rubberazer Rubberazer closed this as not planned Won't fix, can't repro, duplicate, stale Aug 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants