1
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ the 3 main places to check when this type of thing happens: that you really changed the code correctly in every place; that the new pins support what you are trying to do; that the new pins don't have higher priority peripherals hogging the pin. should be pretty easy with a scope to check output pins, input can be harder. \$\endgroup\$ – Grady Player Mar 17 '13 at 14:33
  • \$\begingroup\$ The only thing connected to the pins are the EEPROM, nothing else. And In the datasheet its fine to use them for standard GPIO. \$\endgroup\$ – Dean Mar 17 '13 at 14:36
  • 1
    \$\begingroup\$ Why bit-bang EEPROM? Most MSP430 have I2C interfaces and this will help you run at maximum speed. \$\endgroup\$ – Gustavo Litovsky Mar 17 '13 at 14:54
4
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ wouldn't spi be faster? \$\endgroup\$ – Grady Player Mar 17 '13 at 20:22
  • \$\begingroup\$ @GradyPlayer: SPI is supported by some EEPROM. The standard ones usually support I2C because faster isn't needed. EEPROM isn't intended to be fast, so there's not much point in using SPI. \$\endgroup\$ – Gustavo Litovsky Mar 17 '13 at 20:41
  • \$\begingroup\$ I have often wondered why there are 5x as many i2c EEPROMs as any other serial interface. \$\endgroup\$ – Grady Player Mar 18 '13 at 3:37
  • \$\begingroup\$ @GradyPlayer: Take a look at the access times specified in the EEPROM datasheets. They're relatively slow. \$\endgroup\$ – Gustavo Litovsky Mar 18 '13 at 15:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.