Shop OBEX P1 Docs P2 Docs Learn Events
MPU-9150 PASM Driver — Parallax Forums

MPU-9150 PASM Driver

zlantzzlantz Posts: 136
edited 2014-10-13 18:18 in Propeller 1
I have still been trying to get a driver to be satisfactory. I have wrote a spin driver, which works well, however it is noizy & no matter what I do, I cannot filter the noise. Then there is the mpu-6050 pasm driver, it works for the mpu-9150, but you only get accel, gyro & temp values.

So I have taken the advice to keep working on the pasm version. I can read the MPU Chip ID, the AK8975 ID, and the Mag x, y, & z registers on the AK8975.

The problem is, I need to be able to set $0A on the AK8975 to %00000001 (Single-Measurement Mode) before each read of the Mag. I am pretty confident that I have coded this right, but when I try to set this bit, my Mag reads out 0. But if I Dont put it in single measure mode, it will read the x, y, z position on power up, but will remain constant without changing.

'' MPU-60X0-PASM.spin
'' Reads gyro and accelerometer data from the MPU-60X0 chips
'' Read loop is in Propeller Assembler
''
'' Based on Jason Dorie's code for the ITG-3200 and ADCL345 chips
''
'' Note that this code assumes an 80 MHz clock
''
'' The TestMPU routine can be used to verify correct setup of, and
'' communication with, the MPU-60X0.  Load the object into RAM, then
'' use f12 to bring up the terminal emulator to see the output.
''

{
  *******************************************
  * User Init Select                        *
  * Updates by: Zack Lantz                  *
  *******************************************

  Place in Main .Spin:

  Con
  
    ' // Accelerometer Settings
    mAFS0 = 0
    mAFS1 = 1
    mAFS2 = 2
    mAFS3 = 3
  
    ' // Gyroscope Settings
    mFS0 = 0
    mFS1 = 1
    mFS2 = 2
    mFS3 = 3
    
    ' // Digital Low Pass Filter Settings
    DLP0 = 0          ' Bandwidth = 260 Hz
    DLP1 = 1          ' Bandwidth = 184 Hz
    DLP2 = 2          ' Bandwidth = 94  Hz
    DLP3 = 3          ' Bandwidth = 44  Hz
    DLP4 = 4          ' Bandwidth = 21  Hz
    DLP5 = 5          ' Bandwidth = 10  Hz
    DLP6 = 6          ' Bandwidth = 5   Hz
    DLP7 = 7          ' Reserved
  
    ' // Current Settings (Passed to MPU-6050.spin)
    AccelFS = AFS2
    GyroFS  = FS3
    mAFS    = mAFS2
    mFS     = mFS3
    mDLP    = DLP3

    
  Then start MPU Driver with:
  
  MPU.Start(MPUscl, MPUsda, mAFS, mFS, mDLP)    ' MPU-6050 Gyro & Accel Sensor Data  w/ User Init AFS, FS, & DLP

  
  Everything else works the same.

  
  New Functions:
    Start (SCL, SDA, aFS, gFS, fDLP)
    StartA(SCL, SDA, aFS, gFS, fDLP, PM) ', SF)
    StartX(SCL, SDA)

  *** Note:  I didn't add in SampleRate feature as it is 0 to 255 & Calculated as follows:
  
  Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)

  Where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled (see Register 26) 
}

{{

The slave address of the MPU-60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is
determined by the logic level on pin AD0. This allows two MPU-60X0s to be connected to the same I2C bus.
When used in this configuration, the address of the one of the devices should be b1101000 (pin AD0
is logic low) and the address of the other should be b1101001 (pin AD0 is logic high).

}}


CON
  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000


CON                        ' CONs for TestMPU test routine   
  SDA_PIN        = 10
  SCL_PIN        = 11
  SERIAL_TX_PIN  = 30
  SERIAL_RX_PIN  = 31

VAR
  long x0, y0, z0, a0, b0, c0, t
  long Cog

  long rx, ry, rz, temp, ax, ay, az, arx, ary, mx, my, mz        'PASM code assumes these to be contiguous
  long cid, mid                                                  'PASM code assumes these to be contiguous

OBJ
  debug : "FullDuplexSerial"  

PUB TestMPU  | MPUcog 
 '-----------------------------------------------
  ' Start serial i/o cog
  ' Start cog to pull gyro/accel data from chip
  ' Print data to serial out every few seconds
  '------------------------------------------------
  debug.start(SERIAL_RX_PIN, SERIAL_TX_PIN, 0, 115200) 'Start cog to allow IO with serial terminal
     
  debug.str(string("Starting..."))
  debug.tx(13)
  debug.str(string("GX  GY  GZ    AX  AY  AZ"))
  debug.tx(13)
  debug.str(string("-------------------------"))
  debug.tx(13)

  
  MPUcog := StartX( SCL_PIN, SDA_PIN )

  'Output gyro data, then accel data, once per second
  repeat

     debug.str(string("0x")) 
     debug.hex(GetChipID, 2)
     debug.str(string(", "))
     debug.str(string("0x")) 
     debug.hex(GetMagID, 2)
     debug.str(string("   "))     

     debug.dec(GetRX)
     debug.str(string(", "))
     debug.dec(GetRY)
     debug.str(string(", "))
     debug.dec(GetRZ)
     debug.str(string("   "))

     debug.dec(GetAX)
     debug.str(string(", "))
     debug.dec(GetAY)
     debug.str(string(", "))
     debug.dec(GetAZ)
     debug.str(string("   "))

     debug.dec(GetARX)
     debug.str(string(", "))
     debug.dec(GetARY)     
     debug.str(string("   "))

     debug.dec(GetMX)
     debug.str(string(", "))
     debug.dec(GetMY)
     debug.str(string(", "))
     debug.dec(GetMZ)
     debug.tx(13)
     

' // Basic Start with User Accel AFS & Gyro FS & DLP   
PUB Start( SCL, SDA, aFS, gFS, fDLP ) : Status 

  if aFS == 0
    AccelFS := %00000000  ' ±  2 g
  elseif aFS == 1          
    AccelFS := %00001000  ' ±  4 g
  elseif aFS == 2
    AccelFS := %00010000  ' ±  8 g
  elseif aFS == 3
    AccelFS := %00011000  ' ± 16 g

  if gFS == 0
    GyroFS := %00000000   ' ±  250 ° /s
  elseif gFS == 1
    GyroFS := %00001000   ' ±  500 ° /s
  elseif gFS == 2
    GyroFS := %00010000   ' ± 1000 ° /s
  elseif gFS == 3
    GyroFS := %00011000   ' ± 2000 ° /s

                      '| DLPF_CFG |   Accelerometer    |          Gyroscope          |       
  if fDLP == 0        '             Bw (Hz)  Delay (ms)  Bw (Hz)  Delay (ms)  FS (Khz)
    DLP := %00000000  '      0        260        0         256       0.98        8
  elseif fDLP == 1
    DLP := %00000001  '      1        184       2.0        188       1.9         1
  elseif fDLP == 2
    DLP := %00000010  '      2         94       3.0         98       2.8         1
  elseif fDLP == 3
    DLP := %00000011  '      3         44       4.9         42       4.8         1
  elseif fDLP == 4
    DLP := %00000100  '      4         21       8.5         20       8.3         1
  elseif fDLP == 5
    DLP := %00000101  '      5         10      13.8         10      13.4         1
  elseif fDLP == 6    
    DLP := %00000110  '      6          5      19.0          5      18.6         1
  elseif fDLP == 7
    DLP := %00000111  '      7           RESERVED             RESERVED           8

  PowerMgmt  := %00000001  ' X gyro as clock source
  SampleRate := %00000001  ' 500 Hz
    
  ComputeTimes

  gyroSCL  := 1 << SCL     'save I2C pins
  gyroSDA  := 1 << SDA
  
  Status := Cog := cognew(@Start_Sensors, @rx) + 1

  CalibrateAccel

' // Start w/ Full User Init Settings
PUB StartA( SCL, SDA, aFS, gFS, fDLP, PM ) : Status 

  if aFS == 0
    AccelFS := %00000000
  elseif aFS == 1
    AccelFS := %00001000
  elseif aFS == 2
    AccelFS := %00010000
  elseif aFS == 3
    AccelFS := %00011000

  if gFS == 0
    GyroFS := %00000000
  elseif gFS == 1
    GyroFS := %00001000
  elseif gFS == 2
    GyroFS := %00010000
  elseif gFS == 3
    GyroFS := %00011000

                      '| DLPF_CFG |   Accelerometer    |          Gyroscope          |       
  if fDLP == 0        '             Bw (Hz)  Delay (ms)  Bw (Hz)  Delay (ms)  FS (Khz)
    DLP := %00000000  '      0        260        0         256       0.98        8
  elseif fDLP == 1
    DLP := %00000001  '      1        184       2.0        188       1.9         1
  elseif fDLP == 2
    DLP := %00000010  '      2         94       3.0         98       2.8         1
  elseif fDLP == 3
    DLP := %00000011  '      3         44       4.9         42       4.8         1
  elseif fDLP == 4
    DLP := %00000100  '      4         21       8.5         20       8.3         1
  elseif fDLP == 5
    DLP := %00000101  '      5         10      13.8         10      13.4         1
  elseif fDLP == 6    
    DLP := %00000110  '      6          5      19.0          5      18.6         1
  elseif fDLP == 7
    DLP := %00000111  '      7           RESERVED             RESERVED           8
                      
  if PM == 0
    PowerMgmt := %00000000  ' Internal 8MHz oscillator
  elseif PM == 1
    PowerMgmt := %00000001  ' PLL with X axis gyroscope reference
  elseif PM == 2
    PowerMgmt := %00000010  ' PLL with Y axis gyroscope reference
  elseif PM == 3
    PowerMgmt := %00000011  ' PLL with Z axis gyroscope reference
  elseif PM == 4
    PowerMgmt := %00000100  ' PLL with external 32.768kHz reference
  elseif PM == 5
    PowerMgmt := %00000101  ' PLL with external 19.2MHz referenc
  elseif PM == 6
    PowerMgmt := %00000110  ' Reserved
  elseif PM == 7
    PowerMgmt := %00000111  ' Stops the clock and keeps the timing generator in reset
          
  ' *** Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)  
  ' // 0 threw 255
  SampleRate := %00000001   ' 500 Hz Sample Rate,   %00000000 = 1000 Hz Sample rate
  
  ComputeTimes

  gyroSCL  := 1 << SCL     'save I2C pins
  gyroSDA  := 1 << SDA
  
  Status := Cog := cognew(@Start_Sensors, @rx) + 1

  CalibrateAccel

' // Start Basic, No User Init
PUB StartX( SCL, SDA ) : Status 

  AccelFS := %00010000  ' AFS2
  GyroFS  := %00011000  ' FS3

  DLP        := %00000011  ' 40 Hz
  PowerMgmt  := %00000001  ' X gyro as clock source
  SampleRate := %00000001  ' 500 Hz
  
  ComputeTimes

  gyroSCL  := 1 << SCL     'save I2C pins
  gyroSDA  := 1 << SDA
  
  Status := Cog := cognew(@Start_Sensors, @rx) + 1

  CalibrateAccel
  CalibrateGyro


PUB Stop

  if Cog
    cogstop(Cog~ - 1)


'**********************
'   Accessors
'**********************
PUB GetTemp          ' Temperature
  return temp

'Pub GetTempF         ' Temp Deg F

'Pub GetTempC         ' Temp Deg C

PUB GetRX            ' Accel X - Zero Offset
  return rx - x0

PUB GetRY            ' Accel Y - Zero Offset
  return ry - y0

PUB GetRZ            ' Accel Z - Zero Offset
  return rz - z0

PUB GetAX            ' Gyro X - Zero Offset
  return ax - a0

PUB GetAY            ' Gyro Y - Zero Offset
  return ay - b0

PUB GetAZ            ' Gyrp Z - Zero Offset
  return az - c0

PUB GetARX           ' Pitch Angle
  return arx

PUB GetARY           ' Roll Angle
  return ary

Pub GetAccelOffsetX  ' Accelerometer Zero Offset X
  return x0
  
Pub GetAccelOffsetY  ' Accelerometer Zero Offset Y
  return y0
  
Pub GetAccelOffsetZ  ' Accelerometer Zero Offset Z
  return z0
  
Pub GetGyroOffsetX   ' Gyroscope Zero Offset X
  return a0
  
Pub GetGyroOffsetY   ' Gyroscope Zero Offset Y
  return b0
  
Pub GetGyroOffsetZ   ' Gyroscope Zero Offset Z
  return c0

Pub CalAccel         ' Calibrate Accelerometer
  CalibrateAccel

Pub CalGyro          ' Calibrate Gyroscope
  CalibrateGyro


Pub GetChipID
  return cID

Pub GetMagID
  return mID  

Pub GetMX
  return mx

Pub GetMY
  return my

Pub GetMZ
  return mz

  
PRI computeTimes                                       '' Set up timing constants in assembly
                                                       '  (Done this way to avoid overflow)
  i2cDataSet := ((clkfreq / 10000) *  350) / 100000    ' Data setup time -  350ns (400KHz)
  i2cClkLow  := ((clkfreq / 10000) * 1300) / 100000    ' Clock low time  - 1300ns (400KHz)
  i2cClkHigh := ((clkfreq / 10000) *  600) / 100000    ' Clock high time -  600ns (400KHz)
  i2cPause   := clkfreq / 100000                       ' Pause between checks for operations


PRI CalibrateAccel | tc, xc, yc, zc, dr

  x0 := 0         ' Initialize offsets
  y0 := 0
  z0 := 0
  
  'wait 1/2 second for the body to stop moving
  waitcnt( constant(80_000_000 / 2) + cnt )

  'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
  xc := 0
  yc := 0
  zc := 0

  repeat 256
    xc += rx
    yc += ry
    zc += rz

    waitcnt( constant(80_000_000/192) + cnt )

  'Perform rounding
  if( xc > 0 )
    xc += 128
  elseif( xc < 0 )
    xc -= 128

  if( yc > 0 )
    yc += 128
  elseif( yc < 0 )
    yc -= 128

  if( zc > 0 )
    zc += 128
  elseif( zc < 0 )
    zc -= 128
    
  x0 := xc / 256
  y0 := yc / 256
  z0 := zc / 256
  

PRI CalibrateGyro | tc, xc, yc, zc, dr

  a0 := 0         ' Initialize offsets
  b0 := 0
  c0 := 0
  
  'wait 1/2 second for the body to stop moving
  waitcnt( constant(80_000_000 / 2) + cnt )

  'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
  xc := 0
  yc := 0
  zc := 0

  repeat 256
    xc += ax
    yc += ay
    zc += az

    waitcnt( constant(80_000_000/192) + cnt )

  'Perform rounding
  if( xc > 0 )
    xc += 128
  elseif( xc < 0 )
    xc -= 128

  if( yc > 0 )
    yc += 128
  elseif( yc < 0 )
    yc -= 128

  if( zc > 0 )
    zc += 128
  elseif( zc < 0 )
    zc -= 128
    
  a0 := xc / 256
  b0 := yc / 256
  c0 := zc / 256


DAT
        org   0

Start_Sensors

'  --------- Debugger Kernel add this at Entry (Addr 0) ---------
'   long $34FC1202,$6CE81201,$83C120B,$8BC0E0A,$E87C0E03,$8BC0E0A
'   long $EC7C0E05,$A0BC1207,$5C7C0003,$5C7C0003,$7FFC,$7FF8
'  -------------------------------------------------------------- 

        mov             p1, par                         ' Get data pointer
        mov             prX, p1                         ' Store the pointer to the rx var in HUB RAM
        add             p1, #4
        mov             prY, p1                         ' Store the pointer to the ry var in HUB RAM
        add             p1, #4
        mov             prZ, p1                         ' Store the pointer to the rz var in HUB RAM
        add             p1, #4
        mov             pT, p1                          ' Store the pointer to the temp var in HUB RAM
        add             p1, #4
        mov             paX, p1                         ' Store the pointer to the ax var in HUB RAM
        add             p1, #4
        mov             paY, p1                         ' Store the pointer to the ay var in HUB RAM
        add             p1, #4
        mov             paZ, p1                         ' Store the pointer to the az var in HUB RAM
        add             p1, #4
        mov             paRX, p1                        ' Store the pointer to the arx var in HUB RAM
        add             p1, #4
        mov             paRY, p1                        ' Store the pointer to the ary var in HUB RAM
        add             p1, #4
        mov             pmX, p1                         ' Store the pointer to the ax var in HUB RAM
        add             p1, #4
        mov             pmY, p1                         ' Store the pointer to the ay var in HUB RAM
        add             p1, #4
        mov             pmZ, p1                         ' Store the pointer to the az var in HUB RAM
        add             p1, #4
        mov             pcID, p1                        ' Store the pointer to the ary var in HUB RAM
        add             p1, #4
        mov             pmID, p1                        ' Store the pointer to the ary var in HUB RAM


        mov             i2cTemp,i2cPause
        add             i2cTemp,CNT                     ' Wait 10us before starting
        waitcnt         i2cTemp,#0
       
        call            #SetConfig

        mov             loopCount, CNT
        add             loopCount, loopDelay


'------------------------------------------------------------------------------
       
' Main loop
'   loopDelay defined in data section
'   Nominally set to CLK_FREQ/200 give 200hz update rate, but the update takes less than 
'   500us, so the delay could potentially be set to give an update rate as high as 2000hz
'
:loop
                        call    #MPUReadValues
                        call    #MPUComputeDrift
                        call    #ComputeAngles

                        'call    #MPUGetChipID    ' = 0x68 = Correct
                        'call    #MPUGetMagID     ' = 0x48 = Correct

                        [B]call    #ConfigMag    ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.  [/B]

                        call    #MPUGetMagX
                        call    #MPUGetMagY
                        call    #MPUGetMagZ                                                  

                        wrlong  iT, pT

                        subs    irX, drift
                        wrlong  irX, prX

                        subs    irY, drift
                        wrlong  irY, prY

                        subs    irZ, drift
                        wrlong  irZ, prZ

                        wrlong  iaX, paX
                        wrlong  iaY, paY
                        wrlong  iaZ, paZ
                        
                        'Sign extend the 15th bit
                        test    iaRX, i2cWordReadMask     wc
                        muxc    iaRX, i2cWordMask

                        'Sign extend the 15th bit
                        test    iaRY, i2cWordReadMask     wc
                        muxc    iaRY, i2cWordMask                                                                                      
                        
                        wrlong  iaRX, paRX
                        wrlong  iaRY, paRY

                        wrlong  iMX, pMX
                        wrlong  iMY, pMY
                        wrlong  iMZ, pMZ

                        mov     icID, #$68     ' (Not Calling MPUGetChipID or MPUGetMagID)
                        mov     imID, #$48
                        
                        wrlong  icID, pcID
                        wrlong  imID, pmID                          
                        
                        waitcnt loopCount, loopDelay
                        jmp     #:loop        



'------------------------------------------------------------------------------
' MPUReadValues
'
'   Starting at the ACCEL_X data register,  read in the 3 accel values,
'   the temperature, and the 3 gyro values, as these are held in
'   sequential register locations.
'
MPUReadValues
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        mov     i2cAddr, #59            ' Address of ACCEL_XOUT_H
                        mov     i2cDevID, #%11010000    ' Device ID of the MPU 
                        call    #StartRead              ' Tell the I2C device we're starting

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                        call    #i2cRead                                          
                        call    #i2cRead

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     iaX, i2cData


                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                        call    #i2cRead
                        call    #i2cRead

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     iaY, i2cData


                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                        call    #i2cRead
                        call    #i2cRead

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     iaZ, i2cData


                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                        call    #i2cRead                       
                        call    #i2cRead

                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     iT, i2cData


                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                        call    #i2cRead                                          
                        call    #i2cRead

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     irX, i2cData

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                        call    #i2cRead                                          
                        call    #i2cRead

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     irY, i2cData

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                        call    #i2cRead
                        test    i2cTestCarry, #1 wc      ' Set the carry flag to tell it we're done                                          
                        call    #i2cRead

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     irZ, i2cData
                        
                        call    #i2cStop                        

MPUReadValues_Ret       ret



'------------------------------------------------------------------------------
{

MPUGetChipID
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        mov     i2cAddr, #117            ' Address of ACCEL_XOUT_H
                        mov     i2cDevID, #%11010000    ' Device ID of the MPU 
                        call    #StartRead              ' Tell the I2C device we're starting

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment        
                        call    #i2cRead

                        ' // Single Byte Read; Shift Right 8 Bits
                        shr     i2cData, #8                  
                        mov     icID, i2cData           ' // Fixed = 0x68
                        
                        call    #i2cStop                        

MPUGetChipID_Ret        ret



'------------------------------------------------------------------------------


MPUGetMagID
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        mov     i2cAddr, #00
                        mov     i2cDevID, #%00011000   
                        call    #StartRead              ' Tell the I2C device we're starting

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment        
                        call    #i2cRead

                        ' // Single Byte Read; Shift Right 8 Bits
                        shr     i2cData, #8                  
                        mov     imID, i2cData           ' // Fixed = 0x48
                        
                        call    #i2cStop                        

MPUGetMagID_Ret         ret

}
'------------------------------------------------------------------------------


MPUGetMagX
                        [B]'jmpret  MPUGetMagX, #MPUMag      ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.[/B]

                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        mov     i2cAddr, #03
                        mov     i2cDevID, #%00011000   
                        call    #StartRead              ' Tell the I2C device we're starting

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc           
                        call    #i2cRead
                        test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                        call    #i2cRead                            

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     imX, i2cData           
                        
                        call    #i2cStop                        

