Interfacing PIC with SPI memory

From Mech
Revision as of 15:13, 18 November 2009 by Thomas Peterson (talk | contribs)
Jump to navigationJump to search

Overview

Sometimes, it can be useful to interface the PIC with an external memory for data logging or other reasons, since there is limited memory available on the PIC itself. There are various types of memory which a PIC can communicate with. One common type of memory is EEPROM memory, which is non-volatile, so it keeps its data when power is turned off. One type of EEPROM memory uses SPI to communicate with the PIC. This article discusses connecting the 25AA1024, a one megabit EEPROM, to a PIC 18F4520.

Notes on the 25AA1024

The 25AA1024 has one megabit (125 kbytes) of memory organized in pages of 256 bytes each. Like other EEPROMS, it has a page buffer which stores data as it is received, and then writes the buffer all at once to the EEPROM memory. Writing takes a long time compared to the speed of data transfer- the SPI line can run up to 20 Mhz, but a write cycle is up to 6 ms. Because only one page can be written every 6 ms, the PIC cannot necessarily continuously send data, unless the rate is less than 256 bytes per 6 ms. When sending data, if the address reaches a page boundary the data will "wrap around" in the page buffer. For instance, if the start address is 250, and a page boundary is at 256, then the seventh byte sent will be actually stored in address 0, not 257. Because of this, pages must be written when a page boundary is met and a write cycle needs to be restarted at an address in the next page. Other than the 3-wire serial interface, the chip also has an enable pin that must be low during write and read operations. Because of this, multiple 25AA1024s could be used, with each CE leading to a different pin on the PIC, so that many 25AA1024s could be used by the same PIC.

Circuit

Code

Notes...

Data logging (writing)

//Memtest.c
//Thomas Peterson
//LIMS
//11-05-09
#include <18F4520.h>
#FUSES NOWDT, HS, NODEBUG, NOPROTECT
#use delay(clock=40000000)

#include <string.h>

//SPI registers
#byte MCU_SSPSTAT = 0xFC7
#bit    MCU_CKE = MCU_SSPSTAT.6
#bit    MCU_SMP = MCU_SSPSTAT.7
#byte MCU_SSPCON1 = 0xFC6
#bit    MCU_CKP = MCU_SSPCON1.4
#bit    MCU_SSPEN = MCU_SSPCON1.5
#bit    MCU_SSMP0 = MCU_SSPCON1.0
#bit    MCU_SSMP1 = MCU_SSPCON1.1
#bit    MCU_SSPM2 = MCU_SSPCON1.2
#bit    MCU_SSPM3 = MCU_SSPCON1.3

//Setup communications
#use spi(STREAM=MEM_STREAM,MASTER,SAMPLE_RISE,BITS=8,FORCE_HW)
#use rs232(baud=1000000,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8,UART1,DISABLE_INTS)


#define MEM_SELECT PIN_A3
#define COMM_BYETS 16 //Number of bytes per packet

#define PAGESIZE 256

//function definitions
void spi_mem_read(int addmsb,int16 address, int16 num_bytes, char *buf);
int writing();
void spi_mem_write(int addmsb,int16 address,int num_bytes, char *data);

//mem isr
int8 READ = 0b00000011;    //Read data from memory array beginning at selected add
int8 WRITE = 0b00000010;   //Write data to memory array beginning at selected address
int8 WREN = 0b00000110;    //set the write enable latch (enable write)
//int8 WRDI = 0b00000100;    //reset the write enable latch (disable write)
int8 RDSR = 0b00000101;    //read status register
//int8 WRSR = 0b00000001;    //write status register
//int8 PE = 0b01000010;      //page erase
//int8 SE = 0b11011000;      //sector erase
int8 CE = 0b11000111;      //chip erase
//int8 RDID = 0b10101011;    //relase from deep power down mode, read sig
//int8 DPD = 0b10111001;     //deep power down mode


int32 current_address = 0;


/* Checks to see if the chip is currently writing to memory */
int writing() {
   int status;
   output_low(MEM_SELECT);
   spi_write(RDSR);
   status = spi_read(0);
   output_high(MEM_SELECT);
   return (status & 0x1);
}

/* Reads sequential bytes in memory, starting at address.
   Bytes are stored into *buf, read until num_bytes is reached */
void spi_mem_read(int addmsb,int16 address, int16 num_bytes, char *buf) {
   char *bufptr;
   output_low(MEM_SELECT); //active low
   spi_write(READ); //Start read cycle
   spi_write(addmsb);
   spi_write(address>>8);
   spi_write(address);
   for (bufptr = buf; bufptr<buf+num_bytes; bufptr++) {
      *bufptr = spi_read(0);
   }
   output_high(MEM_SELECT);
}

/* Writes sequential data starting at ADDRESS 
   Note: Caller must ensure page boundary is not passed! 
   Page size is 256. */
void spi_mem_write(int addmsb,int16 address,int num_bytes, char *data) {
   char *bufptr;
   while(writing()) {
      delay_us(1); //Wait for last write to finish
   }
   output_low(MEM_SELECT); //active low
   spi_write(WREN); //write enable latch
   output_high(MEM_SELECT); //latch
   delay_us(5);
   output_low(MEM_SELECT);
   spi_write(WRITE); //Send write command
   spi_write(addmsb);
   spi_write(address>>8);
   spi_write(address);
   for (bufptr=data; bufptr<data+num_bytes; bufptr++) {
      spi_write(*bufptr); //send this byte
   }
   output_high(MEM_SELECT); //Ends write operation, begins write to memory (5ms)
}

/* Erases all memory */
void chip_erase() {
   output_low(MEM_SELECT); //active low
   spi_write(WREN); //write enable latch
   output_high(MEM_SELECT); //latch
   delay_us(1);
   output_low(MEM_SELECT);
   spi_write(CE);
   output_high(MEM_SELECT);
}


void main()
{
   char rcvmsg[30];
   int i;
   
   output_low(PIN_D2);
   output_low(PIN_D3);
   output_high(MEM_SELECT);
   i = input(PIN_D1);

   //Setup the SPI
   //Note: setup_spi() may work, but it wouldn't for me
   MCU_SMP=1;
   MCU_CKE=1;
   MCU_CKP=0;
   MCU_SSPEN=1;
   MCU_SSMP0 = 0;
   MCU_SSMP1 = 0;
   MCU_SSPM2 = 0;
   MCU_SSPM3 = 0;
   

   while(1) {
      //Gets a line from the rs232 line, puts it in memory
      gets_n(rcvmsg); //get the data
      //Note: the following assumes current_address will NOT write past page
      //      boundary!
      spi_mem_write(current_address>>16,current_address,COMM_BYTES, rcvmsg);
      //While writing, setup next address
      current_address += COMM_BYTES;
      if ( (PAGESIZE-(current_address%PAGESIZE)) < COMM_BYTES ) {
         //The next operation will cross page boundary, should move to next page
         current_address = current_address - (current_address%PAGESIZE) + PAGESIZE;
      }
   }
   
   
   delay_ms(10000);
}