Module for MPU9150

Showroom for MicroPython related hardware projects.
Target audience: Users wanting to show off their project!
Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Module for MPU9150

Post by Turbinenreiter » Wed Jul 30, 2014 2:02 pm

I started on a module for the MPU-9150. Way more complex than the bmp180, most of the registers I don't even know what to do with. But I can already wake the device, put it to sleep, read the accelerometer registers and set the accelerometer range. The accelerometer data is raw.

One thing that throws me of a little is that it uses single bits of some registers to control some stuff.
i.e.
register ACCEL_CONFIG:
*Bit7 - x-axis self test
*Bit6 - y
*Bit5 - z
*Bit4:3 - range selection
*Bit3:0 - empty

Now with I2C.mem_write() I can write bytes to the addresses, but not bits within the byte. I guess the answer is in bitwise operations and stuff.

Here's what I got so far. Will continue tommorow.

Code: Select all

import pyb
import os
from struct import unpack as unp

class MPU9150():
    '''
    Module for the MPU9150 9DOF IMU.
    '''

    _mpu_addr = (104, 105)  # addresses of MPU9150
                            # there can be two devices
                            # connected, first on 104,
                            # second on 105

    # init
    def __init__(self, side_str=None):

        # choose wich i2c port to use
        if side_str == 'X':
            side = 1
        elif side_str == 'Y':
            side = 2
        else:
            print('pass either X or Y, defaulting to Y')
            side = 2

        # create i2c object
        self.mpu_addr = self._mpu_addr[0]
        self._mpu_i2c = pyb.I2C(side, pyb.I2C.MASTER)
        self.chip_id = unp('>h', self._mpu_i2c.mem_read(1, self.mpu_addr, 0x75))[0] # who_am_i register

    # wake, sleep and pwr_mgmt maybe should be
    # mode() - returns mode
    # mode('sleep') - sleep
    # mode('wake') - wake
    # but pwr_mgmt also controls other stuff
    # don't know how to change single bits in byte on given address
    # wake
    def wake(self):
        '''Wakes the device from sleep'''
        self._mpu_i2c.mem_write(0x01, self.mpu_addr, 0x6B)

    # sleep
    def sleep(self):
        '''Puts the device to sleep'''
        self._mpu_i2c.mem_write(0x40, self.mpu_addr, 0x6B)

    # pwr_mgmt
    def get_pwr_mgmt(self):
        '''Returns PWR_MGMT_1'''
        return self._mpu_i2c.mem_read(2, self.mpu_addr, 0x6B)

    # sample rate
    def sample_rate(self, rate=None):
        '''Returns the sample rate or sets it to the passed arg'''
        if rate is None:
            return unp('>h', self._mpu_i2c.mem_read(1, selfmpu_addr, 0x19))[0]
        else:
            pass

    # this register also controls self test
    # instead of list lookup bitshift operation possible
    # accelerometer range
    def accel_range(self, accel_range=None):
        if accel_range is None:
            return self._mpu_i2c.mem_read(1, self.mpu_addr, 0x1C)
        else:
            ar = (0x00, 0x08, 0x10, 0x18)
            try:
                print(ar[accel_range])
                self._mpu_i2c.mem_write(ar[accel_range], self.mpu_addr, 0x1C)
            except IndexError:
                print('accel_range can only be 0, 1, 2 or 3')

    # while use of dict is kinda cool, this needs
    # a dict and a list to do something really easy
    # however this makes it possible to do
    # self.get_acceleration['x'] or 'xyz' or 'yz'
    # or whatever
    # but getting a single axis should not return a list
    # get acceleration
    def get_accel(self, xyz=None):
        '''Returns the accelerations on axis passed in arg'''
        if xyz is None:
            xyz = 'xyz'
        scale = 1
        axyz = {'x': unp('>h', self._mpu_i2c.mem_read(2, self.mpu_addr, 0x3B))[0]*scale,
                'y': unp('>h', self._mpu_i2c.mem_read(2, self.mpu_addr, 0x3D))[0]*scale,
                'z': unp('>h', self._mpu_i2c.mem_read(2, self.mpu_addr, 0x3F))[0]*scale}
        aout = []
        for char in xyz:
            aout.append(axyz[char])
        return aout

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: Module for MPU9150

