Jump to content
43oh

Serial EEPROM


Recommended Posts

I just got my hands on 24LC32A serial EEPROM and here is how I got it work with my LaunchPad.

There's really nothing special about it, just I2C functions. I was going to use TI's examples for I2C, but some of them are horrible and decided to write my own functions. This is my first stab at I2C so expect improved version of the code pretty soon.

Also, I did not use USI since 24LC32A can be clocked @400kHz @3.6V and bit banging seemed to be the right and easiest way to do it. External pull up should be between 2k-10k, suggested is lower value for 400kHz, but I am using 10k and it's working fine.

 

post-197-135135497712_thumb.png

 

The code below provides functions to read and write chars and ints.

More functions to come later.

You can remove optional functions and leave the required ones and the ones you will use.

 

#include 

// required
#define SCL BIT6
#define SDA BIT7
#define READ 0xA1
#define WRITE 0xA0
#define FAILURE -1
#define SUCCESS 0
// required
void sendByte(void);
void receiveByte(void);
void sendAck(void);
void receiveAck(void);
void start(void);
void stop(void);
// required
unsigned char txData = 0;
unsigned char rxData = 0;
unsigned char ackFlag = 0;
unsigned char bitCounter = 0;
unsigned int address = 0; // 12 bit address, upper 4 bits should be 0s.

// optional
int writeChar(void);
int readChar(void);
int readCurrentChar(void);
int writeInt(void);
int readInt(void);

// optional
unsigned char charData = 0;
unsigned int intData = 0;

void main(void) {

   WDTCTL = WDTPW + WDTHOLD;

   P1OUT  |= SCL;
   P1DIR |= SCL;

   while(1) {

       address = 0; // set address to 0
       readInt(); // read int from address 0 (2 bytes)
       //handle failure, readInt will return FAILURE or SUCCESS, check before using intData, it might be corrupted

       _delay_cycles(1000000); // delay for testing

       intData = 0xABCD; // set intData, we will save it
       writeInt(); // write intData to address 0 (2 bytes)
       //handle failure

       _delay_cycles(1000000);

       readCurrentChar(); // read char to charData from the current address, since we wrote 2 bytes starting @ address 0, internal address counter of 24LC32A now points to address 2
       //handle failure

       _delay_cycles(1000000);

       address = 2; // set address to 2
       readChar(); // read char (1 byte) from address 2
       //handle failure

       _delay_cycles(1000000);

       charData = 0xEF;
       writeChar(); // write char to address 2
       //handle failure

       // since the writeChar might take a while, we are can do acknowledge polling to see if the EEPROM is ready.
       // this is not the best way because if the nack is a result of something else than busy, we will have an infinite loop.
       while(readCurrentChar()) {}

       _delay_cycles(1000000);
   }
}


// optional
int readCurrentChar(void) {
   start();
   txData = READ;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   receiveByte();
   ackFlag = 0;
   sendAck();
   stop();
   charData = rxData;
   return SUCCESS;
}
// optional
int readChar(void) {
   start();
   txData = WRITE;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address >> 8;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address;
   sendByte();
   receiveAck();
   start();
   txData = READ;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   receiveByte();
   ackFlag = 0;
   sendAck();
   charData = rxData;
   stop();
   return SUCCESS;
}
// optional
int writeChar(void) {
   start();
   txData = WRITE;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address >> 8;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = charData;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   stop();
   return SUCCESS;
}
// optional
int readInt(void) {
   start();
   txData = WRITE;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address >> 8;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address;
   sendByte();
   receiveAck();
   start();
   txData = READ;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   receiveByte();
   ackFlag = 1;
   sendAck();
   intData = rxData;
   intData <<= 8;
   receiveByte();
   ackFlag = 0;
   sendAck();
   intData |= rxData;
   stop();
   return SUCCESS;
}

// optional
int writeInt(void) {
   start();
   txData = WRITE;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address >> 8;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = address;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = intData >> 8;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   txData = intData;
   sendByte();
   receiveAck();
   if(!ackFlag)
       return FAILURE;
   stop();
   return SUCCESS;
}

