SparkFun Forums 

Where electronics enthusiasts find answers.

By BigBish
#178014
I got a problem I need help with.

I'm trying to configure the I2C on my freescale FRDM-KL25Z protoboard to run the HMC5883L magnetometer. My problem is that when I try to read the six access registers on the HMC5883L, all I get is 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF. What am I doing wrong?

Here is an image from my scope of the communication:
Image

I first write a 0x3C (write address of HMC5883L), then a 0x03 for the address of the first data register. I do a repeat start, then write a 0x3D (read address of HMC5883L), I then do a dummy read of the first register than six more reads of a data register. I use a while loop terminated by the IICIF flag to wait for transfers to complete.

The last byte transfer is cut off in the photo. The last three bytes are a 0x3C, 0x02, 0x01 to initiate a new single-measurement (this works).

All this takes place within the MagTask() function which reoccurs every 40ms. No interrupts or RTOS are used at this point.

My relevant code is below.

----- I2C.c -----
Code: Select all
#include "includes.h"

// Hardware Defines
#define   ENABLE_ACK()          (I2C0_C1 &= ~(I2C_C1_TXAK_MASK))
#define   DISABLE_ACK()         (I2C0_C1 |=   I2C_C1_TXAK_MASK)
#define   REPEATED_START()      (I2C0_C1 |=   I2C_C1_RSTA_MASK)
#define   ENTER_MASTER()        (I2C0_C1 |=  (I2C_C1_MST_MASK))
#define   ENTER_SLAVE()         (I2C0_C1 &= ~(I2C_C1_MST_MASK))
#define   ENTER_RX_MODE()       (I2C0_C1 &= ~(I2C_C1_TX_MASK))
#define   ENTER_TX_MODE()       (I2C0_C1 |=  (I2C_C1_TX_MASK))
#define   FLAG_REG()            (I2C0_S)
#define   WRITE_BYTE(x)         (I2C0_D = x)
#define   READ_BYTE()           (I2C0_D)

// Prototypes
void I2C0Init(void);
INT32U I2C0Read(void);
void I2C0Write(INT32U Data);
void I2C0Start(void);
void I2C0RepeatedStart(void);
void I2C0Stop(void);
void I2C0RXMode(void);
void I2C0TXMode(void);
void I2C0DisAck(void);
void I2C0EnAck(void);
void I2C0Wait(void);


void I2C0Init(void)
{
    PORTB_PCR0 = PORT_PCR_MUX(2);
    PORTB_PCR1 = PORT_PCR_MUX(2);
    
    I2C0_F = 0x50;
    
    I2C0_C1 |= I2C_C1_IICEN_MASK;
}

void I2C0Pause(void)
{
    INT32U n;
    for(n=1;n<50;n++)
    {
      asm("nop");
    }
}

INT32U I2C0Read(void)
{
    return READ_BYTE();
}

void I2C0Write(INT32U Data)
{
    WRITE_BYTE(Data);           // Send the device address
    I2C0Wait();
}

void I2C0Start(void)
{
	ENTER_MASTER();
}

void I2C0RepeatedStart(void)
{
	REPEATED_START();
}

void I2C0Stop(void)
{
	ENTER_SLAVE();
}

void I2C0RXMode(void)
{
	ENTER_RX_MODE();
}

void I2C0TXMode(void)
{
	ENTER_TX_MODE();
}

void I2C0DisAck(void)
{
	DISABLE_ACK();
}

void I2C0EnAck(void)
{
	ENABLE_ACK();
}

void I2C0Wait(void)
{
//    INT32U i;
//    for(i=0; i<500; i++){};
	
    while((FLAG_REG() & I2C_S_IICIF_MASK) == 0){}     // Wait for flag to set    
    FLAG_REG() |= I2C_S_IICIF_MASK;                   // Clear flag
}
----- Mag.c -----
Code: Select all
#include "includes.h"

// Prototypes
void MagInit(void);
void MagDelay2p2Us(void);
INT32U MagAxisX(void);
INT32U MagAxisY(void);
INT32U MagAxisZ(void);
void MagWrite(INT32U address, INT32U data);
void MagReadData(void);
void MagTask(void);