Post by dhylands » Wed Jul 30, 2014 6:10 pm

If you enable the uctypes module (its currently not enabled for stmhal) then you could do:

Code: Select all

import uctypes

ACCEL_CONFIG = {
    'x_self_test'   : uctypes.BFUINT8 | 0 | 7 << uctypes.BF_POS | 1 << uctypes.BF_LEN,
    'y_self_test'   : uctypes.BFUINT8 | 0 | 6 << uctypes.BF_POS | 1 << uctypes.BF_LEN,
    'z_self_test'   : uctypes.BFUINT8 | 0 | 5 << uctypes.BF_POS | 1 << uctypes.BF_LEN,
    'range'         : uctypes.BFUINT8 | 0 | 3 << uctypes.BF_POS | 2 << uctypes.BF_LEN,
}

buf = bytearray(1)
buf[0] = 0xa8
print('buf[0] =', hex(buf[0]))

accel_config = uctypes.struct(ACCEL_CONFIG, uctypes.addressof(buf))
print('x_self_test =', accel_config.x_self_test)
print('y_self_test =', accel_config.y_self_test)
print('z_self_test =', accel_config.z_self_test)
print('range =', accel_config.range)

accel_config.y_self_test = 1
print('buf[0] =', hex(buf[0]))
You would read/write buf using the i2c commands and then access the bit fields directly.

I opened issue: https://github.com/micropython/micropython/issues/778 since moductypes.c doesn't currently compile cleanly.

I was able to run the above code by applying this patch:

Code: Select all

diff --git a/extmod/moductypes.c b/extmod/moductypes.c
index dd78102..dd0a519 100644
--- a/extmod/moductypes.c
+++ b/extmod/moductypes.c
@@ -316,6 +316,7 @@ STATIC mp_obj_t get_aligned(uint val_type, void *p, mp_int_t index) {
             return mp_obj_new_float(((double*)p)[index]);
         default:
             assert(0);
+            return mp_const_none;
     }
 }
 
@@ -514,6 +515,7 @@ STATIC mp_obj_t uctypes_struct_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_ob
         }
 
         assert(0);
