Electronic – Bit Bang to I2C EEPROM MSP430

eepromi2cmsp430

I found some code to Bit Bang to an EEPROM on the MSP430 here, but if I change the pins to what is on my design it fails to read and write. I've also added a LED for output this should blink if it fails which it does constantly.

// required
#define SCL BIT6
#define SDA BIT4
#define LED BIT0
#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;

static void __inline__ _delay_cycles(register unsigned int n)
{
    __asm__ __volatile__ (
        "1: \n"
        " dec   %[n] \n"
        " jne   1b \n"
        : [n] "+r"(n));
}

void main(void) {

    WDTCTL = WDTPW + WDTHOLD;

    P1OUT  |= SCL + LED;
    P1DIR |= SCL + LED;
    address = 2; // set address to 2
    charData = 0xEF;
    int result = writeChar();
    _delay_cycles(100000);

    while(1) {
// write char to address 2
        int read = readChar();

        _delay_cycles(100000);
        if(read == FAILURE && result == FAILURE) {
            P1OUT ^= LED;
        }

        _delay_cycles(100000);
    }
}


// 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 & BIT4) ? (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;
}

I am using MSP430-GCC as a compiler not CCS or IAR this is why I have my own _delay_cycles function. I have tried it with CCS and still the same problem. I have attached a Pull up to the SDA.

The datasheet for my EEPROM is here. So how would I go about debugging this?

Best Answer

There is really little to do here but to connect a logic analyzer and see what is going on. Because bit banging depends on the CPU for timing (since there's no timer), you need to see what is really going on.

Your code makes many assumptions which I think are incorrect, this is especially true with the timing. Timing in this case is critical, and a I2C module handles it for you. In this case, instead of using delay cycles, use a timer to wait for a specific amount of time. Using delay cycles should be reserved to just wait some time, without need for accuracy. Here you need to comply with I2C requirements. The MSP430 is much faster than 400kHz and is likely violating I2C timing unless you wait for the required time. As an example, waiting to read, you must wait until the slave has changed the bit output.

When you're bitbanging, you're pushing P1OUT High and Low. This is not what you should do. You should be switching the GPIO between driving Low and High Impedance (P1DIR &= ~BIT) so that the pullup you have on the lines (you do have them right?), pull them up for High. Remember that I2C is open drain and you need to emulate this using the MSP430. Both pins should default to high impedance so that the pull up is allowed to pull up. I don't see in your code the initialization for SDA. Make sure initially both are input(this is usually default but make it explicit so there's no guessing).

I highly recommend you use I2C module if you have it available in the part you've chosen. It will in part offload the CPU and make sure that transactions are handled properly. If bit bang code is used, sometimes things can happen to screw up the timing and this has to be handled.