Jump to content

[Orange Pi] spidev timing issue beetwen CS and CLK


Recommended Posts

Hello,

I work on a gateway built around an orange PI 2. The orange PI has to manage 3 spi devices (an RTC, a flash and a radio).

So I have enabled the spidev module on the orange PI and I use two overlay to get 3 /dev/spidev1.x (x=/dev/spidev1.,1 and2) devices with 3 distinct CS.

 

See my the armbianEnv.txt file:

# cat /boot/armbianEnv.txt
verbosity=1
logo=disabled
console=both
disp_mode=1920x1080p60
overlay_prefix=sun8i-h3
overlays=i2c0 pwm spi-spidev usbhost2 usbhost3
rootdev=UUID=64a7c5d3-32a7-4eaf-9e69-a0dd046580ae
rootfstype=ext4
param_spidev_spi_bus=1
param_spidev_max_freq=100000000
user_overlays= spi1-triple-spidev-cs sun8i-h3-spi1-add-cs1-cs2
usbstoragequirks=0x2537:0x1066:u,0x2537:0x1068:u

 

See dts file to get 3 spi devices:

# cat spi1-triple-spidev-cs.dts
/dts-v1/;
/plugin/;

/ {
	compatible = "allwinner,sun4i-a10", "allwinner,sun7i-a20", "allwinner,sun8i-h3", "allwinner,sun50i-a64", "allwinner,sun50i-h5";

	fragment@0 {
		target = <&spi1>;
		__overlay__ {
			#address-cells = <1>;
			#size-cells = <0>;
			status = "okay";

			spidev@0 {
				reg = <0>; /* Chip Select 0 */
				compatible = "spidev";
				spi-max-frequency = <1000000>;
				status = "okay";
			};

			spidev@1 {
				reg = <1>; /* Chip Select 1 */
				compatible = "spidev";
				spi-max-frequency = <1000000>;
				status = "okay";
			};

                        spidev@2 {
                                reg = <2>; /* Chip Select 2 */
                                compatible = "spidev";
                                spi-max-frequency = <1000000>;
                                status = "okay";
                        };
		};
	};
};

 

See dts file to configure CS for spidev1.1 and spidev1.2 (spidev1.0 has the native CS)

# cat sun8i-h3-spi1-add-cs1-cs2.dts
/dts-v1/;
/plugin/;

/ {
	compatible = "allwinner,sun8i-h3";

	fragment@0 {
		target = <&pio>;
		__overlay__ {
			spi1_cs1: spi1_cs1 {
				pins = "PG6";
				function = "gpio_out";
				output-high;
			};
		};
	};

        fragment@1 {
                target = <&pio>;
                __overlay__ {
                        spi1_cs2: spi1_cs2 {
                                pins = "PA19";
                                function = "gpio_out";
                                output-high;
                        };
                };
        };

	fragment@2 {
		target = <&spi1>;
		__overlay__ {
			pinctrl-names = "default", "default", "default";
			pinctrl-1 = <&spi1_cs1>;
			pinctrl-2 = <&spi1_cs2>;
			cs-gpios = <0>, <&pio 6 6 0>, <&pio 0 19 1>;
		};
	};
};

 

So the spi driver (in mode 0) works fine for the flash and the RTC, but there is an issue with the radio (on /dev/spidev1.1 with the CS on pin PG6 and active low) because there is a falling edge active before the first rising edge of the clock (see following picture):

CS1_comment.thumb.jpg.10770b35627f91fd555fe52eca566bff.jpg

 

See what is expected by the spec of the radio:

image.png.79f1678274a536063d6aa08adb97b223.png

 

 

Now I have to find a solution to fix this issue but I not sure to get it rapidly ...

But may be some people have ever faced this issue as the radio Nordic 905 is probably not the only one chip touchy to this kind of timings between the CS and the CLK.

So any help is welcome !

 

Best Regards,

Damien

Link to comment
Share on other sites

Armbian & Khadas are rewarding contributors

Hello,

I have some news to explain the issue ...

The spi access done in the previous message is done with the spidev tool : https://github.com/rm-hull/spidev-test/blob/master/spidev_test.c

When you launch a command with this tool, the spi mode is always re-configure and so we always get the unexpected clock falling edge leading to command executed with a clock shift for some spi components.

However, I have implemented the following type of code to initialize in a first time the file descriptor use to communicate the the spi device, and the use write primitive to perform two write and read access consecutively:

	// Take care I have just sump up the type of code I use to explain the issue 
	FILE *fd = open("/dev/spidev1.1", O_RDWR);
	uint8_t mode = SPI_MODE_0;
	uint8_t bits = 8;
	uint32_t speed = 500000;
	unsigned char payload_w1[2] = {0x00, 0x55};
	unsigned char payload_r[2] = {0x10, 0x00};
	
	// init file descriptor
	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);

	// spi access
	write(fd,payload_w1,2); // write 0x55 (issue : in fact 0x54 is written instead of 0x55 due to the the unexpected CLK falling edge)
	write(fd,payload_r,2); 	// Read 0x54
	payload_w1[1] = 0x77; // change write value
	write(fd,payload_w1,2); // write 0x77
	write(fd,payload_r,2); 	// read 0x77
	exit(0);

 

With the logic analyzer, we can see that the unexpected CLK falling edge is only present during the first spi access with this type of code implementation:

