RobG 1,892 Posted March 12, 2011 Share Posted March 12, 2011 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. 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; } gwdeveloper, larsie, bluehash and 3 others 6 Quote Link to post Share on other sites
bluehash 1,581 Posted March 13, 2011 Share Posted March 13, 2011 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. Quote Link to post Share on other sites
RobG 1,892 Posted March 13, 2011 Author Share Posted March 13, 2011 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. Quote Link to post Share on other sites
GeekDoc 226 Posted March 15, 2011 Share Posted March 15, 2011 Other project I am thinking of is a keypad entry system which will store pass codes. Would also work for RFID reader for same purpose. Quote Link to post Share on other sites
erc 3 Posted March 15, 2011 Share Posted March 15, 2011 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. RobG and bluehash 2 Quote Link to post Share on other sites
bluehash 1,581 Posted March 15, 2011 Share Posted March 15, 2011 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. Good deal people. Jump on it if you have an idea in mind. Quote Link to post Share on other sites
redbeard 0 Posted March 16, 2011 Share Posted March 16, 2011 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? Quote Link to post Share on other sites
RobG 1,892 Posted March 16, 2011 Author Share Posted March 16, 2011 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. redbeard 1 Quote Link to post Share on other sites
TheDirty 33 Posted March 17, 2011 Share Posted March 17, 2011 SLAA115 is a 24 series EEPROM app note. The code is all in ASM, though. http://focus.ti.com/general/docs/litabs ... er=slaa115 When I needed a bit bang master for an LM75a I used the routines here successfully. http://lakeweb.net/MSP430/ Quote Link to post Share on other sites
zeke 693 Posted March 8, 2012 Share Posted March 8, 2012 When I needed a bit bang master for an LM75a I used the routines here successfully.http://lakeweb.net/MSP430/ Arg! The server was hacked and those files are now gone. Anyone got a copy of them? Please upload them. Quote Link to post Share on other sites
oPossum 1,083 Posted March 8, 2012 Share Posted March 8, 2012 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( ); } Quote Link to post Share on other sites
gordon 229 Posted March 8, 2012 Share Posted March 8, 2012 http://web.archive.org/web/201004030755 ... index.html zeke 1 Quote Link to post Share on other sites
zeke 693 Posted March 13, 2012 Share Posted March 13, 2012 Looks like the site is back up. Cool! gordon 1 Quote Link to post Share on other sites
Fred 453 Posted March 14, 2012 Share Posted March 14, 2012 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. Quote Link to post Share on other sites
Rei Vilo 695 Posted January 15, 2013 Share Posted January 15, 2013 How to port this library to Energia? I'm facing big issues when I try to port it to Energia. Thank you for your help. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.