Timmo
Posts: 4
Joined: Mon Jul 23, 2012 9:03 pm

Need help using a SPI EEPROM

Tue Jul 24, 2012 10:57 pm

First of all hello to everybody as this is my first post!

I received my Raspberry Pi a few days a go and would like to have a play using the SPI bus. I have a AT25512 EEPROM to hand and have decided to make it work, the only problem is I don't know how....yet!

I work in electronics so have a good idea on the hardware side of things, however I have not done any proper programming for a long time and then not in C so am having a few difficulties and thought I'd ask for help!

Things I have done so far:
Updated the Raspberry Pi for SPI communication.
-updated using rpi-update to (amongst other things) add the spidev kernel driver;
-commented out the blacklist spi-bcm2708 entry in /etc/modprobe.d/raspi-blacklist.conf to load the spi-bcm2708 module on boot;
-added spi to /etc/modules to load the spidev module on boot;


and, connected the EEPROM to the GPIO header as follows.
>>AT25512 datasheet here<<
AT25512 pin 1 (CS) to GPIO pin 24 (GPIO8/SPI0_CE0_N)
AT25512 pin 2 (SO) to GPIO pin 21 (GPIO9/SPI0_MISO)
AT25512 pin 3 (WP) to be pulled high
AT25512 pin 4 (GND) to GPIO pin 6 (GND)
AT25512 pin 5 (SI) to GPIO pin 19 (GPIO10/SPI0_MOSI)
AT25512 pin 6 (SCK) to GPIO pin 23 (GPIO11/SPI0_SCLK)
AT25512 pin 7 (HOLD) to be pulled high
AT25512 pin 8 (VCC) to GPIO pin 1 (3V3)


I only found a few examples of code to help me but have started from what seemed to be a comprehensive one and trying to relate it to the other examples I found. Obviously it's not anywhere near useful but how much of the spi communications is done for me and what is the method for the messages being sent to and from the device?

Code: Select all

/*
 * SPI testing utility (using spidev driver)
 *
 * Copyright (c) 2007  MontaVista Software, Inc.
 * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
 */

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

//definitions for AT25512 device
#define WRITE_CYCLE_TIME (0x05)  /* AT25512 write cycle time in ms*/
#define WRSR (0x01)  /* AT25512 write status register*/
#define WRITE (0x02) /* AT25512 write data to memory array*/
#define READ (0x03)  /* AT25512 read data from memory array*/
#define WRDI (0x04)  /* AT25512 reset write enable latch*/
#define RDSR (0x05)  /* AT25512 read status register*/
#define WREN (0x06)  /* AT25512 set write enable latch*/

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))


static void pabort(const char *s)
{
	perror(s);
	abort();
}

static const char *device = "/dev/spidev0.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;		//need to be speeded up when working
static uint16_t delay = 5; 			//changed to AT25512 write cycle time

static void transfer(int fd)
{
	int ret;
	//create array of data to be sent
	uint8_t tx[] = {
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
		0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
		0xF0, 0x0D,
	};
	
	//create an equivalent array of data to be received
	uint8_t rx[ARRAY_SIZE(tx)] = {0, };
	
	//setup spi transfer data structure
	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = ARRAY_SIZE(tx),
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
	};

	//spi check if sent
	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1)
		pabort("can't send spi message");

	//prints returned array on the screen
	for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
		if (!(ret % 6))
			puts("");
		printf("%.2X ", rx[ret]);
	}
	puts("");
}

