#include #include #include #include #include #include #include #include #include struct at25080a_status_register { char rdy : 1; // Inversed char wen : 1; char bp : 2; // Level of block protect - 0 = none, 1 = upper 1/4 of the device, 2 = half of the device, 3 = all of the device char reserved : 3; char wpen : 1; }; enum CMD { CMD_WREN, // No data - Set Write Enable Latch CMD_WRDI, // No data - Reset Write Enable Latch CMD_RDSR, // Read status register - takes struct from above CMD_WRSR, // Write status register - takes struct from above CMD_READ, // Read data from memory array - arg is 16-bit address CMD_WRITE, // Write data to memory array - arg is 16-bit address }; /// Performs a command against the at25080a spi based EEPROM. Helper functions below use this for their actual work int write_at25080a(int fd, enum CMD cmd, char *data, int size, int arg) { int i, n; struct spi_ioc_transfer trans[2]; char buf[3]; memset(&trans, 0, sizeof(trans)); trans[0].tx_buf = (unsigned long)buf; trans[0].rx_buf = (unsigned long)NULL; trans[0].len = 1; for(i=0;i<2;++i) { trans[i].speed_hz = 10000; trans[i].bits_per_word = 0; trans[i].delay_usecs = 0; trans[i].cs_change = 0; } switch(cmd) { case CMD_WREN: buf[0] = 0x06; break; case CMD_WRDI: buf[0] = 0x04; break; case CMD_RDSR: buf[0] = 0x05; break; case CMD_WRSR: buf[0] = 0x01; break; case CMD_READ: buf[0] = 0x03; break; case CMD_WRITE: buf[0] = 0x02; break; default: break; } if(cmd == CMD_READ || cmd == CMD_WRITE) { trans[0].len = 3; buf[1] = (arg >> 8) & 0xff; // A15 - A8 buf[2] = arg & 0xff; // A7 - A0 } switch(cmd) { case CMD_WREN: case CMD_WRDI: break; case CMD_RDSR: case CMD_READ: trans[1].tx_buf = (unsigned long)NULL; trans[1].rx_buf = (unsigned long)data; trans[1].len = size; break; case CMD_WRSR: case CMD_WRITE: trans[1].tx_buf = (unsigned long)data; trans[1].rx_buf = (unsigned long)NULL; trans[1].len = size; break; default: break; } if(cmd == CMD_WREN || cmd == CMD_WRDI) { n = 1; } else { n = 2; } trans[n - 1].cs_change = 1; return ioctl(fd, SPI_IOC_MESSAGE(n), trans); } int get_status(int fd, struct at25080a_status_register *status) { return write_at25080a(fd, CMD_RDSR, (char *)status, 1, 0); } int write_enable(int fd) { return write_at25080a(fd, CMD_WREN, NULL, 0, 0); } int write_disable(int fd) { return write_at25080a(fd, CMD_WRDI, NULL, 0, 0); } int write_test(int fd, int address, char *msg, int size) { return write_at25080a(fd, CMD_WRITE, msg, size, address); } int read_test(int fd, int address, char *msg, int size) { return write_at25080a(fd, CMD_READ, msg, size, address); } #define SPI_MSB 0 #define SPI_LSB 1 int initialize_spi(const char *dev, char mode, char msb, char bpw, __u32 max_speed) { char fd; fd = open(dev, O_RDWR); if(fd < 0) { perror("open"); return -1; } if(ioctl(fd, SPI_IOC_WR_MODE, &mode)) { perror("SPI_IOC_WR_MODE"); return -1; } if(ioctl(fd, SPI_IOC_WR_LSB_FIRST, &msb)) { perror("SPI_IOC_WR_LSB_FIRST"); return -1; } if(ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bpw)) { perror("SPI_IOC_WR_BITS_PER_WORD"); return -1; } if(ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &max_speed)) { perror("SPI_IOC_WR_MAX_SPEED_HZ"); return -1; } return fd; } /// Prints a string in a hexdump format void print_hex(char *buf, int size) { int i, j; for(i=0;i= 0x20 && buf[j] <= 0x7f) { printf("%c", buf[j]); } else { printf("."); } } printf("|"); printf("\n"); } } /// Performs a spi test on the given file descriptor. Reads from address 0, writes "Hello world!" to address 0, reads back address 0 to ensure write committed and compares. void run_test(int fd) { int size; char buf[100]; printf("Sending write enable\n"); write_enable(fd); printf("Done sending write enable\n"); { struct at25080a_status_register reg; get_status(fd, ®); printf("Status is: 0x%02hhx\n", (*((char *)®) & 0xff)); } size = snprintf(buf, 100, "Invalid data \n"); read_test(fd, 0, buf, size); buf[size] = 0; print_hex(buf, size); size = snprintf(buf, 100, "Hello world!"); printf("Writing \"%s\"\n", buf); write_test(fd, 0, buf, size); snprintf(buf, 100, "Invalid data \n"); // Give the EEPROM enough time to commit usleep(5000); read_test(fd, 0, buf, size); buf[size] = 0; print_hex(buf, size); write_disable(fd); if(strcmp(buf, "Hello world!") == 0) { printf("SPI EEPROM Read/Write test successfull!\n"); } else { printf("SPI EEPROM Read/Write failed.\n"); } } int main(int argc, char **argv) { const char *dev = "/dev/spidev3.0"; int fd, opt; while ((opt = getopt(argc, argv, "01")) != -1) { switch(opt) { case '0': dev = "/dev/spidev3.0"; break; case '1': dev = "/dev/spidev3.1"; break; default: fprintf(stderr, "%s: Pass -0 for McSPI3.CS0 test, or -1 for McSPI3.CS1 test\n", argv[0]); exit(-1); } } if(optind < argc) { printf("Opening \"%s\" for SPI communication\n", argv[optind]); fd = initialize_spi(argv[optind], SPI_MODE_0, SPI_MSB, 8, 500000); } else { printf("Opening \"%s\" for SPI communication\n", dev); fd = initialize_spi(dev, SPI_MODE_0, SPI_MSB, 8, 500000); } if(fd < 0) { fprintf(stderr, "initialize_spi failed\n"); return -1; } { struct at25080a_status_register reg; get_status(fd, ®); printf("Status is: 0x%02hhx\n", (*((char *)®) & 0xff)); } run_test(fd); close(fd); return 0; }