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;
}