/*added by me */
static void read_eeprom_byte(uint16_t address, uint8_t data)
{
/*
pull cs low
send read operation code - 0000 X011 'read data from memory array'
send 16 bit address to be read from
get 8 bit data from buffer (no page select required, keep clocking keep getting! (memory wraps around))
drive cs high
*/
}
static void write_eeprom_byte(uint16_t address, uint8_t data)
{
  //is the memory protected in RDSR?
  CS_ENABLE();				//pull cs low
  wr_spi(WREN);				//send write enable operation code
  wr_spi(WRITE);			//send write operation code
  wr_spi(addresshigh);		//send first 8 bits of address to write to
  wr_spi(addresslow);		//send second 8 bits of address to write to
  wr_spi(data);  			//send 8 bit data to device
  /*
  usleep(WRITE_CYCLE_TIME);
  wr_spi(WRITE);			//send read status register operation code
  get 8 bit data from buffer
  Loop
    usleep(WRITE_CYCLE_TIME);
    send RDSR operation code - 0000 X101 'read status register'
    get 8 bit data from buffer
  until bit 0 is low then data write is complete
  */
  CS_DISABLE();				//drive cs high
  //note: internal reset of the write enable latch is automatic
}

static void write_eeprom_page(uint16_t address, uint8_t data)
{
/* is the memory write protected in RDSR?
pull cs low
note: only lowest 7 bits of address are incremented will cause issues if not starting at the beginning of a memory page
send write enable operation code - 0000 X110 'set write enable latch'
send write operation code - 0000 X010 'write data to memory array'
send 16 bit address to write to
Loop
  send 8 bit data to device
  send RDSR operation code - 0000 X101 'read status register'
  get 8 bit data from buffer if bit 0 is low then data write is complete
until max 128 times depending on start address
drive cs high
note: internal reset of the write enable latch is automatic
*/
}
/*added by me */

static void print_usage(const char *prog)
{
	printf("Usage: %s [-DsbdlHOLC3]\n", prog);
	puts("  -D --device   device to use (default /dev/spidev0.0)\n"
	     "  -s --speed    max speed (Hz)\n"
	     "  -d --delay    delay (usec)\n"
	     "  -b --bpw      bits per word \n"
	     "  -l --loop     loopback\n"
	     "  -H --cpha     clock phase\n"
	     "  -O --cpol     clock polarity\n"
	     "  -L --lsb      least significant bit first\n"
	     "  -C --cs-high  chip select active high\n"
	     "  -3 --3wire    SI/SO signals shared\n"
	     "  -N --no-cs    No chip select\n"
	     "  -R --ready    SI/SO signals shared\n");
	exit(1);
}

static void parse_opts(int argc, char *argv[])
{
	while (1) {
		static const struct option lopts[] = {
			{ "device",  1, 0, 'D' },
			{ "speed",   1, 0, 's' },
			{ "delay",   1, 0, 'd' },
			{ "bpw",     1, 0, 'b' },
			{ "loop",    0, 0, 'l' },
			{ "cpha",    0, 0, 'H' },
			{ "cpol",    0, 0, 'O' },
			{ "lsb",     0, 0, 'L' },
			{ "cs-high", 0, 0, 'C' },
			{ "3wire",   0, 0, '3' },
			{ "no-cs",   0, 0, 'N' },
			{ "ready",   0, 0, 'R' },
			{ NULL, 0, 0, 0 },
		};
		int c;

		c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);

		if (c == -1)
			break;

		switch (c) {
		case 'D':
			device = optarg;
			break;
		case 's':
			speed = atoi(optarg);
			break;
		case 'd':
			delay = atoi(optarg);
			break;
		case 'b':
			bits = atoi(optarg);
			break;
		case 'l':
			mode |= SPI_LOOP;
			break;
		case 'H':
			mode |= SPI_CPHA;
			break;
		case 'O':
			mode |= SPI_CPOL;
			break;
		case 'L':
			mode |= SPI_LSB_FIRST;
			break;
		case 'C':
			mode |= SPI_CS_HIGH;
			break;
		case '3':
			mode |= SPI_3WIRE;
			break;
		case 'N':
			mode |= SPI_NO_CS;
			break;
		case 'R':
			mode |= SPI_READY;
			break;
		default:
			print_usage(argv[0]);
			break;
		}
	}
}