MPUGetMagX_Ret          ret

'------------------------------------------------------------------------------


MPUGetMagY
                       [B] 'jmpret  MPUGetMagY, #MPUMag     ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.[/B]  
                                  
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        mov     i2cAddr, #05
                        mov     i2cDevID, #%00011000   
                        call    #StartRead              ' Tell the I2C device we're starting

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc           
                        call    #i2cRead
                        test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                        call    #i2cRead                            

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     imY, i2cData           
                        
                        call    #i2cStop                        

MPUGetMagY_Ret          ret

'------------------------------------------------------------------------------


MPUGetMagZ
                       [B] 'jmpret  MPUGetMagZ, #MPUMag     ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode. [/B] 
                                 
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        mov     i2cAddr, #07
                        mov     i2cDevID, #%00011000   
                        call    #StartRead              ' Tell the I2C device we're starting

                        mov     i2cMask, i2cWordReadMask
                        test    i2cTestCarry, #0 wc           
                        call    #i2cRead
                        test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                        call    #i2cRead                            

                        'Sign extend the 15th bit
                        test    i2cData, i2cWordReadMask     wc
                        muxc    i2cData, i2cWordMask
                        mov     imZ, i2cData           
                        
                        call    #i2cStop                        

MPUGetMagZ_Ret          ret


'------------------------------------------------------------------------------

[B]
        ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.
        
' // From my Spin Driver (Works):
{
      i2c.Start(SCL)            
      i2c.Write(SCL, MagAddrW)     ' AK8975 Write Address
      i2c.Write(SCL, $0A)          ' CTRL Register
      i2c.Write(SCL, %00000001)    ' Set config $0A to %00000001 to turn on the device.    
      i2c.Stop(SCL)
}

ConfigMag
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL

                        call    #i2cReset  
                        mov     i2cDevID, #%00011000   ' MagAddrW
                        mov     i2cMask,  #%10000000                  
                        call    #i2cWrite

                        mov     i2cAddr, #10           ' $0A
                        mov     i2cMask,  #%10000000
                        call    #i2cWrite
                        
                        mov     i2cValue, #%00000001   ' %00000001
                        mov     i2cMask,  #%10000000
                        call    #i2cWrite

                        call    #i2cStop                                      

ConfigMag_Ret           ret[/B]

'------------------------------------------------------------------------------
                        ' Compute drift - for my gyro (Jason's ITG-3200)
                        '(Temp + 15000) / 100 = drift
                        
'------------------------------------------------------------------------------
MPUComputeDrift
                        mov     drift, iT               ' Start with the temperature reading
                        add     drift, tempOffset       ' Offset it by 15,000

                        ' divide drift by 100                        

                        mov     divisor, #100
                        mov     dividend, drift
                        test    dividend, i2cWordReadMask    wc

                        muxc    signbit, #1             ' record the sign of the original value
                        abs     dividend, dividend

                        mov     divCounter, #10     
                        shl     divisor, divCounter
                        mov     resultShifted, #1
                        shl     resultShifted, divCounter

                        add     divCounter, #1
                        mov     drift, #0

:divLoop                        
                        cmp     dividend, divisor   wc
              if_nc     add     drift, resultShifted
              if_nc     sub     dividend, divisor
                        shr     resultShifted, #1
                        shr     divisor, #1     
                        djnz    divCounter, #:divLoop

                        test    signbit, #1     wc
                        negc    drift, drift
                        
                        
MPUComputeDrift_Ret     ret



'------------------------------------------------------------------------------
ComputeAngles
                        mov     cx, iaZ
                        mov     cy, iaX
                        call    #cordic
                        mov     iaRX, ca

                        mov     cx, iaZ
                        mov     cy, iaY
                        call    #cordic
                        mov     iaRY, ca

ComputeAngles_ret       ret

        
'------------------------------------------------------------------------------
' SetConfig
'
'  See MPU-6000/6050 Register Map document for register addresses and
'   valid settings
'
SetConfig
                        mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL
                        call    #i2cReset                'Reset i2c
                         
:MPUSetConfig           mov     i2cDevID, #%11010000     'Device ID for the MPU-6000/6050 

                        ' // Power Management
                        mov     i2cAddr, #107             'Set PWR_MGMT_1 register bit 0 to choose
                        mov     i2cValue, PowerMgmt       ' X gyro as clock source  '
                        call    #i2cWriteRegisterByte
                        
                        ' // I2C Master Disabled
                        mov     i2cAddr, #106       
                        mov     i2cValue, #%00000000
                        call    #i2cWriteRegisterByte
                        
                                                
                        ' // Digital Low Pass Filter _ Config
                        mov     i2cAddr, #26      
                        mov     i2cValue, DLP             'Set DLPF_CONFIG to 3 for 40Hz bandwidth 
                        call    #i2cWriteRegisterByte     
                        
                        ' // Sample Rate Divider
                        mov     i2cAddr, #25              'SMPLRT_DIV = 1 => 1khz/(1+0) = 1000hz sample rate 
                        mov     i2cValue, SampleRate       
                        call    #i2cWriteRegisterByte
                        
                        ' // Gyro _ Config
                        mov     i2cAddr, #27              'GYRO_CONFIG register, set FS_SEL bits to 3 gives a
                        mov     i2cValue, GyroFS          ' full scale range of +-2000 deg/sec  
                        call    #i2cWriteRegisterByte
                        
                        ' // Accel _ Config
                        mov     i2cAddr, #28              'Set ACCEL_CONFIG register AFS_SEL bits to 2 
                        mov     i2cValue, AccelFS        ' sets +-8g full scale range  
                        call    #i2cWriteRegisterByte     'ACCEL_HPF is zero which turns off high-pass filtering
                        
                        ' // I2C Bypass Enabled
                        mov     i2cAddr, #55  
                        mov     i2cValue, #%00000010                           
                        call    #i2cWriteRegisterByte

                        [B]
                        ' // Enable Magnetometer for Reading
MPUMag                  mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                        mov     i2cSCL, gyroSCL
                        call    #i2cReset                'Reset i2c
                        
                        mov     i2cDevID, #%00011000    'Device ID for the MPU-Mag                        
                        
                        mov     i2cAddr, #10            '#$0A
                        mov     i2cValue, #%00000001    ' Set to Single-Measurement Mode                        
                        call    #i2cWriteRegisterByte       [/B]                             

                        {
                        ' // Extra Config Settings:
                        
                        mov     i2cAddr, #Address        
                        mov     i2cValue, #%BIN_Value      
                        call    #i2cWriteRegisterByte
                        
                        }
                        
SetConfig_Ret           ret        


'------------------------------------------------------------------------------
StartRead
                        call    #i2cStart
                        mov     i2cData, i2cDevID
                        mov     i2cMask, #%10000000
                        call    #i2cWrite

                        mov     i2cData, i2cAddr
                        mov     i2cMask,#%10000000
                        call    #i2cWrite

                        call    #i2cStart
                        mov     i2cData, i2cDevID
                        or      i2cData, #1
                        mov     i2cMask, #%10000000
                        call    #i2cWrite
                                                
StartRead_Ret           ret        


'------------------------------------------------------------------------------
i2cWriteRegisterByte
                        call    #i2cStart
                        mov     i2cData, i2cDevID
                        mov     i2cMask,#%10000000
                        call    #i2cWrite

                        mov     i2cTime,i2cClkLow
                        add     i2cTime,cnt             ' Allow for minimum SCL low
                        waitcnt i2cTime, #0

                        mov     i2cData, i2cAddr
                        mov     i2cMask,#%10000000
                        call    #i2cWrite

                        mov     i2cTime,i2cClkLow
                        add     i2cTime,cnt             ' Allow for minimum SCL low
                        waitcnt i2cTime, #0

                        mov     i2cData, i2cValue
                        mov     i2cMask,#%10000000
                        call    #i2cWrite

                        call    #i2cStop                                                                         

i2cWriteRegisterByte_Ret
                        ret



'------------------------------------------------------------------------------
'' Low level I2C routines.  These are designed to work either with a standard I2C bus
'' (with pullups on both SCL and SDA) or the Propellor Demo Board (with a pullup only
'' on SDA).  Timing can be set by the caller to 100KHz or 400KHz.


'------------------------------------------------------------------------------
'' Do I2C Reset Sequence.  Clock up to 9 cycles.  Look for SDA high while SCL
'' is high.  Device should respond to next Start Sequence.  Leave SCL high.

i2cReset                andn    dira,i2cSDA             ' Pullup drive SDA high
                        mov     i2cBitCnt,#9            ' Number of clock cycles
                        mov     i2cTime,i2cClkLow
                        add     i2cTime,cnt             ' Allow for minimum SCL low
:i2cResetClk            andn    outa,i2cSCL             ' Active drive SCL low
                        or      dira,i2cSCL            
                        waitcnt i2cTime,i2cClkHigh
                        or      outa,i2cSCL             ' Active drive SCL high
                        or      dira,i2cSCL
                        andn    dira,i2cSCL             ' Pullup drive SCL high
                        waitcnt i2cTime,i2cClkLow       ' Allow minimum SCL high
                        test    i2cSDA,ina         wz   ' Stop if SDA is high
              if_z      djnz    i2cBitCnt,#:i2cResetClk ' Stop after 9 cycles
i2cReset_ret            ret                             ' Should be ready for Start      


'------------------------------------------------------------------------------
'' Do I2C Start Sequence.  This assumes that SDA is a floating input and
'' SCL is also floating, but may have to be actively driven high and low.
'' The start sequence is where SDA goes from HIGH to LOW while SCL is HIGH.

i2cStart
                        or      outa,i2cSCL             ' Active drive SCL high
                        or      dira,i2cSCL
                        or      outa,i2cSDA             ' Active drive SDA high
                        or      dira,i2cSDA
                        
                        mov     i2cTime,i2cClkHigh
                        add     i2cTime,cnt             ' Allow for bus free time
                        waitcnt i2cTime,i2cClkLow

                        andn    outa,i2cSDA             ' Active drive SDA low
                        waitcnt i2cTime,#0

                        andn    outa,i2cSCL             ' Active drive SCL low
i2cStart_ret            ret                             


'------------------------------------------------------------------------------
'' Do I2C Stop Sequence.  This assumes that SCL is low and SDA is indeterminant.
'' The stop sequence is where SDA goes from LOW to HIGH while SCL is HIGH.
'' i2cStart must have been called prior to calling this routine for initialization.
'' The state of the (c) flag is maintained so a write error can be reported.

i2cStop
                        or      outa,i2cSCL             ' Active drive SCL high

                        mov     i2cTime,i2cClkHigh
                        add     i2cTime,cnt             ' Wait for minimum clock low
                        waitcnt i2cTime,i2cClkLow

                        or      outa,i2cSDA             ' Active drive SDA high
                        waitcnt i2cTime,i2cClkLow
                        
                        andn    dira,i2cSCL             ' Pullup drive SCL high
                        waitcnt i2cTime,i2cClkLow       ' Wait for minimum setup time

                        andn    dira,i2cSDA             ' Pullup drive SDA high
                        waitcnt i2cTime,#0              ' Allow for bus free time

i2cStop_ret             ret


'------------------------------------------------------------------------------
'' Write I2C data.  This assumes that i2cStart has been called and that SCL is low,
'' SDA is indeterminant. The (c) flag will be set on exit from ACK/NAK with ACK == false
'' and NAK == true. Bytes are handled in "little-endian" order so these routines can be
'' used with words or longs although the bits are in msb..lsb order.

i2cWrite                mov     i2cBitCnt,#8
                        mov     i2cTime,i2cClkLow
                        add     i2cTime,cnt             ' Wait for minimum SCL low
:i2cWriteBit            waitcnt i2cTime,i2cDataSet

                        test    i2cData,i2cMask    wz
              if_z      or      dira,i2cSDA             ' Copy data bit to SDA
              if_nz     andn    dira,i2cSDA
                        waitcnt i2cTime,i2cClkHigh      ' Wait for minimum setup time
                        or      outa,i2cSCL             ' Active drive SCL high

                        waitcnt i2cTime,i2cClkLow
                        
                        andn    outa,i2cSCL             ' Active drive SCL low
                        
                        ror     i2cMask,#1              ' Go do next bit if not done
                        djnz    i2cBitCnt,#:i2cWriteBit
                        
                        andn    dira,i2cSDA             ' Switch SDA to input and
                        waitcnt i2cTime,i2cClkHigh      '  wait for minimum SCL low
                        
                        or      outa,i2cSCL             ' Active drive SCL high

                        waitcnt i2cTime,i2cClkLow       ' Wait for minimum high time
                        
                        test    i2cSDA,ina         wc   ' Sample SDA (ACK/NAK) then
                        andn    outa,i2cSCL             '  active drive SCL low
                        andn    outa,i2cSDA             '  active drive SDA low
                        or      dira,i2cSDA             ' Leave SDA low
                        rol     i2cMask,#16             ' Prepare for multibyte write
                        
                        waitcnt i2cTime,#0              ' Wait for minimum low time
                        
i2cWrite_ret            ret


'------------------------------------------------------------------------------
'' Read I2C data.  This assumes that i2cStart has been called and that SCL is low,
'' SDA is indeterminant.  ACK/NAK will be copied from the (c) flag on entry with
'' ACK == low and NAK == high.  Bytes are handled in "little-endian" order so these
'' routines can be used with words or longs although the bits are in msb..lsb order.

i2cRead                 mov     i2cBitCnt,#8
                        andn    dira,i2cSDA             ' Make sure SDA is set to input
                        
                        mov     i2cTime,i2cClkLow
                        add     i2cTime,cnt             ' Wait for minimum SCL low
:i2cReadBit             waitcnt i2cTime,i2cClkHigh

                        or      outa,i2cSCL             ' Active drive SCL high
                        waitcnt i2cTime,i2cClkLow       ' Wait for minimum clock high
                        
                        test    i2cSDA,ina         wz   ' Sample SDA for data bits
                        andn    outa,i2cSCL             ' Active drive SCL low

              if_nz     or      i2cData,i2cMask         ' Accumulate data bits
              if_z      andn    i2cData,i2cMask
                        ror     i2cMask,#1              ' Shift the bit mask and
                        djnz    i2cBitCnt,#:i2cReadBit  '  continue until done
                        
                        waitcnt i2cTime,i2cDataSet      ' Wait for end of SCL low

              if_c      or      outa,i2cSDA             ' Copy the ACK/NAK bit to SDA
              if_nc     andn    outa,i2cSDA
                        or      dira,i2cSDA             ' Make sure SDA is set to output

                        waitcnt i2cTime,i2cClkHigh      ' Wait for minimum setup time
                        
                        or      outa,i2cSCL             ' Active drive SCL high
                        waitcnt i2cTime,i2cClkLow       ' Wait for minimum clock high
                        
                        andn    outa,i2cSCL             ' Active drive SCL low
                        andn    outa,i2cSDA             ' Leave SDA low

                        waitcnt i2cTime,#0              ' Wait for minimum low time

i2cRead_ret             ret

{
ComputeAngles
                        mov     cx, iaZ
                        mov     cy, iaX
                        call    #cordic
                        mov     iaRX, ca

                        mov     cx, iaZ
                        mov     cy, iaY
                        call    #cordic
                        mov     iaRY, ca

ComputeAngles_ret       ret
}

'------------------------------------------------------------------------------
'' Perform CORDIC cartesian-to-polar conversion

''Input = cx(x) and cy(x)
''Output = cx(ro) and ca(theta)

cordic                  abs       cx,cx           wc 
              if_c      neg       cy,cy             
                        mov       ca,#0             
                        rcr       ca,#1
                         
                        movs      :lookup,#cordicTable
                        mov       t1,#0
                        mov       t2,#20
                         
:loop                   mov       dx,cy           wc
                        sar       dx,t1
                        mov       dy,cx
                        sar       dy,t1
                        sumc      cx,dx
                        sumnc     cy,dy
:lookup                 sumc      ca,cordicTable
                         
                        add       :lookup,#1
                        add       t1,#1
                        djnz      t2,#:loop
                        shr       ca, #16
              
cordic_ret              ret


cordicTable             long    $20000000
                        long    $12E4051E
                        long    $09FB385B
                        long    $051111D4
                        long    $028B0D43
                        long    $0145D7E1
                        long    $00A2F61E
                        long    $00517C55
                        long    $0028BE53
                        long    $00145F2F
                        long    $000A2F98
                        long    $000517CC
                        long    $00028BE6
                        long    $000145F3
                        long    $0000A2FA
                        long    $0000517D
                        long    $000028BE
                        long    $0000145F
                        long    $00000A30
                        long    $00000518
                                    
dx                      long    0
dy                      long    0
cx                      long    0
cy                      long    0
ca                      long    0
t1                      long    0
t2                      long    0
              


'' Variables for the gyro routines

p1                      long    0
pT                      long    0                       ' Pointer to Temperature in hub ram
prX                     long    0                       ' Pointer to X rotation in hub ram
prY                     long    0                       ' Pointer to Y rotation in hub ram
prZ                     long    0                       ' Pointer to Z rotation in hub ram
paX                     long    0                       ' Pointer to X accel in hub ram
paY                     long    0                       ' Pointer to Y accel in hub ram
paZ                     long    0                       ' Pointer to Z accel in hub ram
paRX                    long    0                       ' Pointer to X accel angle in hub ram
paRY                    long    0                       ' Pointer to Y accel angle in hub ram
pmX                     long    0
pmY                     long    0
pmZ                     long    0
pcID                    long    0
pmID                    long    0

iT                      long    0                       ' Interim temperature value
irX                     long    0                       ' Interim rX value
irY                     long    0                       ' Interim rY value - These values are temp storage before drift compensation
irZ                     long    0                       ' Interim rZ value

iaX                     long    0                       ' Interim aX value
iaY                     long    0                       ' Interim aY value
iaZ                     long    0                       ' Interim aZ value
iaRX                    long    0                       ' Interim aX value
iaRY                    long    0                       ' Interim aY value
imX                     long    0
imY                     long    0
imZ                     long    0
icID                    long    0
imID                    long    0
                                                                  
i2cWordReadMask         long    %10000000_00000000
i2cWordMask             long    $ffff0000
loopDelay               long    80_000_000 / 200
loopCount               long    0

'' Variables for dealing with drift / division
tempOffset              long    15000
drift                   long    0
divisor                 long    0
dividend                long    0
resultShifted           long    0
signbit                 long    0
divCounter              long    0


'' Variables for i2c routines
    
i2cTemp                 long    0
i2cCount                long    0
i2cValue                long    0
i2cDevID                long    0
i2cAddr                 long    0
i2cDataSet              long    0                       ' Minumum data setup time (ticks)
i2cClkLow               long    0                       ' Minimum clock low time (ticks)
i2cClkHigh              long    0                       ' Minimum clock high time (ticks)
i2cPause                long    0                       ' Pause before re-fetching next operation
i2cTestCarry            long    1                       ' Used for setting the carry flag

     
'' Local variables for low level I2C routines

' // MPU-6050 User Set Init Settings
GyroFS                  long    %00000000
AccelFS                 long    %00000000
PowerMgmt               long    %00000000
SampleRate              long    %00000000
DLP                     long    %00000000         


gyroSCL                 long    0                       ' Bit mask for SCL
gyroSDA                 long    0                       ' Bit mask for SDA

i2cSCL                  long    0                       ' Bit mask for SCL
i2cSDA                  long    0                       ' Bit mask for SDA

i2cTime                 long    0                       ' Used for timekeeping
i2cData                 long    0                       ' Data to be transmitted / received
i2cMask                 long    0                       ' Bit mask for bit to be tx / rx
i2cBitCnt               long    0                       ' Number of bits to tx / rx


        FIT   496