// required
// send byte to slave
void sendByte(void) {
   P1DIR |= SDA;
   bitCounter = 0;
   while(bitCounter < 8) {
       (txData & BIT7) ? (P1OUT |= SDA) : (P1OUT &= ~SDA);
       P1OUT |= SCL;
       txData <<= 1;
       bitCounter++;
       P1OUT &= ~SCL;
   }
   P1OUT |= SDA;
   P1DIR &= ~SDA;
}
// required
// receive byte from slave
void receiveByte(void) {
   bitCounter = 0;
   while(bitCounter < 8) {
       P1OUT |= SCL;
       rxData <<= 1;
       bitCounter++;
       if(P1IN & SDA) {
           rxData |= BIT0;
       }
       P1OUT &= ~SCL;
   }
}
// required
// send master's ACK
void sendAck(void) {
   P1DIR |= SDA;
   (ackFlag) ? (P1OUT &= ~SDA) : (P1OUT |= SDA);
   P1OUT |= SCL;
   P1OUT &= ~SCL;
   P1OUT |= SDA;
   P1DIR &= ~SDA;
}
// required
// receive slave's ACK
void receiveAck(void) {
   P1OUT |= SCL;
   (P1IN & SDA) ? (ackFlag = 0) : (ackFlag = 1);
   P1OUT &= ~SCL;
}
// required
// start condition
void start(void) {
   P1OUT |= SCL;
   P1DIR |= SDA;
   P1OUT &= ~SDA;
   P1OUT &= ~SCL;
   P1OUT |= SDA;
   P1DIR &= ~SDA;

}
// required
// stop condition
void stop(void) {
   P1DIR |= SDA;
   P1OUT &= ~SDA;
   P1OUT |= SCL;
   P1OUT |= SDA;
   P1DIR &= ~SDA;
}

Link to post
Share on other sites
Rob, are you using the EEprom for any project of yours or was this just for fun? It will be interesting to see what you do with it.

I am using it for the counter, kind of energy/gas meter counter, to save value when power goes down. Will post more when finished.

Other project I am thinking of is a keypad entry system which will store pass codes.

Link to post
Share on other sites

Good job. These 24LCxx serial eeproms are perfect for adding non volatile storage to launchpad / msp430 projects -low cost, huge number of r/w cycles, and pretty low power consumption. I'm glad to see someone make the concept a reality. If anyone wants to try one out in their project, ill send one (no cost) to a max of 4 people. You have to post your project using it, usa only. If you want one, post what you would use it for.

Link to post
Share on other sites
Also, I did not use USI since 24LC32A can be clocked @400kHz @3.6V and bit banging seemed to be the right and easiest way to do it.

I'm relatively new to MSP430, microcontrollers in general, and forum posting but I was wondering if you (or anyone) could provide some insight into why bit banging seemed to be the right approach? Is USI too slow for communicating with the EEPROM? Or is using it just overkill?

Link to post
Share on other sites

When running @ 1MHz it's just an overkill, interrupts require 6 cycles to go to and 5 cycles to return from.

If you run your MCU @ 16MHz, then USI might be a better choice to avoid using delays.

Same when your I2C device works @ 100kHz or less.

Also, by not using USI you are making it available for other things.

Link to post
Share on other sites
  • 11 months later...

From archive.org

 

This is the slave code, the master code is missing.

 

I2C.h

/*
Todo.
	Some devices on the bus may use restarts
	without a stop. This condition is not handled...


*/

//our address
#define		USI_OURADDRESS		( 0xB0 >> 1 )

//waiting for start
#define 	USISTATE_NULL		0x00

//doing awknowlege bit
//this is clear through switch
#define		USISTATE_AWK		0x01

//recieving device address
#define 	USISTATE_RCVADR		0x02

//New 1-10-09; We require a command byte
#define		USISTATE_COMMAND	0x04

//We are inputing data from master
#define		USISTATE_INPUT		0x10

//ack command before output state
#define		USISTATE_OUTPUT_AWK	0x20

//We are outputing data to master
#define		USISTATE_OUTPUT		0x40

// The read/write states
#define USI_RW_WRITE			0x00
#define USI_RW_READ				0x01

//State of usi machine
extern volatile unsigned char usi_state;

//The command byte recieved from master
extern unsigned char usi_command;

//The control word is sent with r/w
extern unsigned char usi_read_write;

//pointer used internally by usi
//This is set by the super, requested by this code
extern unsigned char* usi_buf_ptr;

//Define these in user...............

//A command byte was recieved by i2c slave
unsigned char i2c_super_command( );

//..................................

//Call this in main at initialization
void initialize_i2c( void );

//Call this in PORT1_VECTOR interrupt
//void PORT1_6_I2C( void );

 

I2C.c

#include  

#include 
#include 
#include 

#define __MSP430_HAS_PORT1__
#include 
#include 

#include "I2C.h"

//State of usi machine
volatile unsigned char usi_state;

//buffer for inputs from master
//unsigned char usi_buffer[ 8 ];

//pointer used internally by usi
unsigned char* usi_buf_ptr= 0;

//flag reflecting control bit r/w
unsigned char usi_read_write;

//command byte from master
unsigned char usi_command;

interrupt ( PORT1_VECTOR ) PORT1_6_I2C( void )
//void PORT1_6_I2C( void )
{
if( P1IFG & BIT6 ) //hack for I2C awk on input
{
	//clear interrupt enable
	P1IE&= ~BIT6;
	//clear flag
	P1IFG&= ~BIT6;
	//release output now that clock is low
	//if anything but output
	if( ! ( usi_state & USISTATE_OUTPUT ) )
		USICTL0&= ~USIOE;
}

if( P1IFG & BIT7 ) //hack for I2C STOP
{
	//clear flag
	P1IFG&= ~BIT7;

	if( USICTL1 & USISTP
		&& ! ( usi_state & USISTATE_OUTPUT ) ) //STOP detected
	{
		//clear interrupt enable
		P1IE&= ~BIT7;
		initialize_i2c( );//reset USI
	}
}
}