int main(int argc, char *argv[])
{
	int ret = 0;
	int fd;

	parse_opts(argc, argv);

	/* 
	fd = open(device, O_RDWR);
	if (fd < 0)
		pabort("can't open device");

	/* spi write mode */
	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
	if (ret == -1)
		pabort("can't set spi mode");
		
    /* spi read mode */
	ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
	if (ret == -1)
		pabort("can't get spi mode");

	/* spi write bits per word */
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't set bits per word");

	/* spi read bits per word */
	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
	if (ret == -1)
		pabort("can't get bits per word");

	/* spi max write speed hz */
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't set max speed hz");

	/* spi max read speed */	
	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		pabort("can't get max speed hz");

	/* output successful settings to terminal */
	printf("spi mode: %d\n", mode);
	printf("bits per word: %d\n", bits);
	printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

	transfer(fd);

	close(fd);

	return ret;
}

User avatar
Gert van Loo
Posts: 2487
Joined: Tue Aug 02, 2011 7:27 am
Contact: Website

Re: Need help using a SPI EEPROM

Tue Jul 24, 2012 11:24 pm

You can NOT supply power or ground from GPIO pins. Ground must connect to the real ground and 3V3 must connect to the real 3V3.

eggert
Posts: 1
Joined: Wed Jul 25, 2012 8:47 am

Re: Need help using a SPI EEPROM

Wed Jul 25, 2012 8:55 am

That brings me to two questions:

1. Why not? , in the wiki is written: "The maximum permitted current draw from the 3.3 V pin is 50 mA." . The eeprom takes 10mA in worst case, if i read the data sheet correctly.

2. Is there an other good point on the board to get 3.3v supply voltage?

greetings

Thorsten

User avatar
Gert van Loo
Posts: 2487
Joined: Tue Aug 02, 2011 7:27 am
Contact: Website

Re: Need help using a SPI EEPROM

Wed Jul 25, 2012 6:34 pm

It seems we have a misunderstanding.
With GPIO you mean the connector. When you said GPIO I thought you had connected the VCC to a GPIO output pin. (Same for the ground)

Timmo
Posts: 4
Joined: Mon Jul 23, 2012 9:03 pm

Re: Need help using a SPI EEPROM

Wed Jul 25, 2012 8:22 pm

Yes , sorry I should have made that clearer, I meant the GPIO header (P1) I used for power!
I'll edit my first post to make it clearer.

bhensley
Posts: 7
Joined: Tue Jun 26, 2012 7:18 pm

Re: Need help using a SPI EEPROM

Thu Jul 26, 2012 1:32 am

For those that might be struggling with getting SPI working in any shape or form. I've written a nice little blog explaining how to get SPI working and tested on your Raspberry Pi. Check it out.

http://www.brianhensley.net/2012/07/get ... ry-pi.html


Cheers,

Brian Hensley
www.brianhensley.net

waltermixxx
Posts: 34
Joined: Wed May 09, 2012 7:21 pm
Location: Woodbridge, Ontario

Re: Need help using a SPI EEPROM

Thu Jul 26, 2012 3:34 pm

Hi Brian, just wondering, on your blog you mention using a particular build of weezy, the Debian Public Beta, dated from June, is this the only version your instructions will work on? I tried using the Raspian new version, and after I followed the instructions, ( and corrected my typo) no errors, but still no SPI-DEV files in the \dev\ folder...

wiped my sd card and trying again :) incase I mucked something else up ... :)

ok wiped and started from scratch with latest Raspian image, and still no go.
i will download the version from your post and see how that goes :)

cheers :)
Electronics for fun...
Raspberry Pi-1 Occidentalist, Tenda 311m x 2
Raspberry Pi-2 Raspbian Wheezy 07-15-12, Netgear N150 x1
Genius 8000 slimline wireless keyboard and mouse both work great with the usb wifi dongle right in the raspberry pi.

Timmo
Posts: 4
Joined: Mon Jul 23, 2012 9:03 pm

Re: Need help using a SPI EEPROM

Thu Jul 26, 2012 6:02 pm

Hi waltermixxx,
Are the kernel modules you need currently loaded?
What is the output of:

Code: Select all

