|
|
The Home of Electronic Engineering and Embedded Systems Programming |
MicrocontrollersProjects & TutorialsSoftwareHardwareOther Resources |
Xicor SPI Serial Memory - Source Code: XICOR25.C
/*
*********************************************************************************************************
* Module : XICOR25.C
* Author : Randy Rasa
* Description: Xicor X25xxxx Serial Non-Volatile Memory Interface Routines
*
* This module consists of functions to communicate with any of the Xicor non-volatile memory devices
* which use the SPI interface (X25-series). It should work with the following devices:
*
* X25F008/016/032/064/128 .... SerialFlash (1K/2K/4K/8K/16K)
* X25080/160/320/640/128 ..... EEPROM (1K/2K/4K/8K/16K)
*
* The SerialFlash series of devices is available in various densities, ranging from 8K-bits
* (1K x 8) to 128K-bits (16K x 8). All devices share the same pinout and software interface.
* The devices communicate with the host via an SPI-compatible interface, consisting of the
* following signals:
*
* CS = Chip Select ....... Low = Selected, High = De-Selected
* SCLK = Serial Clock ...... May be clocked at up to 1MHz
* SI = Serial Data In .... Data on SI is latched in on the rising edge of SCLK
* SO = Serial Data Out ... Data on SO is clocked out on the falling edge of SCLK
* PP = Program Protect ... Low = Protected, High = Normal Operation
* HOLD = Hold .............. Can be used to "pause" a serial transmission
*
* The following instructions (and their opcodes) are available:
*
* PREN ...... 0x06 ... Enable Programming Operations
* PRDI ...... 0x04 ... Disable Programming Operations
* RDSR ...... 0x05 ... Read Status Register
* PRSR ...... 0x01 ... Program Status Register
* READ ...... 0x03 ... Read from memory array, beginning at selected address
* PROGRAM ... 0x02 ... Write to memory array, beginning at selected address (32 bytes)
*
* Note that the memory must be written to in 32-byte blocks (this is the sector size).
* Before writing, the PREN command must be issued. Also,
*
* PREN Command Sequence:
* 1. Pull CS low
* 2. Send 8-bit PREN instruction (MSB first)
* 3. Bring CS high to end sequence
*
* PRDI Command Sequence:
* 1. Pull CS low
* 2. Send 8-bit PRDI instruction (MSB first)
* 3. Bring CS high to end sequence
*
* RDSR Command Sequence:
* 1. Pull CS low
* 2. Send 8-bit RDSR instruction (MSB first)
* 3. Read 8-bit status register (MSB first)
* 4. Bring CS high to end sequence
*
* PRSR Command Sequence:
* 1. Send PREN command
* 2. Pull CS low
* 3. Send 8-bit PRSR instruction (MSB first)
* 4. Send 8-bit status register (MSB first)
* 5. Bring CS high to end sequence
* 6. Send PRDI command
*
* Read Sequence:
* 1. Pull CS low
* 2. Send 8-bit READ instruction (MSB first)
* 3. Send 16-bit address (MSB first)
* 4. Read 8-bit data (MSB first)
* 5. Data bytes can continue to be read as long as CS is held low
* 6. Bring CS high to end sequence
*
* Write Sequence:
* 1. Send PREN command
* 2. Pull CS low
* 3. Send 8-bit PROGRAM instruction (MSB first)
* 4. Send 16-bit address (MSB first)
* 5. Send 32 8-bit data bytes (MSB first)
* 6. Bring CS high to start programming cycle
* 7. Read status register until PIP bit (LSB) is "0"
* 8. Send PRDI command
*
* This code assumes that the signals are provided by microcontroller I/O pins configured
* as outputs. Macros are provided to simplify control of these signals. You may also use
* memory-mapped output ports such as a 74HC374, but you'll need to replace the macros with
* functions, and use a shadow register to store the state of all the output bits. You can
* also use an SPI port to simplify (and speed up) the data transfer.
*
* I have tested this code using an X25F032 and X25F128 connected to a PC parallel port.
* The "TARGET" symbol selects whether to use the printer port or microcontroller pins.
* To use microcontroller pins, you'll need to set up the macros within the "#if TARGET
* == TARGET_UC" section...
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* Specify Target
*********************************************************************************************************
*/
#define TARGET_PC 0 // target = PC printer port
#define TARGET_UC 1 // target = microcontroller
#define TARGET TARGET_PC // select the target
/*
*********************************************************************************************************
* Include Header Files
*********************************************************************************************************
*/
#include "xicor25.h"
#if TARGET == TARGET_PC
#include "prn_io.h"
#endif
/*
*********************************************************************************************************
* Constants
*********************************************************************************************************
*/
// SerialFlash Command Opcodes:
#define CMD_PRSR 0x01 // Program Status Register
#define CMD_PROGRAM 0x02 // Write to memory array
#define CMD_READ 0x03 // Read from memory array
#define CMD_PRDI 0x04 // Disable programming
#define CMD_RDSR 0x05 // Read Status Register
#define CMD_PREN 0x06 // Enable programming
// Status Register bits:
#define SR_PIP 0x01 // PIP = Programming In Progress
#define SR_PEL 0x02 // PEL = Program Enable Latch
#define SR_BL0 0x04 // BL0 = Block Lock 0 (LSB)
#define SR_BL1 0x08 // BL0 = Block Lock 1 (MSB)
#define SR_PPEN 0x80 // PPEN = Program-Protect-Enable
/*
*********************************************************************************************************
* Macros for Hardware Access
*********************************************************************************************************
*/
#if TARGET == TARGET_PC // if target is PC printer port,
#define CS_0() (PrnIO_Out0(0)) // "CS" is on output 0 (D0)
#define CS_1() (PrnIO_Out0(1))
#define SI_0() (PrnIO_Out1(0)) // "SI" is on output 1 (D1)
#define SI_1() (PrnIO_Out1(1))
#define SCLK_0() (PrnIO_Out2(0)) // "SCLK" is on output 0 (D2)
#define SCLK_1() (PrnIO_Out2(1))
#define SO_STATE() (PrnIO_In3()) // "SO" is on input 3 (Acknowledge)
#endif
#if TARGET == TARGET_UC // if target is microcontroller,
#define CS_PORT PORTC // "CS" is on PC1
#define CS_DDR DDRC
#define CS_BIT 0x01
#define CS_0() (CS_PORT &= ~CS_BIT)
#define CS_1() (CS_PORT |= CS_BIT)
#define SI_PORT PORTC // "SI" is on PC2
#define SI_DDR DDRC
#define SI_BIT 0x02
#define SI_0() (SI_PORT &= ~SI_BIT)
#define SI_1() (SI_PORT |= SI_BIT)
#define SCLK_PORT PORTC // "SCLK" is on PC3
#define SCLK_DDR DDRC
#define SCLK_BIT 0x04
#define SCLK_0() (SCLK_PORT &= ~SCLK_BIT)
#define SCLK_1() (SCLK_PORT |= SCLK_BIT)
#define SO_PORT PORTC // "SO" is on PC4
#define SO_DDR DDRC
#define SO_BIT 0x08
#define SO_STATE() (SO_PORT & SO_BIT)
#endif
/*
*********************************************************************************************************
* Private Data
*********************************************************************************************************
*/
// ... none ...
/*
*********************************************************************************************************
* Private Function Prototypes
*********************************************************************************************************
*/
static void X25_ByteSend (unsigned char data);
static unsigned char X25_ByteRead (void);
// ...................................... Public Functions ..............................................
/*
*********************************************************************************************************
* X25_Init()
*
* Description: Initialize module; must be called before any other functions in this module.
* Arguments : none
* Returns : none
*********************************************************************************************************
*/
void X25_Init (void)
{
#if TARGET == TARGET_PC // if target is PC printer port,
PrnIO_Init(); // intialize printer port
#endif
CS_1(); // start with CS = 1
SI_1(); // SI = 1
SCLK_0(); // SCLK = 0
#if TARGET == TARGET_UC // if target is microcontroller,
CS_DDR |= CS_BIT; // configure "CS" as output
SI_DDR |= SI_BIT; // configure "SI" as output
SCLK_DDR |= SCLK_BIT; // configure "SCLK" as output
SO_DDR &= ~SO_BIT; // configure "SO" as input
#endif
}
/*
*********************************************************************************************************
* X25_ReadByte()
*
* Description: Read a byte (8 bits) of memory from the serial memory.
* Arguments : address = address within serial memory (0x0000-0x3fff)
* Returns : data read from serial memory
*********************************************************************************************************
*/
unsigned char X25_ReadByte (unsigned int address)
{
unsigned char data;
CS_0(); // take CS low to begin
X25_ByteSend(CMD_READ); // send READ command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
data = X25_ByteRead(); // read byte
CS_1(); // take CS high to end
return data;
}
/*
*********************************************************************************************************
* X25_ReadWord()
*
* Description: Read a word (16 bits) of memory from the serial memory.
* Arguments : address = address within serial memory (0x0000-0x3ffe)
* Returns : data read from serial memory
* Note : You may have to change the order in which the bytes are
* written, depending whether this is running on a big-endian
* or little-endian machine ...
*********************************************************************************************************
*/
unsigned int X25_ReadWord (unsigned int address)
{
union {
unsigned char _8[2];
unsigned int _16;
} data;
CS_0(); // take CS low to begin
X25_ByteSend(CMD_READ); // send READ command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
data._8[0] = X25_ByteRead(); // read byte (upper 8 bits)
data._8[1] = X25_ByteRead(); // read byte (lower 8 bits)
CS_1(); // take CS high to end
return data._16;
}
/*
*********************************************************************************************************
* X25_ReadLong()
*
* Description: Read a long (32 bits) of memory from the serial memory.
* Arguments : address = address within serial memory (0x0000-0x3ffc)
* Returns : data read from serial memory
* Note : If you're using a compiler that does not support 32-bit
* long data, remove this function!
* Note : You may have to change the order in which the bytes are
* written, depending whether this is running on a big-endian
* or little-endian machine ...
*********************************************************************************************************
*/
unsigned long X25_ReadLong (unsigned int address)
{
union {
unsigned char _8[4];
unsigned long _32;
} data;
CS_0(); // take CS low to begin
X25_ByteSend(CMD_READ); // send READ command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
data._8[0] = X25_ByteRead(); // read byte (upper 8 bits)
data._8[1] = X25_ByteRead(); // read byte
data._8[2] = X25_ByteRead(); // read byte
data._8[3] = X25_ByteRead(); // read byte (lower 8 bits)
CS_1(); // take CS high to end
return data._32;
}
/*
*********************************************************************************************************
* X25_ReadData()
*
* Description: Read an arbitrary number of bytes of memory from the serial memory.
* Arguments : address = address within serial memory (0x0000-0x3fe0)
* dest = address of memory where block will be stored
* numbytes = number of bytes to read
* Returns : none
*********************************************************************************************************
*/
void X25_ReadData (unsigned int address, unsigned char *dest, int numbytes)
{
int i;
CS_0(); // take CS low to begin
X25_ByteSend(CMD_READ); // send READ command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
for (i=0; i<numbytes; i++) {
*dest++ = X25_ByteRead(); // read byte, save to destination
}
CS_1(); // take CS high to end
}
/*
*********************************************************************************************************
* X25_WriteData()
*
* Description: Write an arbitrary number of bytes from RAM to serial memory.
* Arguments : address = address within serial memory (0x0000-0x3fe0)
* source = address of memory where block to be written is stored
* numbytes = number of bytes to write
* Returns : none
* Note : This routine will not work with SerialFlash devices, but will work with
* other non-volatile memory such as EEPROMs ...
*********************************************************************************************************
*/
void X25_WriteData (unsigned int address, unsigned char *source, int numbytes)
{
int i;
X25_PREN(); // enable programming
CS_0(); // take CS low to begin
X25_ByteSend(CMD_PROGRAM); // send PROGRAM command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
for (i=0; i<numbytes; i++) {
X25_ByteSend(*source++); // read byte from buffer, send
}
CS_1(); // take CS high to end
while (X25_ReadStatusRegister() & 0x01); // wait for PIP bit to go low ...
X25_PRDI(); // disable programming
}
/*
*********************************************************************************************************
* X25_ReadBlock()
*
* Description: Read a block (32 bytes) of memory from the serial memory.
* Arguments : address = address within serial memory (0x0000-0x3fe0)
* dest = address of memory where block will be stored
* Returns : none
*********************************************************************************************************
*/
void X25_ReadBlock (unsigned int address, unsigned char *dest)
{
char i;
CS_0(); // take CS low to begin
X25_ByteSend(CMD_READ); // send READ command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
for (i=0; i<32; i++) {
*dest++ = X25_ByteRead(); // read byte, save to destination
}
CS_1(); // take CS high to end
}
/*
*********************************************************************************************************
* X25_WriteBlock()
*
* Description: Write a block (32 bytes) of memory from RAM to the serial memory.
* Arguments : address = address within serial memory (0x0000-0x3fe0)
* source = address of memory where block to be written is stored
* Returns : none
*********************************************************************************************************
*/
void X25_WriteBlock (unsigned int address, unsigned char *source)
{
char i;
X25_PREN(); // enable programming
CS_0(); // take CS low to begin
X25_ByteSend(CMD_PROGRAM); // send PROGRAM command
X25_ByteSend(address / 0x0100); // send address (upper 8 bits)
X25_ByteSend(address & 0x00ff); // send address (lower 8 bits)
for (i=0; i<32; i++) {
X25_ByteSend(*source++); // read byte from buffer, send
}
CS_1(); // take CS high to end
while (X25_ReadStatusRegister() & 0x01); // wait for PIP bit to go low ...
X25_PRDI(); // disable programming
}
/*
*********************************************************************************************************
* X25_PREN()
*
* Description: Send PREN command to serial memory
* Arguments : none
* Returns : none
*********************************************************************************************************
*/
void X25_PREN (void)
{
CS_0(); // take CS low to begin
X25_ByteSend(CMD_PREN); // send command
CS_1(); // take CS high to end
}
/*
*********************************************************************************************************
* X25_PRDI()
*
* Description: Send PRDI command to serial memory
* Arguments : none
* Returns : none
*********************************************************************************************************
*/
void X25_PRDI (void)
{
CS_0(); // take CS low to begin
X25_ByteSend(CMD_PRDI); // send command
CS_1(); // take CS high to end
}
/*
*********************************************************************************************************
* X25_ReadStatusRegister()
*
* Description: Read serial memory status register
* Arguments : none
* Returns : contents of status register
*********************************************************************************************************
*/
unsigned char X25_ReadStatusRegister (void)
{
unsigned char data;
CS_0(); // take CS low to begin
X25_ByteSend(CMD_RDSR); // send command
data = X25_ByteRead(); // read byte
CS_1(); // take CS high to end
return data; // return result
}
/*
*********************************************************************************************************
* X25_WriteStatusRegister()
*
* Description: Write serial memory status register
* Arguments : data to write to status register
* Returns : none
*********************************************************************************************************
*/
void X25_WriteStatusRegister (unsigned char reg)
{
X25_PREN(); // enable programming
CS_0(); // take CS low to begin
X25_ByteSend(CMD_PRSR); // send command
X25_ByteSend(reg); // send data
CS_1(); // take CS high to end
while (X25_ReadStatusRegister() & 0x01); // wait for PIP bit to go low ...
X25_PRDI(); // disable programming
}
// ..................................... Private Functions ..............................................
/*
*********************************************************************************************************
* ByteSend()
*
* Description: Send one byte to the serial memory
* Arguments : data = data to send
* Returns : none
*********************************************************************************************************
*/
static void X25_ByteSend (unsigned char dataout)
{
char i;
const unsigned char bitmask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
for (i=0; i<8; i++) {
if (dataout & bitmask[i]) // output one data bit
SI_1(); // "1"
else // or
SI_0(); // "0"
SCLK_1(); // bring SCLK high
SCLK_0(); // bring SCLK low
}
}
/*
*********************************************************************************************************
* X25_ByteRead()
*
* Description: Read one byte from the serial memory
* Arguments : none
* Returns : byte read
*********************************************************************************************************
*/
static unsigned char X25_ByteRead (void)
{
char i;
unsigned char data = 0x00;
const unsigned char bitmask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
for (i=0; i<8; i++) {
if (SO_STATE() != 0) // read one data bit
data |= bitmask[i]; // "1"
SCLK_1(); // bring SCLK high
SCLK_0(); // bring SCLK low
}
return data;
}
Xicor SPI Serial Memory - Source Code: XICOR25.H/* ********************************************************************************************************* * Module : XICOR25.H * Author : Randy Rasa * Description: Header file for XICOR25.C (Xicor Serial Memory Interface Routines) ********************************************************************************************************* */ /* ********************************************************************************************************* * Public Function Prototypes ********************************************************************************************************* */ void X25_Init (void); void X25_PREN (void); void X25_PRDI (void); unsigned char X25_ReadStatusRegister (void); void X25_WriteStatusRegister (unsigned char); unsigned char X25_ReadByte (unsigned int address); unsigned int X25_ReadWord (unsigned int address); unsigned long X25_ReadLong (unsigned int address); void X25_ReadData (unsigned int address, unsigned char *dest, int numbytes); void X25_WriteData (unsigned int address, unsigned char *source, int numbytes); void X25_ReadBlock (unsigned int address, unsigned char *dest); void X25_WriteBlock (unsigned int address, unsigned char *source);
|
EE Links |
|||||
|
Home
T H E E E C O M P E N D I U M
|