void initialize_i2c( void )
{
USICTL0= USIPE6 | USIPE7 | USISWRST;//Port, I2C slave, hold reset
USICTL1= USII2C | USISTTIE;			//Enable I2C mode, disable counter
USICKCTL= USICKPL;					//Setup clock polarity
USICNT= USISCLREL;					//SCL released
//Initialize State machine and sit idle until START condition
usi_state= USISTATE_NULL;
USICTL0&= ~USISWRST;				// Clear Reset of USI
}

interrupt ( USI_VECTOR ) usi_interrupt( void )
{
if( USICTL1 & USISTTIFG ) // START recieved....
{
	USICTL1&= ~USISTTIFG;		// Clear START flag...
	USICNT= 0x08 | USISCLREL;	//  Clears USISCLREL
    usi_state= USISTATE_RCVADR;	//Next is recieve address
    USICTL1|= USIIE;			// Enable Counter Interrupt

    //Clock is low, set up to monitor for STOP condition
	P1IFG&= ~BIT7; //make sure flag is clear
	P1IES&= ~BIT7; //interrupt on rising edge of I2C SDA
	P1IE|= BIT7; //enable interrupt for rising edge on SDA

	return;
}
//awk is a special case not for switch
if( usi_state & USISTATE_AWK )
{
	if( usi_state & USISTATE_OUTPUT )
	{
		if( USISRL ) //master said no more
		{
			initialize_i2c( );//reset USI, Wait for next start
			return;
		}
		USISRL= *usi_buf_ptr++; //Load output byte
		USICTL0|= USIOE; //Take SDA.
	}
	else //Input in progress, control, command or data
	{
		P1IES|= BIT6; //interrupt on falling edge of SCL
		P1IFG&= ~BIT6; //make sure flag is clear
		P1IE|= BIT6; //enable interrupt for input awk hack
		if( usi_state & USISTATE_OUTPUT_AWK )
		{
			USISRL= *usi_buf_ptr++; //Load output byte
			usi_state= USISTATE_OUTPUT;
		}
	}
	USICNT= 0x08 | USISCLREL;
	usi_state&= ~USISTATE_AWK;
	return;
}
switch( usi_state )
{
case USISTATE_RCVADR:
{
	//Is it _not_ our address?
	if( USISRL >> 1 != USI_OURADDRESS )
	{
		initialize_i2c( );//reset USI, Wait for next start
		return;
	}
	//else our address, check the r/w bit next
	if( USISRL & 0x01 )
		usi_read_write= USI_RW_READ;
	else
		usi_read_write= USI_RW_WRITE;

	USISRL= 0;					//Zero to awk
	USICNT= 0x01 | USISCLREL;	//One bit to awk
	USICTL0|= USIOE;			//Take SDA.

	//Next is to recieve command word
	usi_state= USISTATE_COMMAND | USISTATE_AWK;

	LPM0_EXIT;
	return;
}
case USISTATE_COMMAND:
{
	usi_command= USISRL;
	if( ! i2c_super_command( ) ) //super said don't awk
	{
		initialize_i2c( );
		return;
	}
	//super should have set pointer
	//next is to read or write data, to/from master
	if( usi_read_write ) //read is true
		usi_state= USISTATE_OUTPUT_AWK | USISTATE_AWK;
	else
		usi_state= USISTATE_INPUT | USISTATE_AWK;

	//This is a call?????
	USICTL0|= USIOE;			//Take SDA.
	USISRL= 0;					//Zero to awk
	USICNT= 0x01 | USISCLREL;	//One bit to awk
	return;
}
case USISTATE_INPUT:
{
	*usi_buf_ptr++= USISRL;

	//Do awk
	USICTL0|= USIOE; //Take SDA.

	usi_state|= USISTATE_AWK;
	USISRL= 0;					//Zero to awk
	USICNT= 0x01 | USISCLREL;
	return;
}
case USISTATE_OUTPUT:
{
	USICTL0&= ~USIOE; //Release SDA.

	usi_state|= USISTATE_AWK;
	USISRL= 0;
	USICNT= 0x01 | USISCLREL;
	return;
}
}//switch(...)!!!!!!

//Should never get here!!!!!!
initialize_i2c( );
}

Link to post
Share on other sites

Very useful - thanks. I've got some similar EEPROMs that I've not used yet. I didn't realise they were SOIC when I ordered them and have only just got into using SMD components. I've got about 10 of them so I'm happy to echo erc's offer for anyone in the UK. PM me and I'll pop one in the post for you free of charge.

Link to post
Share on other sites
  • 10 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...