+        return MP_OBJ_NULL;
     } else {
         // store
         return MP_OBJ_NULL; // op not supported
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index cbdfcf4..a70782f 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -51,6 +51,7 @@
 #define MICROPY_PY_CMATH            (1)
 #define MICROPY_PY_IO               (1)
 #define MICROPY_PY_IO_FILEIO        (1)
+#define MICROPY_PY_UCTYPES          (1)
 
 #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF   (1)
 #define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE  (0)

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: Module for MPU9150

Post by Turbinenreiter » Mon Aug 04, 2014 2:48 pm

I have a module now that allows to do following:

set wake/sleep
set sample rate
set acceleromter range
set gyro range
read acceleromter
read gyro
read magentometer

This sensor is crazy. Reading the register map is a constant wtf. They split values over two registers, with 4 bits in one register, 2 more bits in the next. Hell, the magneto even uses different endianness then the rest. And the coolest thing that this sensor can do - Motion Fusing - is not really mentioned in the docs, except for them telling you how awesome it is. No word on how to use it.

I'll drop the code on github later, have to clean it up a little.

Oh, also: It seems that having two modules each initialzing their own i2c doesn't crash anything. Had both the mpu9150 and the bmp180 connected on the same i2c-pins, each module initializes a bus, and it worked just fine. Not sure if there is any downside to having multiple buses on the same time.


User avatar
JonHylands
Posts: 69
Joined: Sun Dec 29, 2013 1:33 am

Re: Module for MPU9150

Post by JonHylands » Thu Sep 04, 2014 2:13 pm

Turbinenreiter,

So I've got my first NanoSeeker board up and running, and I've tried your code (from github) with my on-board MPU-9150.

The device responds, and I can access the accelerometer and the gyro, but the compass doesn't show up. I tried setting the pass-through registers, but the compass device doesn't show up when I do i2c.scan(), and the imu.get_mag() function fails when it tries to talk to a device at address 12.

Did you do anything specific with yours to make that work? Which board are you using?

- Jon

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: Module for MPU9150

Post by Turbinenreiter » Thu Sep 04, 2014 3:19 pm

I'm using the Sparkfun Breakout Board.
Have you connected ESD and ESC (I2C of the compass) to the the same I2C bus as the accel/gyros? That's how I do it - basically using the accel/gyro and the compass as two seperate I2C devices, because the magic to use the internal power of the chip is not documented.
As far as I remember I didn't take any extra steps.
I left all the stuff at work, but I will look into it tommorow - it's quite possible that the github repo is outdated.

Maybe there is something on the Breakout Board? https://www.sparkfun.com/products/11486
It does have some jumpers to solder, one to switch the LSB of the I2C address.

I'll have a look at it tomorrow.

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: Module for MPU9150

Post by Turbinenreiter » Fri Sep 05, 2014 9:11 am

Ok, the same code that is on Github works for me.

register dump:

Code: Select all

reg:13 val:b'PQ'
reg:14 val:b'Q\xb7'
reg:15 val:b'\xb7\xd1'
reg:16 val:b'\xd1\x00'
reg:17 val:b'\x00\x00'
reg:18 val:b'\x00\x00'
reg:19 val:b'\x00\x00'
reg:20 val:b'\x00\x00'
reg:21 val:b'\x00\x00'
reg:22 val:b'\x00\x00'
reg:23 val:b'\x00\x00'
reg:24 val:b'\x00\x00'
reg:25 val:b'\x00\x00'
reg:26 val:b'\x00\x00'
reg:27 val:b'\x00\x00'
reg:28 val:b'\x00\x00'
reg:29 val:b'\x00\x00'
reg:30 val:b'\x00\x00'
reg:31 val:b'\x00\x00'
reg:32 val:b'\x00\x00'
reg:33 val:b'\x00\x00'
reg:34 val:b'\x00\x00'
reg:35 val:b'\x00\x00'
reg:36 val:b'\x00\x00'
reg:37 val:b'\x00\x00'
reg:38 val:b'\x00\x00'
reg:39 val:b'\x00\x00'
reg:40 valb'\x00\x00'
reg:41 val:b'\x00\x00'
reg:42 val:b'\x00\x00'
reg:43 val:b'\x00\x00'
reg:44 val:b'\x00\x00'
reg:45 val:b'\x00\x00'
reg:46 val:b'\x00\x00'
reg:47 val:b'\x00\x00'
reg:48 val:b'\x00\x00'
reg:49 val:b'\x00\x00'
reg:50 val:b'\x00\x00'
reg:51 val:b'\x00\x00'
reg:52 val:b'\x00\x00'
reg:53 val:b'\x00\x00'
reg:54 val:b'\x00\x00'
reg:55 val:b'\x00\x00'
reg:56 val:b'\x00\x00'
reg:57 val:b'\x00\x01'
reg:58 val:b'\x01\xfe'
reg:59 val:b'\xfe8'
reg:60 val:b'\x98\x01'
reg:61 val:b'\x01\xf0'
reg:62 val:b'\xcc?'
reg:63 val:b'?\x98'
reg:64 val:b'\xb0\xf0'
reg:65 val:b'\xf00'
reg:66 val:b'0\xfe'
reg:67 val:b'\xfe\xb4'
reg:68 val:b'\xaf\xff'
reg:69 val:b'\xff('
reg:70 val:b'1\xff'
reg:71 val:b'\xff\xf7'
reg:72 val:b'\xec\x00'
reg:73 val:b'\x00\x00'
reg:74 val:b'\x00\x00'
reg:75 val:b'\x00\x00'
reg:76 val:b'\x00\x00'
reg:77 val:b'\x00\x00'
reg:78 val:b'\x00\x00'
reg:79 val:b'\x00\x00'
reg:80 val:b'\x00\x00'
reg:81 val:b'\x00\x00'
reg:82 val:b'\x00\x00'
reg:83 val:b'\x00\x00'
reg:84 val:b'\x00\x00'
reg:85 val:b'\x00\x00'
reg:86 val:b'\x00\x00'
reg:87 val:b'\x00\x00'
reg:88 val:b'\x00\x00'
reg:89 val:b'\x00\x00'
reg:90 val:b'\x00\x00'
reg:91 val:b'\x00\x00'
reg:92 val:b'\x00\x00'
reg:93 val:b'\x00\x00'
reg:94 val:b'\x00\x00'
reg:95 val:b'\x00\x00'
reg:96 val:b'\x00\x00'
reg:97 val:b'\x00\x00'
reg:98 val:b'\x00\x00'
reg:99 val:b'\x00\x00'
reg:100 val:b'\x00\x00'
reg:101 val:b'\x00\x00'
reg:102 val:b'\x00\x00'
reg:103 val:b'\x00\x00'
reg:104 val:b'\x00\x00'
reg:105 val:b'\x00\x00'
reg:106 val:b'\x00\x01'
reg:107 val:b'\x01\x00'
reg:108 val:b'\x00\x00'
reg:109 val:b'\x00\x8b'
reg:110 val:b'\x8c\xbf'
reg:111 val:b'\x97\xff'
reg:112 val:b'\x00\x00'
reg:113 val:b'\x00\x00'
reg:114 val:b'\x00\x00'
reg:115 val:b'\x00\x00'
reg:116 val:b'\x00\x00'
reg:117 val:b'h\x00'

User avatar
JonHylands
Posts: 69
Joined: Sun Dec 29, 2013 1:33 am

Re: Module for MPU9150

Post by JonHylands » Fri Sep 05, 2014 10:50 am

Yeah, I don't have the two buses connected, so I have to use bypass mode. Fortunately, I found the right sequence of bytes to send to the device initially that puts it in bypass mode (last comment): http://diydrones.com/profiles/blogs/sec ... 2#comments

With that, I see two devices show up on my I2C bus: 104 and 12.

>>> i2c = pyb.I2C(2, pyb.I2C.MASTER)
>>> i2c.mem_write(1, 104, 0x6B)
>>> i2c.mem_write(2, 104, 0x37)
>>> i2c.mem_write(0, 104, 0x6A)
>>> i2c.scan()
[12, 104]

I'm going to try and add that initialization sequence to your code, and see if it works.

- Jon

Turbinenreiter
Posts: 288
Joined: Sun May 04, 2014 8:54 am

Re: Module for MPU9150

Post by Turbinenreiter » Fri Sep 05, 2014 11:16 am

If that works out let me know, so I can add it to the lib.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Module for MPU9150

Post by pythoncoder » Sun Oct 05, 2014 10:02 am

This code has saved me a lot of work! Thanks. A few comments: there are trivial typos in lines 52 and 53, and a spurious print statement in line 193. More importantly I'm getting erratic results from the magnetometer. I have the two buses connected as per your readme file. But if I repeatedly issue imu.get_mag() while moving the board around I start to get values of 0.0 in some or all of the fields. This behaviour seems to come and go erratically. The accelerometer and the gyro appear to be working perfectly.

My project doesn't use the magnetometer but it's possible I have a faulty unit: I'd be interested to see if anyone else can replicate this.

As a general query, is there any information out there on the implications of the value of the sample rate?

Regards, Pete
Peter Hinch
Index to my micropython libraries.

Post Reply