TwoAccess.thumb.JPG.9f18ef57b124f659afbcac9a538b20ce.JPG

 

Conclusion:

It seems that the unexpected CLK falling edge is only present during the first access (when the spi mode is configured).

So I have to find the good way to implement the spi driver to avoid this issue that can appear with some SPI components.

 

Link to comment
Share on other sites

Hello !

I thought to have found a work around by doing a first access to by pass "the unexpected timing", but time to time it does not work !

See code of the work around tested:

void spi_transfer(enum Chip chip, unsigned char* txBuffer, size_t len, bool with_rx)
{
	int ret;
	int fd;
	uint8_t bits;
	uint32_t speed;
	uint8_t mode;
	unsigned short delay;
	char * tx = NULL;
	char * rx = NULL;
	char hex_dump_tx_prefix[9] = {0};
	char hex_dump_rx_prefix[9] = {0};

	switch(chip){
		case RADIO:
			fd = st_spi.fd_radio;
			speed = st_spi.speed_radio;
			mode = st_spi.mode_radio;
			bits = st_spi.bits_radio;
			delay = st_spi.delay_radio;
			strcpy(hex_dump_tx_prefix,"tx_radio");
			strcpy(hex_dump_rx_prefix,"rx_radio");
		break;
		case RTC:
			fd = st_spi.fd_rtc;
			speed = st_spi.speed_rtc;
			mode = st_spi.mode_rtc;
			bits = st_spi.bits_rtc;
			delay = st_spi.delay_rtc;
			strcpy(hex_dump_tx_prefix,"tx___rtc");
			strcpy(hex_dump_rx_prefix,"rx___rtc");
		break;
		case FLASH:
			fd = st_spi.fd_flash;
			speed = st_spi.speed_flash;
			mode = st_spi.mode_flash;
			bits = st_spi.bits_flash;
			delay = st_spi.delay_flash;
			strcpy(hex_dump_tx_prefix,"tx_flash");
			strcpy(hex_dump_rx_prefix,"rx_flash");
		break;
		default:
			printf("Unexpected chip value !\n");
			exit_with_trace(EXIT_STATUS_SPI_ERROR, ulTraceModule, __FUNCTION__, __FILE__, __LINE__);
		break;
	}

	tx = malloc(len);
	check_malloc(tx, ulTraceModule, __FILE__, __LINE__);
	rx = malloc(len);
	check_malloc(rx, ulTraceModule, __FILE__, __LINE__);

	memcpy (tx, txBuffer, len);

	unsigned char payload_wa_radio[2] = {0x20, 0x00};
	struct spi_ioc_transfer tr_wa_radio = {
		.tx_buf = (unsigned long)payload_wa_radio,
		.rx_buf = (unsigned long)0,
		.len = 2,
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};

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

	if (mode & SPI_TX_QUAD) {
		tr.tx_nbits = 4;
	}
	else if (mode & SPI_TX_DUAL) {
		tr.tx_nbits = 2;
	}
	if (mode & SPI_RX_QUAD) {
		tr.rx_nbits = 4;
	}
	else if (mode & SPI_RX_DUAL) {
		tr.rx_nbits = 2;
	}
	if (!(mode & SPI_LOOP)) {
		if (mode & (SPI_TX_QUAD | SPI_TX_DUAL)) {
			tr.rx_buf = 0;
		}
		else if (mode & (SPI_RX_QUAD | SPI_RX_DUAL)) {
			tr.tx_buf = 0;
		}
	}

#if DBG_SPI
	hex_dump(tx, len, 32, hex_dump_tx_prefix);
#endif
	//toolMutexLock(&MutexSPI);
	if (chip == RADIO) {
		// This part of code is a workaround for the radio because the radio spi is touchy to the clock timing of
		// the first spi access as the mode is set during the first access.
		// The workaround consists in doing a first access without impact on the radio configuration to bypass the
		// issue, and then a second access to really execute the spi command.
		// For the first access, we send command "0x00, 0x20" which is interpreted by the radio "0x00, 0x10" due to the
		// the clock timing. So we only do a read access at the address 0 of the config register without impact on the radio configuration.
		// Let's see task #30190 for more details.
		ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr_wa_radio);
		if (ret < 1) {
			printf("can't send spi message\n");
			exit_with_trace(EXIT_STATUS_SPI_ERROR, ulTraceModule, __FUNCTION__, __FILE__, __LINE__);
		}
	}
	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1) {
		printf("can't send spi message\n");
		exit_with_trace(EXIT_STATUS_SPI_ERROR, ulTraceModule, __FUNCTION__, __FILE__, __LINE__);
	}
	//toolMutexUnlock(&MutexSPI);

	if (with_rx == true)
		memcpy (txBuffer, rx, len);

#if DBG_SPI
	hex_dump(rx, len, 32, hex_dump_rx_prefix);
#endif

	free(rx);
	free(tx);
}

 

See capture bellow doing a read access in loop on the radio spi. Time to time, there is also an expected CLK timing on the second access.

still_ko.thumb.jpg.f0a0cae95d0b43df320d70a1a71cb232.jpg

 

Conclusion:

Doing a first access to bypass the unexpected timing is not enough. I have to add something else or find another solution ...

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...

Important Information

Terms of Use - Privacy Policy - Guidelines