Fra Hougaard
/*********************************************************************
*
* Data EEPROM Access Routines
*
*********************************************************************
* FileName: xeeprom.c
* Dependencies: compiler.h
* xeeprom.h
* Processor: PIC18
* Complier: MCC18 v1.00.50 or higher
* HITECH PICC-18 V8.10PL1 or higher
* Company: Microchip Technology, Inc.
*
* Software License Agreement
*
* The software supplied herewith by Modtronix Engineering is based on v2.20.04.01
* of the Microchip TCP/IP stack. The original code base is owned by Microchip
* Technology Incorporated (the Company), and is protected under applicable
* copyright laws. The modifications are owned by Modtronix Engineering, and is
* protected under applicable copyright laws. The software supplied herewith is
* intended and supplied to you, the Company customer, for use solely and
* exclusively on Microchip PICmicro Microcontroller based products manufactured
* by Modtronix Engineering. The code may be modified and can be used free of charge
* for non commercial and commercial applications. All rights are reserved. Any use
* in violation of the foregoing restrictions may subject the user to criminal
* sanctions under applicable laws, as well as to civil liability for the breach
* of the terms and conditions of this license.
*
* THIS SOFTWARE IS PROVIDED IN AN 'AS IS' CONDITION. NO WARRANTIES, WHETHER EXPRESS,
* IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE
* COMPANY SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
* CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
*
* This is a modified version of C18 library functions.
* This version supports data EEPROM with two bytes of address.
* It still uses some of the routines from C18 library.
*
*
* HiTech PICC18 Compiler Options excluding device selection:
* -FAKELOCAL -G -O -Zg -E -C
*
*
* Author Date Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Nilesh Rajbharti 5/20/02 Original (Rev. 1.0)
********************************************************************/
#include "xeeprom.h"
#define IdleI2C() while ( ( SSPCON2 & 0x1F ) | ( SSPSTAT_R_W ) );
#define StartI2C() SSPCON2_SEN=1
#define RestartI2C() SSPCON2_RSEN=1
#define StopI2C() SSPCON2_PEN=1
#define NotAckI2C() SSPCON2_ACKDT=1, SSPCON2_ACKEN=1
static unsigned char getcI2C( void );
static unsigned char WriteI2C( unsigned char data_out );
XEE_RESULT XEEClose(void);
/*********************************************************************
* Function: void XEEInit(unsigned char baud)
*
* PreCondition: None
*
* Input: baud - SSPADD value for bit rate.
*
* Output: None
*
* Side Effects: None
*
* Overview: Initialize I2C module to communicate to serial
* EEPROM.
*
* Note: None
********************************************************************/
#define MASTER 8 /* I2C Master mode */
/* SSPSTAT REGISTER */
#define SLEW_OFF 0xC0 /* Slew rate disabled for 100kHz mode */
#define SLEW_ON 0x00 /* Slew rate enabled for 400kHz mode */
#define SSPENB 0x20 /* Enable serial port and configures
SCK, SDO, SDI */
void XEEInit(unsigned char baud)
{
SSPSTAT &= 0x3F; // power on state
SSPCON1 = 0x00; // power on state
SSPCON2 = 0x00; // power on state
SSPCON1 |= MASTER; // select serial mode
SSPSTAT |= SLEW_ON; // slew rate on/off
TRISC_RC3 = 1; // Set SCL (PORTC,3) pin to input
TRISC_RC4 = 1; // Set SDA (PORTC,4) pin to input. Is controlled by I2C hardware
SSPCON1 |= SSPENB; // enable synchronous serial port
SSPADD = baud;
}
/*********************************************************************
* Function: XEE_RESULT XEESetAddr(unsigned char control,
* XEE_ADDR address)
*
* PreCondition: XEEInit() is already called.
*
* Input: control - data EEPROM control code
* address - address to be set
*
* Output: XEE_SUCCESS if successful
* other value if failed.
*
* Side Effects: None
*
* Overview: Modifies internal address counter of EEPROM.
*
* Note: This function does not release the I2C bus.
* User must close XEEClose() after this function
* is called.
********************************************************************/
XEE_RESULT XEESetAddr(unsigned char control, XEE_ADDR address)
{
union
{
unsigned short int Val;
struct
{
unsigned char LSB;
unsigned char MSB;
} bytes;
} tempAddress;
tempAddress.Val = address;
IdleI2C(); // ensure module is idle
StartI2C(); // initiate START condition
while ( SSPCON2_SEN ); // wait until start condition is over
if ( PIR2_BCLIF ) // test for bus collision
return XEE_BUS_COLLISION; // return with Bus Collision error
if ( WriteI2C( control ) ) // write 1 byte
return XEE_BUS_COLLISION; // set error for write collision
IdleI2C(); // ensure module is idle
if ( !SSPCON2_ACKSTAT ) // test for ACK condition, if received
{
if (control == 0xa0)
{
if ( WriteI2C( tempAddress.bytes.MSB ) ) // WRITE word address to EEPROM
return XEE_BUS_COLLISION; // return with write collision error
IdleI2C(); // ensure module is idle
if ( WriteI2C( tempAddress.bytes.LSB ) ) // WRITE word address to EEPROM
return XEE_BUS_COLLISION; // return with write collision error
}
else
{
// Devices with 1 bytes address
if (WriteI2C(tempAddress.bytes.LSB))
return XEE_BUS_COLLISION;
}
IdleI2C(); // ensure module is idle
if ( !SSPCON2_ACKSTAT ) // test for ACK condition received
return XEE_SUCCESS;
else
return XEE_NAK; // return with Not Ack error
}
else
return XEE_NAK; // return with Not Ack error
}
/*********************************************************************
* Function: XEE_RESULT XEEBeginRead(unsigned char control,
* XEE_ADDR address)
*
* PreCondition: XEEInit() is already called.
*
* Input: control - EEPROM control and address code.
* address - Address at which read is to be performed.
*
* Output: XEE_SUCCESS if successful
* other value if failed.
*
* Side Effects: None
*
* Overview: Sets internal address counter to given address.
* Puts EEPROM in sequential read mode.
*
* Note: This function does not release I2C bus.
* User must call XEEEndRead() when read is not longer
* needed; I2C bus will released after XEEEndRead()
* is called.
********************************************************************/
XEE_RESULT XEEBeginRead(unsigned char control, XEE_ADDR address )
{
unsigned char r;
r = XEESetAddr(control, address);
if ( r != XEE_SUCCESS )
return r;
r = XEEClose();
if ( r != XEE_SUCCESS )
return r;
IdleI2C();
StartI2C();
while( SSPCON2_SEN );
if ( PIR2_BCLIF )
return XEE_BUS_COLLISION;
if ( WriteI2C(control+1) )
return XEE_BUS_COLLISION;
IdleI2C();
if ( !SSPCON2_ACKSTAT )
return XEE_SUCCESS;
return XEE_NAK;
}
XEE_RESULT XEEWrite(unsigned char val)
{
IdleI2C(); // ensure module is idle
if ( WriteI2C( val ) ) // data byte for EEPROM
return XEE_BUS_COLLISION; // set error for write collision
IdleI2C();
if ( !SSPCON2_ACKSTAT )
return XEE_SUCCESS;
return XEE_NAK;
}
/*********************************************************************
* Function: XEE_RESULT XEEEndWrite(void)
*
* PreCondition: XEEInit() && XEEBeginWrite() are already called.
*
* Input: None
*
* Output: XEE_SUCCESS if successful
* other value if failed.
*
* Side Effects: None
*
* Overview: Instructs EEPROM to begin write cycle.
*
* Note: Call this function after either page full of bytes
* written or no more bytes are left to load.
* This function initiates the write cycle.
* User must call for XEEWait() to ensure that write
* cycle is finished before calling any other
* routine.
********************************************************************/
XEE_RESULT XEEEndWrite(void)
{
IdleI2C();
StopI2C();
while(SSPCON2_PEN);
return XEE_SUCCESS;
}
/*********************************************************************
* Function: XEE_RESULT XEERead(void)
*
* PreCondition: XEEInit() && XEEBeginRead() are already called.
*
* Input: None
*
* Output: XEE_SUCCESS if successful
* other value if failed.
*
* Side Effects: None
*
* Overview: Reads next byte from EEPROM; internal address
* is incremented by one.
*
* Note: This function does not release I2C bus.
* User must call XEEEndRead() when read is not longer
* needed; I2C bus will released after XEEEndRead()
* is called.
********************************************************************/
unsigned char XEERead(void)
{
getcI2C();
while( SSPCON2_RCEN ); // check that receive sequence is over.
SSPCON2_ACKDT = 0; // Set ack bit
SSPCON2_ACKEN = 1;
while( SSPCON2_ACKEN );
return SSPBUF;
}
/*********************************************************************
* Function: XEE_RESULT XEEEndRead(void)
*
* PreCondition: XEEInit() && XEEBeginRead() are already called.
*
* Input: None
*
* Output: XEE_SUCCESS if successful
* other value if failed.
*
* Side Effects: None
*
* Overview: Ends sequential read cycle.
*
* Note: This function ends seuential cycle that was in
* progress. It releases I2C bus.
********************************************************************/
XEE_RESULT XEEEndRead(void)
{
getcI2C();
while( SSPCON2_RCEN ); // check that receive sequence is over.
NotAckI2C();
while( SSPCON2_ACKEN );
StopI2C();
while( SSPCON2_PEN );
return XEE_SUCCESS;
}
/*********************************************************************
* Function: XEE_RESULT XEEReadArray(unsigned char control,
* XEE_ADDR address,
* unsigned char *buffer,
* unsigned char length)
*
* PreCondition: XEEInit() is already called.
*
* Input: control - EEPROM control and address code.
* address - Address from where array is to be read
* buffer - Caller supplied buffer to hold the data
* length - Number of bytes to read.
*
* Output: XEE_SUCCESS if successful
* other value if failed.
*
* Side Effects: None
*
* Overview: Reads desired number of bytes in sequential mode.
* This function performs all necessary steps
* and releases the bus when finished.
*
* Note: None
********************************************************************/
XEE_RESULT XEEReadArray(unsigned char control,
XEE_ADDR address,
unsigned char *buffer,
unsigned char length)
{
XEE_RESULT r;
r = XEEBeginRead(control, address);
if ( r != XEE_SUCCESS )
return r;
while( length-- )
*buffer++ = XEERead();
r = XEEEndRead();
return r;
}
XEE_RESULT XEEClose(void)
{
IdleI2C();
StopI2C();
while( SSPCON2_PEN ); // wait until stop condition is over.
if ( PIR2_BCLIF )
return XEE_BUS_COLLISION;
return XEE_SUCCESS;
}
/*********************************************************************
* Function: XEE_RESULT XEEIsBusy(unsigned char control)
*
* PreCondition: XEEInit() is already called.
*
* Input: control - EEPROM control and address code.
*
* Output: XEE_READY if EEPROM is not busy
* XEE_BUSY if EEPROM is busy
* other value if failed.
*
* Side Effects: None
*
* Overview: Requests ack from EEPROM.
*
* Note: None
********************************************************************/
XEE_RESULT XEEIsBusy(unsigned char control)
{
XEE_RESULT r;
IdleI2C(); // ensure module is idle
StartI2C(); // initiate START condition
while ( SSPCON2_SEN ); // wait until start condition is over
if ( PIR2_BCLIF ) // test for bus collision
{
return XEE_BUS_COLLISION; // return with Bus Collision error
}
else
{
if ( WriteI2C( control ) ) // write byte - R/W bit should be 0
return XEE_BUS_COLLISION; // set error for write collision
IdleI2C(); // ensure module is idle
if ( PIR2_BCLIF ) // test for bus collision
return XEE_BUS_COLLISION; // return with Bus Collision error
if ( !SSPCON2_ACKSTAT )
r = XEE_READY;
else
r = XEE_BUSY;
#if 0
while ( SSPCON2_ACKSTAT ) // test for ACK condition received
{
RestartI2C(); // initiate Restart condition
while ( SSPCON2_RSEN ); // wait until re-start condition is over
if ( PIR2_BCLIF ) // test for bus collision
return XEE_BUS_COLLISION; // return with Bus Collision error
if ( WriteI2C( control ) ) // write byte - R/W bit should be 0
return XEE_BUS_COLLISION; // set error for write collision
IdleI2C(); // ensure module is idle
}
#endif
}
StopI2C(); // send STOP condition
while ( SSPCON2_PEN ); // wait until stop condition is over
if ( PIR2_BCLIF ) // test for bus collision
return XEE_BUS_COLLISION; // return with Bus Collision error
return r;
//return XEE_READY; // return with no error
}
/********************************************************************
* Function Name: WriteI2C *
* Return Value: Status byte for WCOL detection. *
* Parameters: Single data byte for I2C bus. *
* Description: This routine writes a single byte to the *
* I2C bus. *
********************************************************************/
static unsigned char WriteI2C( unsigned char data_out )
{
SSPBUF = data_out; // write single byte to SSPBUF
if ( SSPCON1_WCOL ) // test if write collision occurred
return ( -1 ); // if WCOL bit is set return negative #
else
{
while( SSPSTAT_BF ); // wait until write cycle is complete
return ( 0 ); // if WCOL bit is not set return non-negative #
}
}
/********************************************************************
* Function Name: ReadI2C *
* Return Value: contents of SSPBUF register *
* Parameters: void *
* Description: Read single byte from I2C bus. *
********************************************************************/
static unsigned char getcI2C( void )
{
SSPCON2_RCEN = 1; // enable master for 1 byte reception
while ( !SSPSTAT_BF ); // wait until byte received
return ( SSPBUF ); // return with read byte
}