// Hardware Defines
#define  CONFIG_REG_A    0x00
#define  CONFIG_REG_B    0x01
#define  MODE_REG        0x02
#define  X_REG_MSB       0x03
#define  X_REG_LSB       0x04
#define  Z_REG_MSB       0x05
#define  Z_REG_LSB       0x06
#define  Y_REG_MSB       0x07
#define  Y_REG_LSB       0x08

// Private Variables
volatile INT32U xAxis;
volatile INT32U yAxis;
volatile INT32U zAxis;


void MagInit(void)
{
    xAxis = 0;
    yAxis = 0;
    zAxis = 0;
    
    MagWrite(CONFIG_REG_A, 0x70);
    MagWrite(CONFIG_REG_B, 0xA0);
}

void MagDelay2p2Us(void)
{
	INT32U i;
	for(i=0; i<100; i++){}
}

INT32U getAxisX(void)
{
    return xAxis;
}

INT32U getAxisY(void)
{
    return yAxis;
}

INT32U getAxisZ(void)
{
    return zAxis;
}

void MagWrite(INT32U address, INT32U data)
{
    I2C0Start();
	I2C0TXMode();
    
    I2C0Write(0x3C);
    I2C0Write(address);
    I2C0Write(data);

    I2C0RXMode();
    I2C0Stop();
    
    MagDelay2p2Us();
}

void MagReadData(void)
{
    I2C0Start();
    I2C0TXMode();
    I2C0EnAck();

    I2C0Write(0x3C);
    I2C0Write(X_REG_MSB);

    I2C0RepeatedStart();

    I2C0Write(0x3D);
//    I2C0Write(0x06);

//    I2C0DisAck();
    I2C0RXMode();

    xAxis   =  (I2C0Read() & 0xFF);           // Dummy Read
    I2C0Wait();
    xAxis   = ((I2C0Read() & 0xFF) << 0x08);
    I2C0Wait();
    xAxis  |=  (I2C0Read() & 0xFF);
    I2C0Wait();
    zAxis   = ((I2C0Read() & 0xFF) << 0x08);
    I2C0Wait();
    zAxis  |=  (I2C0Read() & 0xFF);
    I2C0Wait();
    yAxis   = ((I2C0Read() & 0xFF) << 0x08);
    I2C0Wait();

    I2C0DisAck();
    I2C0Stop();

    yAxis  |=  (I2C0Read() & 0xFF);

    MagDelay2p2Us();
}

void MagTask(void)
{
        MagReadData();
        MagWrite(MODE_REG, 0x01);
}
----- main.c -----
Code: Select all
#include "includes.h" // include peripheral declarations

// Task Prototypes
void WaitForSlice(void);
void TempTask(void);


int main(void)
{	
//-------------------------------------------------------------------------

    // Initializations
    SysTickInit();
    I2C0Init();
    MagInit();

    while(TRUE)
    {
        WaitForSlice(); // This function assures the main while loop is run every 40ms
                        // This function works fine
        MagTask();
    }
}
Thank you for your help, ask me any questions. :D This is my first time using I2C.

Andrew
By MarkMapo
#181892
Hi,
Saw your post and thought I'd reply - may be a little too late, but I'll try anyway.

I'm having some similar troubles with the HMC5883, but I've done lot's of I2C communications. If you can communicate on I2C to anything other than the HMC5883, then your code and microcontroller settings are correct. The problem that I'm having is the HMC5883 isn't sending an ACK on the 9th bit.

I can't see detail of your scope, but when you send the 0x3c command, it will actually be 9 bits. During the 9th bit, the master "releases" control of the sda line, and allows the pull up resistor to set the sda line to high. If the slave (in this instance the HMC5883) recognizes the device ID (0x3c) then it will pull the sda line low for the "9th" bit for the duration of the clock.

This should be the same thing with the device id for reading (0x3d). During reading, the master needs to send the ack on the 9th bit so the slave knows that the master received the byte. Looks like your code is set to do the ack, but you'll need the scope to check if the I2C communication is correct.

So if the 9th bit is high, you'll never get anything out of the HMC5883 - your data will always be 0xff because of the pull up resistor on the sda line.

I've been trying all sorts of addresses to get the HMC5883 to respond, but as yet I got nothin'. Let me know if you get the ACK bit!