This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F5529 DriverLib and HMC5883L via I2C



I am struggling to get I2C working using the most recent DriverLib (contained in MspWare 1.90.00.03 to talk to an HMC5883L magnometer.  (FWIW, it works fine with other MCU's, so it isn't a dead IC)

I am able to set the configuration registers, but any attempts to read data result in receiving one byte, and then the launchpad sending a NACK and ending the read.  The communication (viewed by protocol analyzer) looks like this:

Decoding I2C serial bus transactions between the X1 and X2 cursors
Please wait...

START
0x3C Control Byte: Slave Address 1E Write
 ACK
0x47
 ACK

STOP

START
0x3D Control Byte: Slave Address 1E Read
 ACK
0x 0
 ACK
0x 0
 NACK

START
0x3C Control Byte: Slave Address 1E Write
 ACK
0x 0
 ACK
0x67
 ACK

STOP

START
0x3D Control Byte: Slave Address 1E Read
 ACK
0x20
 ACK
0x83
 NACK

START
0x3C Control Byte: Slave Address 1E Write
 ACK
0x 0
 ACK
0x 7
 ACK

STOP

START
0x3D Control Byte: Slave Address 1E Read
 ACK
0x20
 ACK
0x83
 NACK

START
0x3C Control Byte: Slave Address 1E Write
 ACK
0x 0
 ACK
0x27
 ACK

STOP

Every send seems to work, but every read is failing. Here is the code I am using to try to read data:

uint8_t I2C_masterReadMultiple(uint8_t hmcRegister, uint8_t rxData[],
		uint16_t rxLength, uint32_t timeout) {
	uint8_t byte = 0;
	uint16_t passedInLength = rxLength;
	if (rxLength < 1) {
		if (BackChannel_Connected())
			BackChannel_WriteLine("Bad length passed in.");
		return STATUS_FAIL;
	}

	USCI_B_I2C_setMode(HMCI2C_BASE, USCI_B_I2C_TRANSMIT_MODE);
	USCI_B_I2C_disableInterrupt(HMCI2C_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
	USCI_B_I2C_enableInterrupt(HMCI2C_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
	if (USCI_B_I2C_masterMultiByteSendStartWithTimeout(HMCI2C_BASE, hmcRegister,
			timeout) == STATUS_FAIL) {
		if (BackChannel_Connected())
			BackChannel_WriteLine("Sending register address to slave failed.");
		return STATUS_FAIL;
	}

	USCI_B_I2C_setMode(HMCI2C_BASE, USCI_B_I2C_RECEIVE_MODE);
	USCI_B_I2C_disableInterrupt(HMCI2C_BASE, USCI_B_I2C_TRANSMIT_INTERRUPT);
	USCI_B_I2C_enableInterrupt(HMCI2C_BASE, USCI_B_I2C_RECEIVE_INTERRUPT);
	USCI_B_I2C_masterMultiByteReceiveStart(HMCI2C_BASE);   //Not sure if needed
	while (rxLength > 1) {
		rxLength--;
		while (~USCI_B_I2C_RECEIVE_INTERRUPT & UCRXIFG)
			;
		rxData[byte++] = USCI_B_I2C_masterMultiByteReceiveNext(HMCI2C_BASE);
	}
	while (~USCI_B_I2C_RECEIVE_INTERRUPT & UCRXIFG)
		;
	if (USCI_B_I2C_masterMultiByteReceiveFinishWithTimeout(
	HMCI2C_BASE, (uint8_t *) (*rxData + byte), timeout) == STATUS_SUCCESS) {
		rxLength = passedInLength;
		return rxLength;
	}
	return STATUS_FAIL;
}

Failure always occurs on the last call to USCI_B_I2C_masterMultiByteReceiveFinishWithTimeout (part of the Driver Library) which is:

//*****************************************************************************
//
//! \brief Finishes multi-byte reception at the Master end with timeout
//!
//! This function is used by the Master module to initiate completion of a
//! multi-byte reception. This function does the following: - Receives the
//! current byte and initiates the STOP from Master to Slave
//!
//! \param baseAddress is the base address of the I2C Master module.
//! \param rxData is a pointer to the location to store the received byte at
//!        master end
//! \param timeout is the amount of time to wait until giving up
//!
//! Modified bits are \b UCTXSTP of \b UCBxCTL1 register.
//!
//! \return STATUS_SUCCESS or STATUS_FAILURE of the transmission process.
//
//*****************************************************************************
bool USCI_B_I2C_masterMultiByteReceiveFinishWithTimeout(uint16_t baseAddress,
		uint8_t *rxData, uint32_t timeout) {
	assert(timeout > 0);

	uint32_t timeout2 = timeout;

	//Send stop condition.
	HWREG8(baseAddress + OFS_UCBxCTL1) |= UCTXSTP;

	//Capture data from receive buffer after setting stop bit due to
	//MSP430 I2C critical timing.
	*rxData = (HWREG8(baseAddress + OFS_UCBxRXBUF));

	//Wait for Stop to finish
	while ((HWREG8(baseAddress + OFS_UCBxCTL1) & UCTXSTP) && --timeout)
		;

	//Check if transfer timed out, then reset
	if (timeout == 0)
		return STATUS_FAIL;
	timeout = timeout2;

	// Wait for RX buffer
	while ((!(HWREG8(baseAddress + OFS_UCBxIFG) & UCRXIFG)) && --timeout)
		;

	//Check if transfer timed out
	if (timeout == 0) {
		_nop();
		return STATUS_FAIL;
	}

	return STATUS_SUCCESS;
}

The failure always occurs by timing out while waiting for the STOP condition.

What am I doing wrong?  Why isn't the MSP430 detecting the stop condition?  Why is it generating a NACK?



  • Moving to the MSP430 forum.

  • In case it helps, here is my full code.

  • Since I haven't received any help, I've been digging at this, and discovered a mistake above, the NACK at the end of reads is supposed to be there at the end to tell the slave to stop transmitting.

    However, that means the reads are only getting 2 bytes, rather than the 6 I am expecting.

  • Some things I noticed…

    You don’t need to disable the interrupts. In I2C mode, RX interrupt is only ever happening when an RX operation is ongoing, and TX will only ever happen when a TX operation is ongoing. Enabling and disabling interrupts when switching operation mode is superfluous.

     You call masterMultiByteSendStart. However, one parameter is hmcRegister, which is a uint8_t. I wonder how a function could send multiple bytes when the parameter you pass it is a single char. I guess it requires a pointer to a char (array) as parameter, so it can read multiple (including one) bytes from it. But maybe it is meant to just send the first of multiple bytes.
    However, it looks like the whole thing is using polling, so why are interrupts enabled?

    Well, perhaps do I simply not understand the concept behind these DriverLib functions.

    In any case, this line makes no sense:
    while (~USCI_B_I2C_RECEIVE_INTERRUPT & UCRXIFG);
    UCRXIFG is a constant. Likely the same as USCI_B_I2C_RECEIVE_INTERRUPT. And ~ is inverting the first constant, so the result of this expression is always zero.
    You probably meant to use a logic inversion (‘!’) and to use the interrupt register UCB0IFG (don’t forget the brackets around the AND operation). Nevertheless, if the RX interrupt is enabled (which it is) your main code will never see UCRXIFG set because then the ISR will be called until it is no longer set.
    I think this is your problem: you don’t wait until a byte is available before calling ReceiveFinishWithTimeout. So the function sets UCTXSTP, then reads RXBUF (which is empty)..

    Timeout is a strange construct too, as its duration depends on CPU speed and not on I2C speed or an absolute time.

    Finally, you pass (unint8_t*)(*rxData + byte) to this function. Which means, you are telling it to store the result to an address that is the content of the first byte of the RxData array plus the byte offset. Probably writing somewhere into the hardware register area. Instead, pass (rxData+byte), which is the pointer to the array plus byte offset, and most likely the place where you want your result stored.

  • Jens-Michael Gross said:
    However, it looks like the whole thing is using polling, so why are interrupts enabled?

    Well, perhaps do I simply not understand the concept behind these DriverLib functions.

    It took me a while to figure out, but masterMultiByteSendStart is for sending the first byte of a multibyte message, and doesn't send a stop after the byte is sent.  (In this scenario, a repeated start follows sending the register address to the HMC5883l, followed by Reading what it sends back, and then a stop.

    Jens-Michael Gross said:
    In any case, this line makes no sense:
    while (~USCI_B_I2C_RECEIVE_INTERRUPT & UCRXIFG);
    UCRXIFG is a constant. Likely the same as USCI_B_I2C_RECEIVE_INTERRUPT. And ~ is inverting the first constant, so the result of this expression is always zero.
    You probably meant to use a logic inversion (‘!’) and to use the interrupt register UCB0IFG (don’t forget the brackets around the AND operation). Nevertheless, if the RX interrupt is enabled (which it is) your main code will never see UCRXIFG set because then the ISR will be called until it is no longer set.
    I think this is your problem: you don’t wait until a byte is available before calling ReceiveFinishWithTimeout. So the function sets UCTXSTP, then reads RXBUF (which is empty)..

    Good catch.  I'll look into this next chance I get. 

    I'm a bit fuzzy on interrupts vs. polling, and how each works.  Does polling only work when no ISR is defined, but Interrupts are enabled?  I'm a bit frustrated in figuring out why some of my tries at this have failed and some succeeded.  If Interrupts are disabled, do the corresponding IFG flags still get set (without the ISR firing)?  Or does disabling interrupts stop the flag from being set at all?

    Lastly, since ISRs are defined at compile time, is there a clean way to change the code that executes for the ISR at runtime?  E.G.  Have one set of ISR code run while I'm transmitting, another when I'm receiving, and another if I'm testing it a slave exists?  I know that I can write a jumbo ISR with all the code inside various branches, but that seems rather clunky.

  • In my own library, I have ‘first byte’, ‘next byte’ and ‘last byte’ functions for SPI and I2C. Quite similar.

    Many people have been confused by the interrupt mechanism.
    Basically, the IFG flags are signals of an event. They are always set when the even thappens and can be reset by software or do reset automatically on certain actions (like readign the IV register, rading/writing from/to a buffer register, sometimes even by entering the associated ISR).
    However, they do not trigger an interrupt by themselves. To have that happen, the associated IE bit needs to be set too, and also the global GIE bit.

    If GIE or the individual IE bit are not set, the ISR is not called, and the main code can manually cheack the IFG bits for what happened and what to do. This is called polling.
    So to use polling, don't set the IE bits. If there is an ISR for this interrupt, it will be ignored. As soon as you set the IE bits, the associated ISR will be called and has to be there.

**Attention** This is a public forum