Shop OBEX P1 Docs P2 Docs Learn Events
Using the HMC5883L compass. — Parallax Forums

Using the HMC5883L compass.

DONNODONNO Posts: 2
edited 2013-12-11 11:18 in General Discussion
I would like to know if this will work using the BS2SX stamp, or only the Propeller stamp.

Comments

  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2013-12-11 08:09
    DONNO,

    Welcome to the Parallax forum! Please feel free to ask any questions here. There are many people in this forum that are more than willing to help.

    The current code example on our product page is designed to work with the BS2p/pe/px. Below I have modified it so that it will work with the BS2/e/sx
    ' {$STAMP BS2sx}
    ' {$PBASIC 2.5}
       ' ============================================================================
    
    ' HMC5883L_Demo.bs2 - This is a Demo program for the HMC5883L Compass
    ' Module (PN: 29133). It displays the compass heading north, east, south and west
    ' clockwise from north.
    '
    ' Author.... (C) John A. Williams
    '
    ' Beau Schwabe (12-11-2013)
    ' Adapted from a BS2p/pe/px only version to be compatible with ALL BS2 versions
    ' ============================================================================
    
    ' -----[ Pins/Constants/Variables ]-------------------------------------------
    
    SDA            PIN      0                    'P0 transceives to/from SDA
    SCL            PIN      1                    'P1 sends clock pulses
    
    WRITE_DATA     CON     $3C  'Used to perform a Write operation
    READ_DATA      CON     $3D  'Used to perform a Read operation
    
    MODE           CON     $02  'Read/Write Register, Selects the operating mode. Default = Single measurement
                                'Send $3C $02 $00 on power up to change to continuous measurement mode.
    X_MSB          CON     $03  'Read Register, Output of X MSB 8-bit value.
    X_LSB          CON     $04  'Read Register, Output of X LSB 8-bit value.
    Z_MSB          CON     $05  'Read Register, Output of Z MSB 8-bit value.
    Z_LSB          CON     $06  'Read Register, Output of Z LSB 8-bit value.
    Y_MSB          CON     $07  'Read Register, Output of Y MSB 8-bit value.
    Y_LSB          CON     $08  'Read Register, Output of Y LSB 8-bit value.
    Cont_Mode      CON     $0
    SCALEFACTOR    CON     4081 'SCALEFACTOR = 65536 * (255/4095) = 4081
    SCALEOFFSET    CON     2048 'The output range of the HMC5883L is -2048 to 2047; therefore to get a 0 (LOWRANGE)to 4095 range
                                'to 4095 (HIGHRANGE) range 2048 must be added to the sensor value.
    ATNFACTOR      CON     127  'the ATN function input range is -127 TO 127. In order to get the positive scaled range of
                                '0 to 255 into the range of -127 to 127, 127 must be subtracted.
    LOWRANGE       CON     0    'see SCALEOFFSET
    HIGHRANGE      CON     4095 'see SCALEOFFSET
    Ack            CON     0    ' acknowledge bit
    Nak            CON     1    ' no ack bit
    
    X              VAR      Word 'x sensor measured value
    Y              VAR      Word 'y sensor measured value
    Z              VAR      Word 'z sensor measured value
    brads          VAR      Word 'angle in brads
    degr           VAR      Word 'angle in degrees
    azimuth        VAR      Word 'value calculated using X ATN -Y
    i2cWork        VAR     Byte  ' work byte for TX routine
    slvAddr        VAR     Byte  ' slave address
    i2cAck         VAR     Bit   ' Ack bit from device
    addrLen        VAR     Nib   ' 0, 1 or 2
    devAddr        VAR     Word  ' address in device
    i2cData        VAR     Byte  ' data to/from device
    
    ' -----[ Main Routine ]-------------------------------------------------------
    
    PAUSE 10                                     ' Give sensor needed power up time
    
      'I2COUT Pin, SlaveID   , { Address { \LowAddress }, } [ OutputData ]
      'I2COUT SDA, WRITE_DATA, MODE, [Cont_Mode]  ' Send continuous output command
      slvAddr = WRITE_DATA
      addrLen = 1
      devAddr = MODE
      i2cData = Cont_Mode
      GOSUB Write_Byte
    DO
      GOSUB GetRawReading                       ' Get RAW Compass reading
      'RAW X,Y,Z data...
      DEBUG HOME, "RAW X,Y,Z", CR
      DEBUG       "X = ",11, SDEC x, CR
      DEBUG       "Y = ",11, SDEC y, CR
      DEBUG       "Z = ",11, SDEC z, CR         ' Print angle
      DEBUG CR
    
      'Calculate Azimuth
      GOSUB GetAzimuth
    
      'Calculated Heading in Degrees
    
      brads = azimuth     'get angle
      degr = brads */ 361 'convert to degrees
    
      DEBUG CR
      DEBUG "Heading in NORTH, EAST, SOUTH, and WEST clockwise from NORTH:", CLREOL, CR, CR
      DEBUG "Degree " ,11, SDEC degr, CR 'display degrees
      GOSUB GetHeading
    
    LOOP
    
    ' -----[ Subroutines ]--------------------------------------------------------
    
    GetRawReading:                                 'Compass module subroutine
    
        PAUSE 400                                  'Wait for new data to become available
    
        'I2CIN PIN, SlaveID, { Address { \LowAddress }, } [ InputData ]
        'I2CIN SDA, READ_DATA, X_MSB, [X.HIGHBYTE]  'Read the data starting at x_msb 3
        slvAddr = READ_DATA
        addrLen = 1
        devAddr = X_MSB
        GOSUB Read_Byte
        X.HIGHBYTE = i2cData
    
        'I2CIN SDA, READ_DATA ,X_LSB, [X.LOWBYTE]
        slvAddr = READ_DATA
        addrLen = 1
        devAddr = X_LSB
        GOSUB Read_Byte
        X.LOWBYTE = i2cData
    
    'Do the same for Z and Y:
    
        'I2CIN SDA, READ_DATA, Z_MSB, [Z.HIGHBYTE]
        slvAddr = READ_DATA
        addrLen = 1
        devAddr = Z_MSB
        GOSUB Read_Byte
        Z.HIGHBYTE = i2cData
    
        'I2CIN SDA, READ_DATA ,Z_LSB, [Z.LOWBYTE]
        slvAddr = READ_DATA
        addrLen = 1
        devAddr = Z_LSB
        GOSUB Read_Byte
        Z.LOWBYTE = i2cData
    
        'I2CIN SDA, READ_DATA, Y_MSB, [Y.HIGHBYTE]
        slvAddr = READ_DATA
        addrLen = 1
        devAddr = Y_MSB
        GOSUB Read_Byte
        Y.HIGHBYTE = i2cData
    
        'I2CIN SDA, READ_DATA ,Y_LSB, [Y.LOWBYTE]
        slvAddr = READ_DATA
        addrLen = 1
        devAddr = Y_LSB
        GOSUB Read_Byte
        Y.LOWBYTE = i2cData
    
    RETURN
    
    
    
    GetAzimuth:
    'The output range of the HMC5883L is -2048 to 2047 and the input range of the ATN function
    'is -127 to 127.  I used the method to scale the output to the input discribed in Parallax's Smart
    'Sensors Student Guide v1 starting on page 76.
    'SCALEFACTOR = 65536 * (255/4095) = 4081
    'SCALEOFFSET = 2048
    'ATNFACTOR = 127
    'LOWRANGE = 0
    'HIGRANGE = 4095 (2048 + 2047 = 4095)
    
        x = x + SCALEOFFSET
        x = (x MIN LOWRANGE MAX HIGHRANGE) ** SCALEFACTOR - ATNFACTOR
        y = y + SCALEOFFSET
        y = (y MIN LOWRANGE MAX HIGHRANGE) ** SCALEFACTOR - ATNFACTOR
    
      DEBUG       "SCALED X,Y", CR
      DEBUG       "X = ",11, SDEC x, CR
      DEBUG       "Y = ",11, SDEC y, CR
    
        azimuth = x ATN -y
    
      DEBUG "Azimuth ",11, SDEC azimuth, CR ' display scaled azimuth
      DEBUG "COS Azimuth ",11, SDEC COS azimuth, CR ' display scaled cos azimuth
    
    RETURN
    
    GetHeading:
    'Gives a heading in North, South, East and West.
    
        IF degr = 90 THEN
            DEBUG "NORTH"
            DEBUG CLREOL
        ENDIF
    
        IF degr = 0 THEN
             DEBUG "EAST"
             DEBUG CLREOL
        ENDIF
    
        IF degr = 270 THEN
             DEBUG "SOUTH"
             DEBUG CLREOL
        ENDIF
    
        IF degr = 180 THEN
             DEBUG "WEST"
             DEBUG CLREOL
        ENDIF
    
    RETURN
    
    
    ' -----[ High Level I2C Subroutines]---------------------------------------
    
    ' Random location write
    ' -- pass device slave address in "slvAddr"
    ' -- pass address bytes (0, 1 or 2) in "addrLen"
    ' -- register address passed in "devAddr"
    ' -- data byte to be written is passed in "i2cData"
    
    Write_Byte:
      GOSUB I2C_Start                               ' send Start
      i2cWork = slvAddr & %11111110                 ' send slave ID
      GOSUB I2C_TX_Byte
      IF (i2cAck = Nak) THEN Write_Byte             ' wait until not busy
      IF (addrLen > 0) THEN
        IF (addrLen = 2) THEN
          i2cWork = devAddr.BYTE1                   ' send word address (1)
          GOSUB I2C_TX_Byte
        ENDIF
        i2cWork = devAddr.BYTE0                     ' send word address (0)
        GOSUB I2C_TX_Byte
      ENDIF
      i2cWork = i2cData                             ' send data
      GOSUB I2C_TX_Byte
      GOSUB I2C_Stop
      RETURN
    
    
    ' Random location read
    ' -- pass device slave address in "slvAddr"
    ' -- pass address bytes (0, 1 or 2) in "addrLen"
    ' -- register address passed in "devAddr"
    ' -- data byte read is returned in "i2cData"
    
    Read_Byte:
      GOSUB I2C_Start                               ' send Start
      IF (addrLen > 0) THEN
        i2cWork = slvAddr & %11111110               ' send slave ID (write)
        GOSUB I2C_TX_Byte
        IF (i2cAck = Nak) THEN Read_Byte            ' wait until not busy
        IF (addrLen = 2) THEN
          i2cWork = devAddr.BYTE1                   ' send word address (1)
          GOSUB I2C_TX_Byte
        ENDIF
        i2cWork = devAddr.BYTE0                     ' send word address (0)
        GOSUB I2C_TX_Byte
        GOSUB I2C_Start
      ENDIF
      i2cWork = slvAddr | %00000001                 ' send slave ID (read)
      GOSUB I2C_TX_Byte
      GOSUB I2C_RX_Byte_Nak
      GOSUB I2C_Stop
      i2cData = i2cWork
      RETURN
    
    
    ' -----[ Low Level I2C Subroutines]----------------------------------------
    
    ' *** Start Sequence ***
    
    I2C_Start:                                      ' I2C start bit sequence
      INPUT SDA
      INPUT SCL
      LOW SDA
    
    Clock_Hold:
      DO : LOOP UNTIL (SCL = 1)                     ' wait for clock release
      RETURN
    
    
    ' *** Transmit Byte ***
    
    I2C_TX_Byte:
      SHIFTOUT SDA, SCL, MSBFIRST, [i2cWork\8]      ' send byte to device
      SHIFTIN SDA, SCL, MSBPRE, [i2cAck\1]          ' get acknowledge bit
      RETURN
    
    
    ' *** Receive Byte ***
    
    I2C_RX_Byte_Nak:
      i2cAck = Nak                                  ' no Ack = high
      GOTO I2C_RX
    
    I2C_RX_Byte:
      i2cAck = Ack                                  ' Ack = low
    
    I2C_RX:
      SHIFTIN SDA, SCL, MSBPRE, [i2cWork\8]         ' get byte from device
      SHIFTOUT SDA, SCL, LSBFIRST, [i2cAck\1]       ' send ack or nak
      RETURN
    
    
    ' *** Stop Sequence ***
    
    I2C_Stop:                                       ' I2C stop bit sequence
      LOW SDA
      INPUT SCL
      INPUT SDA
      RETURN
    
  • DONNODONNO Posts: 2
    edited 2013-12-11 10:31
    Works, but "I2C_Start:" is missing "RETURN".
    Thanks..
  • Duane DegnDuane Degn Posts: 10,588
    edited 2013-12-11 10:56
    DONNO wrote: »
    Works, but "I2C_Start:" is missing "RETURN".
    Thanks..

    Are you sure?

    I don't use Stamps much but I think "Clock_Hold" is part of the start subroutine.

    BTW, The Propeller is not a Stamp.
  • banjobanjo Posts: 447
    edited 2013-12-11 10:56
    Would the same code work with good ol' BS2? I've ordered the same compass and have a Boe-Bot where I could try it when the customs releases the package...
  • Beau SchwabeBeau Schwabe Posts: 6,566
    edited 2013-12-11 11:03
    DONNO:
    Works, but "I2C_Start:" is missing "RETURN".
    This is correct, the START sequence sets the SDA and SCL to inputs. SDA is made LOW to initiate a START(transition from HIGH to LOW). The pullup resistors on the I2C line would normally make both the SDA and SCL HIGH, but if another device on the line is still talking the SCL will remain LOW. The START sequence falls through to "Clock Hold" where it remains until the SCL line goes HIGH.


    banjo:
    "Would the same code work with good ol' BS2?"
    Yes, this is what I tested the code it with.
  • banjobanjo Posts: 447
    edited 2013-12-11 11:18
    Thx Beau, I should've read your post more thoroughly. Sorry for sidetracking the OP
Sign In or Register to comment.