SPI fail: Episode 2!


(click on the thumbnail above to start the video, for some stupid reason i forgot to turn the camera resolution down, so i got a little stutter, but hey, its in 2160p…)

It’s me again, and it’s another SPI fail…
This time i got round to setting up the SDK in a VM, whip’d out a few lines of code, and this is the result.
It as if, when using ioctl(which is needed for full-duplex transfers), the CS line wont go high between transfers, which causes it to write garbage all over the VRAM(it produces fairly intresting patterns when left running a few minutes).

What should happen:
Write string to 0x0000 using write (works)
Write string to 0x0040 using ioctl(works,but doesnt make CS go high after)
Write a counting up byte to 0x0100(doesn’t work due to CS not going high)

(Note about gameduino memory layout:
The first 4 kilobytes (0x0000 to 0x0fff are a 64x64 array of chars, of which (dy default) the top-left 50x37,5 are shown onscreen. So 0x0000 is the start of the first line, 0x0040 is the start of the second line, 0x0100 is the start of line 5. Also notice how in the video, it also overwrites the font, the font pallete and the contol registers)
Code:

//What a shiteload of includes(copied from some kernel.org example...)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

#ifndef POINTER_TYPE
#define POINTER_TYPE __u32
#endif

#define true (1)
#define false (0)

int gameduino;

char helloraw[] = "\x80\x00This is transferred through write()!";
char hello[] = "This is transferred through ioctl()!";
struct spi_ioc_transfer xfer[2];
unsigned char addrbuf[2] = {0,0};
char cntchar = 0;

inline int setaddr(unsigned short addr) {
	addrbuf[0] = (addr >> 8) & 0xff;
	addrbuf[1] = addr & 0xff;
}

int main(int argc, char **argv) {
	//simple test
	gameduino = open("/dev/gameduino",O_RDWR);
	if (gameduino < 0) { perror("Failed to open /dev/gameduino"); return EXIT_FAILURE;} //WUUUT?
	write(gameduino,helloraw,sizeof helloraw);
	close(gameduino);
	//ioctl test
	gameduino = open("/dev/gameduino",O_RDWR);
	if (gameduino < 0) { perror("Failed to open /dev/gameduino"); return EXIT_FAILURE;} //WUUUT?
	//prepare some stuff
	setaddr(0x8040);
	memset(xfer, 0, sizeof(xfer));
	xfer[0].tx_buf = (POINTER_TYPE)addrbuf;
	xfer[0].len = 2;
	xfer[0].cs_change = false;
	xfer[1].tx_buf = (POINTER_TYPE)hello;
	xfer[1].len = sizeof(hello);
	xfer[1].cs_change = true;
	xfer[1].delay_usecs = 1;
	printf("ioctl returned %X\n",ioctl(gameduino, SPI_IOC_MESSAGE(2), xfer));
	//counting char
	while (true) {
		cntchar = (cntchar + 1) & 0x7f;
		setaddr(0x8100);
		memset(xfer, 0, sizeof(xfer));
		xfer[0].tx_buf = (POINTER_TYPE)addrbuf;
		xfer[0].len = 2;
		xfer[0].cs_change = false;
		xfer[1].tx_buf = (POINTER_TYPE) &cntchar;
		xfer[1].len = 1;
		xfer[1].cs_change = true;
		xfer[1].delay_usecs = 1;
		ioctl(gameduino, SPI_IOC_MESSAGE(2), xfer);
		usleep(1000000/72);
	}
}

I found out! I added another spi_ioc_transfer of length 0 to the ioctl call, also the delay stuff wasn’t needed.

New code:

//What a shiteload of includes(copied from some kernel.org example...)
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/types.h>
#include <linux/spi/spidev.h>

#ifndef POINTER_TYPE
#define POINTER_TYPE __u32
#endif

#define true (1)
#define false (0)

int gameduino;

char helloraw[] = "\x80\x00This is transferred through write()!";
char hello[] = "This is transferred through ioctl()!";
struct spi_ioc_transfer xfer[3];
unsigned char addrbuf[2] = {0,0};
char cntchar = 0;

inline int setaddr(unsigned short addr) {
	addrbuf[0] = (addr >> 8) & 0xff;
	addrbuf[1] = addr & 0xff;
}

int main(int argc, char **argv) {
	//simple test
	gameduino = open("/dev/gameduino",O_RDWR);
	if (gameduino < 0) { perror("Failed to open /dev/gameduino"); return EXIT_FAILURE;} //WUUUT?
	write(gameduino,helloraw,sizeof helloraw);
	close(gameduino);
	//ioctl test
	gameduino = open("/dev/gameduino",O_RDWR);
	if (gameduino < 0) { perror("Failed to open /dev/gameduino"); return EXIT_FAILURE;} //WUUUT?
	//prepare some stuff
	setaddr(0x8040);
	memset(xfer, 0, sizeof(xfer));
	xfer[0].tx_buf = (POINTER_TYPE)addrbuf;
	xfer[0].len = 2;
	xfer[0].cs_change = false;
	xfer[1].tx_buf = (POINTER_TYPE)hello;
	xfer[1].len = sizeof(hello);
	xfer[1].cs_change = true;
	xfer[2].cs_change = true;
	printf("ioctl returned %X\n",ioctl(gameduino, SPI_IOC_MESSAGE(3), xfer));
	//counting char
	while (true) {
		cntchar = (cntchar + 1) & 0x7f;
		setaddr(0x8100);
		memset(xfer, 0, sizeof(xfer));
		xfer[0].tx_buf = (POINTER_TYPE)addrbuf;
		xfer[0].len = 2;
		xfer[0].cs_change = false;
		xfer[1].tx_buf = (POINTER_TYPE) &cntchar;
		xfer[1].len = 1;
		xfer[1].cs_change = true;
		xfer[2].cs_change = true;
		ioctl(gameduino, SPI_IOC_MESSAGE(3), xfer);
		usleep(1000000/72);
	}
}