Comments

  • zlantzzlantz Posts: 136
    edited 2014-02-21 10:44
    I have it working, but I have to use Minimal_i2c_Driver (spin) to send the init code, and the output is slightly noisy (from calling minimal_i2c_driver while pasm i2c driver is running).

    It should be possible with only the pasm i2c driver:
    '' MPU-60X0-PASM.spin
    '' Reads gyro and accelerometer data from the MPU-60X0 chips
    '' Read loop is in Propeller Assembler
    ''
    '' Based on Jason Dorie's code for the ITG-3200 and ADCL345 chips
    ''
    '' Note that this code assumes an 80 MHz clock
    ''
    '' The TestMPU routine can be used to verify correct setup of, and
    '' communication with, the MPU-60X0.  Load the object into RAM, then
    '' use f12 to bring up the terminal emulator to see the output.
    ''
    
    {
      *******************************************
      * User Init Select & MPU-9150             *
      * Updates by: Zack Lantz                  *
      *******************************************
    
      Place in Main .Spin:
    
      Con
      
        ' // Accelerometer Settings
        mAFS0 = 0
        mAFS1 = 1
        mAFS2 = 2
        mAFS3 = 3
      
        ' // Gyroscope Settings
        mFS0 = 0
        mFS1 = 1
        mFS2 = 2
        mFS3 = 3
        
        ' // Digital Low Pass Filter Settings
        DLP0 = 0          ' Bandwidth = 260 Hz
        DLP1 = 1          ' Bandwidth = 184 Hz
        DLP2 = 2          ' Bandwidth = 94  Hz
        DLP3 = 3          ' Bandwidth = 44  Hz
        DLP4 = 4          ' Bandwidth = 21  Hz
        DLP5 = 5          ' Bandwidth = 10  Hz
        DLP6 = 6          ' Bandwidth = 5   Hz
        DLP7 = 7          ' Reserved
      
        ' // Current Settings (Passed to MPU-6050.spin)
        AccelFS = AFS2
        GyroFS  = FS3
        mAFS    = mAFS2
        mFS     = mFS3
        mDLP    = DLP3
    
        
      Then start MPU Driver with:
      
      MPU.Start(MPUscl, MPUsda, mAFS, mFS, mDLP)    ' MPU-6050 Gyro & Accel Sensor Data  w/ User Init AFS, FS, & DLP
    
      
      Everything else works the same.
    
      
      New Functions:
        Start (SCL, SDA, aFS, gFS, fDLP)
        StartA(SCL, SDA, aFS, gFS, fDLP, PM) ', SF)
        StartX(SCL, SDA)
    
      *** Note:  I didn't add in SampleRate feature as it is 0 to 255 & Calculated as follows:
      
      Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
    
      Where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled (see Register 26) 
    }
    
    {{
    
    The slave address of the MPU-60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is
    determined by the logic level on pin AD0. This allows two MPU-60X0s to be connected to the same I2C bus.
    When used in this configuration, the address of the one of the devices should be b1101000 (pin AD0
    is logic low) and the address of the other should be b1101001 (pin AD0 is logic high).
    
    }}
    
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    CON                        ' CONs for TestMPU test routine   
      SDA_PIN        = 10
      SCL_PIN        = 11
      SERIAL_TX_PIN  = 30
      SERIAL_RX_PIN  = 31
    
    VAR
      long x0, y0, z0, a0, b0, c0, d0, e0, f0, t
      long Cog
    
      long rx, ry, rz, temp, ax, ay, az, arx, ary, mx, my, mz        'PASM code assumes these to be contiguous
      long cid, mid                                                  'PASM code assumes these to be contiguous
    
    OBJ
      debug : "FullDuplexSerial"
      i2c   : "Minimal_i2c_Driver" 
    
    PUB TestMPU  | MPUcog 
     '-----------------------------------------------
      ' Start serial i/o cog
      ' Start cog to pull gyro/accel data from chip
      ' Print data to serial out every few seconds
      '------------------------------------------------
      debug.start(SERIAL_RX_PIN, SERIAL_TX_PIN, 0, 115200) 'Start cog to allow IO with serial terminal
         
      debug.str(string("Starting..."))
      debug.tx(13)
      debug.str(string("GX  GY  GZ    AX  AY  AZ"))
      debug.tx(13)
      debug.str(string("-------------------------"))
      debug.tx(13)
    
      [B]i2c.Initialize(SCL_PIN)[/B]
      
      MPUcog := StartX( SCL_PIN, SDA_PIN )
    
    
      repeat
    
         
        debug.str(string("0x")) 
        debug.hex(GetChipID, 2)
        debug.str(string(", "))
        debug.str(string("0x")) 
        debug.hex(GetMagID, 2)
        debug.str(string("   "))     
         
        debug.dec(GetRX)
        debug.str(string(", "))
        debug.dec(GetRY)
        debug.str(string(", "))
        debug.dec(GetRZ)
        debug.str(string("   "))
         
        debug.dec(GetAX)
        debug.str(string(", "))
        debug.dec(GetAY)
        debug.str(string(", "))
        debug.dec(GetAZ)
        debug.str(string("   "))
         
        debug.dec(GetARX)
        debug.str(string(", "))
        debug.dec(GetARY)     
        debug.str(string("   "))
         
        [B]InitMag            ' // Couldnt get this working below in the PASM section[/B]
        
        debug.dec(GetMX)
        debug.str(string(", "))
        debug.dec(GetMY)
        debug.str(string(", "))
        debug.dec(GetMZ)
        debug.tx(13)
         
    [B]' // Set Mag to Single-Measurement Mode
    Pri InitMag
        i2c.Start(gyroSCL)            
        i2c.Write(gyroSCL, %00011000)    ' AK8975 Write Address
        i2c.Write(gyroSCL, $0A)          ' CTRL Register
        i2c.Write(gyroSCL, %00000001)    ' Set config $0A to %00000001 to turn on the device.    
        i2c.Stop(gyroSCL)       [/B]
    
    ' // Basic Start with User Accel AFS & Gyro FS & DLP   
    PUB Start( SCL, SDA, aFS, gFS, fDLP ) : Status 
    
      if aFS == 0
        AccelFS := %00000000  ' ±  2 g
      elseif aFS == 1          
        AccelFS := %00001000  ' ±  4 g
      elseif aFS == 2
        AccelFS := %00010000  ' ±  8 g
      elseif aFS == 3
        AccelFS := %00011000  ' ± 16 g
    
      if gFS == 0
        GyroFS := %00000000   ' ±  250 ° /s
      elseif gFS == 1
        GyroFS := %00001000   ' ±  500 ° /s
      elseif gFS == 2
        GyroFS := %00010000   ' ± 1000 ° /s
      elseif gFS == 3
        GyroFS := %00011000   ' ± 2000 ° /s
    
                          '| DLPF_CFG |   Accelerometer    |          Gyroscope          |       
      if fDLP == 0        '             Bw (Hz)  Delay (ms)  Bw (Hz)  Delay (ms)  FS (Khz)
        DLP := %00000000  '      0        260        0         256       0.98        8
      elseif fDLP == 1
        DLP := %00000001  '      1        184       2.0        188       1.9         1
      elseif fDLP == 2
        DLP := %00000010  '      2         94       3.0         98       2.8         1
      elseif fDLP == 3
        DLP := %00000011  '      3         44       4.9         42       4.8         1
      elseif fDLP == 4
        DLP := %00000100  '      4         21       8.5         20       8.3         1
      elseif fDLP == 5
        DLP := %00000101  '      5         10      13.8         10      13.4         1
      elseif fDLP == 6    
        DLP := %00000110  '      6          5      19.0          5      18.6         1
      elseif fDLP == 7
        DLP := %00000111  '      7           RESERVED             RESERVED           8
    
      PowerMgmt  := %00000001  ' X gyro as clock source
      SampleRate := %00000001  ' 500 Hz
        
      ComputeTimes
    
      gyroSCL  := 1 << SCL     'save I2C pins
      gyroSDA  := 1 << SDA
      
      Status := Cog := cognew(@Start_Sensors, @rx) + 1
    
      CalibrateAccel
      'CalibrateGyro
      'CalibrateMag
      
    ' // Start w/ Full User Init Settings
    PUB StartA( SCL, SDA, aFS, gFS, fDLP, PM ) : Status 
    
      if aFS == 0
        AccelFS := %00000000
      elseif aFS == 1
        AccelFS := %00001000
      elseif aFS == 2
        AccelFS := %00010000
      elseif aFS == 3
        AccelFS := %00011000
    
      if gFS == 0
        GyroFS := %00000000
      elseif gFS == 1
        GyroFS := %00001000
      elseif gFS == 2
        GyroFS := %00010000
      elseif gFS == 3
        GyroFS := %00011000
    
                          '| DLPF_CFG |   Accelerometer    |          Gyroscope          |       
      if fDLP == 0        '             Bw (Hz)  Delay (ms)  Bw (Hz)  Delay (ms)  FS (Khz)
        DLP := %00000000  '      0        260        0         256       0.98        8
      elseif fDLP == 1
        DLP := %00000001  '      1        184       2.0        188       1.9         1
      elseif fDLP == 2
        DLP := %00000010  '      2         94       3.0         98       2.8         1
      elseif fDLP == 3
        DLP := %00000011  '      3         44       4.9         42       4.8         1
      elseif fDLP == 4
        DLP := %00000100  '      4         21       8.5         20       8.3         1
      elseif fDLP == 5
        DLP := %00000101  '      5         10      13.8         10      13.4         1
      elseif fDLP == 6    
        DLP := %00000110  '      6          5      19.0          5      18.6         1
      elseif fDLP == 7
        DLP := %00000111  '      7           RESERVED             RESERVED           8
                          
      if PM == 0
        PowerMgmt := %00000000  ' Internal 8MHz oscillator
      elseif PM == 1
        PowerMgmt := %00000001  ' PLL with X axis gyroscope reference
      elseif PM == 2
        PowerMgmt := %00000010  ' PLL with Y axis gyroscope reference
      elseif PM == 3
        PowerMgmt := %00000011  ' PLL with Z axis gyroscope reference
      elseif PM == 4
        PowerMgmt := %00000100  ' PLL with external 32.768kHz reference
      elseif PM == 5
        PowerMgmt := %00000101  ' PLL with external 19.2MHz referenc
      elseif PM == 6
        PowerMgmt := %00000110  ' Reserved
      elseif PM == 7
        PowerMgmt := %00000111  ' Stops the clock and keeps the timing generator in reset
              
      ' *** Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)  
      ' // 0 threw 255
      SampleRate := %00000001   ' 500 Hz Sample Rate,   %00000000 = 1000 Hz Sample rate
      
      ComputeTimes
    
      gyroSCL  := 1 << SCL     'save I2C pins
      gyroSDA  := 1 << SDA
      
      Status := Cog := cognew(@Start_Sensors, @rx) + 1
    
      CalibrateAccel
      'CalibrateGyro
      'CalibrateMag
      
    ' // Start Basic, No User Init
    PUB StartX( SCL, SDA ) : Status 
    
      AccelFS := %00010000  ' AFS2
      GyroFS  := %00011000  ' FS3
    
      DLP        := %00000011  ' 40 Hz
      PowerMgmt  := %00000001  ' X gyro as clock source
      SampleRate := %00000001  ' 500 Hz
      
      ComputeTimes
    
      gyroSCL  := 1 << SCL     'save I2C pins
      gyroSDA  := 1 << SDA
      
      Status := Cog := cognew(@Start_Sensors, @rx) + 1
    
      CalibrateAccel
      'CalibrateGyro
      'CalibrateMag
    
    
    PUB Stop
    
      if Cog
        cogstop(Cog~ - 1)
    
    
    '**********************
    '   Accessors
    '**********************
    PUB GetTemp          ' Temperature
      return temp
    
    'Pub GetTempF         ' Temp Deg F
    
    'Pub GetTempC         ' Temp Deg C
    
    PUB GetRX            ' Accel X - Zero Offset
      return rx - x0
    
    PUB GetRY            ' Accel Y - Zero Offset
      return ry - y0
    
    PUB GetRZ            ' Accel Z - Zero Offset
      return rz - z0
    
    PUB GetAX            ' Gyro X - Zero Offset
      return ax - a0
    
    PUB GetAY            ' Gyro Y - Zero Offset
      return ay - b0
    
    PUB GetAZ            ' Gyrp Z - Zero Offset
      return az - c0
    
    PUB GetARX           ' Pitch Angle
      return arx
    
    PUB GetARY           ' Roll Angle
      return ary
    
    Pub GetAccelOffsetX  ' Accelerometer Zero Offset X
      return x0
      
    Pub GetAccelOffsetY  ' Accelerometer Zero Offset Y
      return y0
      
    Pub GetAccelOffsetZ  ' Accelerometer Zero Offset Z
      return z0
      
    Pub GetGyroOffsetX   ' Gyroscope Zero Offset X
      return a0
      
    Pub GetGyroOffsetY   ' Gyroscope Zero Offset Y
      return b0
      
    Pub GetGyroOffsetZ   ' Gyroscope Zero Offset Z
      return c0
    
    Pub CalAccel         ' Calibrate Accelerometer
      CalibrateAccel
    
    Pub CalGyro          ' Calibrate Gyroscope
      CalibrateGyro
    
    
    Pub GetChipID
      return cID
    
    Pub GetMagID
      return mID  
    
    Pub GetMX
      return mx
    
    Pub GetMY
      return my
    
    Pub GetMZ
      return mz
    
      
    PRI computeTimes                                       '' Set up timing constants in assembly
                                                           '  (Done this way to avoid overflow)
      i2cDataSet := ((clkfreq / 10000) *  350) / 100000    ' Data setup time -  350ns (400KHz)
      i2cClkLow  := ((clkfreq / 10000) * 1300) / 100000    ' Clock low time  - 1300ns (400KHz)
      i2cClkHigh := ((clkfreq / 10000) *  600) / 100000    ' Clock high time -  600ns (400KHz)
      i2cPause   := clkfreq / 100000                       ' Pause between checks for operations
    
    
    PRI CalibrateAccel | tc, xc, yc, zc, dr
    
      x0 := 0         ' Initialize offsets
      y0 := 0
      z0 := 0
      
      'wait 1/2 second for the body to stop moving
      waitcnt( constant(80_000_000 / 2) + cnt )
    
      'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
      xc := 0
      yc := 0
      zc := 0
    
      repeat 256
        xc += rx
        yc += ry
        zc += rz
    
        waitcnt( constant(80_000_000/192) + cnt )
    
      'Perform rounding
      if( xc > 0 )
        xc += 128
      elseif( xc < 0 )
        xc -= 128
    
      if( yc > 0 )
        yc += 128
      elseif( yc < 0 )
        yc -= 128
    
      if( zc > 0 )
        zc += 128
      elseif( zc < 0 )
        zc -= 128
        
      x0 := xc / 256
      y0 := yc / 256
      z0 := zc / 256
      
    
    PRI CalibrateGyro | tc, xc, yc, zc, dr
    
      a0 := 0         ' Initialize offsets
      b0 := 0
      c0 := 0
      
      'wait 1/2 second for the body to stop moving
      waitcnt( constant(80_000_000 / 2) + cnt )
    
      'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
      xc := 0
      yc := 0
      zc := 0
    
      repeat 256
        xc += ax
        yc += ay
        zc += az
    
        waitcnt( constant(80_000_000/192) + cnt )
    
      'Perform rounding
      if( xc > 0 )
        xc += 128
      elseif( xc < 0 )
        xc -= 128
    
      if( yc > 0 )
        yc += 128
      elseif( yc < 0 )
        yc -= 128
    
      if( zc > 0 )
        zc += 128
      elseif( zc < 0 )
        zc -= 128
        
      a0 := xc / 256
      b0 := yc / 256
      c0 := zc / 256
    
    
    Pri CalibrateMag | tc, xc, yc, zc, dr
    
      d0 := 0         ' Initialize offsets
      e0 := 0
      f0 := 0
      
      'wait 1/2 second for the body to stop moving
      waitcnt( constant(80_000_000 / 2) + cnt )
    
      'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
      xc := 0
      yc := 0
      zc := 0
    
      repeat 256
        xc += mX
        yc += mY
        zc += mZ
    
        waitcnt( constant(80_000_000/192) + cnt )
    
      'Perform rounding
      if( xc > 0 )
        xc += 128
      elseif( xc < 0 )
        xc -= 128
    
      if( yc > 0 )
        yc += 128
      elseif( yc < 0 )
        yc -= 128
    
      if( zc > 0 )
        zc += 128
      elseif( zc < 0 )
        zc -= 128
        
      d0 := xc / 256
      e0 := yc / 256
      f0 := zc / 256
    
    
    DAT
            org   0
    
    Start_Sensors
    
    '  --------- Debugger Kernel add this at Entry (Addr 0) ---------
    '   long $34FC1202,$6CE81201,$83C120B,$8BC0E0A,$E87C0E03,$8BC0E0A
    '   long $EC7C0E05,$A0BC1207,$5C7C0003,$5C7C0003,$7FFC,$7FF8
    '  -------------------------------------------------------------- 
    
            mov             p1, par                         ' Get data pointer
            mov             prX, p1                         ' Store the pointer to the rx var in HUB RAM
            add             p1, #4
            mov             prY, p1                         ' Store the pointer to the ry var in HUB RAM
            add             p1, #4
            mov             prZ, p1                         ' Store the pointer to the rz var in HUB RAM
            add             p1, #4
            mov             pT, p1                          ' Store the pointer to the temp var in HUB RAM
            add             p1, #4
            mov             paX, p1                         ' Store the pointer to the ax var in HUB RAM
            add             p1, #4
            mov             paY, p1                         ' Store the pointer to the ay var in HUB RAM
            add             p1, #4
            mov             paZ, p1                         ' Store the pointer to the az var in HUB RAM
            add             p1, #4
            mov             paRX, p1                        ' Store the pointer to the arx var in HUB RAM
            add             p1, #4
            mov             paRY, p1                        ' Store the pointer to the ary var in HUB RAM
            add             p1, #4
            mov             pmX, p1                         ' Store the pointer to the ax var in HUB RAM
            add             p1, #4
            mov             pmY, p1                         ' Store the pointer to the ay var in HUB RAM
            add             p1, #4
            mov             pmZ, p1                         ' Store the pointer to the az var in HUB RAM
            add             p1, #4
            mov             pcID, p1                        ' Store the pointer to the ary var in HUB RAM
            add             p1, #4
            mov             pmID, p1                        ' Store the pointer to the ary var in HUB RAM
    
    
            mov             i2cTemp,i2cPause
            add             i2cTemp,CNT                     ' Wait 10us before starting
            waitcnt         i2cTemp,#0
           
            call            #SetConfig
    
            mov             loopCount, CNT
            add             loopCount, loopDelay
    
    
    '------------------------------------------------------------------------------
           
    ' Main loop
    '   loopDelay defined in data section
    '   Nominally set to CLK_FREQ/200 give 200hz update rate, but the update takes less than 
    '   500us, so the delay could potentially be set to give an update rate as high as 2000hz
    '
    :loop
                            call    #MPUReadValues
                            call    #MPUComputeDrift
                            call    #ComputeAngles
    
                            call    #MPUGetChipID    ' = 0x68 = Correct
                            call    #MPUGetMagID     ' = 0x48 = Correct
    
                            'call    #ConfigMag    ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.  
    
                            call    #MPUGetMagX
                            call    #MPUGetMagY
                            call    #MPUGetMagZ                                                  
    
                            wrlong  iT, pT
    
                            subs    irX, drift
                            wrlong  irX, prX
    
                            subs    irY, drift
                            wrlong  irY, prY
    
                            subs    irZ, drift
                            wrlong  irZ, prZ
    
                            wrlong  iaX, paX
                            wrlong  iaY, paY
                            wrlong  iaZ, paZ
                            
                            'Sign extend the 15th bit
                            test    iaRX, i2cWordReadMask     wc
                            muxc    iaRX, i2cWordMask
    
                            'Sign extend the 15th bit
                            test    iaRY, i2cWordReadMask     wc
                            muxc    iaRY, i2cWordMask                                                                                      
                            
                            wrlong  iaRX, paRX
                            wrlong  iaRY, paRY
    
                            wrlong  iMX, pMX
                            wrlong  iMY, pMY
                            wrlong  iMZ, pMZ
    
                            'mov     icID, #$68     ' (Not Calling MPUGetChipID or MPUGetMagID)
                            'mov     imID, #$48
                            
                            wrlong  icID, pcID
                            wrlong  imID, pmID                          
                            
                            waitcnt loopCount, loopDelay
                            jmp     #:loop        
    
    
    
    '------------------------------------------------------------------------------
    ' MPUReadValues
    '
    '   Starting at the ACCEL_X data register,  read in the 3 accel values,
    '   the temperature, and the 3 gyro values, as these are held in
    '   sequential register locations.
    '
    MPUReadValues
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #59            ' Address of ACCEL_XOUT_H
                            mov     i2cDevID, #%11010000    ' Device ID of the MPU 
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iaX, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                            call    #i2cRead
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iaY, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                            call    #i2cRead
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iaZ, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                            call    #i2cRead                       
                            call    #i2cRead
    
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iT, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     irX, i2cData
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     irY, i2cData
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc      ' Set the carry flag to tell it we're done                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     irZ, i2cData
                            
                            call    #i2cStop                        
    
    MPUReadValues_Ret       ret
    
    
    
    '------------------------------------------------------------------------------
    
    
    MPUGetChipID
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #117            ' Address of ACCEL_XOUT_H
                            mov     i2cDevID, #%11010000    ' Device ID of the MPU 
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead
    
                            ' // Single Byte Read; Shift Right 8 Bits
                            shr     i2cData, #8                  
                            mov     icID, i2cData           ' // Fixed = 0x68
                            
                            call    #i2cStop                        
    
    MPUGetChipID_Ret        ret
    
    
    
    '------------------------------------------------------------------------------
    
    
    MPUGetMagID
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #00
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead
    
                            ' // Single Byte Read; Shift Right 8 Bits
                            shr     i2cData, #8                  
                            mov     imID, i2cData           ' // Fixed = 0x48
                            
                            call    #i2cStop                        
    
    MPUGetMagID_Ret         ret
    
    
    '------------------------------------------------------------------------------
    
    
    MPUGetMagX
                            'jmpret  MPUGetMagX, #MPUMag      ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.
    
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #03
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc           
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                            call    #i2cRead                            
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     imX, i2cData           
                            
                            call    #i2cStop                        
    
    MPUGetMagX_Ret          ret
    
    '------------------------------------------------------------------------------
    
    
    MPUGetMagY
                            'jmpret  MPUGetMagY, #MPUMag     ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.  
                                      
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #05
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc           
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                            call    #i2cRead                            
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     imY, i2cData           
                            
                            call    #i2cStop                        
    
    MPUGetMagY_Ret          ret
    
    '------------------------------------------------------------------------------
    
    
    MPUGetMagZ
                            'jmpret  MPUGetMagZ, #MPUMag     ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.  
                                     
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #07
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc           
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                            call    #i2cRead                            
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     imZ, i2cData           
                            
                            call    #i2cStop                        
    
    MPUGetMagZ_Ret          ret
    
    
    '------------------------------------------------------------------------------
    
    
            ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.
            
    
    {
          i2c.Start(SCL)            
          i2c.Write(SCL, MagAddrW)     ' AK8975 Write Address
          i2c.Write(SCL, $0A)          ' CTRL Register
          i2c.Write(SCL, %00000001)    ' Set config $0A to %00000001 to turn on the device.    
          i2c.Stop(SCL)
    }
    
    ConfigMag
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            call    #i2cReset  
                            mov     i2cDevID, #%00011000   ' MagAddrW
                            mov     i2cMask,  #%10000000                  
                            call    #i2cWrite
    
                            mov     i2cAddr, #10           ' $0A
                            mov     i2cMask,  #%10000000
                            call    #i2cWrite
                            
                            mov     i2cValue, #%00000001   ' %00000001
                            mov     i2cMask,  #%10000000
                            call    #i2cWrite
    
                            call    #i2cStop                                      
    
    ConfigMag_Ret           ret
    
    '------------------------------------------------------------------------------
                            ' Compute drift - for my gyro (Jason's ITG-3200)
                            '(Temp + 15000) / 100 = drift
                            
    '------------------------------------------------------------------------------
    MPUComputeDrift
                            mov     drift, iT               ' Start with the temperature reading
                            add     drift, tempOffset       ' Offset it by 15,000
    
                            ' divide drift by 100                        
    
                            mov     divisor, #100
                            mov     dividend, drift
                            test    dividend, i2cWordReadMask    wc
    
                            muxc    signbit, #1             ' record the sign of the original value
                            abs     dividend, dividend
    
                            mov     divCounter, #10     
                            shl     divisor, divCounter
                            mov     resultShifted, #1
                            shl     resultShifted, divCounter
    
                            add     divCounter, #1
                            mov     drift, #0
    
    :divLoop                        
                            cmp     dividend, divisor   wc
                  if_nc     add     drift, resultShifted
                  if_nc     sub     dividend, divisor
                            shr     resultShifted, #1
                            shr     divisor, #1     
                            djnz    divCounter, #:divLoop
    
                            test    signbit, #1     wc
                            negc    drift, drift
                            
                            
    MPUComputeDrift_Ret     ret
    
    
    
    '------------------------------------------------------------------------------
    ComputeAngles
                            mov     cx, iaZ
                            mov     cy, iaX
                            call    #cordic
                            mov     iaRX, ca
    
                            mov     cx, iaZ
                            mov     cy, iaY
                            call    #cordic
                            mov     iaRY, ca
    
    ComputeAngles_ret       ret
    
            
    '------------------------------------------------------------------------------
    ' SetConfig
    '
    '  See MPU-6000/6050 Register Map document for register addresses and
    '   valid settings
    '
    SetConfig
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
                            call    #i2cReset                'Reset i2c
                             
    :MPUSetConfig           mov     i2cDevID, #%11010000     'Device ID for the MPU-6000/6050 
    
                            ' // Power Management
                            mov     i2cAddr, #107             'Set PWR_MGMT_1 register bit 0 to choose
                            mov     i2cValue, PowerMgmt       ' X gyro as clock source  '
                            call    #i2cWriteRegisterByte
                            
                            ' // I2C Master Disabled
                            mov     i2cAddr, #106       
                            mov     i2cValue, #%00000000
                            call    #i2cWriteRegisterByte
                            
                                                    
                            ' // Digital Low Pass Filter _ Config
                            mov     i2cAddr, #26      
                            mov     i2cValue, DLP             'Set DLPF_CONFIG to 3 for 40Hz bandwidth 
                            call    #i2cWriteRegisterByte     
                            
                            ' // Sample Rate Divider
                            mov     i2cAddr, #25              'SMPLRT_DIV = 1 => 1khz/(1+0) = 1000hz sample rate 
                            mov     i2cValue, SampleRate       
                            call    #i2cWriteRegisterByte
                            
                            ' // Gyro _ Config
                            mov     i2cAddr, #27              'GYRO_CONFIG register, set FS_SEL bits to 3 gives a
                            mov     i2cValue, GyroFS          ' full scale range of +-2000 deg/sec  
                            call    #i2cWriteRegisterByte
                            
                            ' // Accel _ Config
                            mov     i2cAddr, #28              'Set ACCEL_CONFIG register AFS_SEL bits to 2 
                            mov     i2cValue, AccelFS        ' sets +-8g full scale range  
                            call    #i2cWriteRegisterByte     'ACCEL_HPF is zero which turns off high-pass filtering
                            
                            ' // I2C Bypass Enabled
                            mov     i2cAddr, #55  
                            mov     i2cValue, #%00000010                           
                            call    #i2cWriteRegisterByte
    
                            
                            ' // Enable Magnetometer for Reading
    MPUMag                  mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
                            call    #i2cReset                'Reset i2c
                            
                            mov     i2cDevID, #%00011000    'Device ID for the MPU-Mag                        
                            
                            mov     i2cAddr, #10            '#$0A
                            mov     i2cValue, #%00000001    ' Set to Single-Measurement Mode                        
                            call    #i2cWriteRegisterByte                                    
    
                            {
                            ' // Extra Config Settings:
                            
                            mov     i2cAddr, #Address        
                            mov     i2cValue, #%BIN_Value      
                            call    #i2cWriteRegisterByte
                            
                            }
                            
    SetConfig_Ret           ret        
    
    
    '------------------------------------------------------------------------------
    StartRead
                            call    #i2cStart
                            mov     i2cData, i2cDevID
                            mov     i2cMask, #%10000000
                            call    #i2cWrite
    
                            mov     i2cData, i2cAddr
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            call    #i2cStart
                            mov     i2cData, i2cDevID
                            or      i2cData, #1
                            mov     i2cMask, #%10000000
                            call    #i2cWrite
                                                    
    StartRead_Ret           ret        
    
    
    '------------------------------------------------------------------------------
    i2cWriteRegisterByte
                            call    #i2cStart
                            mov     i2cData, i2cDevID
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Allow for minimum SCL low
                            waitcnt i2cTime, #0
    
                            mov     i2cData, i2cAddr
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Allow for minimum SCL low
                            waitcnt i2cTime, #0
    
                            mov     i2cData, i2cValue
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            call    #i2cStop                                                                         
    
    i2cWriteRegisterByte_Ret
                            ret
    
    
    
    '------------------------------------------------------------------------------
    '' Low level I2C routines.  These are designed to work either with a standard I2C bus
    '' (with pullups on both SCL and SDA) or the Propellor Demo Board (with a pullup only
    '' on SDA).  Timing can be set by the caller to 100KHz or 400KHz.
    
    
    '------------------------------------------------------------------------------
    '' Do I2C Reset Sequence.  Clock up to 9 cycles.  Look for SDA high while SCL
    '' is high.  Device should respond to next Start Sequence.  Leave SCL high.
    
    i2cReset                andn    dira,i2cSDA             ' Pullup drive SDA high
                            mov     i2cBitCnt,#9            ' Number of clock cycles
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Allow for minimum SCL low
    :i2cResetClk            andn    outa,i2cSCL             ' Active drive SCL low
                            or      dira,i2cSCL            
                            waitcnt i2cTime,i2cClkHigh
                            or      outa,i2cSCL             ' Active drive SCL high
                            or      dira,i2cSCL
                            andn    dira,i2cSCL             ' Pullup drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Allow minimum SCL high
                            test    i2cSDA,ina         wz   ' Stop if SDA is high
                  if_z      djnz    i2cBitCnt,#:i2cResetClk ' Stop after 9 cycles
    i2cReset_ret            ret                             ' Should be ready for Start      
    
    
    '------------------------------------------------------------------------------
    '' Do I2C Start Sequence.  This assumes that SDA is a floating input and
    '' SCL is also floating, but may have to be actively driven high and low.
    '' The start sequence is where SDA goes from HIGH to LOW while SCL is HIGH.
    
    i2cStart
                            or      outa,i2cSCL             ' Active drive SCL high
                            or      dira,i2cSCL
                            or      outa,i2cSDA             ' Active drive SDA high
                            or      dira,i2cSDA
                            
                            mov     i2cTime,i2cClkHigh
                            add     i2cTime,cnt             ' Allow for bus free time
                            waitcnt i2cTime,i2cClkLow
    
                            andn    outa,i2cSDA             ' Active drive SDA low
                            waitcnt i2cTime,#0
    
                            andn    outa,i2cSCL             ' Active drive SCL low
    i2cStart_ret            ret                             
    
    
    '------------------------------------------------------------------------------
    '' Do I2C Stop Sequence.  This assumes that SCL is low and SDA is indeterminant.
    '' The stop sequence is where SDA goes from LOW to HIGH while SCL is HIGH.
    '' i2cStart must have been called prior to calling this routine for initialization.
    '' The state of the (c) flag is maintained so a write error can be reported.
    
    i2cStop
                            or      outa,i2cSCL             ' Active drive SCL high
    
                            mov     i2cTime,i2cClkHigh
                            add     i2cTime,cnt             ' Wait for minimum clock low
                            waitcnt i2cTime,i2cClkLow
    
                            or      outa,i2cSDA             ' Active drive SDA high
                            waitcnt i2cTime,i2cClkLow
                            
                            andn    dira,i2cSCL             ' Pullup drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum setup time
    
                            andn    dira,i2cSDA             ' Pullup drive SDA high
                            waitcnt i2cTime,#0              ' Allow for bus free time
    
    i2cStop_ret             ret
    
    
    '------------------------------------------------------------------------------
    '' Write I2C data.  This assumes that i2cStart has been called and that SCL is low,
    '' SDA is indeterminant. The (c) flag will be set on exit from ACK/NAK with ACK == false
    '' and NAK == true. Bytes are handled in "little-endian" order so these routines can be
    '' used with words or longs although the bits are in msb..lsb order.
    
    i2cWrite                mov     i2cBitCnt,#8
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Wait for minimum SCL low
    :i2cWriteBit            waitcnt i2cTime,i2cDataSet
    
                            test    i2cData,i2cMask    wz
                  if_z      or      dira,i2cSDA             ' Copy data bit to SDA
                  if_nz     andn    dira,i2cSDA
                            waitcnt i2cTime,i2cClkHigh      ' Wait for minimum setup time
                            or      outa,i2cSCL             ' Active drive SCL high
    
                            waitcnt i2cTime,i2cClkLow
                            
                            andn    outa,i2cSCL             ' Active drive SCL low
                            
                            ror     i2cMask,#1              ' Go do next bit if not done
                            djnz    i2cBitCnt,#:i2cWriteBit
                            
                            andn    dira,i2cSDA             ' Switch SDA to input and
                            waitcnt i2cTime,i2cClkHigh      '  wait for minimum SCL low
                            
                            or      outa,i2cSCL             ' Active drive SCL high
    
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum high time
                            
                            test    i2cSDA,ina         wc   ' Sample SDA (ACK/NAK) then
                            andn    outa,i2cSCL             '  active drive SCL low
                            andn    outa,i2cSDA             '  active drive SDA low
                            or      dira,i2cSDA             ' Leave SDA low
                            rol     i2cMask,#16             ' Prepare for multibyte write
                            
                            waitcnt i2cTime,#0              ' Wait for minimum low time
                            
    i2cWrite_ret            ret
    
    
    '------------------------------------------------------------------------------
    '' Read I2C data.  This assumes that i2cStart has been called and that SCL is low,
    '' SDA is indeterminant.  ACK/NAK will be copied from the (c) flag on entry with
    '' ACK == low and NAK == high.  Bytes are handled in "little-endian" order so these
    '' routines can be used with words or longs although the bits are in msb..lsb order.
    
    i2cRead                 mov     i2cBitCnt,#8
                            andn    dira,i2cSDA             ' Make sure SDA is set to input
                            
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Wait for minimum SCL low
    :i2cReadBit             waitcnt i2cTime,i2cClkHigh
    
                            or      outa,i2cSCL             ' Active drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum clock high
                            
                            test    i2cSDA,ina         wz   ' Sample SDA for data bits
                            andn    outa,i2cSCL             ' Active drive SCL low
    
                  if_nz     or      i2cData,i2cMask         ' Accumulate data bits
                  if_z      andn    i2cData,i2cMask
                            ror     i2cMask,#1              ' Shift the bit mask and
                            djnz    i2cBitCnt,#:i2cReadBit  '  continue until done
                            
                            waitcnt i2cTime,i2cDataSet      ' Wait for end of SCL low
    
                  if_c      or      outa,i2cSDA             ' Copy the ACK/NAK bit to SDA
                  if_nc     andn    outa,i2cSDA
                            or      dira,i2cSDA             ' Make sure SDA is set to output
    
                            waitcnt i2cTime,i2cClkHigh      ' Wait for minimum setup time
                            
                            or      outa,i2cSCL             ' Active drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum clock high
                            
                            andn    outa,i2cSCL             ' Active drive SCL low
                            andn    outa,i2cSDA             ' Leave SDA low
    
                            waitcnt i2cTime,#0              ' Wait for minimum low time
    
    i2cRead_ret             ret
    
    {
    ComputeAngles
                            mov     cx, iaZ
                            mov     cy, iaX
                            call    #cordic
                            mov     iaRX, ca
    
                            mov     cx, iaZ
                            mov     cy, iaY
                            call    #cordic
                            mov     iaRY, ca
    
    ComputeAngles_ret       ret
    }
    
    '------------------------------------------------------------------------------
    '' Perform CORDIC cartesian-to-polar conversion
    
    ''Input = cx(x) and cy(x)
    ''Output = cx(ro) and ca(theta)
    
    cordic                  abs       cx,cx           wc 
                  if_c      neg       cy,cy             
                            mov       ca,#0             
                            rcr       ca,#1
                             
                            movs      :lookup,#cordicTable
                            mov       t1,#0
                            mov       t2,#20
                             
    :loop                   mov       dx,cy           wc
                            sar       dx,t1
                            mov       dy,cx
                            sar       dy,t1
                            sumc      cx,dx
                            sumnc     cy,dy
    :lookup                 sumc      ca,cordicTable
                             
                            add       :lookup,#1
                            add       t1,#1
                            djnz      t2,#:loop
                            shr       ca, #16
                  
    cordic_ret              ret
    
    
    cordicTable             long    $20000000
                            long    $12E4051E
                            long    $09FB385B
                            long    $051111D4
                            long    $028B0D43
                            long    $0145D7E1
                            long    $00A2F61E
                            long    $00517C55
                            long    $0028BE53
                            long    $00145F2F
                            long    $000A2F98
                            long    $000517CC
                            long    $00028BE6
                            long    $000145F3
                            long    $0000A2FA
                            long    $0000517D
                            long    $000028BE
                            long    $0000145F
                            long    $00000A30
                            long    $00000518
                                        
    dx                      long    0
    dy                      long    0
    cx                      long    0
    cy                      long    0
    ca                      long    0
    t1                      long    0
    t2                      long    0
    
    MagInit                 long    0              
    
    
    '' Variables for the gyro routines
    
    p1                      long    0
    pT                      long    0                       ' Pointer to Temperature in hub ram
    prX                     long    0                       ' Pointer to X rotation in hub ram
    prY                     long    0                       ' Pointer to Y rotation in hub ram
    prZ                     long    0                       ' Pointer to Z rotation in hub ram
    paX                     long    0                       ' Pointer to X accel in hub ram
    paY                     long    0                       ' Pointer to Y accel in hub ram
    paZ                     long    0                       ' Pointer to Z accel in hub ram
    paRX                    long    0                       ' Pointer to X accel angle in hub ram
    paRY                    long    0                       ' Pointer to Y accel angle in hub ram
    pmX                     long    0
    pmY                     long    0
    pmZ                     long    0
    pcID                    long    0
    pmID                    long    0
    
    iT                      long    0                       ' Interim temperature value
    irX                     long    0                       ' Interim rX value
    irY                     long    0                       ' Interim rY value - These values are temp storage before drift compensation
    irZ                     long    0                       ' Interim rZ value
    
    iaX                     long    0                       ' Interim aX value
    iaY                     long    0                       ' Interim aY value
    iaZ                     long    0                       ' Interim aZ value
    iaRX                    long    0                       ' Interim aX value
    iaRY                    long    0                       ' Interim aY value
    imX                     long    0
    imY                     long    0
    imZ                     long    0
    icID                    long    0
    imID                    long    0
                                                                      
    i2cWordReadMask         long    %10000000_00000000
    i2cWordMask             long    $ffff0000
    loopDelay               long    80_000_000 / 200
    loopCount               long    0
    
    '' Variables for dealing with drift / division
    tempOffset              long    15000
    drift                   long    0
    divisor                 long    0
    dividend                long    0
    resultShifted           long    0
    signbit                 long    0
    divCounter              long    0
    
    
    '' Variables for i2c routines
        
    i2cTemp                 long    0
    i2cCount                long    0
    i2cValue                long    0
    i2cDevID                long    0
    i2cAddr                 long    0
    i2cDataSet              long    0                       ' Minumum data setup time (ticks)
    i2cClkLow               long    0                       ' Minimum clock low time (ticks)
    i2cClkHigh              long    0                       ' Minimum clock high time (ticks)
    i2cPause                long    0                       ' Pause before re-fetching next operation
    i2cTestCarry            long    1                       ' Used for setting the carry flag
    
         
    '' Local variables for low level I2C routines
    
    ' // MPU-6050 User Set Init Settings
    GyroFS                  long    %00000000
    AccelFS                 long    %00000000
    PowerMgmt               long    %00000000
    SampleRate              long    %00000000
    DLP                     long    %00000000         
    
    
    gyroSCL                 long    0                       ' Bit mask for SCL
    gyroSDA                 long    0                       ' Bit mask for SDA
    
    i2cSCL                  long    0                       ' Bit mask for SCL
    i2cSDA                  long    0                       ' Bit mask for SDA
    
    i2cTime                 long    0                       ' Used for timekeeping
    i2cData                 long    0                       ' Data to be transmitted / received
    i2cMask                 long    0                       ' Bit mask for bit to be tx / rx
    i2cBitCnt               long    0                       ' Number of bits to tx / rx
    
    
            FIT   496
    
  • zlantzzlantz Posts: 136
    edited 2014-02-21 11:26
    I have managed to clean up the results a bit by changing the main loop to this:
    :loop
                            call    #MPUReadValues
                            call    #MPUComputeDrift
                            call    #ComputeAngles
    
                            call    #MPUGetChipID    ' = 0x68 = Correct
                            call    #MPUGetMagID     ' = 0x48 = Correct
    
                            ' // if ChipID <> $68
                            ' // jmp  #:loop
                            mov     MagTmp, icID     ' MagTmp = icID
                            sub     MagTmp, #$68     ' MagTmp - $68   (IF CID = $68 will = 0)
                            tjnz    MagTmp, #:loop   ' if MagTmp <> 0 jump to :loop
                                                    
                            'call    #ConfigMag    ' // *** FINAL PROBLEM HERE!!!  Cannot get AK8975 to go into Single-Read Mode.  
    
                            call    #MPUGetMagX
                            call    #MPUGetMagY
                            call    #MPUGetMagZ                                                  
    
                            wrlong  iT, pT
    
                            subs    irX, drift
                            wrlong  irX, prX
    
                            subs    irY, drift
                            wrlong  irY, prY
    
                            subs    irZ, drift
                            wrlong  irZ, prZ
    
                            wrlong  iaX, paX
                            wrlong  iaY, paY
                            wrlong  iaZ, paZ
                            
                            'Sign extend the 15th bit
                            test    iaRX, i2cWordReadMask     wc
                            muxc    iaRX, i2cWordMask
    
                            'Sign extend the 15th bit
                            test    iaRY, i2cWordReadMask     wc
                            muxc    iaRY, i2cWordMask                                                                                      
                            
                            wrlong  iaRX, paRX
                            wrlong  iaRY, paRY
    
                            wrlong  iMX, pMX
                            wrlong  iMY, pMY
                            wrlong  iMZ, pMZ
    
                            'mov     icID, #$68     ' (Not Calling MPUGetChipID or MPUGetMagID)
                            'mov     imID, #$48
                            
                            wrlong  icID, pcID
                            wrlong  imID, pmID                          
                            
                            waitcnt loopCount, loopDelay
                            jmp     #:loop        
    
    
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-02-21 12:54
    I bought one of these MPU-9150 modules from SparkFun, largely to see what the fuss is in communicating with it, and it works perfectly with both my spin and pasm I2C drivers without any modification.

    Attached is a demo program that I've been working on.

    MPU-9150 demo 2 - Archive.zip
  • zlantzzlantz Posts: 136
    edited 2014-02-21 13:31
    ChrisGadd wrote: »
    I bought one of these MPU-9150 modules from SparkFun, largely to see what the fuss is in communicating with it, and it works perfectly with both my spin and pasm I2C drivers without any modification.

    Attached is a demo program that I've been working on.

    MPU-9150 demo 2 - Archive.zip

    Wow! your mag code works! Everything is giving values but I dont understand your Accel results, they appear to be more like Angle values, acceleration has nothing to do with their value. And there is no Zero point.

    Your code is difficult to understand.

    How are you doing the Temperature calculations?!?! It gets Read along with gyro & accel, but then "L3 := L3 / 340 + 35" does something (and the only existence of "L3" in the project is that line, but is part of the temp calc? how?)

    I can still use the angle calculations, but I do also need the g-force, and I cannot obtain this from your code.

    Thanks,

    Zack
  • zlantzzlantz Posts: 136
    edited 2014-02-21 14:38
    So I added some comments to your code trying to figure out how it works. It would appear that it mostly runs on Magic.

    Values are being created out of thin air, that respond fairly accurately (temperature). Math is being done and visibly not used but behind the vail of magic it makes the temperature work.

    I also noticed that your MPU Address is Wrong, but wont work with the correct address.

    Incorrect: MPU = %0110_1000 *** Works
    Correct: MPU = %1101_0000 *** Don't Work

    See here my comments:
    CON
    _clkmode = xtal1 + pll16x                                                      
    _xinfreq = 5_000_000
    
    [B]SDA_PIN        = 10
    SCL_PIN        = 11[/B]
    
    Bitrate = 400_000
    
    VAR
    
      long  Stack[60]
      
      long  Acc_X, Acc_Y, Acc_Z, Temp, Gyro_X, Gyro_Y, Gyro_Z
      long  Mag_X, Mag_Y, Mag_Z
    
      word  word_buffer[7]
      
      byte  ASAX, ASAY, ASAZ
    ' byte  buffer[14]
    
    OBJ
      I2C : "I2C Spin driver v1.2"
    ' I2C : "I2C PASM driver v1.4"
      FDS : "FullDuplexSerial"
      
      f             : "FloatMath"              ' 0 Cog
      fS            : "FloatString"            ' 0 Cog
        
    PUB Demo | AccelX, AccelY, AccelZ
      FDS.start(31,30,0,115_200)
      waitcnt(cnt + clkfreq)
    
      start
      
      repeat
    
    [I]    ' // Convert Accel Value to G-Force
        ' G-Force = Accel[x] / 2,048  (AFS-3)
        AccelX := f.fDiv(f.fFloat(Acc_X), 2048.0)
        AccelY := f.fDiv(f.fFloat(Acc_Y), 2048.0)
        AccelZ := f.fDiv(f.fFloat(Acc_Z), 2048.0)[/I]
          
        waitcnt((clkfreq / 50) + cnt)
    
        ' // Raw Accel    
        FDS.tx($00)
        fds.str(string("Acc_X",$09))      
        fds.dec(Acc_X)                    
        fds.str(string($0D,"Acc_Y",$09))   
        fds.dec(Acc_Y)                    
        fds.str(string($0D,"Acc_Z",$09))   
        fds.dec(Acc_Z)                    
    
    [I]    ' // G-Force
        fds.str(string($0D, $0D,"G-Force_X",$09))      
        fds.str(fs.floattostring(AccelX))                    
        fds.str(string($0D,"G-Force_Y",$09))   
        fds.str(fs.floattostring(AccelY))                    
        fds.str(string($0D,"G-Force_Z",$09))   
        fds.str(fs.floattostring(AccelZ))              [/I]      
    
        ' // Raw Gyro        
        fds.str(string($0D,$0D,"Gyro_X",$09))
        fds.dec(Gyro_X)                   
        fds.str(string($0D,"Gyro_Y",$09))  
        fds.dec(Gyro_Y)                   
        fds.str(string($0D,"Gyro_Z",$09))  
        fds.dec(Gyro_Z)
    
        ' // Mag uT
        FDS.str(string($0D,$0D,"Mag_X",$09))
        FDS.dec(Mag_X)
        FDS.str(string($0D,"Mag_Y",$09))
        FDS.dec(Mag_Y)
        FDS.str(string($0D,"Mag_Z",$09))
        FDS.dec(Mag_Z)
    
        fds.str(string($0D,$0D,"Temp",$09))
        fds.dec(Temp)
        fds.str(string("°C",$09))                                                   ' die temperature, about 8°C hotter than ambient?
    
       [B] ' // Finish Temp Calculation  (Where was the Raw values converted to Deg C in the first place???)[/B]
        
        fds.dec(Temp * 9 / 5 + 32)
        fds.str(string("°F"))
    
        
    PUB start
      cognew(Main,@Stack)
    
    PUB Main | L0, L1, L2, [B]L3[/B], L4, L5, L6, i, cx, cy, cz
      I2C.start(SCL_pin,SDA_pin)                                                     
    ' I2C.start(SCL_pin,SDA_pin,Bitrate)
      waitcnt(clkfreq + cnt)
    [B]
      ' // MPU_SetConfig Start[/B]
      
      \I2C.write(I2C#MPU,$6B,$01)                     ' take out of sleep and use gyro-x as clock source
      \I2C.write(I2C#MPU,$37,$02)                     ' enable I2C bypass in order to communicate with the magnetometer at address $0C
      \I2C.write($0C,$0A,%1111)                       ' access the magnetometer Fuse ROM
      \I2C.read_page($0C,$10,@ASAX,3)                 ' Read the magnetometer adjustment values   ($A2, $A4, $AF)
      ASAX := (ASAX - 128) / 2                        '  equation from datasheet:    Hadj = H x (((ASA - 128) x 0.5 / 128) + 1)
      ASAY := (ASAY - 128) / 2                        '  microcontroller friendlier: Hadj = (H x (ASA - 128) / 2 + 1) / 128 + H
      ASAZ := (ASAZ - 128) / 2                        ' These values never change so might as well do a partial calculation here
    
      \I2C.write(I2C#MPU,$19,$01)                     ' Sample rate divider (divide gyro_rate by 1 + x)
      \I2C.write(I2C#MPU,$1A,%00_000_110)             ' Digital low-pass filtering (0 = 8KHz gyro_rate, !0 = 1KHz gyro_rate)
      \I2C.write(I2C#MPU,$1B,%000_11_000)             ' Accelerometer sensitivity ±2000°/s
      \I2C.write(I2C#MPU,$1C,%000_11_000)             ' Accelerometer sensitivity ±16g
    
    [B]  ' // MPU_SetConfig End
    
      ' // Calibrate_gyro Start[/B]
      cx := 0
      cy := 0
      cz := 0
      
      repeat 25
        repeat until \I2C.read(I2C#MPU,$3A) & $01
        \I2C.read_words(I2C#MPU,$43,@Word_buffer,3)
        cx += ~~Word_buffer[0]
        cy += ~~Word_buffer[1]
        cz += ~~Word_buffer[2]
    
      cx /= 25
      cy /= 25
      cz /= 25
    
    [B] ' // Calibrate_gyro End[/B]
      
      repeat
    
        [B]' // Wait for Data, then Read 7 Bytes[/B]
        repeat until \I2C.read(I2C#MPU,$3A) & $01      ' wait for new data (based on sampling rate and digital low-pass filtering registers)
        \I2C.read_words(I2C#MPU,$3B,@Word_buffer,7)    ' Read the samples into a word buffer
    
       [B] ' // Copy Buffer to L0[/B]
        repeat i from 0 to 6                           ' Copy the word buffer into sign-extended local longs
          L0[i] := ~~Word_buffer[i]                                                 
    
       [B] ' // Temperature Calculation ????  *** Cannot Remove & ONLY used HERE [/B]
       [U] L3 := L3 / 340 + 35                            ' Perform any required calculations on the local longs (also the place to do averaging)[/U]
    
        [B]' // Does Nothing ????  *** Can be Removed[/B]
        L4 -= cx
        L5 -= cy
        L6 -= cz
        
       [B] ' // Re-Copy Buffer (L0) to Acc_X  (7 Bytes)[/B]
        repeat i from 0 to 6
          Acc_X[i] := L0[i]                            ' Copy samples to the global vars
    
        ' // ????
        L0~
        L1~
        L2~
    
        ' // Read Magnetometer  [B]Works 100%[/B]
        repeat 16                                      ' Gather 16 samples on each axis
          I2C.write($0C,$0A,%0001)                     ' Single_measurement
          repeat until I2C.read($0C,$02) & 1           ' Wait until DRDY bit is set
          I2C.read_page($0C,$03,@word_buffer,6)        ' Read XOUT_L through ZOUT_H registers into buffer
          L0 += ~~word_buffer[0]
          L1 += ~~word_buffer[1]
          L2 += ~~word_buffer[2]
    
        L0 ~>= 4                                        ' Divide each sample by 16, keeping the same sign
        L1 ~>= 4
        L2 ~>= 4
                                                    
        Mag_X := ((~~L0 * ASAX + 1) / 128 + ~~L0)       ' Apply the adjustment values 
        Mag_Y := ((~~L1 * ASAY + 1) / 128 + ~~L1)       ' and store in the global vars
        Mag_Z := ((~~L2 * ASAZ + 1) / 128 + ~~L2)
    
        
    DAT                     
    {{
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474;                                                   TERMS OF USE: MIT License                                                  &#9474;                                                            
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation    &#9474; 
    &#9474;files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,    &#9474;
    &#9474;modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software&#9474;
    &#9474;is furnished to do so, subject to the following conditions:                                                                   &#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.&#9474;
    &#9474;                                                                                                                              &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE          &#9474;
    &#9474;WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR         &#9474;
    &#9474;COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,   &#9474;
    &#9474;ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                         &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    }}                                        
    


    The MPU-9150 still remains a Mysterious Creature.
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-02-21 15:11
    Not sure I understand what you mean, the accelerometer is measuring 9.8 meters per second squared worth of acceleration due to gravity. Flat on the bench, one axis reads ~2000, and the other two read 0. Rotate 90 degrees and Z reads near zero as gravity stops pulling on it and a different axis is oriented toward the gravity generator in the center of the planet. To get a useful G-force independent of tilt angle some calculations are required.

    The code is still in a pretty rough shape, trying to figure out how I want to handle everything. The top section of the loop reads 14 bytes (as seven words) and stores them in a word buffer. The words are copied into L0 through L6 (local variables that I was experimenting with) in the same order that they were written into the buffer. L0, L1, L2 contain the sign-extended accelerometer values, L3 contains the temperature reading, L4, L5, L6 contain the gyro readings.
        repeat i from 0 to 6         
          L0[i] := ~~Word_buffer[i]  
    
    The formula for converting the temperature sensor reading into Celsius is described in section 4.18 of the MPU-9150 register map. After any necessary calculations are performed on the local variables, L0 through L6 are copied into the top VAR section, using
        repeat i from 0 to 6
          Acc_X[i] := L0[i]                                                       
    
    It takes a bit of time to gather all of these readings and average, convert, and correct them, so I thought to run it in a separate cog. But having done that I needed to have someplace to perform those averages, conversions, and corrections before sharing them for other cogs to enjoy. Just a thought, and probably not a very good one as it complicates other matters, such as having the I2C routine controlled from inside this sampling routine rather than the main routine. And it makes it rather difficult to read.

    Here's an earlier version of the demo before I tried to get fancy ... you'll have to un-comment sections to get things to work, but it should be easier to see how the measurements are done.
    MPU-9150 demo - Archive.zip


    Wow you post fast.
    I also noticed that your MPU Address is Wrong, but wont work with the correct address.

    Incorrect: MPU = %0110_1000 *** Works
    Correct: MPU = %1101_0000 *** Don't Work
    My I2C drivers work with 7-bit addressing, the drivers shift the address and append the read/write bit. I've noticed that most, but not all, datasheets give the device ID as 7 bits so thought to try to keep it simple. Oh well, for my drivers, $68 is the correct device ID for the accelerometer and gyro, and $0C is the correct address for the magnetometer.
    ' // Does Nothing ???? *** Can be Removed
    L4 -= cx
    L5 -= cy
    L6 -= cz
    That applies a correction factor to the gyro. cx, cy, and cz were zeroed, accumulated 25 samples worth of readings, and divided by 25. They're then subtracted from L4, L5, L6 (temporary registers holding Gyro_X, Gyro_Y, and Gyro_Z) to get a near-zero reading when not rotating.
    ' // Re-Copy Buffer (L0) to Acc_X (6 Bytes ?!?!?)
    repeat i from 0 to 6
    Acc_X := L0 ' Copy samples to the global vars
    close, copies seven longs.
  • zlantzzlantz Posts: 136
    edited 2014-02-21 15:24
    ChrisGadd wrote: »
    Wow you post fast.

    Lol yea, I am pretty on it with getting this thing working, I have spent many hours (now days) on just this sensor. I need it mostly of Heading Hold mode on my heli. I can get by without it & use the 6050 & Rate mode, but why should I if I have a 9150 sitting right in front of me? Or I could do 6050 + hmc5883 but I think that is a little excessive with a 9150 here.

    G-Force = Accel[x] / 8,192 (AFS-1) ' x = 0, 1, 2 for X, Y, Z

    Here is a snap of the code I just posted, all I added was the G-Force division in italics.

    Level:
    New Driver 1.jpg
    Nose Down:
    New Driver 2.jpg


    I am kinda starting to see how your code works, your methos to copy to memory locations similar to PASM. I didnt know that works in spin the same as pasm. Rather interesting of a method to code, will prob adopt some of it.

    I have been trying to compare the results with other drivers. What I see and what I expect are two totally different things. Except when I use MPU-6050 Pasm Driver, then it reads close to 2000, 0, 0 when flat.

    I still dont understand what your Accel outputs are, as they dont respond to acceleration.
    PUB Main | L0, L1, L2, L3, L4, L5, L6, i, cx, cy, cz
      I2C.start(SCL_pin,SDA_pin)                                                     
    ' I2C.start(SCL_pin,SDA_pin,Bitrate)
      waitcnt(clkfreq + cnt)
    
      ' // MPU_SetConfig Start
      
      \I2C.write(I2C#MPU,$6B,$01)                     ' take out of sleep and use gyro-x as clock source
      \I2C.write(I2C#MPU,$37,$02)                     ' enable I2C bypass in order to communicate with the magnetometer at address $0C
      \I2C.write($0C,$0A,%1111)                       ' access the magnetometer Fuse ROM
      \I2C.read_page($0C,$10,@ASAX,3)                 ' Read the magnetometer adjustment values   ($A2, $A4, $AF)
      ASAX := (ASAX - 128) / 2                        '  equation from datasheet:    Hadj = H x (((ASA - 128) x 0.5 / 128) + 1)
      ASAY := (ASAY - 128) / 2                        '  microcontroller friendlier: Hadj = (H x (ASA - 128) / 2 + 1) / 128 + H
      ASAZ := (ASAZ - 128) / 2                        ' These values never change so might as well do a partial calculation here
    
      \I2C.write(I2C#MPU,$19,$01)                     ' Sample rate divider (divide gyro_rate by 1 + x)
      \I2C.write(I2C#MPU,$1A,%00_000_110)             ' Digital low-pass filtering (0 = 8KHz gyro_rate, !0 = 1KHz gyro_rate)
      \I2C.write(I2C#MPU,$1B,%000_11_000)             ' Accelerometer sensitivity ±2000°/s
      \I2C.write(I2C#MPU,$1C,%000_11_000)             ' Accelerometer sensitivity ±16g
    
      ' // MPU_SetConfig End
    
      ' // Calibrate_gyro Start
      cx := 0
      cy := 0
      cz := 0
      
      repeat 25
        repeat until \I2C.read(I2C#MPU,$3A) & $01
        \I2C.read_words(I2C#MPU,$43,@Word_buffer,3)
        cx += ~~Word_buffer[0]
        cy += ~~Word_buffer[1]
        cz += ~~Word_buffer[2]
    
      cx /= 25
      cy /= 25
      cz /= 25
    
     ' // Calibrate_gyro End
      
      repeat
    
        ' // Wait for Data, then Read 7 Word's
        repeat until \I2C.read(I2C#MPU,$3A) & $01      ' wait for new data (based on sampling rate and digital low-pass filtering registers)
        \I2C.read_words(I2C#MPU,$3B,@Word_buffer,7)    ' Read the samples into a word buffer
    
        ' // Copy Buffer to L0    Word to Long   *** Starting at L0 to L6
        repeat i from 0 to 6                           ' Copy the word buffer into sign-extended local longs
          L0[i] := ~~Word_buffer[i]
    
        ' // Calculate Temperature
        L3 := L3 / 340 + 35                            ' Perform any required calculations on the local longs (also the place to do averaging)
    
        ' // Subtract Offset from Gyro
        L4 -= cx
        L5 -= cy
        L6 -= cz
        
        ' // Re-Copy Buffer (L0) to Acc_X  (7 Longs)
        repeat i from 0 to 6                   ' *** Starting at Acc_X to Gyro_Z   (Temp gets copied Here)
          Acc_X[i] := L0[i]                            ' Copy samples to the global vars     
    
        ' // Clear L0, L1, L2
        L0~
        L1~
        L2~
    
        ' // Read Magnetometer  Works 100%
        repeat 16                                      ' Gather 16 samples on each axis
          I2C.write($0C,$0A,%0001)                     ' Single_measurement
          repeat until I2C.read($0C,$02) & 1           ' Wait until DRDY bit is set
          I2C.read_page($0C,$03,@word_buffer,6)        ' Read XOUT_L through ZOUT_H registers into buffer
          L0 += ~~word_buffer[0]
          L1 += ~~word_buffer[1]
          L2 += ~~word_buffer[2]
    
        L0 ~>= 4                                        ' Divide each sample by 16, keeping the same sign
        L1 ~>= 4
        L2 ~>= 4
                                                    
        Mag_X := ((~~L0 * ASAX + 1) / 128 + ~~L0)       ' Apply the adjustment values 
        Mag_Y := ((~~L1 * ASAY + 1) / 128 + ~~L1)       ' and store in the global vars
        Mag_Z := ((~~L2 * ASAZ + 1) / 128 + ~~L2)
    
    
    970 x 691 - 61K
    969 x 688 - 58K
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-02-21 16:26
    Yeah, I don't understand your accelerometer readings. My readings flat on the bench were +2000, 0, 0. Rotating it saw the other two axis go to +/- 2000. Even with huge numbers, a 90 degree change in tilt should show a much larger change in readings.
    I'm away from my Prop at the moment so I can't experiment at the moment. Maybe try increasing the stack size some more, I had to bump it up a couple times while creating this monster.

    Okay, the temperature is read into the word_buffer along with the accelerometer and gyro readings, specifically it's read into word_buffer[3]. That is copied into L3, also known as L0[3]. The value in L3 is divided by 340 and added to 35, then stored back into L3. Finally, L3 is copied to the VARs at the top into Temp, also known as Acc_X[3].

    The reason for a lot that is trying to format everything properly. The sensors were read in bytes, corrected for endian-ness and stored as words, needed to be operated on as longs due to the possibility of certain operations overflowing 16 bits, and finally stored as longs in order to keep the sign extended to bit 31.
  • zlantzzlantz Posts: 136
    edited 2014-02-21 16:35
    ChrisGadd wrote: »
    Yeah, I don't understand your accelerometer readings. My readings flat on the bench were +2000, 0, 0. Rotating it saw the other two axis go to +/- 2000. Even with huge numbers, a 90 degree change in tilt should show a much larger change in readings.
    I'm away from my Prop at the moment so I can't experiment at the moment. Maybe try increasing the stack size some more, I had to bump it up a couple times while creating this monster.

    Okay, the temperature is read into the word_buffer along with the accelerometer and gyro readings, specifically it's read into word_buffer[3]. That is copied into L3, also known as L0[3]. The value in L3 is divided by 340 and added to 35, then stored back into L3. Finally, L3 is copied to the VARs at the top into Temp, also known as Acc_X[3].

    The reason for a lot that is trying to format everything properly. The sensors were read in bytes, corrected for endian-ness and stored as words, needed to be operated on as longs due to the possibility of certain operations overflowing 16 bits, and finally stored as longs in order to keep the sign extended to bit 31.

    I am starting to get your code. It is a little different than what I am used to.

    As for the accelerometer, I know it is working as I have tested it with other code. The MPU-6050 PASM driver works fine for everything but the Mag. And is why I started down the road to writing my own drivers. Of every code I have tested, only part (or none) of them typically works. It has been very frustrating with this little sensor getting full access to it's most basic features.

    I wrote a minimal i2c spin version that works, but has a random glitch in all the values that cannot be found nor filtered. Then I tried re-modding the pasm driver, I can read from the Mag ok, but cannot write to it. Plus I am out of FIT 496 space.
  • T ChapT Chap Posts: 4,223
    edited 2014-02-22 04:24
    Did you ever verify that your spin I2C loop was at least as fast as the sample rate/refresh rate of the register you are reading? If the spin loop is slower, you will get strange behavior.
  • zlantzzlantz Posts: 136
    edited 2014-02-22 08:43
    T Chap wrote: »
    Did you ever verify that your spin I2C loop was at least as fast as the sample rate/refresh rate of the register you are reading? If the spin loop is slower, you will get strange behavior.

    How could I test this? I was concerned this might be a possibility.

    One spin driver works for everything but the accelerometer (values totally wrong), another works for everything but is glitchy, the one that works perfect is for the 6050 and doesn't include the mag and is pasm. Ive tried modding it, I can read from the mag, just cant write to it.

    I have a modded 6050 pasm driver that is "working", but I had to use a Spin i2c driver to write to the mag while the pasm i2c driver reads from it. So I have 2 i2c drivers running at once (only way I could get it to work), but this causes frequent glitching on the pasm readouts. I cleaned up the results by checking the Chip ID, if = $68 then I allow the data, otherwise it gets dropped.

    I am seriously trying everything under the sun to get this module to work 100%.
  • zlantzzlantz Posts: 136
    edited 2014-02-22 09:18
    You sir hit the spot. TIMING was the problem!

    I tried re-writing the 6050 PASM driver again, this time with success!

    All that was needed was a delay after writing to the mag for the new data to arrive! Now I have a 100% working PASM Driver!
    '' MPU-60X0-PASM.spin
    '' Reads gyro and accelerometer data from the MPU-60X0 chips
    '' Read loop is in Propeller Assembler
    ''
    '' Based on Jason Dorie's code for the ITG-3200 and ADCL345 chips
    ''
    '' Note that this code assumes an 80 MHz clock
    ''
    '' The TestMPU routine can be used to verify correct setup of, and
    '' communication with, the MPU-60X0.  Load the object into RAM, then
    '' use f12 to bring up the terminal emulator to see the output.
    ''
    
    {
      *******************************************
      * User Init Select                        *
      * Updates by: Zack Lantz                  *
      *******************************************
    
      Place in Main .Spin:
    
      Con
      
        ' // Accelerometer Settings
        mAFS0 = 0
        mAFS1 = 1
        mAFS2 = 2
        mAFS3 = 3
      
        ' // Gyroscope Settings
        mFS0 = 0
        mFS1 = 1
        mFS2 = 2
        mFS3 = 3
        
        ' // Digital Low Pass Filter Settings
        DLP0 = 0          ' Bandwidth = 260 Hz
        DLP1 = 1          ' Bandwidth = 184 Hz
        DLP2 = 2          ' Bandwidth = 94  Hz
        DLP3 = 3          ' Bandwidth = 44  Hz
        DLP4 = 4          ' Bandwidth = 21  Hz
        DLP5 = 5          ' Bandwidth = 10  Hz
        DLP6 = 6          ' Bandwidth = 5   Hz
        DLP7 = 7          ' Reserved
      
        ' // Current Settings (Passed to MPU-6050.spin)
        AccelFS = AFS2
        GyroFS  = FS3
        mAFS    = mAFS2
        mFS     = mFS3
        mDLP    = DLP3
    
        
      Then start MPU Driver with:
      
      MPU.Start(MPUscl, MPUsda, mAFS, mFS, mDLP)    ' MPU-6050 Gyro & Accel Sensor Data  w/ User Init AFS, FS, & DLP
    
      
      Everything else works the same.
    
      
      New Functions:
        Start (SCL, SDA, aFS, gFS, fDLP)
        StartA(SCL, SDA, aFS, gFS, fDLP, PM) ', SF)
        StartX(SCL, SDA)
    
      *** Note:  I didn't add in SampleRate feature as it is 0 to 255 & Calculated as follows:
      
      Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)
    
      Where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled (see Register 26) 
    }
    
    {{
    
    The slave address of the MPU-60X0 is b110100X which is 7 bits long. The LSB bit of the 7 bit address is
    determined by the logic level on pin AD0. This allows two MPU-60X0s to be connected to the same I2C bus.
    When used in this configuration, the address of the one of the devices should be b1101000 (pin AD0
    is logic low) and the address of the other should be b1101001 (pin AD0 is logic high).
    
    }}
    
    
    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    CON                        ' CONs for TestMPU test routine   
      SDA_PIN        = 10
      SCL_PIN        = 11
      SERIAL_TX_PIN  = 30
      SERIAL_RX_PIN  = 31
    
    VAR
      long x0, y0, z0, a0, b0, c0, t
      long Cog
      long rx, ry, rz, temp, ax, ay, az, arx, ary, mx, my, mz   'PASM code assumes these to be contiguous
    
    OBJ
      debug : "FullDuplexSerial"  
    
    PUB TestMPU  | MPUcog 
     '-----------------------------------------------
      ' Start serial i/o cog
      ' Start cog to pull gyro/accel data from chip
      ' Print data to serial out every few seconds
      '------------------------------------------------
      debug.start(SERIAL_RX_PIN, SERIAL_TX_PIN, 0, 115200) 'Start cog to allow IO with serial terminal
      
      'repeat 4
      '   waitcnt(clkfreq + cnt)
         
      debug.str(string("Starting..."))
      debug.tx(13)
      debug.str(string("GX  GY  GZ    AX  AY  AZ"))
      debug.tx(13)
      debug.str(string("-------------------------"))
      debug.tx(13)
    
      
      MPUcog := StartX( SCL_PIN, SDA_PIN )
    
      'Output gyro data, then accel data, once per second
      repeat
         
        debug.dec(GetRX)
        debug.str(string(", "))
        debug.dec(GetRY)
        debug.str(string(", "))
        debug.dec(GetRZ)
         
        debug.str(string("   "))
        debug.dec(GetAX)
        debug.str(string(", "))
        debug.dec(GetAY)
        debug.str(string(", "))
        debug.dec(GetAZ)
         
        debug.str(string("   "))
        debug.dec(GetARX)
        debug.str(string(", "))
        debug.dec(GetARY)
        debug.str(string("   "))
         
        debug.str(string("   "))
        debug.dec(GetMX)
        debug.str(string(", "))
        debug.dec(GetMY)
        debug.str(string(", "))
        debug.dec(GetMZ)
    
         
        debug.tx(13)
        'waitcnt((clkfreq / 10) + cnt)
    
    ' // Basic Start with User Accel AFS & Gyro FS & DLP   
    PUB Start( SCL, SDA, aFS, gFS, fDLP ) : Status 
    
      if aFS == 0
        AccelFS := %00000000  ' ±  2 g
      elseif aFS == 1          
        AccelFS := %00001000  ' ±  4 g
      elseif aFS == 2
        AccelFS := %00010000  ' ±  8 g
      elseif aFS == 3
        AccelFS := %00011000  ' ± 16 g
    
      if gFS == 0
        GyroFS := %00000000   ' ±  250 ° /s
      elseif gFS == 1
        GyroFS := %00001000   ' ±  500 ° /s
      elseif gFS == 2
        GyroFS := %00010000   ' ± 1000 ° /s
      elseif gFS == 3
        GyroFS := %00011000   ' ± 2000 ° /s
    
                          '| DLPF_CFG |   Accelerometer    |          Gyroscope          |       
      if fDLP == 0        '             Bw (Hz)  Delay (ms)  Bw (Hz)  Delay (ms)  FS (Khz)
        DLP := %00000000  '      0        260        0         256       0.98        8
      elseif fDLP == 1
        DLP := %00000001  '      1        184       2.0        188       1.9         1
      elseif fDLP == 2
        DLP := %00000010  '      2         94       3.0         98       2.8         1
      elseif fDLP == 3
        DLP := %00000011  '      3         44       4.9         42       4.8         1
      elseif fDLP == 4
        DLP := %00000100  '      4         21       8.5         20       8.3         1
      elseif fDLP == 5
        DLP := %00000101  '      5         10      13.8         10      13.4         1
      elseif fDLP == 6    
        DLP := %00000110  '      6          5      19.0          5      18.6         1
      elseif fDLP == 7
        DLP := %00000111  '      7           RESERVED             RESERVED           8
    
      PowerMgmt  := %00000001  ' X gyro as clock source
      SampleRate := %00000001  ' 500 Hz
        
      ComputeTimes
    
      gyroSCL  := 1 << SCL     'save I2C pins
      gyroSDA  := 1 << SDA
      
      Status := Cog := cognew(@Start_Sensors, @rx) + 1
    
      CalibrateAccel
    
    ' // Start w/ Full User Init Settings
    PUB StartA( SCL, SDA, aFS, gFS, fDLP, PM ) : Status 
    
      if aFS == 0
        AccelFS := %00000000
      elseif aFS == 1
        AccelFS := %00001000
      elseif aFS == 2
        AccelFS := %00010000
      elseif aFS == 3
        AccelFS := %00011000
    
      if gFS == 0
        GyroFS := %00000000
      elseif gFS == 1
        GyroFS := %00001000
      elseif gFS == 2
        GyroFS := %00010000
      elseif gFS == 3
        GyroFS := %00011000
    
                          '| DLPF_CFG |   Accelerometer    |          Gyroscope          |       
      if fDLP == 0        '             Bw (Hz)  Delay (ms)  Bw (Hz)  Delay (ms)  FS (Khz)
        DLP := %00000000  '      0        260        0         256       0.98        8
      elseif fDLP == 1
        DLP := %00000001  '      1        184       2.0        188       1.9         1
      elseif fDLP == 2
        DLP := %00000010  '      2         94       3.0         98       2.8         1
      elseif fDLP == 3
        DLP := %00000011  '      3         44       4.9         42       4.8         1
      elseif fDLP == 4
        DLP := %00000100  '      4         21       8.5         20       8.3         1
      elseif fDLP == 5
        DLP := %00000101  '      5         10      13.8         10      13.4         1
      elseif fDLP == 6    
        DLP := %00000110  '      6          5      19.0          5      18.6         1
      elseif fDLP == 7
        DLP := %00000111  '      7           RESERVED             RESERVED           8
                          
      if PM == 0
        PowerMgmt := %00000000  ' Internal 8MHz oscillator
      elseif PM == 1
        PowerMgmt := %00000001  ' PLL with X axis gyroscope reference
      elseif PM == 2
        PowerMgmt := %00000010  ' PLL with Y axis gyroscope reference
      elseif PM == 3
        PowerMgmt := %00000011  ' PLL with Z axis gyroscope reference
      elseif PM == 4
        PowerMgmt := %00000100  ' PLL with external 32.768kHz reference
      elseif PM == 5
        PowerMgmt := %00000101  ' PLL with external 19.2MHz referenc
      elseif PM == 6
        PowerMgmt := %00000110  ' Reserved
      elseif PM == 7
        PowerMgmt := %00000111  ' Stops the clock and keeps the timing generator in reset
              
      ' *** Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV)  
      ' // 0 threw 255
      SampleRate := %00000001   ' 500 Hz Sample Rate,   %00000000 = 1000 Hz Sample rate
      
      ComputeTimes
    
      gyroSCL  := 1 << SCL     'save I2C pins
      gyroSDA  := 1 << SDA
      
      Status := Cog := cognew(@Start_Sensors, @rx) + 1
    
      CalibrateAccel
    
    ' // Start Basic, No User Init
    PUB StartX( SCL, SDA ) : Status 
    
      AccelFS := %00010000  ' AFS2
      GyroFS  := %00011000  ' FS3
    
      DLP        := %00000011  ' 40 Hz
      PowerMgmt  := %00000001  ' X gyro as clock source
      SampleRate := %00000001  ' 500 Hz
      
      ComputeTimes
    
      gyroSCL  := 1 << SCL     'save I2C pins
      gyroSDA  := 1 << SDA
      
      Status := Cog := cognew(@Start_Sensors, @rx) + 1
    
      CalibrateAccel
      CalibrateGyro
    
    
    PUB Stop
    
      if Cog
        cogstop(Cog~ - 1)
    
    
    '**********************
    '   Accessors
    '**********************
    PUB GetTemp          ' Temperature
      return temp
    
    'Pub GetTempF         ' Temp Deg F
    
    'Pub GetTempC         ' Temp Deg C
    
    PUB GetRX            ' Accel X - Zero Offset
      return rx - x0
    
    PUB GetRY            ' Accel Y - Zero Offset
      return ry - y0
    
    PUB GetRZ            ' Accel Z - Zero Offset
      return rz - z0
    
    PUB GetAX            ' Gyro X - Zero Offset
      return ax - a0
    
    PUB GetAY            ' Gyro Y - Zero Offset
      return ay - b0
    
    PUB GetAZ            ' Gyro Z - Zero Offset
      return az - c0
    
    
    PUB GetMX            ' Mag X 
      return mx 
    
    PUB GetMY            ' Mag Y 
      return my 
    
    PUB GetMZ            ' Mag Z 
      return mz 
    
    
    
    PUB GetARX           ' Pitch Angle
      return arx
    
    PUB GetARY           ' Roll Angle
      return ary
    
    Pub GetAccelOffsetX  ' Accelerometer Zero Offset X
      return x0
      
    Pub GetAccelOffsetY  ' Accelerometer Zero Offset Y
      return y0
      
    Pub GetAccelOffsetZ  ' Accelerometer Zero Offset Z
      return z0
      
    Pub GetGyroOffsetX   ' Gyroscope Zero Offset X
      return a0
      
    Pub GetGyroOffsetY   ' Gyroscope Zero Offset Y
      return b0
      
    Pub GetGyroOffsetZ   ' Gyroscope Zero Offset Z
      return c0
    
    Pub CalAccel         ' Calibrate Accelerometer
      CalibrateAccel
    
    Pub CalGyro          ' Calibrate Gyroscope
      CalibrateGyro
        
    PRI computeTimes                                       '' Set up timing constants in assembly
                                                           '  (Done this way to avoid overflow)
      i2cDataSet := ((clkfreq / 10000) *  350) / 100000    ' Data setup time -  350ns (400KHz)
      i2cClkLow  := ((clkfreq / 10000) * 1300) / 100000    ' Clock low time  - 1300ns (400KHz)
      i2cClkHigh := ((clkfreq / 10000) *  600) / 100000    ' Clock high time -  600ns (400KHz)
      i2cPause   := clkfreq / 100000                       ' Pause between checks for operations
    
    
    PRI CalibrateAccel | tc, xc, yc, zc, dr
    
      x0 := 0         ' Initialize offsets
      y0 := 0
      z0 := 0
      
      'wait 1/2 second for the body to stop moving
      waitcnt( constant(80_000_000 / 2) + cnt )
    
      'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
      xc := 0
      yc := 0
      zc := 0
    
      repeat 256
        xc += rx
        yc += ry
        zc += rz
    
        waitcnt( constant(80_000_000/192) + cnt )
    
      'Perform rounding
      if( xc > 0 )
        xc += 128
      elseif( xc < 0 )
        xc -= 128
    
      if( yc > 0 )
        yc += 128
      elseif( yc < 0 )
        yc -= 128
    
      if( zc > 0 )
        zc += 128
      elseif( zc < 0 )
        zc -= 128
        
      x0 := xc / 256
      y0 := yc / 256
      z0 := zc / 256
      
    
    PRI CalibrateGyro | tc, xc, yc, zc, dr
    
      a0 := 0         ' Initialize offsets
      b0 := 0
      c0 := 0
      
      'wait 1/2 second for the body to stop moving
      waitcnt( constant(80_000_000 / 2) + cnt )
    
      'Find the zero points of the 3 axis by reading for ~1 sec and averaging the results
      xc := 0
      yc := 0
      zc := 0
    
      repeat 256
        xc += ax
        yc += ay
        zc += az
    
        waitcnt( constant(80_000_000/192) + cnt )
    
      'Perform rounding
      if( xc > 0 )
        xc += 128
      elseif( xc < 0 )
        xc -= 128
    
      if( yc > 0 )
        yc += 128
      elseif( yc < 0 )
        yc -= 128
    
      if( zc > 0 )
        zc += 128
      elseif( zc < 0 )
        zc -= 128
        
      a0 := xc / 256
      b0 := yc / 256
      c0 := zc / 256
    
    
    DAT
            org   0
    
    Start_Sensors
    
    '  --------- Debugger Kernel add this at Entry (Addr 0) ---------
    '   long $34FC1202,$6CE81201,$83C120B,$8BC0E0A,$E87C0E03,$8BC0E0A
    '   long $EC7C0E05,$A0BC1207,$5C7C0003,$5C7C0003,$7FFC,$7FF8
    '  -------------------------------------------------------------- 
    
            mov             p1, par                         ' Get data pointer
            mov             prX, p1                         ' Store the pointer to the rx var in HUB RAM
            add             p1, #4
            mov             prY, p1                         ' Store the pointer to the ry var in HUB RAM
            add             p1, #4
            mov             prZ, p1                         ' Store the pointer to the rz var in HUB RAM
            add             p1, #4
            mov             pT, p1                          ' Store the pointer to the temp var in HUB RAM
            add             p1, #4
            mov             paX, p1                         ' Store the pointer to the ax var in HUB RAM
            add             p1, #4
            mov             paY, p1                         ' Store the pointer to the ay var in HUB RAM
            add             p1, #4
            mov             paZ, p1                         ' Store the pointer to the az var in HUB RAM
            add             p1, #4
            mov             paRX, p1                        ' Store the pointer to the arx var in HUB RAM
            add             p1, #4
            mov             paRY, p1                        ' Store the pointer to the ary var in HUB RAM
            add             p1, #4
            mov             pmX, p1                         ' Store the pointer to the ax var in HUB RAM
            add             p1, #4
            mov             pmY, p1                         ' Store the pointer to the ay var in HUB RAM
            add             p1, #4
            mov             pmZ, p1                         ' Store the pointer to the az var in HUB RAM
    
            mov             i2cTemp,i2cPause
            add             i2cTemp,CNT                     ' Wait 10us before starting
            waitcnt         i2cTemp,#0
           
            call            #SetConfig
    
            mov             loopCount, CNT
            add             loopCount, loopDelay
    
    
    '------------------------------------------------------------------------------
           
    ' Main loop
    '   loopDelay defined in data section
    '   Nominally set to CLK_FREQ/200 give 200hz update rate, but the update takes less than 
    '   500us, so the delay could potentially be set to give an update rate as high as 2000hz
    '
    :loop
                            call    #MPUReadValues
                            call    #MPUComputeDrift
                            call    #ComputeAngles
    
                            call    #MPUGetMagVals
    
                            wrlong  iT, pT
    
                            subs    irX, drift
                            wrlong  irX, prX
    
                            subs    irY, drift
                            wrlong  irY, prY
    
                            subs    irZ, drift
                            wrlong  irZ, prZ
    
                            wrlong  iaX, paX
                            wrlong  iaY, paY
                            wrlong  iaZ, paZ
                            
                            'Sign extend the 15th bit
                            test    iaRX, i2cWordReadMask     wc
                            muxc    iaRX, i2cWordMask
    
                            'Sign extend the 15th bit
                            test    iaRY, i2cWordReadMask     wc
                            muxc    iaRY, i2cWordMask                                                                                      
                            
                            wrlong  iaRX, paRX
                            wrlong  iaRY, paRY
    
                            wrlong  imX, pmX
                            wrlong  imY, pmY
                            wrlong  imZ, pmZ
                            
                            waitcnt loopCount, loopDelay
                            jmp     #:loop        
    
    
    
    '------------------------------------------------------------------------------
    ' MPUReadValues
    '
    '   Starting at the ACCEL_X data register,  read in the 3 accel values,
    '   the temperature, and the 3 gyro values, as these are held in
    '   sequential register locations.
    '
    MPUReadValues
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #59            ' Address of ACCEL_XOUT_H
                            mov     i2cDevID, #%11010000    ' Device ID of the MPU 
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iaX, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                            call    #i2cRead
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iaY, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                            call    #i2cRead
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iaZ, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc      ' Clear the carry flag to make reads auto-increment                       
                            call    #i2cRead
                            'test    i2cTestCarry, #1 wc      ' Set the carry flag to tell it we're done                       
                            call    #i2cRead
    
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     iT, i2cData
    
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     irX, i2cData
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     irY, i2cData
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc     ' Clear the carry flag to make reads auto-increment        
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc      ' Set the carry flag to tell it we're done                                          
                            call    #i2cRead
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     irZ, i2cData
                            
                            call    #i2cStop                        
    
    MPUReadValues_Ret       ret
    
    
    ConfigMag
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
                            call    #i2cReset                'Reset i2c
                             
                            mov     i2cDevID, #%00011000     'Device ID for the AK8975 
    
                            mov     i2cAddr, #$0A            ' CTRL Register         
                            mov     i2cValue, #%00000001     ' Single-Measurement Mode
                            call    #i2cWriteRegisterByte                        
    ConfigMag_Ret           ret
    
    
    
    MPUGetMagVals
                            call    #ConfigMag      ' *** Makes ALL Mag Values = 0
    
                          [B]  waitcnt loopCount, loopDelay     ' // Wait for New Data[/B]
    
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
    
                            mov     i2cAddr, #03
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc           
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                            call    #i2cRead                            
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     imX, i2cData           
                            
                                      
                            mov     i2cAddr, #05
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc           
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                            call    #i2cRead                            
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     imY, i2cData           
                            
    
                            mov     i2cAddr, #07
                            mov     i2cDevID, #%00011000   
                            call    #StartRead              ' Tell the I2C device we're starting
    
                            mov     i2cMask, i2cWordReadMask
                            test    i2cTestCarry, #0 wc           
                            call    #i2cRead
                            test    i2cTestCarry, #1 wc     ' Clear the carry flag to make reads auto-increment                        
                            call    #i2cRead                            
    
                            'Sign extend the 15th bit
                            test    i2cData, i2cWordReadMask     wc
                            muxc    i2cData, i2cWordMask
                            mov     imZ, i2cData           
                            
                            call    #i2cStop                        
    
    MPUGetMagVals_Ret          ret
    
    '------------------------------------------------------------------------------
                            ' Compute drift - for my gyro (Jason's ITG-3200)
                            '(Temp + 15000) / 100 = drift
                            
    '------------------------------------------------------------------------------
    MPUComputeDrift
                            mov     drift, iT               ' Start with the temperature reading
                            add     drift, tempOffset       ' Offset it by 15,000
    
                            ' divide drift by 100                        
    
                            mov     divisor, #100
                            mov     dividend, drift
                            test    dividend, i2cWordReadMask    wc
    
                            muxc    signbit, #1             ' record the sign of the original value
                            abs     dividend, dividend
    
                            mov     divCounter, #10     
                            shl     divisor, divCounter
                            mov     resultShifted, #1
                            shl     resultShifted, divCounter
    
                            add     divCounter, #1
                            mov     drift, #0
    
    :divLoop                        
                            cmp     dividend, divisor   wc
                  if_nc     add     drift, resultShifted
                  if_nc     sub     dividend, divisor
                            shr     resultShifted, #1
                            shr     divisor, #1     
                            djnz    divCounter, #:divLoop
    
                            test    signbit, #1     wc
                            negc    drift, drift
                            
                            
    MPUComputeDrift_Ret     ret
    
    
    
    '------------------------------------------------------------------------------
    ComputeAngles
                            mov     cx, iaZ
                            mov     cy, iaX
                            call    #cordic
                            mov     iaRX, ca
    
                            mov     cx, iaZ
                            mov     cy, iaY
                            call    #cordic
                            mov     iaRY, ca
    
    ComputeAngles_ret       ret
    
            
    '------------------------------------------------------------------------------
    ' SetConfig
    '
    '  See MPU-6000/6050 Register Map document for register addresses and
    '   valid settings
    '
    SetConfig
                            mov     i2cSDA, gyroSDA          'Use gyro SDA,SCL
                            mov     i2cSCL, gyroSCL
                            call    #i2cReset                'Reset i2c
                             
    :MPUSetConfig           mov     i2cDevID, #%11010000     'Device ID for the MPU-6000/6050 
    
                            ' // Power Management
                            mov     i2cAddr, #107             'Set PWR_MGMT_1 register bit 0 to choose
                            mov     i2cValue, PowerMgmt       ' X gyro as clock source  '
                            call    #i2cWriteRegisterByte
    
                            ' // I2C Master Disabled
                            mov     i2cAddr, #106       
                            mov     i2cValue, #%00000000
                            call    #i2cWriteRegisterByte
                            
                                                    
                            ' // Digital Low Pass Filter _ Config
                            mov     i2cAddr, #26      
                            mov     i2cValue, DLP             'Set DLPF_CONFIG to 3 for 40Hz bandwidth 
                            call    #i2cWriteRegisterByte     
    
                            ' // Sample Rate Divider
                            mov     i2cAddr, #25              'SMPLRT_DIV = 1 => 1khz/(1+0) = 1000hz sample rate 
                            mov     i2cValue, SampleRate       
                            call    #i2cWriteRegisterByte
    
                            ' // Gyro _ Config
                            mov     i2cAddr, #27              'GYRO_CONFIG register, set FS_SEL bits to 3 gives a
                            mov     i2cValue, GyroFS          ' full scale range of +-2000 deg/sec  
                            call    #i2cWriteRegisterByte
    
                            ' // Accel _ Config
                            mov     i2cAddr, #28              'Set ACCEL_CONFIG register AFS_SEL bits to 2 
                            mov     i2cValue, AccelFS        ' sets +-8g full scale range  
                            call    #i2cWriteRegisterByte     'ACCEL_HPF is zero which turns off high-pass filtering
    
                            ' // I2C Bypass Enabled
                            mov     i2cAddr, #55  
                            mov     i2cValue, #%00000010                           
                            call    #i2cWriteRegisterByte
                            
    
                            {
                            ' // Extra Config Settings:
                            
                            mov     i2cAddr, #Address        
                            mov     i2cValue, #%BIN_Value      
                            call    #i2cWriteRegisterByte
                            
                            }
                            
    SetConfig_Ret           
                            ret        
    
    
    '------------------------------------------------------------------------------
    StartRead
                            call    #i2cStart
                            mov     i2cData, i2cDevID
                            mov     i2cMask, #%10000000
                            call    #i2cWrite
    
                            mov     i2cData, i2cAddr
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            call    #i2cStart
                            mov     i2cData, i2cDevID
                            or      i2cData, #1
                            mov     i2cMask, #%10000000
                            call    #i2cWrite
                                                    
    StartRead_Ret           ret        
    
    
    '------------------------------------------------------------------------------
    i2cWriteRegisterByte
                            call    #i2cStart
                            mov     i2cData, i2cDevID
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Allow for minimum SCL low
                            waitcnt i2cTime, #0
    
                            mov     i2cData, i2cAddr
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Allow for minimum SCL low
                            waitcnt i2cTime, #0
    
                            mov     i2cData, i2cValue
                            mov     i2cMask,#%10000000
                            call    #i2cWrite
    
                            call    #i2cStop                                                                         
    
    i2cWriteRegisterByte_Ret
                            ret
    
    
    
    '------------------------------------------------------------------------------
    '' Low level I2C routines.  These are designed to work either with a standard I2C bus
    '' (with pullups on both SCL and SDA) or the Propellor Demo Board (with a pullup only
    '' on SDA).  Timing can be set by the caller to 100KHz or 400KHz.
    
    
    '------------------------------------------------------------------------------
    '' Do I2C Reset Sequence.  Clock up to 9 cycles.  Look for SDA high while SCL
    '' is high.  Device should respond to next Start Sequence.  Leave SCL high.
    
    i2cReset                andn    dira,i2cSDA             ' Pullup drive SDA high
                            mov     i2cBitCnt,#9            ' Number of clock cycles
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Allow for minimum SCL low
    :i2cResetClk            andn    outa,i2cSCL             ' Active drive SCL low
                            or      dira,i2cSCL            
                            waitcnt i2cTime,i2cClkHigh
                            or      outa,i2cSCL             ' Active drive SCL high
                            or      dira,i2cSCL
                            andn    dira,i2cSCL             ' Pullup drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Allow minimum SCL high
                            test    i2cSDA,ina         wz   ' Stop if SDA is high
                  if_z      djnz    i2cBitCnt,#:i2cResetClk ' Stop after 9 cycles
    i2cReset_ret            ret                             ' Should be ready for Start      
    
    
    '------------------------------------------------------------------------------
    '' Do I2C Start Sequence.  This assumes that SDA is a floating input and
    '' SCL is also floating, but may have to be actively driven high and low.
    '' The start sequence is where SDA goes from HIGH to LOW while SCL is HIGH.
    
    i2cStart
                            or      outa,i2cSCL             ' Active drive SCL high
                            or      dira,i2cSCL
                            or      outa,i2cSDA             ' Active drive SDA high
                            or      dira,i2cSDA
                            
                            mov     i2cTime,i2cClkHigh
                            add     i2cTime,cnt             ' Allow for bus free time
                            waitcnt i2cTime,i2cClkLow
    
                            andn    outa,i2cSDA             ' Active drive SDA low
                            waitcnt i2cTime,#0
    
                            andn    outa,i2cSCL             ' Active drive SCL low
    i2cStart_ret            ret                             
    
    
    '------------------------------------------------------------------------------
    '' Do I2C Stop Sequence.  This assumes that SCL is low and SDA is indeterminant.
    '' The stop sequence is where SDA goes from LOW to HIGH while SCL is HIGH.
    '' i2cStart must have been called prior to calling this routine for initialization.
    '' The state of the (c) flag is maintained so a write error can be reported.
    
    i2cStop
                            or      outa,i2cSCL             ' Active drive SCL high
    
                            mov     i2cTime,i2cClkHigh
                            add     i2cTime,cnt             ' Wait for minimum clock low
                            waitcnt i2cTime,i2cClkLow
    
                            or      outa,i2cSDA             ' Active drive SDA high
                            waitcnt i2cTime,i2cClkLow
                            
                            andn    dira,i2cSCL             ' Pullup drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum setup time
    
                            andn    dira,i2cSDA             ' Pullup drive SDA high
                            waitcnt i2cTime,#0              ' Allow for bus free time
    
    i2cStop_ret             ret
    
    
    '------------------------------------------------------------------------------
    '' Write I2C data.  This assumes that i2cStart has been called and that SCL is low,
    '' SDA is indeterminant. The (c) flag will be set on exit from ACK/NAK with ACK == false
    '' and NAK == true. Bytes are handled in "little-endian" order so these routines can be
    '' used with words or longs although the bits are in msb..lsb order.
    
    i2cWrite                mov     i2cBitCnt,#8
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Wait for minimum SCL low
    :i2cWriteBit            waitcnt i2cTime,i2cDataSet
    
                            test    i2cData,i2cMask    wz
                  if_z      or      dira,i2cSDA             ' Copy data bit to SDA
                  if_nz     andn    dira,i2cSDA
                            waitcnt i2cTime,i2cClkHigh      ' Wait for minimum setup time
                            or      outa,i2cSCL             ' Active drive SCL high
    
                            waitcnt i2cTime,i2cClkLow
                            
                            andn    outa,i2cSCL             ' Active drive SCL low
                            
                            ror     i2cMask,#1              ' Go do next bit if not done
                            djnz    i2cBitCnt,#:i2cWriteBit
                            
                            andn    dira,i2cSDA             ' Switch SDA to input and
                            waitcnt i2cTime,i2cClkHigh      '  wait for minimum SCL low
                            
                            or      outa,i2cSCL             ' Active drive SCL high
    
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum high time
                            
                            test    i2cSDA,ina         wc   ' Sample SDA (ACK/NAK) then
                            andn    outa,i2cSCL             '  active drive SCL low
                            andn    outa,i2cSDA             '  active drive SDA low
                            or      dira,i2cSDA             ' Leave SDA low
                            rol     i2cMask,#16             ' Prepare for multibyte write
                            
                            waitcnt i2cTime,#0              ' Wait for minimum low time
                            
    i2cWrite_ret            ret
    
    
    '------------------------------------------------------------------------------
    '' Read I2C data.  This assumes that i2cStart has been called and that SCL is low,
    '' SDA is indeterminant.  ACK/NAK will be copied from the (c) flag on entry with
    '' ACK == low and NAK == high.  Bytes are handled in "little-endian" order so these
    '' routines can be used with words or longs although the bits are in msb..lsb order.
    
    i2cRead                 mov     i2cBitCnt,#8
                            andn    dira,i2cSDA             ' Make sure SDA is set to input
                            
                            mov     i2cTime,i2cClkLow
                            add     i2cTime,cnt             ' Wait for minimum SCL low
    :i2cReadBit             waitcnt i2cTime,i2cClkHigh
    
                            or      outa,i2cSCL             ' Active drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum clock high
                            
                            test    i2cSDA,ina         wz   ' Sample SDA for data bits
                            andn    outa,i2cSCL             ' Active drive SCL low
    
                  if_nz     or      i2cData,i2cMask         ' Accumulate data bits
                  if_z      andn    i2cData,i2cMask
                            ror     i2cMask,#1              ' Shift the bit mask and
                            djnz    i2cBitCnt,#:i2cReadBit  '  continue until done
                            
                            waitcnt i2cTime,i2cDataSet      ' Wait for end of SCL low
    
                  if_c      or      outa,i2cSDA             ' Copy the ACK/NAK bit to SDA
                  if_nc     andn    outa,i2cSDA
                            or      dira,i2cSDA             ' Make sure SDA is set to output
    
                            waitcnt i2cTime,i2cClkHigh      ' Wait for minimum setup time
                            
                            or      outa,i2cSCL             ' Active drive SCL high
                            waitcnt i2cTime,i2cClkLow       ' Wait for minimum clock high
                            
                            andn    outa,i2cSCL             ' Active drive SCL low
                            andn    outa,i2cSDA             ' Leave SDA low
    
                            waitcnt i2cTime,#0              ' Wait for minimum low time
    
    i2cRead_ret             ret
    
    {
    ComputeAngles
                            mov     cx, iaZ
                            mov     cy, iaX
                            call    #cordic
                            mov     iaRX, ca
    
                            mov     cx, iaZ
                            mov     cy, iaY
                            call    #cordic
                            mov     iaRY, ca
    
    ComputeAngles_ret       ret
    }
    
    '------------------------------------------------------------------------------
    '' Perform CORDIC cartesian-to-polar conversion
    
    ''Input = cx(x) and cy(x)
    ''Output = cx(ro) and ca(theta)
    
    cordic                  abs       cx,cx           wc 
                  if_c      neg       cy,cy             
                            mov       ca,#0             
                            rcr       ca,#1
                             
                            movs      :lookup,#cordicTable
                            mov       t1,#0
                            mov       t2,#20
                             
    :loop                   mov       dx,cy           wc
                            sar       dx,t1
                            mov       dy,cx
                            sar       dy,t1
                            sumc      cx,dx
                            sumnc     cy,dy
    :lookup                 sumc      ca,cordicTable
                             
                            add       :lookup,#1
                            add       t1,#1
                            djnz      t2,#:loop
                            shr       ca, #16
                  
    cordic_ret              ret
    
    
    cordicTable             long    $20000000
                            long    $12E4051E
                            long    $09FB385B
                            long    $051111D4
                            long    $028B0D43
                            long    $0145D7E1
                            long    $00A2F61E
                            long    $00517C55
                            long    $0028BE53
                            long    $00145F2F
                            long    $000A2F98
                            long    $000517CC
                            long    $00028BE6
                            long    $000145F3
                            long    $0000A2FA
                            long    $0000517D
                            long    $000028BE
                            long    $0000145F
                            long    $00000A30
                            long    $00000518
                                        
    dx                      long    0
    dy                      long    0
    cx                      long    0
    cy                      long    0
    ca                      long    0
    t1                      long    0
    t2                      long    0
                  
    
    
    '' Variables for the gyro routines
    
    p1                      long    0
    pT                      long    0                       ' Pointer to Temperature in hub ram
    prX                     long    0                       ' Pointer to X rotation in hub ram
    prY                     long    0                       ' Pointer to Y rotation in hub ram
    prZ                     long    0                       ' Pointer to Z rotation in hub ram
    paX                     long    0                       ' Pointer to X accel in hub ram
    paY                     long    0                       ' Pointer to Y accel in hub ram
    paZ                     long    0                       ' Pointer to Z accel in hub ram
    paRX                    long    0                       ' Pointer to X accel angle in hub ram
    paRY                    long    0                       ' Pointer to Y accel angle in hub ram
    pcID                    long    0
    pmX                     long    0                       ' Pointer to mX value
    pmY                     long    0                       ' Pointer to mY value
    pmZ                     long    0                       ' Pointer to mZ value
    
    
    iT                      long    0                       ' Interim temperature value
    irX                     long    0                       ' Interim rX value
    irY                     long    0                       ' Interim rY value - These values are temp storage before drift compensation
    irZ                     long    0                       ' Interim rZ value
    iaX                     long    0                       ' Interim aX value
    iaY                     long    0                       ' Interim aY value
    iaZ                     long    0                       ' Interim aZ value
    iaRX                    long    0                       ' Interim aX value
    iaRY                    long    0                       ' Interim aY value
    icID                    long    0
    imX                     long    0                       ' Interim aX value
    imY                     long    0                       ' Interim aY value
    imZ                     long    0                       ' Interim aZ value
                                                                      
    i2cWordReadMask         long    %10000000_00000000
    i2cWordMask             long    $ffff0000
    loopDelay               long    80_000_000 / 200
    loopCount               long    0
    
    '' Variables for dealing with drift / division
    tempOffset              long    15000
    drift                   long    0
    divisor                 long    0
    dividend                long    0
    resultShifted           long    0
    signbit                 long    0
    divCounter              long    0
    
    
    '' Variables for i2c routines
        
    i2cTemp                 long    0
    i2cCount                long    0
    i2cValue                long    0
    i2cDevID                long    0
    i2cAddr                 long    0
    i2cDataSet              long    0                       ' Minumum data setup time (ticks)
    i2cClkLow               long    0                       ' Minimum clock low time (ticks)
    i2cClkHigh              long    0                       ' Minimum clock high time (ticks)
    i2cPause                long    0                       ' Pause before re-fetching next operation
    i2cTestCarry            long    1                       ' Used for setting the carry flag
    
         
    '' Local variables for low level I2C routines
    
    ' // MPU-6050 User Set Init Settings
    GyroFS                  long    %00000000
    AccelFS                 long    %00000000
    PowerMgmt               long    %00000000
    SampleRate              long    %00000000
    DLP                     long    %00000000         
    
    
    gyroSCL                 long    0                       ' Bit mask for SCL
    gyroSDA                 long    0                       ' Bit mask for SDA
    
    i2cSCL                  long    0                       ' Bit mask for SCL
    i2cSDA                  long    0                       ' Bit mask for SDA
    
    i2cTime                 long    0                       ' Used for timekeeping
    i2cData                 long    0                       ' Data to be transmitted / received
    i2cMask                 long    0                       ' Bit mask for bit to be tx / rx
    i2cBitCnt               long    0                       ' Number of bits to tx / rx
    
    
            FIT   496
    
  • T ChapT Chap Posts: 4,223
    edited 2014-02-22 10:22
    There may be something like a FIFO buffer with data ready flag in some register, so that you can read the data ONLY after the data ready flag is set. Reading the data will ( or should ) automatically reset the data ready flag. This may avoid timing collisions.
  • zlantzzlantz Posts: 136
    edited 2014-02-22 11:19
    T Chap wrote: »
    There may be something like a FIFO buffer with data ready flag in some register, so that you can read the data ONLY after the data ready flag is set. Reading the data will ( or should ) automatically reset the data ready flag. This may avoid timing collisions.

    It does have this, the data is stored in register $02 bit 0. However, I am very new to pasm, so I opted for putting a waitcnt in to see if it would work and it sure enough did. I would like to correct this if possible.

    I am finishing it up now with the extra magnetometer math I couldnt fit, nor knew how to do, in the pasm section.

    Having a little issue with my number line, I am pretty confident that I will figure it out eventually...

    In the Mag read section, it is Low byte High byte, wereas the mpu is High byte Low byte. It is currently being read backwards & I need to figure out how to fix this in the pasm section. This should correct my number line with the mag.
  • zlantzzlantz Posts: 136
    edited 2014-02-22 12:56
    So I just came up with a simple method to fix the High byte Low byte / Low byte High byte issue of the Magnetometer.
    ' // Return Magnetometer X, Y, Z Raw
    Pub GetMX | a, b
      ' // Bytes are Flipped (MPU Reads MSB First, Mag Reads LSB First)
      ' // Return Un-Flipped Sign-Extended Bytes
    
      a := mx
      b := a >< 8
      b := b << 8 + a >> 8
    
      return ~~b
    
    Pub GetMY | a, b
      ' // Bytes are Flipped (MPU Reads MSB First, Mag Reads LSB First)
      ' // Return Un-Flipped Sign-Extended Bytes
    
      a := my
      b := a >< 8
      b := b << 8 + a >> 8
      
      return ~~b
    
    Pub GetMZ | a, b
      ' // Bytes are Flipped (MPU Reads MSB First, Mag Reads LSB First)
      ' // Return Un-Flipped Sign-Extended Bytes
    
      a := mz
      b := a >< 8
      b := b << 8 + a >> 8
    
      return ~~b
    

    I just kept the read data intact until here (not sign extended), then rotated the bytes around then did Sign Extension last.

    I am fairly confident all of the Mag data is now correct, as I can compare it against the code posted by ChrisGadd up top.

    Now im looking at a problem in the Original MPU-6050 PASM code dealing with the angle computations.
    ComputeAngles
                            mov     cx, iaZ
                            mov     cy, iaX
                            call    #cordic
                            mov     iaRX, ca
    
                            mov     cx, iaZ
                            mov     cy, iaY
                            call    #cordic
                            mov     iaRY, ca
    
    ComputeAngles_ret       ret
    
    
    '------------------------------------------------------------------------------
    '' Perform CORDIC cartesian-to-polar conversion
    
    ''Input = cx(x) and cy(x)
    ''Output = cx(ro) and ca(theta)
    
    cordic                  abs     cx,cx           wc 
                  if_c      neg     cy,cy             
                            mov     ca,#0             
                            rcr     ca,#1
                             
                            movs    :lookup,#cordicTable
                            mov     t1,#0
                            mov     t2,#20
                             
    :loop                   mov     dx,cy           wc
                            sar     dx,t1
                            mov     dy,cx
                            sar     dy,t1
                            sumc    cx,dx
                            sumnc   cy,dy
    :lookup                 sumc    ca,cordicTable
                             
                            add     :lookup,#1
                            add     t1,#1
                            djnz    t2,#:loop
                            shr     ca, #16
                  
    cordic_ret              ret
    
    
    cordicTable             long    $20000000
                            long    $12E4051E
                            long    $09FB385B
                            long    $051111D4
                            long    $028B0D43
                            long    $0145D7E1
                            long    $00A2F61E
                            long    $00517C55
                            long    $0028BE53
                            long    $00145F2F
                            long    $000A2F98
                            long    $000517CC
                            long    $00028BE6
                            long    $000145F3
                            long    $0000A2FA
                            long    $0000517D
                            long    $000028BE
                            long    $0000145F
                            long    $00000A30
                            long    $00000518
                                        
    dx                      long    0
    dy                      long    0
    cx                      long    0
    cy                      long    0
    ca                      long    0
    t1                      long    0
    t2                      long    0
    

    I am not sure how to scale the resulting numbers to a number line that has a Zero point (Level). I can do all the calculations in spin, but they will slow things down quite substantially.
  • zlantzzlantz Posts: 136
    edited 2014-02-23 14:26
    Strange enough, today I re-tried a non-working code and now it appears to be working. I didnt change one single thing at all! For the most part the propeller is a pretty robust chip, but having a driver that works quit working at random to start working again is a little odd. Same for the pasm driver, i copied the 6050 and added in the mag and now the accel values from the 6050 code is off & it is identical code to the 6050 for that part.
  • zlantzzlantz Posts: 136
    edited 2014-02-23 16:39
    Pasm driver works Fast, but only reading the LSB of each axis from the mag correctly. The MSB is constantly 11111111. And would explain why my values didnt match the above code's values, even tho my adjustment values matched.

    I figured out the Pitch & Roll 6050 calculations. Its just 0 to 65535 on each axis so I just converted them to degrees so they are visibly legible.

    Neither code's magnetometer readings are usable for calculating heading thus far. The code posted up top's mag appears correct, however, when you apply the heading algorithm to it you get stuck with a number between 63.0 and 63.9 for a 360 & very bouncy, no way it could be used. The Pasm code gets it the closest, but again the number line is all off (because of no MSB most likely)

    I do like the idea of a spin driver as I can code in the angle calculations using floating point math quite easily into the mag cog.
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-02-24 06:07
    So I ran the exact code from post #6 and got decent readings:
    Level:
    MPU-9150 level.jpg
    Nose down:
    MPU-9150 nosedown.jpg


    I also had a look through of the PASM code from post #13, and it does look like you're reading it properly.
    MPU-60X0-PASM.jpg


    I'm not sure what those readings are telling me, but they look okay on the logic analyzer; no NAKs or stuck $FF or $00 readings anywhere. The stop bit could be better formed but it doesn't seem to be harming anything.
    MPU-60X0.jpg


    There was a bit of a surprise when I connected the module using your I2C pins 10 and 11 and the module stopped working. Seems it does need external pull-ups even though the datasheet says they're included on board the module. A couple 10K-ohms solved that.
    1024 x 598 - 78K
    1024 x 600 - 41K
    1024 x 598 - 41K
    1024 x 213 - 43K
  • zlantzzlantz Posts: 136
    edited 2014-02-25 10:56
    ChrisGadd wrote: »
    So I ran the exact code from post #6 and got decent readings:
    Level:
    MPU-9150 level.jpg
    Nose down:
    MPU-9150 nosedown.jpg


    I also had a look through of the PASM code from post #13, and it does look like you're reading it properly.
    MPU-60X0-PASM.jpg


    I'm not sure what those readings are telling me, but they look okay on the logic analyzer; no NAKs or stuck $FF or $00 readings anywhere. The stop bit could be better formed but it doesn't seem to be harming anything.
    MPU-60X0.jpg


    There was a bit of a surprise when I connected the module using your I2C pins 10 and 11 and the module stopped working. Seems it does need external pull-ups even though the datasheet says they're included on board the module. A couple 10K-ohms solved that.

    I do have pullups on 10 & 11. I decided to switch back to the spin code from above as it is now working and because tho the pasm code is working, the data is wrong. Plus I was able to do additional math in the cog I couldn't do in pasm.

    I have the driver updated for the most part to what I am after, however, i am still having a bit of trouble getting the Heading to calculate. In SerialChart you can see as it approaches 0 deg there is an error on either side of 0.

    ser cht.jpg


    [code]
    {
    ************************************************************************************************************
    * *
    * AUTO-RECOVER NOTICE: This file was automatically recovered from an earlier Propeller Tool session. *
    * *
    * ORIGINAL FOLDER: C:\...\Parallax Inc\Propeller Tool v1.3.2\Library\Autopilot Basestation\ *
    * TIME AUTO-SAVED: 49 minutes ago (2/25/2014 4:19:35 AM) *
    * *
    * OPTIONS: 1) RESTORE THIS FILE by deleting these comments and selecting File -> Save. *
    * The existing file in the original folder will be replaced by this one. *
    * *
    * -- OR -- *
    * *
    * 2) IGNORE THIS FILE by closing it without saving. *
    * This file will be discarded and the original will be left intact. *
    * *
    ************************************************************************************************************
    .}
    {{┌──────────────────────────────────────────┐
    │ MPU-9150 demo using my I2C driver │
    │ Author: Chris Gadd │
    │ Copyright (c) 2012 Chris Gadd │
    │ See end of file for terms of use. │
    └──────────────────────────────────────────┘

    Demonstrates the MPU-9150A 9-Axis Module from SparkFun

    MPU-9150 400KHz max. clock frequency from datasheet
    ┌───────────┐
    │• GND │ SCL 
    │• VCC │ SDA ────────────
    │• SDA │ S 1 1 0 1 0 0 x 0 │ Register address │ Register data │ Next register data │ │
    │• SCL │ └──────Device CODE──────┘ │ (Ack) (00h - 3Fh) (Ack) (See table below) (Ack) (Automatically advances) (Ack) Stop
    │• ESD │ Read (1) (See table below)
    │• ESC │ Write(0)
    │• COUT │
    │• CIN │ Accelerometer, gyroscope, and temperature sensor use device ID $68 ($69 if AD0 pulled high)
    │• AD0 │ Magnetometer uses device ID $0C - must first be enabled by writing $02 to register $37 of device $68 ($69 if AD0 high)
    │• FSYNC │
    │• INT │
    └───────────┘

    GYRO AND ACCELEROMETER REGISTERS
    ┌───────┬────────────────────────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
    │Address│ Definition │ BIT 7 │ BIT 6 │ BIT 5 │ BIT 4 │ BIT 3 │ BIT 2 │ BIT 1 │ BIT 0 │
    ├───────┼────────────────────────────┼─────────┴─────────┴─────────┼─────────┴─────────┴─────────┴─────────┴─────────┤
    │ 0Dh │ SELF_TEST_X │ XA_TEST[4-2] │ XG_TEST[4-0] │
    ├───────┼────────────────────────────┼─────────────────────────────┼─────────────────────────────────────────────────┤
    │ 0Eh │ SELF_TEST_Y │ YA_TEST[4-2] │ YG_TEST[4-0] │
    ├───────┼────────────────────────────┼─────────────────────────────┼─────────────────────────────────────────────────┤
    │ 0Fh │ SELF_TEST_Z │ ZA_TEST[4-2] │ ZG_TEST[4-0] │
    ├───────┼────────────────────────────┼───────────────────┬─────────┴─────────┬───────────────────┬───────────────────┤
    │ 10h │ SELF_TEST_A │ RESERVED │ XA_TEST[1-0] │ YA_TEST[1-0] │ ZA_TEST[1-0] │
    ├───────┼────────────────────────────┼───────────────────┴───────────────────┴───────────────────┴───────────────────┤
    │ 19h │ SMPLRT_DIV │ SMPLRT_DIV[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────────────────────────┬─────────────────────────────┤
    │ 1Ah │ CONFIG │ --- │ --- │ EXT_SYNC_SET[2:0] │ SLPF_CFG[2:0] │
    ├───────┼────────────────────────────┼─────────┼─────────┼─────────┬───────────────────┼─────────┬─────────┬─────────┤
    │ 1Bh │ GYRO_CONFIG │ XG_ST │ YG_ST │ ZG_ST │ FS_SEL[A:0] │ --- │ --- │ --- │
    ├───────┼────────────────────────────┼─────────┼─────────┼─────────┼───────────────────┼─────────┴─────────┴─────────┤
    │ 1Ch │ ACCEL_CONFIG │ XA_ST │ YA_ST │ ZA_ST │ AFS_SEL[A:0] │ ACCEL_HPF[2:0] │
    ├───────┼────────────────────────────┼─────────┴─────────┴─────────┴───────────────────┴─────────────────────────────┤
    │ 1Dh │ FF_THR │ FF_THR[7:0] │ * Register map V4.2 omits these registers
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 1Eh │ FF_DUR │ FF_DUR[7:0] │ *
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 1Fh │ MOT_THR │ MOT_THR[7:0] │ *
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 20h │ MOT_DUR │ MOT_DUR[7:0] │ *
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 21h │ ZRMOT_THR │ ZRMOT_THR[7:0] │ *
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 22h │ ZRMOT_DUR │ ZRMOT_DUR[7:0] │ *
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┤
    │ 23h │ FIFO_EN │ TEMP │ XG │ YG │ ZG │ ACCEL │ SLV2 │ SLV1 │ SLV0 │
    │ │ │ _FIFO_EN│ _FIFO_EN│ _FIFO_EN│ _FIFO_EN│ _FIFO_EN│ _FIFO_EN│ _FIFO_EN│ _FIFO_EN│
    ├───────┼────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┴─────────┴─────────┴─────────┤
    │ 24h │ I2C_MST_CTRL │ MULT │ WAIT │ SLV3 │ I2C_MST │ I2C_MST_CLK[3:0] │
    │ │ │ _MST_EN │ _FOR_ES │ _FIFO_EN│ _P_NSR │ │
    ├───────┼────────────────────────────┼─────────┼─────────┴─────────┴─────────┴───────────────────────────────────────┤
    │ 25h │ I2C_SLV0_ADDR │ I2C_SLV0│ I2C_SLV0_ADDR[6:0] │
    │ │ │ _RW │ │
    ├───────┼────────────────────────────┼─────────┴─────────────────────────────────────────────────────────────────────┤
    │ 26h │ I2C_SLV0_REG │ I2C_SLV0_REG[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────┬───────────────────────────────────────┤
    │ 27h │ I2C_SLV0_CTRL │ I2C_SLV0│ I2C_SLV0│ I2C_SLV0│ I2C_SLV0│ I2C_SLV0_LEN[3:0] │
    │ │ │ _EN │ _BYTE_SW│ _REG_DIS│ _GRP │ │
    ├───────┼────────────────────────────┼─────────┼─────────┴─────────┴─────────┴───────────────────────────────────────┤
    │ 28h │ I2C_SLV1_ADDR │ I2C_SLV1│ I2C_SLV1_ADDR[6:0] │
    │ │ │ _RW │ │
    ├───────┼────────────────────────────┼─────────┴─────────────────────────────────────────────────────────────────────┤
    │ 29h │ I2C_SLV1_REG │ I2C_SLV1_REG[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────┬───────────────────────────────────────┤
    │ 2Ah │ I2C_SLV1_CTRL │ I2C_SLV1│ I2C_SLV1│ I2C_SLV1│ I2C_SLV1│ I2C_SLV1_LEN[3:0] │
    │ │ │ _EN │ _BYTE_SW│ _REG_DIS│ _GRP │ │
    ├───────┼────────────────────────────┼─────────┼─────────┴─────────┴─────────┴───────────────────────────────────────┤
    │ 2Bh │ I2C_SLV2_ADDR │ I2C_SLV2│ I2C_SLV2_ADDR[6:0] │
    │ │ │ _RW │ │
    ├───────┼────────────────────────────┼─────────┴─────────────────────────────────────────────────────────────────────┤
    │ 2Ch │ I2C_SLV2_REG │ I2C_SLV2_REG[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────┬───────────────────────────────────────┤
    │ 2Dh │ I2C_SLV2_CTRL │ I2C_SLV2│ I2C_SLV2│ I2C_SLV2│ I2C_SLV2│ I2C_SLV2_LEN[3:0] │
    │ │ │ _EN │ _BYTE_SW│ _REG_DIS│ _GRP │ │
    ├───────┼────────────────────────────┼─────────┼─────────┴─────────┴─────────┴───────────────────────────────────────┤
    │ 2Eh │ I2C_SLV3_ADDR │ I2C_SLV3│ I2C_SLV3_ADDR[6:0] │
    │ │ │ _RW │ │
    ├───────┼────────────────────────────┼─────────┴─────────────────────────────────────────────────────────────────────┤
    │ 2Fh │ I2C_SLV3_REG │ I2C_SLV3_REG[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────┬───────────────────────────────────────┤
    │ 30h │ I2C_SLV3_CTRL │ I2C_SLV3│ I2C_SLV3│ I2C_SLV3│ I2C_SLV3│ I2C_SLV3_LEN[3:0] │
    │ │ │ _EN │ _BYTE_SW│ _REG_DIS│ _GRP │ │
    ├───────┼────────────────────────────┼─────────┼─────────┴─────────┴─────────┴───────────────────────────────────────┤
    │ 31h │ I2C_SLV4_ADDR │ I2C_SLV4│ I2C_SLV4_ADDR[6:0] │
    │ │ │ _RW │ │
    ├───────┼────────────────────────────┼─────────┴─────────────────────────────────────────────────────────────────────┤
    │ 32h │ I2C_SLV4_REG │ I2C_SLV4_REG[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 33h │ I2C_SLV4_DO │ I2C_SLV4_DO[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────────────────────────────────────────────┤
    │ 34h │ I2C_SLV4_CTRL │ I2C_SLV4│ I2C_SLV4│ I2C_SLV4│ I2C_MST_DLY[3:0] │
    │ │ │ _EN │ _INT_EN │ _REG_DIS│ │
    ├───────┼────────────────────────────┼─────────┴─────────┴─────────┴─────────────────────────────────────────────────┤
    │ 35h │ I2C_SLV4_DI │ I2C_SLV4_DI[7:0] │
    ├───────┼────────────────────────────┼─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┤
    │ 36h │ I2C_MST_STATUS │ PASS_ │ I2C_SLV4│ I2C_LOST│ I2C_SLV4│ I2C_SLV3│ I2C_SLV2│ I2C_SLV1│ I2C_SLV0│
    │ │ │ THROUGH │ _DONE │ _ARB │ _NACK │ _NACK │ _NACK │ _NACK │ _NACK │
    ├───────┼────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
    │ 37h │ INT_PIN_CONFIG │ INT │ INT │ LATCH │ INT_RD │ FSYNC_ │ FSYNC │ I2C │ CLKOUT │
    │ │ │ _LEVEL │ _OPEN │ _INT_EN │ _CLEAR │INT_LEVEL│ _ENT_EN │BYPASS_EN│ _EN │
    ├───────┼────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
    │ 38h │ INT_ENABLE │ FF_EN │ MOT_EN │ ZMOT_EN │ FIFO_ │ I2C_MST │ --- │ --- │ DATA │
    │ │ │ │ │ │ OFLOW_EN│ _INT_EN │ │ │ _RDY_EN │
    ├───────┼────────────────────────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
    │ 3Ah │ INT_STATUS │ FF_INT │ MOT_INT│ ZMOT_INT│ FIFO_ │ I2C_MST │ --- │ --- │ DATA │
    │ │ │ │ │ │OFLOW_INT│ _INT │ │ │ _RDY_INT│
    ├───────┼────────────────────────────┼─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┤
    │ 3Bh │ ACCEL_XOUT_H │ ACCEL_XOUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 3Ch │ ACCEL_XOUT_L │ ACCEL_XOUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 3Dh │ ACCEL_YOUT_H │ ACCEL_YOUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 3Eh │ ACCEL_YOUT_L │ ACCEL_YOUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 3Fh │ ACCEL_ZOUT_H │ ACCEL_ZOUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 40h │ ACCEL_ZOUT_L │ ACCEL_ZOUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 41h │ TEMP_OUT_H │ TEMP_OUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 42h │ TEMP_OUT_L │ TEMP_OUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 43h │ GYRO_XOUT_H │ GYRO_XOUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 44h │ GYRO_XOUT_L │ GYRO_XOUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 45h │ GYRO_YOUT_H │ GYRO_YOUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 46h │ GYRO_YOUT_L │ GYRO_YOUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 47h │ GYRO_ZOUT_H │ GYRO_ZOUT[15:8] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 48h │ GYRO_ZOUT_L │ GYRO_ZOUT[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 49h │ EXT_SENS_DATA_00 │ EXT_SENS_DATA_00[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────────────────────────────────┤
    │ 4Ah │ EXT_SENS_DATA_01 │ EXT_SENS_DATA_01[7:0] │
    ├───────┼────────────────────────────┼───────────────────────────────────────────────────
    1024 x 640 - 94K
  • T ChapT Chap Posts: 4,223
    edited 2014-02-26 04:30
    When the graph jumps from extreme low to extreme high like that, you may be seeing a non signed value being read by your display software that is looking for signed values. It also may be something funky still in your bit shifting,
  • vanmunchvanmunch Posts: 568
    edited 2014-10-07 08:54
    It looks like there are three different drivers for the MPU-9150. Two in the OBEX http://obex.parallax.com/search/9150 and the one in this thread and quoted below.

    I've been testing the three and was wondering which one people prefer, or if anyone has had problems with a particular one? Faster?

    Thanks!

    Dave

    ChrisGadd wrote: »
    I bought one of these MPU-9150 modules from SparkFun, largely to see what the fuss is in communicating with it, and it works perfectly with both my spin and pasm I2C drivers without any modification.

    Attached is a demo program that I've been working on.

    MPU-9150 demo 2 - Archive.zip
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2014-10-07 09:43
    I haven't yet purchased an MPU-9150 device, but the options in code that I certainly would want to know about would be which form of Sensor Fusion was attempted.

    I have purchase a HoverFlyGimal Experimenters board with 9Dof, somewhat similar code compexity.

    The MPU-9150 has the ability to do its own rather proprietary senor fusion or to merely provide raw data for other choices.

    Kalman filter was the original standard, but for 9Dof devices, Madjwick has taken the lead.

    Madjwick produces results in Quaternion format; other choices are Directional Cosine Matrix or Euler Angles.

    =========
    So the problem of which of the three objects is the fastest or easiest to learn is pretty much dependent on where you are starting from and where you desire to go.

    I have read that Madjwick will outperform internal sensor fusion on the MPU-9150. So I have pretty much accepted personal learning goals of Madjwick, Quaternion output, and conversion of Quaternion output to Direction Cosine Matrix.

    Euler Angles have a technical problem that is called Gimbal Lock.

    ++++++++++
    Other issues with code are whether you merely want to Demo the device or you have a particular applications, such as a Quad-copter.

    Demo code will work better as an entry level means of learning. Code that is actually intended for flight will have many more features attached and may be overwhelming for some.

    I guess you will have to open each one and survey the code for what you feel is best for you. Jason Dorie has other 9Dof code that is also available, has been up-graded to Madjwick, and has a graphic viewing pack that can be compiled from C## or can be view on a Propeller driven VGA via Dave Hein's code.
  • vanmunchvanmunch Posts: 568
    edited 2014-10-07 11:29
    Hey Loopy,

    I'm working on a project that has been on and off of my self a couple of times. Last summer I created my own 6 DOF Kalman filter using separate accel and gyro that ran at about 80 Hz. I think that's fast enough, but who knows. Recently I've decided to pick up the project again I thought it would be good to try and use an "all in one" chip. My goal is pretty lofty, an autonomous flying "ball" similar to this one. http://youtu.be/55d5ppwQBQ4

    Have fun!
    Dave
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-10-10 14:59
    I've cleaned up my driver and placed it into the obex here. There are a couple archives, one is a standalone object that continuously samples and writes values from the sensor, using a dedicated I2C bus. The other archive contains a demo that can use any I2C driver, and which can be put on a common bus with other devices.

    During testing I noticed that the sampling rate from both versions was pretty much identical; the continuously sampling version spends a great deal of its time waiting for the sensor. The only real difference was when using a Spin-based I2C driver, taking an extra couple hundred milliseconds or so between samples.

    I haven't attempted anything with the built-in digital motion processor to produce what InvenSense calls MotionFusion, it's locked behind proprietary code, but this loader routine looks translatable. I can sort of follow along with this I2C traffic dump, which makes use of some more undocumented registers. Even that will only fuse the accelerometer and gyroscope data, leaving the magnetometer to be handled externally. Supposedly the latest version of the SDK does combine everything.

    Chris
  • vanmunchvanmunch Posts: 568
    edited 2014-10-13 07:49
    ChrisGadd wrote: »
    I've cleaned up my driver and placed it into the obex here. There are a couple archives, one is a standalone object that continuously samples and writes values from the sensor, using a dedicated I2C bus. The other archive contains a demo that can use any I2C driver, and which can be put on a common bus with other devices.

    During testing I noticed that the sampling rate from both versions was pretty much identical; the continuously sampling version spends a great deal of its time waiting for the sensor. The only real difference was when using a Spin-based I2C driver, taking an extra couple hundred milliseconds or so between samples.

    I haven't attempted anything with the built-in digital motion processor to produce what InvenSense calls MotionFusion, it's locked behind proprietary code, but this loader routine looks translatable. I can sort of follow along with this I2C traffic dump, which makes use of some more undocumented registers. Even that will only fuse the accelerometer and gyroscope data, leaving the magnetometer to be handled externally. Supposedly the latest version of the SDK does combine everything.

    Chris

    Hey Chris, this looks great. You've even added scaling and pitch and roll!

    I was able to play with it a little bit this morning and it looks like there is a disconnect between the Gyro and accel X &Y out put. (X Accel increase shows as Y increase on the Gyro).

    Dave
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-10-13 12:47
    Rotating the board around the X-axis shows a rate change on the gyro X-axis, and shows a force change on the accelerometer Y and Z axes. Rotating about the Y-axis shows a rate change on the gyro Y-axis, and a change on the accelerometer X and Z axes. Is that what you mean? Or is the direction wrong? On my board the accelerometer X-axis increases with a negative rate (counter-clockwise rotation) on gyro y-axis
     MPU-9150                            
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;             &#9516;Y-axis    
    &#9474;&#8226; GND      &#9474;             &#9474;          
    &#9474;&#8226; VCC      &#9474;             &#9474;          
    &#9474;&#8226; SDA      &#9474;             &#9474;          
    &#9474;&#8226; SCL      &#9474;             &#9474;          
    &#9474;&#8226; ESD      &#9474;             &#9474;    X-axis
    &#9474;&#8226; ESC      &#9474;    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9547;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508; 
    &#9474;&#8226; COUT     &#9474;             &#9474;          
    &#9474;&#8226; CIN      &#9474;             &#9474;          
    &#9474;&#8226; AD0      &#9474;             &#9474;          
    &#9474;&#8226; FSYNC    &#9474;             &#9474;          
    &#9474;&#8226; INT      &#9474;             &#9474;          
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;             &#9524;          
    
  • vanmunchvanmunch Posts: 568
    edited 2014-10-13 17:49
    Hi Chris, I'm sorry you're correct. I wasn't reading the output correctly. It took me a bit to realize what was going on between the two. Thank you.

    I was wondering if you used a filter for calculating pitch and roll? They seemed stable. Is there a reason yaw is left out? Am I missing something?

    I was also wondering if you had any thoughts on how to increase the speed that the program to ~100Hz? Do you think reading all the values form the gyro, accel, and compass at once instead of one at a time would help? I've used address pointer before that that seemed to really increase the speed that the program was able to run.

    Thanks again for all of your help and insight. This is really a wonderful object that you've created! :)

    Dave
  • ChrisGaddChrisGadd Posts: 310
    edited 2014-10-13 18:18
    Unfortunately I can't take credit for the cordic routine that computes the pitch and roll; I copied it from Zlantz's object without any deep understanding of it.

    For the sampling rate, the largest speed bump is in sampling the magnetometers. As it's currently written, the magnetometers are sampled 32 times and the average returned. If only a single sample is used, the entire loop runs at ~122Hz, and the readings still look quite stable.

    MPU-9150 PASM driver 122Hz.spin

    Chris
Sign In or Register to comment.