dmesg | grep spi
you are looking for the bcm2708_spi module being loaded, if it's not you can try making sure they are loaded at boot as I mentioned above (you will need to reboot!)
-commented out the blacklist spi-bcm2708 entry in /etc/modprobe.d/raspi-blacklist.conf to load the spi-bcm2708 module on boot;
-added spidev to /etc/modules to load the spidev module on boot;

waltermixxx
Posts: 34
Joined: Wed May 09, 2012 7:21 pm
Location: Woodbridge, Ontario

Re: Need help using a SPI EEPROM

Thu Jul 26, 2012 11:07 pm

I commented out the spi reference in /etc/modprobe.d/raspi-blacklist.conf file mentioned and now the spi module appears to load:

bcm2708_spi.0: SPI Controller at 0x20204000 (irq 80) is what I get now... :)

so am I good to go now?

also i commented out the I2C line as well to enable that... just incase :)

thanks for the hint :) cheers :)

now then, lets see what i can do with them :)
Electronics for fun...
Raspberry Pi-1 Occidentalist, Tenda 311m x 2
Raspberry Pi-2 Raspbian Wheezy 07-15-12, Netgear N150 x1
Genius 8000 slimline wireless keyboard and mouse both work great with the usb wifi dongle right in the raspberry pi.

User avatar
engineerbynight
Posts: 3
Joined: Fri Jul 27, 2012 2:32 am

Re: Need help using a SPI EEPROM

Fri Jul 27, 2012 2:37 am

I've written up a tutorial using the Raspbian image.
http://www.instructables.com/id/Prelimi ... g-with-a-/

My tutorial uses an accelerometer, but if you look at the examples in the bcm2835 SPI library, you'll see some more generic examples to work from.

I tried going the route that others have gone with the spidev stuff, but I didn't have any luck with it on Raspbian.

Feedback is greatly appreciated. I've been playing with my Pi for only a few days, and I certainly have some learning to do with regards to the Linux environment.

kryogenic
Posts: 1
Joined: Sun Jul 29, 2012 6:19 am

Re: Need help using a SPI EEPROM

Sun Jul 29, 2012 6:23 am

Hi Timmo, you've got one detail in the AT25512 wrong...you're waiting in your psuedo code after issuing WRITE, without disabling chip select. The AT25512 writes data only after de-asserting the CS, after the issuing a WRITE.

Timmo
Posts: 4
Joined: Mon Jul 23, 2012 9:03 pm

Re: Need help using a SPI EEPROM

Sat Aug 04, 2012 12:00 pm

Finally got some time to look further into this (and learn a little C in the process).

Thanks for your input.
I used Brian's tutorial to prove the spi driver was installed and working properly, it also lead me to using an old hard drive jumper link to short out the serial out and in pins for debugging;
I then used engineerbynight's tutorial to learn to write a program to use the EEPROM as I found it easier to follow what was going on (kryogenic's comment got me to check what I was doing with the EEPROM and made me realise I'd not released the chip select after setting the write enable);

However I wanted to use spidev (as it is preinstalled and the gcc command is easier to remember!) so I did a little more research and found this blog by Mobile Mechanic which helped me on my way.

I've got the following to work writing and reading a byte, not sure how good the coding really is an I'm still learning!

Code: Select all

/*
 * AT25 SPI EEPROM (using spidev driver)
 */

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

//definitions for AT25512 device
#define WRITE_CYCLE_TIME (5000)							//AT25512 write cycle time in us
#define WRSR (0x01)										//AT25512 write status register
#define WRITE (0x02)									//AT25512 write data to memory array
#define READ (0x03)										//AT25512 read data from memory array
#define WRDI (0x04)										//AT25512 reset write enable latch
#define RDSR (0x05)										//AT25512 read status register
#define WREN (0x06)										//AT25512 set write enable latch

static void pabort(const char *s)
{
	perror(s);
	abort();
}

static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 10000000;						//need to be speeded up when working
static uint16_t delay = 5; 								//changed to AT25512 write cycle time

