MPU-9150 PASM Driver
zlantz
Posts: 136
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.
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
It should be possible with only the pasm i2c driver:
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
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:
The MPU-9150 still remains a Mysterious Creature.
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. 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 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. 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.
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. close, copies seven longs.
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: Nose Down:
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.
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.
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%.
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!
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.
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.
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.
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.
Level: Nose down:
I also had a look through of the PASM code from post #13, and it does look like you're reading it properly.
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.
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.
[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] │
├───────┼────────────────────────────┼───────────────────────────────────────────────────
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
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.
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
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
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
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