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.

cc430F6137 i2c interface with HMC5883l (compass AKA magnetometer)

Other Parts Discussed in Thread: CC430F6137

I have many questions from HMC5883L datasheet. This might be a question for Honeywell, but I am hoping E2E will give me a faster answer.

Below is a link to the HMC5883L datasheet.

http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Magneto/HMC5883L-FDS.pdf

Anyway, from the datasheet of HMC, I determined that the slave address is 0x1E (I don't know why they refer to it as a 7 bit address)

so when I want to configure the HMC registers, I set up the cc430 to master mode, multiple bytes TX (from the example code).

  • With slave address as 0x1E, and data as 0x3C 0x00 0x70 (to configure register A, at least on the logic analyzer, I2C lines work as they should, I don't know if this is actually setting the register A in the slave.)
  • again, With slave address as 0x1E, and data as 0x3C 0x01 0xA0 ( to configure register B, at least on the logic analyzer, I2C lines work as they should, I don't know if this is actually setting the register B in the slave.)
  • again, With slave address as 0x1E, and data as 0x3C 0x02 0x00 ( to configure mode register, at least on the logic analyzer, I2C lines work as they should, I don't know if this is actually setting the mode register in the slave.)

Now here is where my confusion is:

The datasheet for HMC says send 0x3D 0x06 for a read operation.

So I tried the following,

1st attempt

  • Setup cc430 as master TX. send <start>  0x1E( slave address:Write operation) data: 0x3D 0x06 <stop>
  • Then, setup cc430 as master RX mode. send <start>  0x1E( slave read operation) 0x00 0x00 0x00 0x00 0x00 0x00 <stop> And all I get back are 6 0's. 
2nd attempt:
  • Setup cc430 as master TX. send <start>  0x1E( slave address:Write operation) data: 0x3D 0x06, don't send stop.
  • Then, setup cc430 as master RX mode. send <start repeat> I get no response from the HMC. both my SCL and SDA are low.
Couple of questions I have here. From a basic google search I found code where people don't use 0x3C and 0x3D. Am I using 0x3C and 0x3D in the wrong way?
But I think the most important question I have is:
How should I set the cc430 to be able to read data from the HMC.
Someone please help me.
  • Dominic,

    Hope you are doing well.  I2C is a 7 bit address and the last bit determines if the operation is a read or write operation.  If you post your read and write functions here that might help me tell you if you should be inputting a 7bit address, an 8 bit address, or a 7bit address bit shifted by 1.  It can be confusing.  At the end of the day the best way is to look on the logic analyzer and make sure that the first 8 bits lining up with SCL are 3C for a write and 3D for a read.  If that is true then you are sending the proper address in your I2C read and write commands to the device.

    Now going through the Honeywell datasheet it looks like you are trying the sequence to read data continously from the sensor.  Here is the pseudocode they recommend? Can you send us your code to verify it matches? Particularly the interesting part will be how you are doing the waiting and how you are incrementing the address on the device.  The way this device decides what values to send you on a read command is to actually issue a write command first.  This is very common.  So for example the first time you issue the 0x3D 0x06 it is reading from register 0x03 because it automatically increments the register pointer inside the device.  It is very important to issue a write before every read or after every read in the looping case to reset the register point back to 0x03 to get the data from this point. 

    Below is an example of a (power-on) initialization process for "continuous-measurement mode":

    1. Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default, normal measurement)

    2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5, or any other desired gain)

    3. Write Mode (02) – send 0x3C 0x02 0x00 (Continuous-measurement mode)

    4. Wait 6 ms or monitor status register or DRDY hardware interrupt pin

    5. Loop

               Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain)

               Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively.

               Send 0x3C 0x03 (point to first data register 03)

               Wait about 67 ms (if 15 Hz rate) or monitor status register or DRDY hardware interrupt pin

        End_loop

  • Jason:

    I am making some changes to my code, I ll e-mail it to you once I am done. 

    But in the mean time:

     Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default, normal measurement)

    • The 0x3C, it is part of the data, or is that the address 0x1E, which under write operation, is 0x3C? I am asking if should explicitly be Txing 0x3C or if 0x1E is shifted to 0x3C "under the hood"
    "To clock out the new data, send:
    0x3D, and clock out DXRA, DXRB, DZRA, DZRB, DYRA, and DYRB located in registers 3 through 8." page 18 of the HMC datasheet.
    What about that 0x06 that should be along with 0x3D. Now, if 0x3D is for a read operation, how can we send a 0x06? 
    Do you agree with me, if I say that in an I2C read operation for device set up in Master mode, we can't Tx data? 
    
    
    Thanks for your input Jason, appreciate the time!
  • Dominic,

    When you look on the logic analyzer you should see the first full byte being 0x3C.  If you break up 0x3C into its bit form it is 00111100.  The LSB of that byte is a 1 if it is a read (0x3D) and a 0 if it is write (0x3C).  The address is embedded in this byte.  The address is 0x1E but then appended/shifted with a 0 or 1 depending on a read or write.  So in the datasheet the address of the device is 0011110 (7-bit) which when shifted left for a write becomes 00111100 (0x3C).  does that make sense? 

    For the read operation you send <0x3d> <number of bytes to be read from current register pointer>.  So to actually read from register 0x03 you would have to set the register pointer by issuing a write.  Here is the pseudocode which parlallels how the datasheet outlines it:

    To read 0x03:

    send 0x3c 0x03 //set register pointer to 0x03

    send 0x3d 0x01 //read 1 byte from register pointer at 0x03

    The resultant output you receive should the value stored at register 0x03.

    Make sense?

  • I know the datasheet says send 0x3D 0x06.

    But in I2C, once you set up the micro for a read operation, you can't send 0x06. If you want to send any data, you have to sent up the micro for a write operation.

    whatever you explained in the previous post makes perfect sense. I can get the HMC to repond now, but I CANT send 0x06 along with 0x3D.

    You know what I mean?

    If you set up the micro for a read operation master mode. You cannot write out to a slave. You can only expect to RX data from a slave. Am I correct here?

  • The USCI module automatically sends the slave address when UCTXSTT is set. The salve address is taken from the slave address register and teh UCTR bit, so if the slave datasheet says teh address is 0x3c/0x3d, then the USCI requires 0x1e as slave address.
    So as soon as you set UCTXSTT, a start condition is generated and then the slave address with R/W bit is sent. Once UCTXSTT is clear again, either the slave has responded or UCNACKIFG is set. If UCNACKIFG is clear, then you only need to send the data, not the slave address.

    It is superfluous (and destructive) to manually send the slave address with the USCI hardware. It is already sent when you regain control of what to send.

    JasonHaedt said:
    send 0x3c 0x03 //set register pointer to 0x03
    send 0x3d 0x01 //read 1 byte from register pointer at 0x03


    This cannot work. Sending 0x3d means that you're reading from the slave, so you cannot send a length byte. I2C is either read or write. After a start condition, the direction is fixed.
    It isn't necessary to defien the numbe rof bytes to read anway, USCI I2C continues to read until you set UCTXNACK or initiate a stop or repeated start condition.

    For the original operation, the sequence would be:

    UCB0SA = 0x1e; // set slave address
    set UCTR and UCTXSTT
    write 0x06 to UCB0TXBUF
    wait for UCTXSTT to clear
    check for UCNACKIFG
    if set, bail out. (UCTXBUF is discarded)
    if clear, UCB0TXBUF is being sent right now, so proceed...
    clear UCTR
    set UCTXSTT (will send a repeated start once the currently sending byte is transmitted to the slave)
    wait for UCTXSTT clear
    no need to check for UCNACKIFG now, if the slave has responded once, it will have this time too (but might be different for some specific slaves that are busy after the write for some time)
    loop: wait for UCRXIFG set
    check whether last byte received
    if so, set UCTXNACK
    read UCB0RXBUF
    if more to read, go to loop:
    set UCTXSTP
    done.

  • Hi there. I stumbled on this thread and coincidentally am working on a project which is utilizing this exact same micrcontroller and magnetometer (the sparkfun version on a breakout board)

    I am new to the TI 430 line of micrcontrollers, and although familiar with arduino micrcontrollers, have limited experience working with micrcontrollers where I had to set registers in code(did a little bit of this with Atmega AVR chips, but only timers and pin voltage change interrupts). This is also my first time using I2C.

    I have been trying to get this magnetometer working with this microcontroller by piecing together the example code for I2C provided for this micrcontroller. Specifically, I combined code from the files "cc430x613x_uscib0_i2c_08.c" (code for transmitting arrays of data via I2C) and "cc430x613x_uscib0_i2c_10.c" (code for receiving arrays of data via I2C).

    My code can be found at the following link (http://pastebin.com/x7iZ5sCR).

    My goal with that code right now is to set up the magnetometer for single measurement mode, and just get one measurement (aka, read 6 bytes). I am lighting up leds on the dev board for visual indication when stepping through the code with a debugger. I am following these instructions from the magnetometer data sheet except for the conversion at the end, I'll figure that out later. I can see the rx array values in my debugger at the end of my code when it should hypothetically have read 6 bytes.

    1. Write CRA (00) – send 0x3C 0x00 0x70 (8-average, 15 Hz default or any other rate, normal measurement)
    2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5, or any other desired gain)
    3. For each measurement query:
    Write Mode (02) – send 0x3C 0x02 0x01 (Single-measurement mode)
    Wait 6 ms or monitor status register or DRDY hardware interrupt pin
    Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain)
    Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively

    First off, I apologize for the very repetitive and crude nature of my code, I want to successfully get a measurement and understand what everything is doing before I compact and refine the code. Second, I am compiling this using the mspgcc compiler on linux which is why the interrupt syntax is different and I have different includes at the beginning. mspgcc also does not have several TI functions like __even_in_range, but from what I understand these aren't exactly necessary and just for optimization. I am also currently lacking a delay cycles function (not present in mspgcc) but will find a work around.

    Is there any advice you could give me or point me in the right direction? Am I doing anything completely wrong? I previously tested the first two transmission parts of my code and it seemed to be working since the code was stepping through correctly on the debugger and going through the interrupt. After I added an earlier version of the receiving code, something went wrong and I somehow broke my magnetometer board. I saw the switch statement receive the wrong value in the interrupt and go into the TX section of it instead of the RX section of it. I think that is because I had UCB0IE |= UCRXIE; to enable the RX interrupt, using orequals, instead of just UCB0IE = UCRXIE;. I'm guessing the microcontroller may have held the sda, sclk lines high for too long, but regardless my code no longer steps through (also hooked up the sensor to my arduino with code off the internet for that specific chip and it also did not respond). And yes, I did have pullup resistors.

    I have a new board to play with, and I also have a sparkfun usb logic analyzer to spy on what is going on the sda and sclk lines. I'll probably use the logic analyzer first with my arduino and the magnetometer to see what the correct sequence of events is supposed to look like, then try it on the cc430f6137. I'm hoping to get any input before I try this, though.

  • Did you check your SDA and SCL lines with a scope? Does something happen on them at all? But I guess it does, since your code does not contain any error handling (e.g. slave not responding). So it would never get past the first transmit attempt if something were wrong with th econnection.

    Michael Romanko said:
    First off, I apologize for the very repetitive and crude nature of my code, I want to successfully get a measurement and understand what everything is doing before I compact and refine the code.

    Hey, it's fine. Maybe it's not the most efficient way to code for this problem, but until you got the code running, it's easy to analyze or follow if you do it this way.

    Michael Romanko said:
    Second, I am compiling this using the mspgcc compiler on linux which is why the interrupt syntax is different and I have different includes at the beginning. mspgcc also does not have several TI functions like __even_in_range, but from what I understand these aren't exactly necessary and just for optimization. I am also currently lacking a delay cycles function (not present in mspgcc) but will find a work around.

    I use MSPGCC too (old V3.23).

    Current versions do support both, even_in_range and __delay_cycles (I think, it is called __delaycycles there). The latter has been implemented in version 20110809 for up to MAXLONG cycles.
    And you're right, even_in_range isn't necessary, only efficient. (and can be easily simulated with inline assembly, if really required)

    However, when trying to read the 6 bytes, you don't clear the UCTR bit (it does not auto-clear). So instead of starting a read, you're starting a write. Since you implicitely cleared (in the pasted code) TXIE when settign RXIE, the ISR is never called, as no RX interupt ever happens and the TX interrupt is blocked.
    In the code you described (where you only ORed RXIE and therefore didn't clear TXIE), the ISR was called for TX interrupt, and since nothing was to send, the TX ISR stopped transmission and waked main. With no bytes received.
    But with the code you posted to pastebin (with TXIE cleared), it will never wake up from the last LPM.

    Change

         RXByteCtr = 6;                          // Load RX byte counter
         while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
         UCB0CTL1 |= UCTXSTT;                    // I2C start condition

    to

         RXByteCtr = 6;                          // Load RX byte counter
         while (UCB0CTL1 & UCTXSTP);             // Ensure stop condition got sent
         UCB0CTL1 &= ~UCTXR;                    // clear transmit mode
         UCB0CTL1 |= UCTXSTT;                    // I2C start condition

    and it should work

**Attention** This is a public forum