static void eepromwritebyte(int fd)
{
	int ret;
	int address;
    char addresslow, addresshigh;
	
    address = 0x0000;
	
    addresslow = (address&255);							// set addresslow to the lower 8 bits of the address
    addresshigh = (address>>8)&255;						// set addresshigh to the upper 8 bits of the address
	
	char writeenable[1] = {WREN, };
	char writecommand[3] = {WRITE, addresshigh, addresslow, };
	char data[] = {0x12, };

	struct spi_ioc_transfer message[3] = {0, };			//setup spi transfer data structure
	
	message[0].tx_buf = (unsigned long)writeenable;		//send the write enable command 
	message[0].rx_buf = (unsigned long)NULL;
	message[0].len = sizeof(writeenable);
	message[0].cs_change = 1;							//chip select needs to be released

	message[1].tx_buf = (unsigned long)writecommand;	//send the write command and address
	message[1].rx_buf = (unsigned long)NULL;
	message[1].len = sizeof(writecommand);
	message[1].cs_change = 0;							//keep holding chip select state
	
	message[2].tx_buf = (unsigned long)data;			//send the data
	message[2].rx_buf = (unsigned long)NULL;
	message[2].len = sizeof(data);
	message[2].cs_change = 1;							//release the chip select line

	ret = ioctl(fd, SPI_IOC_MESSAGE(3), &message);		//spi check if sent
	if (ret < 1)
		pabort("can't send spi message");
	
	usleep(5000);										//wait 5ms for write command to complete
}

static void eepromread(int fd)
{
	int ret;
	int address;
    char addresslow, addresshigh;
	
    address = 0x0000;
	
    addresslow = (address&255);							// set addresslow to the lower 8 bits of the address
    addresshigh = (address>>8)&255;						// set addresshigh to the upper 8 bits of the address
	
	char readcommand[3] = {READ, addresshigh, addresslow, };
		
	uint8_t rx[1] = {0, };								//create an array of data to be received

	struct spi_ioc_transfer message[2] = {0, };			//setup spi transfer data structure
	
	message[0].tx_buf = (unsigned long)readcommand;
	message[0].rx_buf = (unsigned long)NULL;
	message[0].len = sizeof(readcommand);
	message[0].cs_change = 0;

	message[1].tx_buf = (unsigned long)NULL;
	message[1].rx_buf = (unsigned long)rx;
	message[1].len = sizeof(rx);
	message[1].cs_change = 1;

	ret = ioctl(fd, SPI_IOC_MESSAGE(2), &message);		//spi check if sent
	if (ret < 1)
		pabort("can't send spi message");

	for (ret = 0; ret < sizeof(rx); ret++) {			//prints returned array on the screen
		if (!(ret % 32))
			puts("");
		printf("%.2X ", rx[ret]);
	}
	puts("");
	puts("");
}

int main(int argc, char *argv[])
{
	int ret = 0;
	int fd;

	fd = open("/dev/spidev0.0", O_RDWR);				//open the spi device
	if (fd < 0)
		pabort("can't open device");

	ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);			//set the spi mode
	if (ret == -1)
		pabort("can't set spi mode");
	
	ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);			//get the spi mode (test)
	if (ret == -1)
		pabort("can't get spi mode");

	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);	//set the spi bits per word
	if (ret == -1)
		pabort("can't set bits per word");

	ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);	//get the spi bits per word (test)
	if (ret == -1)
		pabort("can't get bits per word");

	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);	//set the spi max speed
	if (ret == -1)
		pabort("can't set max speed hz");

	ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);	//get the spi max speed (test)
	if (ret == -1)
		pabort("can't get max speed hz");

	puts("");
	printf("The spi mode is set to: %d\n", mode);		//output successful settings to the terminal
	printf("Bits per word: %d\n", bits);
	printf("Max speed is set to: %d Hz (%d KHz) (%d MHz)\n", speed, speed/1000, speed/1000000);

    eepromwritebyte(fd);
	eepromread(fd);
	close(fd);

	return ret;
}
Next I'm going to try to add page writing...

Return to “Interfacing (DSI, CSI, I2C, etc.)”