LSM9DS1 A 9 degree of freedom Inertial Navigation Chip

Posted on
Page
of 3
Prev
/ 3
Next
  • Translating Arduino code is only as good as the original code.
    Having reviewed the fine Vogan poetry in the datasheet a rewrite seems in order.

  • Translating Arduino Code into Javascript

    The first attempt

    /** 'readAccel() reads X,Y,Z accelerometer values'*/
    LSM9DS1.prototype.readAccel=function(){
     var temp=this.xgReadBytes(Regs.OUT_X_L_XL, 6);
     var i;var mm=[0,0,0];
     this.a[0]=this.twos_comp(temp[0],temp[1]);
     this.a[1]=this.twos_comp(temp[2],temp[3]);
     this.a[2]=this.twos_comp(temp[4],temp[5]);
    etc etc
    }
    /** 'xgReadBytes(subAddress,count) read count bytes from gyro/accelerometer at subAddress'*/
    LSM9DS1.prototype.xgReadBytes=function(subAddress,count){
     var dest= new Uint8Array(count);
     var x=this.xgAddress;
     this.i2c.writeTo(x, subAddress|0x80);
     dest=this.i2c.readFrom(x, count);
     return dest;
    };//end xgReadBytes
    
    /** 'prototype.twos_comp(low,high) converts low and high bytes'*/
    LSM9DS1.prototype.twos_comp=function(low,high){
     var t=(high << 8) | low;
      return(t & 0x8000 ? t - 0x10000 : t);
    };//end twos_comp
    

    But it can be done differently using ArrayBuffer and DataView
    Read the byte stream into an ArrarBuffer, then use a DataView to convert to signed integers, note the littleEndian flag.

    var A=new ArrayBuffer(12);
    A[0]=1;
    A[1]=0;
    A[2]=0xfe;
    A[3]=0xff;
    var B= new Int16Array(A,0,6);
    console.log(A);
    console.log(B);
    console.log("B0= "+B[0]+" B[1]= "+B[1]);
    //var C=dataview.getUint16(byteOffset [, littleEndian])
    var dataview = new DataView(A);
    console.log("A "+dataview.getInt16(0, true)); console.log("A1 "+dataview.getInt16(0, false)); 
    console.log("A2 "+dataview.getInt16(2, true)); console.log("A3 "+dataview.getInt16(2, false)); 
    B[0]=23;B[1]=-23;B[2]=56;B[3]=-56;
    console.log(A);
    console.log(B);
    A[0]=2;A[1]=0;
    A[2]=0xfe;A[3]=0xff;
    console.log(A);
    console.log(B);
    
    

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getUint16
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getInt16>

  • Getting the I2C Interface Working With the Chip

    What is I2C?

    https://en.wikipedia.org/wiki/I%C2%B2C
    So we have to connect the ground, power, SCL and SDA from the PICO to the chip.
    The SCL and SDA lines require a pull-up resistor.
    I’m using a breakout board from Sparkfun and the pullup resistors are on the board.
    https://www.sparkfun.com/products/13284
    As for the pins to use on the PICO
    https://www.espruino.com/I2C
    look at the PICO picture
    http://www.espruino.com/Pico
    Looking at the I2C section of the Espruino Reference Page
    https://www.espruino.com/Reference
    We find the I2C.find command.

    ////////////////////////////////////
    //Configuration
    //The I2C pins that the LSM9D01 is connected to
    //PICO I2C pins
    //IC1  sda=B7  scl=B6
    //IC1  sda=B9  scl=B8  shim pins
    //IC3  sda=B4  scl=A8
    //IC2  sda=B3  scl=B10
    
    console.log(I2C.find(B7));
    console.log(I2C.find(B6));
    
    console.log(I2C.find(B9));
    console.log(I2C.find(B8));
    
    console.log(I2C.find(B4));
    console.log(I2C.find(A8));
    
    console.log(I2C.find(B3));
    console.log(I2C.find(B10));
    
    console.log(I2C.find(B1));
    
    

    Which produces:

    1v92 Copyright 2016 G.Williams
    >I2C {  }
    I2C {  }
    I2C {  }
    I2C {  }
    I2C {  }
    I2C {  }
    I2C {  }
    I2C {  }
    undefined
    =undefined
    

    I2C devices have a 7 bit bus address. The LSM9DS1 has two, one for the magnetometer and one for the gyroscope and accelerometer. Jumpers on the Sparkfun board can be used to change the addresses.
    There is a register labeled WHO_AM_I for the Gyro-Accel, and one for the magnetometer.
    Reading these registers and checking the returned value validates that the I2C interface is working.
    The following code sets up an LSM9DS1 object, initializes the I2C interface and reads the WHO_AM_I_XG register on the LSM9DS1 chip connected to I2C3.setup({ scl :A8, sda: B4} ).

    //Test I2C connection
    var Regs={
     WHO_AM_I_XG: 0x0F,
    };
    
    //////////////////////////////////////////////
    /** the LSM9DS1 object*/
    function LSM9DS1(i2c,xgAddress,mAddress) {
      this.i2c = i2c;
      this.xgAddress= xgAddress;
      this.mAddress= mAddress;
    }
    
    /** 'run() initializes the LSM9DS1' */
    LSM9DS1.prototype.run=function(){
    var xgTest = this.xgReadByte(Regs.WHO_AM_I_XG);
    console.log("xgTest= "+xgTest);
    };//end run
    
    /** 'xgReadByte(subAddress) read a byte from the gyro/accelerometer at subAddress'*/
    LSM9DS1.prototype.xgReadByte=function(subAddress){
     var x=this.xgAddress;
     var data=Uint8Array(1);
     this.i2c.writeTo(x, subAddress);
     data=this.i2c.readFrom(x, 1);
     return data[0];
    };//end xgReadByte
    
    ////////////////////////////////////
    //Configuration
    //The I2C pins that the LSM9D01 is connected to
    //PICO I2C pins
    //IC1  sda=B7  scl=B6
    //IC1  sda=B9  scl=B8  shim pins
    //IC3  sda=B4  scl=A8
    //IC2  sda=B3  scl=B10
    var W;
    function start(){
    //  console.log("start");
     I2C3.setup({ scl :A8, sda: B4} );
    //console.log(I2C3);
    var xgAddress= 0x6B;// Would be 0x1C if SDO_M is LOW
    var mAddress= 0x1e;// Would be 0x6A if SDO_AG is LOW 
    //W=require("slimLSM9DS1").connect(I2C3,xgAddress,mAddress);
      W=new LSM9DS1(I2C3,xgAddress,mAddress);
    W.run();//Get it started
    }//end start
    
    //Wait for program to finish loading
    setTimeout(function () {
     start();
    }, 1000);
    

    Which outputs:

    1v92 Copyright 2016 G.Williams
    >
    =undefined
    xgTest= 104
    >
    
  • How to Bobble the Bits in the Registers

    The Vogan poetry in the datasheet for the LSM9DS1 can be found here:
    http://www.st.com/content/ccc/resource/technical/document/datasheet/1e/3f/2a/d6/25/eb/48/46/DM00103319.pdf/files/DM00103319.pdf/jcr:content/translations/en.DM00103319.pdf
    In section six the register mapping is described. This lets us write a table to reference the registers in the code. For example the gyro and accelerometer registers are defined:

    var Regs={
    ACT_THS: 04, ACT_DUR: 05, INT_GEN_CFG_XL: 06, INT_GEN_THS_X_XL: 07, INT_GEN_THS_Y_XL: 08,
    INT_GEN_THS_Z_XL: 09, INT_GEN_DUR_XL: 0x0A, REFERENCE_G: 0x0B, INT1_CTRL: 0x0C,
    INT2_CTRL: 0x0D, //Reserved -- 0E -- -- Reserved
    WHO_AM_I: 0x0F, CTRL_REG1_G: 0x10, CTRL_REG2_G: 0x11, CTRL_REG3_G: 0x12,
    ORIENT_CFG_G: 0x13, INT_GEN_SRC_G: 0x14, OUT_TEMP_L: 0x15, OUT_TEMP_H: 0x16,
    STATUS_REG1: 0x17, OUT_X_L_G: 0x18, OUT_X_H_G: 0x19, OUT_Y_L_G: 0x1A,
    OUT_Y_H_G: 0x1B, OUT_Z_L_G: 0x1C, OUT_Z_H_G: 0x1D, 
    CTRL_REG4: 0x1E, CTRL_REG5_XL: 0x1F, CTRL_REG6_XL: 0x20, CTRL_REG7_XL: 0x21,
    CTRL_REG8: 0x22, CTRL_REG9: 0x23, CTRL_REG10:0x24, //Reserved -- 25 -- -- Reserved
    INT_GEN_SRC_XL: 0x26, STATUS_REG2: 0x27,
    OUT_X_L_XL: 0x28, OUT_X_H_XL: 0x29, OUT_Y_L_XL: 0x2A, 
    OUT_Y_H_XL: 0x2B,  OUT_Z_L_XL: 0x2C, OUT_Z_H_XL: 0x2D,
    FIFO_CTRL: 0x2E, FIFO_SRC: 0x2F,
    INT_GEN_CFG_G: 0x30, 
    INT_GEN_THS_XH_G: 0x31, INT_GEN_THS_XL_G: 0x32, INT_GEN_THS_YH_G: 0x33,
    INT_GEN_THS_YL_G: 0x34, INT_GEN_THS_ZH_G: 0x35, INT_GEN_THS_ZL_G: 0x36,
    INT_GEN_DUR_G: 0x37,
    //Reserved r 38-7F
    };
    

    In Section7 of the datasheet the bits in the Accelerometer and gyroscope registers are described.
    For example:
    7.12 CTRL_REG1_G (10h) Angular rate sensor Control Register 1.
    | Bit7 | Bit6| Bit5| Bit4| Bit3| Bit2| Bit1| Bit0|
    | --- | --- | --- | --- | --- | --- | --- | --- |
    |ODR_G2 |ODR_G1 |ODR_G0 |FS_G1 |FS_G0 |0(1)| BW_G1 |BW_G0 |

    1. This bit must be set to ‘0’ for the correct operation of the device. BW_G1 BW_G0
      Bits 5, 6, and 7 control the output data rate of the gyro.
      Bits 3, and 4 select the full scale of the gyro.
      Bit 2 is always 0, and
      Bits 0 and 1 select the gyro bandwidth.

    Setting up a data structure to bobble the bits

    var REGval={
    /*
    CTRL_REG1_G (10h)
    Angular rate sensor Control Register
    ODR_G2, ODR_G1, ODR_G0, FS_G1, FS_G0, 0,BW_G1, BW_G0
    */
      ODR_G:{reg:Regs.CTRL_REG1_G,shift:5,mask:7},
      FS_G:{reg:Regs.CTRL_REG1_G,shift:3,mask:3},
      BW_G:{reg:Regs.CTRL_REG1_G,shift:0,mask:3},
    //more stuff
    

    And setup functions to read or write the bits

    /** Use the datastructure to write bits to AG register */
    LSM9DS1.prototype.AGwrite=function(R,value){
    var A=this.xgReadByte(R.reg);
    //console.log(A,value,R,R.mask,R.shift);
    var v=value&R.mask;
    //  console.log("v= "+v);
     A=A&(~(R.mask<<R.shift));
     A=A|(v<<R.shift);
    // console.log("A= "+A.toString(2));
     this.xgWriteByte(R.reg,A);
    };//end AGwrite
    
    /** Use the data structure to read bit settings from AG register*/
    LSM9DS1.prototype.AGread=function(R){
     var A=this.xgReadByte(R.reg);
     A=A>>R.shift;
     A=A&R.mask;
    // console.log("A= "+A.toString(2));
     return A;
    };//end AGread
    

    The following tests to see if the communication is working, performs a reset, writes and then reads a value to the ODR bits and then iterates thru the data structure to read and display all the bits.

    /** 'run() initializes the LSM9DS1' */
    LSM9DS1.prototype.run=function(){
     var i;
    var xgTest = this.xgReadByte(Regs.WHO_AM_I_XG);
     console.log("XG replied "+xgTest);
    
    console.log("do a reset");
    this.AGwrite(REGval.SW_RESET,1);
    console.log("SWreset= "+this.AGread(REGval.SW_RESET));
    //
    console.log("try writing to the ODR_G bits");
    this.AGwrite(REGval.ODR_G,7);
      console.log("ODR_G= "+this.AGread(REGval.ODR_G));
    console.log("iterate thru all the AG settings");
    for (var x in REGval)
      console.log(x,this.AGread(REGval[x]));
    
      return 1;
    };//end run
    

    The output looks like this:

    1v92 Copyright 2016 G.Williams
    >
    =undefined
    XG replied 104
    do a reset
    SWreset= 0
    try writing to the ODR_G bits
    ODR_G= 7
    iterate thru all the AG settings
    ODR_G 7
    FS_G 0
    BW_G 0
    G_INT_SEL 0
    G_OUT_SEL 0
    G_LP_mode 0
    G_HP_EN 0
    HPCF_G 0
    SignX_G 0
    SignY_G 0
    SignZ_G 0
    Orient_G 0
    Dec_XL 0
    Zen_XL 1
    Yen_XL 1
    Xen_XL 1
    ODR_XL 0
    FS_XL 0
    BW_SCAL_ODR_XL 0
    BW_XL 0
    HR_XL 0
    DCF_XL 0
    FDS_XL 0
    HPIS1_XL 0
    BOOT 0
    BDU 0
    H_LACTIVE 0
    PP_OD 0
    SIM 0
    IF_ADD_INC 1
    BLE 0
    SW_RESET 0
    SLEEPG 0
    FIFO_TEMP_EN 0
    DRDY_mask_bit 0
    I2C_DISABLE 0
    FIFO_EN 0
    STOP_ON_FTH 0
    ST_G 0
    ST_XL 0
    FMODE 0
    FTH 0
    >
    

    1 Attachment

  • @ClearMemory041063 -> nice work !
    I plan to use a LSM9DS0 9-DOF ( from adafruit ) for another project & I wonder how much work it'd be ( & how close it is to the LSM9DS1 ) to adapt you nearly-module-code to handle it ? ( also I'd be willing to help/do so if it's not "too advanced" for me for now ;) )

    this being said, kudos ++ ;)

  • @stephaneAG

    I'll take a look at the adafruit part for grins. Might be a close fit.
    Advanced is often confused with familiarity with a subject, so don't sell yourself short.
    I often puzzle over some of the projects that you post as well and usually learn something in the process.

  • The Magnetometer Bits

    The Registers

    The way this was done is to pull up the datasheet PDF, highlight the relevant table, paste it into a new Wordpad document, edit search and replace and save it as a text document with .js extension.
    Here is a portion of the magnetometer address map. Referring to the test in quotes in the following.
    Search “ r/w “ and replace with “: 0x”. Other edits apply as well.

    Table 22. Magnetic sensor register address map
    Registers marked as Reserved must not be changed. Writing to those registers may cause
    permanent damage to the device.
    To guarantee proper behavior of the device, all registers addresses not listed in the above
    table must not be accessed and the content stored on those registers must not be changed.
    The content of the registers that are loaded at boot should not be changed. They contain the
    factory calibration values. Their content is automatically restored when the device is
    powered up.
    Name Type
    Register address
    Default Comment
    Hex Binary
    Reserved 00 - 04 -- -- Reserved
    OFFSET_X_REG_L_M r/w 05 00000000
    Offset in order to compensate
    environmental effects
    OFFSET_X_REG_H_M r/w 06 00000000
    OFFSET_Y_REG_L_M r/w 07 00000000
    OFFSET_Y_REG_H_M r/w 08 00000000
    OFFSET_Z_REG_L_M r/w 09 00000000
    OFFSET_Z_REG_H_M r/w 0A 00000000
    Reserved 0B - 0E -- -- Reserved
    WHO_AM_I_M r 0F 0000 1111 00111101 Magnetic Who I am ID
    Reserved 10 - 1F -- -- Reserved
    CTRL_REG1_M r/w 20 0010 0000 00010000
    

    Here is a portion of the edited text

    var MagRegs={
    //Reserved 00 - 04 -- -- Reserved
    OFFSET_X_REG_L_M:0x05,
    OFFSET_X_REG_H_M:0x06,
    OFFSET_Y_REG_L_M:0x07,
    OFFSET_Y_REG_H_M:0x08,
    OFFSET_Z_REG_L_M:0x09,
    OFFSET_Z_REG_H_M:0x0A,
    //Reserved 0B - 0E -- -- Reserved
    WHO_AM_I_M: 0x0F,
    //Reserved 10 - 1F -- -- Reserved
    CTRL_REG1_M:0x20,
    CTRL_REG2_M:0x21,
    CTRL_REG3_M:0x22,
    CTRL_REG4_M:0x23,
    CTRL_REG5_M:0x24,
    //Reserved 25 - 26 -- -- Reserved
    

    Creating the Data Structure

    A similar process is used to edit the pasted PDF text into the data structure used to bobble the bits.
    Each register description in individually cut and pasted to reduce the editing clutter, for example.

    8.5 CTRL_REG1_M (20h)
    Table 110. X and Y axes operative mode selection
    Table 111. Output data rate configuration
    Table 107. WHO_AM_I_M register
    0 0 1 1 1 1 0 1
    Table 108. CTRL_REG1_M register
    TEMP_
    COMP OM1 OM0 DO2 DO1 DO0 FAST_ODR ST
    
    8.6 CTRL_REG2_M (21h)
    8.7 CTRL_REG3_M (22h)
    Table 112. CTRL_REG2_M register
    0(1)
    1. These bits must be set to ‘0’ for the correct operation of the device.
    FS1 FS0 0(1) REBOOT SOFT_RST 0(1) 0(1)
    

    I used the bit labels and retained some information as a comment. A portion is shown here:

    /**Table 108. CTRL_REG1_M register
    TEMP_COMP, OM1 OM0, DO2 DO1 DO0, FAST_ODR ,ST */
    
    TEMP_COMP:{reg:MagRegs.CTRL_REG1_M,shift:7,mask:1},
    OM:{reg:MagRegs.CTRL_REG1_M,shift:5,mask:3},
    DO:reg:{MagRegs.CTRL_REG1_M,shift:2,mask:7},
    FAST_ODR:{reg:MagRegs.CTRL_REG1_M,shift:1,mask:1},
    ST:{reg:MagRegs.CTRL_REG1_M,shift:0,mask:1},
    
    /** 8.6 CTRL_REG2_M (21h)
    0(1),FS1, FS0, 0(1), REBOOT, SOFT_RST, 0(1,) 0(1*/
    FS:{reg:MagRegs.CTRL_REG2_M,shift:5,mask:3},
    REBOOT:{reg:MagRegs.CTRL_REG2_M,shift:3,mask:1},
    SOFT_RST{reg:MagRegs.CTRL_REG2_M,shift:2,mask:1},
    
    

    2 Attachments

  • @stephaneAG

    I pulled up the datasheet for the LSM9DS0 and did a quick glance and found there are differences. For instance:

    LSM9DS0
    Table 40. FIFO mode configuration
    FM2 FM1 FM0 FIFO mode
    0 0 0 Bypass mode
    0 0 1 FIFO mode
    0 1 0 Stream mode
    0 1 1 Stream-to-FIFO mode
    1 0 0 Bypass-to-Stream mode
    
    LSM9DS1
    Table 84. FIFO mode selection
    FMODE2 FMODE1 FMODE0 Mode
    0 0 0 Bypass mode. FIFO turned off
    0 0 1 FIFO mode. Stops collecting data when FIFO is full.
    0 1 0 Reserved
    0 1 1 Continuous mode until trigger is deasserted, then FIFO
    mode.
    1 0 0 Bypass mode until trigger is deasserted, then Continuous
    mode.
    1 1 0 Continuous mode. If the FIFO is full, the new sample overwrites
    the older sample.```
    
  • @stephaneAG
    Adafruit has the LSM9DS1 as well and it's less expensive.
    https://www.adafruit.com/product/3387

  • It is useful to document the bits,, and perhaps sorting them later by function, interaction, ones that have to be set a particular way to work with the module code. (A todo)

    Magnetometer bits

    TEMP_COMP

    Temperature compensation enable. Default value: 0
    (0: temperature compensation disabled; 1: temperature compensation enabled)
    For the output of the magnetic data compensated by temperature, the
    TEMP_COMP bit in CTRL_REG1_M (20h) must be set to ‘1’.

    OM

    0 = Low-power mode
    1 = Medium-performance mode
    2 = High-performance mode
    3 = Ultra-high performance

    DO

    0 = 0.625 Hz.
    1= 1.25
    2 = 2.5
    3 = 5
    4 = 10
    5 = 20
    6 = 40
    7 = 80

    FAST_ODR

    enables data rates higher than 80 Hz. Default value: 0
    (0: Fast_ODR disabled; 1: FAST_ODR enabled)

    ST

    Self-test enable. Default value: 0
    (0: self-test disabled; 1: self-test enabled)

    FS

    Full scale
    0 = ± 4 gauss
    1 = ± 8 gauss
    2 = ± 12 gauss
    3 = ± 16 gauss

    REBOOT

    Reboot memory content. Default value: 0
    (0: normal mode; 1: reboot memory content)

    SOFT_RST

    Configuration registers and user register reset function.
    (0: default value; 1: reset operation)

    I2C_DISABLE

    Disable I2C interface. Default value 0. (0: I2C enable; 1: I2C disable)

    LP

    Low-power mode configuration. Default value: 0
    If this bit is ‘1’, the DO[2:0] is set to 0.625 Hz and the system performs, for each
    channel, the minimum number of averages. Once the bit is set to ‘0’, the magnetic
    data rate is configured by the DO bits in the CTRL_REG1_M (20h) register.

    SIM

    SPI Serial Interface mode selection. Default value: 0
    (0: SPI only write operations enabled; 1: SPI read and write operations enable).

    MD

    Operating mode selection. Default value: 11
    0 = Continuous-conversion mode
    1 = Single-conversion mode
    2 = Power-down mode
    3 = Power-down mode
    The magnetic sensor has three operating modes available: power-down (default),
    continuous-conversion mode and single-conversion mode. Switching from power-down to
    the other modes requires one write operation to CTRL_REG3_M (22h), setting values in the
    MD[1:0] bits. For the output of the magnetic data compensated by temperature, the
    TEMP_COMP bit in CTRL_REG1_M (20h) must be set to ‘1’.

    OMZ

    Z-axis operative mode selection
    0 = Low-power mode
    1 = Medium-performance mode
    2 = High-performance mode
    3 = Ultra-high performance mode

    BLE

    Big/Little Endian data selection. Default value: 0
    (0: data LSb at lower address; 1: data MSb at lower address)

    FAST_READ

    FAST_READ allows reading the high part of DATA OUT only in order to increase
    reading efficiency. Default value: 0
    (0: FAST_READ disabled; 1: FAST_READ enabled)

    BDU

    Block data update for magnetic data. Default value: 0
    (0: continuous update; 1: output registers not updated until MSB and LSB have
    been read)

    ZYXOR

    X, Y and Z-axis data overrun. Default value: 0
    (0: no overrun has occurred;
    1: a new set of data has overwritten the previous set)

    ZOR

    Z-axis data overrun. Default value: 0
    (0: no overrun has occurred;
    1: new data for the Z-axis has overwritten the previous data)

    YOR

    Y-axis data overrun. Default value: 0
    (0: no overrun has occurred;
    1: new data for the Y-axis has overwritten the previous data)

    XOR

    X-axis data overrun. Default value: 0
    (0: no overrun has occurred;
    1: new data for the X-axis has overwritten the previous data)

    ZYXDA

    X, Y and Z-axis new data available. Default value: 0
    (0: a new set of data is not yet available;
    1: a new set of data is available)

    ZDA

    Z-axis new data available. Default value: 0
    (0: new data for the Z-axis is not yet available;
    1: new data for the Z-axis is available)

    YDA

    Y-axis new data available. Default value: 0
    (0: new data for the Y-axis is not yet available;
    1: new data for the Y-axis is available)

    XDA

    X-axis new data available. Default value: 0
    (0: a new data for the X-axis is not yet available;
    1: a new data for the X-axis is available)

    XIEN

    Enable interrupt generation on X-axis. Default value: 0
    0: disable interrupt request; 1: enable interrupt request

    YIEN

    Enable interrupt generation on Y-axis. Default value: 0
    0: disable interrupt request; 1: enable interrupt request

    ZIEN

    Enable interrupt generation on Z-axis. Default value: 0
    0: disable interrupt request; 1: enable interrupt request

    IEA

    Interrupt active configuration on INT_MAG. Default value: 0
    0: low; 1: high

    IEL

    Latch interrupt request. Default value: 0
    0: interrupt request latched; 1: interrupt request not latched)
    Once latched, the INT_M pin remains in the same state until INT_SRC_M (31h)) is
    read.

    IEN

    Interrupt enable on the INT_M pin. Default value: 0
    0: disable; 1: enable

    PTH_X

    Value on X-axis exceeds the threshold on the positive side.
    Default value: 0

    PTH_Y

    Value on Y-axis exceeds the threshold on the positive side.
    Default value: 0

    PTH_Z

    Value on Z-axis exceeds the threshold on the positive side.
    Default value: 0

    NTH_X

    Value on X-axis exceeds the threshold on the negative side.
    Default value: 0

    NTH_Y

    Value on Y-axis exceeds the threshold on the negative side.
    Default value: 0

    NTH_Z

    Value on Z-axis exceeds the threshold on the negative side.
    Default value: 0

    MROI

    Internal measurement range overflow on magnetic value.
    Default value: 0

    INT

    This bit signals when the interrupt event occurs.

  • Testing the LSM9DS1 Module

    Note that the module is still under construction at this point.

    Setup a project in WebIDE

    WebIDE will use a projects folder with the LSM9DS1.js placed in the Modules sub-folder.
    In WebIDE click on the gear icon in the upper right corner and then click on Project. You can then create a project if you haven’t done so already. When you “require” a module, the project module subfolder is part of the search chain.

    Magtest.js

    //Magtest.js 30Apr2017
    ////////////////////////////////////
    function testMag(){
    var temp;
    var i;
    W.Mwrite(W.MREGval.TEMP_COMP,1);
    /* ON OMZ
    0 = Low-power mode
    1 = Medium-performance mode
    2 = High-performance mode
    3 = Ultra-high performance
    */
    W.Mwrite(W.MREGval.OM,3);
    W.Mwrite(W.MREGval.OMZ,3);
    /* DO
    0 =  0.625 Hz.
    1= 1.25
    2 = 2.5
    3 = 5
    4 = 10
    5 = 20
    6 = 40
    7 = 80
    */
    W.Mwrite(W.MREGval.DO,3);//5 Hz
    //W.Mwrite(W.MREGval.DO,7);//80 Hz
    W.Mwrite(W.MREGval.FAST_ODR,0);
    W.Mwrite(W.MREGval.ST,0);
    /* FS
    Full scale
    0 = ± 4 gauss
    1 = ± 8 gauss
    2 = ± 12 gauss
    3 = ± 16 gauss
    */
    W.Mwrite(W.MREGval.FS,1);
    W.Mwrite(W.MREGval.MD,0);//continuous convert
    //W.Mwrite(W.MREGval.MD,1);//single convert
    W.Mwrite(W.MREGval.BLE,0);//Endian of data
    //W.Mwrite(W.MREGval.BDU,0);//continuous update
    W.Mwrite(W.MREGval.BDU,1);//block update
    W.Mwrite(W.MREGval.FAST_READ,0);
    }//end testMag
    
    function displayMag(){
    var m;
    if(W.Mread(W.MREGval.ZYXDA)){  //new data available
    // console.log("Mag data available");
     m=W.mReadBytes(W.MagRegs.OUT_X_L_M, 6);
     console.log(" "+m[0]+","+m[1]+","+m[2]);
    }//endif
    }//end displayMag
    
    
    //Configuration
    //The I2C pins that the LSM9D01 is connected to
    //PICO I2C pins
    //IC1  sda=B7  scl=B6
    //IC1  sda=B9  scl=B8  shim pins
    //IC3  sda=B4  scl=A8
    //IC2  sda=B3  scl=B10
    var W;
    function start(){
    //  console.log("start");
     I2C3.setup({ scl :A8, sda: B4} );
    //console.log(I2C3);
     var xgAddress= 0x6B;// Would be 0x1C if SDO_M is LOW
     var mAddress= 0x1e;// Would be 0x6A if SDO_AG is LOW 
     W=require("LSM9DS1").connect(I2C3,xgAddress,mAddress);
    // W=new LSM9DS1(I2C3,xgAddress,mAddress);
     W.run();//Get it started
     W.showAG();
     W.showMag();
     testMag();
     setInterval(function () {
      displayMag();
     }, 200);
    }//end start
    
    //function startPgm(){
    //Wait for program to load
    setTimeout(function () {
     start();
    }, 1000);
    //}//end startPgm
    

    Some raw output of the magnetometer, X, Y, and Zaxis

    ( note units are raw counts and no zero offset has been applied )

    700,-950,-802
     678,-944,-802
     703,-947,-806
     700,-936,-825
     678,-945,-807
     677,-948,-830
     675,-938,-826
     686,-944,-830
     685,-964,-831
     686,-949,-838
     695,-938,-848
     672,-975,-855
     684,-960,-827
     679,-962,-841
     680,-968,-845
     672,-943,-846
    >clearInterval();
    =undefined
    > 
    

    The module needs code to read the accelerometer and gyro data.


    2 Attachments

  • Converting the Raw Magnetometer Counts to Calibrated Engineering Units

    The ideal case

    Three magnetometer sensors place on three orthogonal axis X, Y and Z.
    Each magnetometer produces an output of 0.0 when exposed to a zero magnetic field and the same values when each axis is point at the Earth’s magnetic pole.

    The real world magnetometer chip

    Each axis has to have an offset value applied to the raw counts to obtain a zero.
    For a given magnetic field each axis may produce a slightly different value.
    The axis may not be perfectly orthogonal.

    Some math:

    Let X, Y and Z be the raw readings obtained from the magnetometer.
    Let Xoffset, Yoffset and Zoffset be the zero offset that is applied to each axis X, Y and Z to obtain
    X1 = X – Xoffset, Y1= Y - Yoffset, and Z1 = Z – Zoffset.
    Let X2 = XA*X1 + XB*Y1 + XC*Z1,
    Let Y2 = YA*X1 + YB*Y1 + YC*Z1,
    Let Z2 = ZA*X1 + ZB*Y1 + ZC*Z1, where
    XA, XB, XC,
    YA, YB, YC, and
    ZA, ZB, ZC are calibration values

    Let R = sqrt ( X2 ^2 + Y2^2 + Z2^2), and
    For a large number of samples let avgR = sumof( Rn)/N, the average radius.
    Let Error(n) = R(n)- avgR, and Error^2(n)= Error(n) * Error(n).
    Finally SumError^2 = sumof(Error^2(n))
    All this is done in the attached spreadsheet.

    Collect some calibration data using the Magtest.js code.

    Start the program and point each axis of the magnetometer toward the North magnetic pole.
    At my location the pole is 60 degrees below the horizon. Each axis has to be point at and away from the pole to get a good calibration sample. When calibrated the magnetic radius R should be a constant ( at least statistically.

    Finding the zero offsets and calibration values

    The raw data are then pasted into the spreadsheet X, Y , and Z columns.

    raw raw raw
    N X Y Z
    0 -735 478 118
    1 -700 555 38
    2 -660 645 -26
    ... More data

    The X1 etc. columns have to be copied and pasted such that each XYZ sample is calculated.

    raw-offset

    X1 Y1 Z1 X2 Y2 Z2 R Error^2
    -1795.01 461.26 -438.90 -1756.92 235.48 -478.70 1836.13 35.58
    -1760.01 538.26 -518.90 -1721.45 315.47 -557.92 1836.90 45.29

    The formula view:

    raw-offset

    X1 Y1 Z1
    =+B11-$B$3 =+C11-$B$4 =+D11-$B$5
    =+B12-$B$3 =+C12-$B$4 =+D12-$B$5
    X2 Y2 Z2
    =+$E11$C$3+$F11$D$3+$G11*$E$3 =+$E11$C$4+$F11$D$4+$G11*$E$4 =+$E11$C$5+$F11$D$5+$G11*$E$5
    =+$E12$C$3+$F12$D$3+$G12*$E$3 =+$E12$C$4+$F12$D$4+$G12*$E$4 =+$E12$C$5+$F12$D$5+$G12*$E$5
    R Error^2
    =+SQRT(H11*H11+I11*I11+J11*J11) =+(K11-$K$8)^2
    =+SQRT(H12*H12+I12*I12+J12*J12) =+(K12-$K$8)^2

    The calculation range in Cells K8 and L7 have to be adjusted to fit the data range.

    J K L
    Sumerr^2 243533.8512
    Avg R= 1830.168415
    J K L
    Sumerr^2 =+SUM(L11:L163)
    Avg R= =+AVERAGE(K11:K163)

    We start with the offsets in B3 ,B4, and B5 set to zero and the calibration values
    Cells C3,D4, and E5 equal to 1
    Cells C4, C5, D3, D5, E3, and E4 equal to 0.0.

    Offset A B C
    X 0 1 0 0
    Y 0 0 1 0
    Z 0 0 0 1

    The Solver function of Excel (part of the analysis tool pack add in) is used to find the offsets and calibration coefficients.
    Track your progress by copying the value in cell L8 into a progress column.
    First use the solver to minimize cell L8 by changing cells B3, B4, and B5.
    The value in cell L8 should be smaller and cells B3, B4, and B5 should have changed.

    Offset A B C
    X 956.5255249 1 0 0
    Y 57.48926514 0 1 0
    Z 564.9867808 0 0 1

    Apply solver once again this time minimizing cell L8 by changing cells
    C3, C4, C5, D3, D4, D5, E3, and E4. Note that cell E5 is left to have a value of 1.0.
    At this point the error squared should been reduced by a lot.

    Offset A B C
    X 1060.011759 0.982416927 0.014146407 0
    Y 16.73968712 0.121577401 0.983634984 0
    Z 556.9017955 0.02217041 0 1

    I use the Descriptive Statistics function in the analysis tool pack to analyze the X2, Y2, Z2, and R values.

    R X2 Y2 Z2
    Mean 1829.74 -364.59 391.43 632.47
    Standard Error 3.23 67.06 76.42 84.77
    Median 1829.47 -437.90 486.95 869.72
    Standard Deviation 39.96 829.43 945.26 1048.54
    Sample Variance 1597.08 687950.89 893521.30 1099436.03
    Kurtosis 13.36 -0.80 0.03 -0.81
    Skewness -1.24 0.03 -0.81 -0.70
    Range 400.83 3116.84 3511.30 3364.00
    Minimum 1589.21 -1839.88 -1767.80 -1533.31
    Maximum 1990.03 1276.96 1743.49 1830.68
    Sum 279950.92 -55782.91 59888.09 96768.49
    Count 153.00 153.00 153.00 153.00

    Additional measures that may be applied is to remove data that produces an R value more than two standard deviations from the average R, and then recalculate the calibration values.


    2 Attachments

  • Elimination of data outside of 2 standard deviations

    The following cell formulas were used to flag data with R > Ravg + 2* Standard Deviation, data with R < Ravg – 2 *Standard Deviations. Addif combines the two test. Sort is a copy of sort but pasting the values.
    The data set is then sorted on the sort column so that the data that fails the two test appears at the end.
    The range of the average R and error squared cells is then adjusted to omit the failed data.

    If> If< addif sort
    =+IF(K11>2*$W$16+$K$8,1,0) =+IF(K11<-2*$W$16+$K$8,1,0) =+M11+N11 0
    =+IF(K12>2*$W$16+$K$8,1,0) =+IF(K12<-2*$W$16+$K$8,1,0) =+M12+N12 0
    =+IF(K13>2*$W$16+$K$8,1,0) =+IF(K13<-2*$W$16+$K$8,1,0) =+M13+N13 0

    The statistics before and after culling points

    X2 Y2 Z2 R R culled
    Mean -364.5748567 391.871392 632.4737844 1830.168415 1836.695918
    Standard Error 67.04595875 76.48066792 84.76943069 3.236027167 1.512956317
    Median -437.737373 492.0129977 869.7152389 1831.87751 1837.17791
    Mode #N/A #N/A #N/A #N/A #N/A
    Standard Deviation 829.3127091 946.0136164 1048.53995 40.02744545 18.1554758
    Sample Variance 687759.5696 894941.7624 1099436.026 1602.19639 329.6213015
    Kurtosis -0.804853975 0.040187749 -0.812408283 13.51531831 1.605392491
    Skewness 0.029365323 -0.813405054 -0.704273669 -1.200036996 0.376826799
    Range 3116.687258 3519.235059 3363.996524 402.5993238 125.1034715
    Minimum -1839.702418 -1770.78729 -1533.311636 1588.830427 1780.98384
    Maximum 1276.984841 1748.447769 1830.684887 1991.429751 1906.087312
    Sum -55779.95308 59956.32298 96768.48901 280015.7674 264484.2122
    Count 153 153 153 153 144

    Compare the Range of the R and R culled items.

    The calibration constants

    Mag8Gauss 30Apr2017

    Offset A B C
    X 1060.852426 0.987424002 0.01417124 -0.000599842
    Y 24.46995621 0.124389675 0.996380859 -0.000561968
    Z 561.2217795 0.020594208 0.00552387 1

    Next using the calibrations.


    1 Attachment

  • Applying the Calibrations

    There are 4 values to be applied to 3 axis, for each scale and for the magnetometer, accelerometer and the gyroscope sensors.

    A data structure for the magnetometer calibration values

    var magcal={
      scale4:{scale:4,table:[
      {offset:0,A:1,B:0,C:0},
      {offset:0,A:0,B:1,C:0},
      {offset:0,A:0,B:0,C:1}
      ]},
      scale8:{scale:4,table:[
      {offset:1060.85,A:0.98742,B:0.01417,C:-0.0005998},
      {offset:24.469956,A:	0.124389675,B:0.996380859,C:-0.000561968},
      {offset:561.2217795,A:0.020594208	,B:0.00552387,C:1}
      ]},
      scale12:{scale:4,table:[
      {offset:0,A:1,B:0,C:0},
      {offset:0,A:0,B:1,C:0},
      {offset:0,A:0,B:0,C:1}
      ]},
      scale16:{scale:4,table:[
      {offset:0,A:1,B:0,C:0},
      {offset:0,A:0,B:1,C:0},
      {offset:0,A:0,B:0,C:1}
      ]},
    };
    

    Code to apply the calibration values

    function useCalibration(cal,autocalc,data){
     var i;var D=[0,0,0];var F=[0,0,0];
    if(autocalc<1){
      for(i=0;i<3;i++)D[i]=data[i];
      return D;
    }//endif
     for(i=0;i<3;i++)D[i]=data[i]-cal.table[i].offset;
    if(autocalc===1)return D;
    for(i=0;i<3;i++){
      F[i]=D[0]*cal.table[i].A;
      F[i]+=D[1]*cal.table[i].B;
      F[i]+=D[2]*cal.table[i].C;
    }//next i
    for(i=0;i<3;i++)F[i]=F[i]*cal.scale/(1<<15);
     return F;
    }//end useCalibration
    

    Applying the Use calibration function

    function displayMag(){
    var m;
    if(W.Mread(W.MREGval.ZYXDA)){  //new data available
    // console.log("Mag data available");
     m=W.mReadBytes(W.MagRegs.OUT_X_L_M, 6);
    //autocalc  0=raw, 1=raw-offset, 2 Gauss  
      var autocalc=2;
    M=useCalibration(magcal.scale8,autocalc,m);
    console.log(" "+M[0]+","+M[1]+","+M[2]); //raw
    }//endif
    }//end displayMag
    

    Listing the scales

    function showscales(){
    for(var x in magcal)console.log(x);
    }
    

    An Output Sample

    >
    =undefined
    AG replied 104
    M replied 61
    do a reset of AG
    AG SWreset= 0
    reset the magnetometer
    scale4
    scale8
    scale12
    scale16
     -0.00014387969,-0.09589182669,-0.20146481881
     0.00023358480,-0.09475134273,-0.20193948954
     -0.00110189705,-0.09552740429,-0.20038360033
     0.00167522343,-0.09481362313,-0.19971340522
     0.00177061744,-0.09662211209,-0.20106377922
     0.00193627169,-0.09344444901,-0.20128787409
     0.00032607760,-0.09668255780,-0.20133741284
     0.00229249432,-0.09388396965,-0.20384650603
    

    1 Attachment

  • Code to Read the Gyroscope and Accelerometer Using the FIFO

    The Gyroscope ODR (Output Data Rate) Also Controls the Accelerometer ODR if it is On.

    /*
    ODR_G 
    Gyroscope output data rate selection. Default value: 000
    Table 46
    | Value | ODR [Hz] | Cutoff [Hz] |
    | --- | --- | --- |
    | 0 | Power-down | n.a. |
    | 1 | 14.9 | 5 |
    | 2 | 59.5 | 19 |
    | 3 | 119 | 38 |
    | 4 | 238 | 76 |
    | 5 | 476 | 100 |
    | 6 | 952 | 100 |
    | 7 | n.a. | n.a.|
    */
    W.AGwrite(W.REGval.ODR_G,0);
    

    By setting it to 0 (Off) the Accelerometer is controlled by

    /*
    ODR_XL 
    Accelerometer Output data rate and power mode selection. default value: 000 
    ###### Table 68
    | ODR_XL | ODR selection [Hz] |
    | --- | --- |
    | 0 | Power-down |
    | 1 | 10 Hz |
    | 2 | 50 Hz |
    | 3 | 119 Hz |
    | 4 | 238 Hz |
    | 5 | 476 Hz |
    | 6 | 952 Hz |
    | 7 | n.a. |
    */
    W.AGwrite(W.REGval.ODR_XL,2);
    

    Producing this output:

    Alength= 30
    A=, -455,-1526,16800
    A=, -715,-1489,16801
    A=, -595,-1488,16801
    A=, -593,-1497,16854
    A=, -757,-1442,16755
    A=, -527,-1473,16816
    A=, -623,-1513,16852
    A=, -736,-1483,16771
    A=, -511,-1495,16811
    A=, -679,-1517,16867
    >clearInterval();
    

    Changing the gyro ODR to non zero

    W.AGwrite(W.REGval.ODR_G,1);
    

    Produces this output:

    Alength= 18
    G=, -741,206,-16, A=, -715,-1464,16811
    G=, -738,200,-19, A=, -787,-1478,16819
    G=, -734,197,-23, A=, -755,-1489,16795
    Alength= 18
    G=, -737,199,-20, A=, -854,-1499,16796
    G=, -740,201,-17, A=, -829,-1453,16775
    G=, -727,198,-17, A=, -788,-1484,16802
    >clearInterval();
    

    Some Notes

    G is Gyro X,Y ,and Z raw counts. A= Accelerometer X,Y, and Z raw counts.
    Note a new version of LSM9DS1.js is attached and the test program AGtest1.js
    The LSM9DS1.js goes in the modules subdirectory of your WebIDE project.
    The data are read every 200 ms, determined by a set interval.
    The ODR and the interval interact. At low ODR fewer samples are in the FIFO in a 200 ms interval.
    The FIFO can contain up to 32 samples and can be set to fewer samples if needed.


    2 Attachments

  • Thanks for posting this! There's an internal FIFO? That could really help with power efficiency in some devices - in Puck.js for instance I have to wake it up every time a reading is received. It'd be great to be able to wake up only every so often and drain the FIFO.

  • @Gordon
    The Accelerometer, Temperature and Gyroscope use the FIFO, the magnetometer is not included in the FIFO. One interrupt mode is based on the number of samples in the FIFO.
    My Sparkfun breakout board didn't bring the interrupt pins to a header. There are also some low power modes as well to explore.

  • Accelerometer/Gyroscope Bits

    Used as input to AGread and AGwrite functions.

    General Control Bits

    BOOT

    Reboot memory content. Default value: 0 (0: normal mode; 1: reboot memory content)

    1. Boot request is executed as soon as internal oscillator is turned-on. It is possible to set bit while in powerdown
      mode, in this case it will be served at the next normal mode or sleep mode.

    BDU

    Block data update. Default value: 0 (0: continuous update; 1: output registers not updated until MSB and LSB read)

    H_LACTIVE

    Interrupt activation level. Default value: 0
    (0: interrupt output pins active high; 1: interrupt output pins active low)

    PP_OD

    Push-pull/open-drain selection on the INT1_A/G pin and INT2_A/G pin.
    Default value: 0
    (0: push-pull mode; 1: open-drain mode)

    SIM

    SPI serial interface mode selection. Default value: 0
    (0: 4-wire interface; 1: 3-wire interface).

    IF_ADD_INC

    Register address automatically incremented during a multiple byte access with a
    serial interface (I2C or SPI). Default value: 1
    (0: disabled; 1: enabled)

    BLE

    Big/Little Endian data selection. Default value 0
    (0: data LSB @ lower address; 1: data MSB @ lower address)

    SW_RESET

    Software reset. Default value: 0
    (0: normal mode; 1: reset device)
    This bit is cleared by hardware after next flash boot.

    DRDY_mask_bit

    Data available enable bit. Default value: 0
    (0: DA timer disabled; 1: DA timer enabled)

    I2C_DISABLE

    Disable I2C interface. Default value: 0
    (0: both I2C and SPI enabled; 1: I2C disabled, SPI only)

    ST_G

    Angular rate sensor self-test enable. Default value: 0
    (0: Self-test disabled; 1: Self-test enabled)

    ST_XL

    Linear acceleration sensor self-test enable. Default value: 0
    (0: Self-test disabled; 1: Self-test enabled)

    Gyro Configuration Bits

    SLEEPG

    Gyroscope sleep mode enable. Default value: 0
    (0: disabled; 1: enabled)

    ODR_G

    Gyroscope output data rate selection. Default value: 000

    Table 46

    Value ODR [Hz] Cutoff [Hz]
    0 Power-down n.a.
    1 14.9 5
    2 59.5 19
    3 119 38
    4 238 76
    5 476 100
    6 952 100
    7 n.a. n.a.
    Table 47.
    ODR_G [2:0] BW_G [1:0] ODR [Hz] Cutoff [Hz]
    0 0 Power-down n.a.
    0 1 Power-down n.a.
    0 2 Power-down n.a.
    0 3 Power-down n.a
    1 0 14.9 n.a.
    1 1 14.9 n.a.
    1 2 14.9 n.a.
    1 3 14.9 n.a.
    2 0 59.5 16
    2 1 59.5 16
    2 2 59.5 16
    2 3 59.5 16
    3 0 119 14
    3 1 119 31
    3 2 119 31
    3 3 119 31
    4 0 238 14
    4 1 238 29
    4 2 238 63
    4 3 238 78
    5 0 476 21
    5 1 476 28
    5 2 476 57
    5 3 476 100
    6 0 952 33
    6 1 952 40
    6 2 952 58
    6 3 952 100
    7 0 n.a. n.a.
    7 1 n.a. n.a.
    7 2 n.a. n.a.
    7 3 n.a. n.a.

    ODR_G [2:0] are used to set ODR selection when both the accelerometer and gyroscope
    are activated. BW_G [1:0] are used to set gyroscope bandwidth selection.

    FS_G

    Gyroscope full-scale selection. Default value: 00
    (00: 245 dps; 01: 500 dps; 10: Not Available; 11: 2000 dps)

    BW_G

    Gyroscope bandwidth selection. Default value: 00
    ODR_G [2:0] are used to set ODR selection when both the accelerometer and gyroscope
    are activated. BW_G [1:0] are used to set gyroscope bandwidth selection.

    G_INT_SEL

    INT selection configuration. Default value: 00 (Refer to Figure 28)

    G_OUT_SEL

    Out selection configuration. Default value: 00 (Refer to Figure 28)

    G_LP_mode

    Low-power mode enable. Default value: 0
    (0: Low-power disabled; 1: Low-power enabled)

    G_HP_EN

    High-pass filter enable. Default value: 0
    (0: HPF disabled; 1: HPF enabled, refer to Figure 28)

    HPCF_G

    Gyroscope high-pass filter cutoff frequency selection. Default value: 0000

    Table 52
    HPCF_G [3:0] ODR= 14.9 Hz ODR= 59.5 Hz ODR= 119 Hz ODR= 238 Hz ODR= 476 Hz ODR= 952 Hz
    0 1 4 8 15 30 57
    1 0.5 2 4 8 15 30
    2 0.2 1 2 4 8 15
    3 0.1 0.5 1 2 4 8
    4 0.05 0.2 0.5 1 2 4
    5 0.02 0.1 0.2 0.5 1 2
    6 0.01 0.05 0.1 0.2 0.5 1
    7 0.005 0.02 0.05 0.1 0.2 0.5
    8 0.002 0.01 0.02 0.05 0.1 0.2
    9 0.001 0.005 0.01 0.02 0.05 0.1
    SignX_G

    Pitch axis (X) angular rate sign. Default value: 0
    (0: positive sign; 1: negative sign)

    SignY_G

    Roll axis (Y) angular rate sign. Default value: 0
    (0: positive sign; 1: negative sign)

    SignZ_G

    Yaw axis (Z) angular rate sign. Default value: 0
    (0: positive sign; 1: negative sign)

    Orient_G

    Directional user orientation selection. Default value: 000

    REF_G

    Reference value for gyroscope’s digital high-pass filter (r/w).
    Default value: 0000 0000

    Accelerometer Bits

    Dec_XL

    Decimation of acceleration data on OUT REG and FIFO. Default value: 00
    (00: no decimation;
    01: update every 2 samples;
    10: update every 4 samples;
    11: update every 8 samples)

    Zen_XL

    Accelerometer’s Z-axis output enable. Default value: 1
    (0: Z-axis output disabled; 1: Z-axis output enabled)

    Yen_XL

    Accelerometer’s Y-axis output enable. Default value: 1
    (0: Y-axis output disabled; 1: Y-axis output enabled)

    Xen_XL

    Accelerometer’s X-axis output enable. Default value: 1
    (0: X-axis output disabled; 1: X-axis output enabled)

    ODR_XL

    Output data rate and power mode selection. default value: 000

    Table 68
    ODR_XL ODR selection [Hz]
    0 Power-down
    1 10 Hz
    2 50 Hz
    3 119 Hz
    4 238 Hz
    5 476 Hz
    6 952 Hz
    7 n.a.
    FS_XL

    Accelerometer full-scale selection. Default value: 00
    (00: ±2g; 01: ±16 g; 10: ±4 g; 11: ±8 g)

    BW_SCAL_ODR_XL

    Bandwidth selection. Default value: 0
    (0: bandwidth determined by ODR selection:

    • BW = 408 Hz when ODR = 952 Hz, 50 Hz, 10 Hz;
    • BW = 211 Hz when ODR = 476 Hz;
    • BW = 105 Hz when ODR = 238 Hz;
    • BW = 50 Hz when ODR = 119 Hz;
      1: bandwidth selected according to BW_XL [2:1] selection)

      BW_XL

      Anti-aliasing filter bandwidth selection. Default value: 00
      (00: 408 Hz; 01: 211 Hz; 10: 105 Hz; 11: 50 Hz)

    HR_XL

    High resolution mode for accelerometer enable. Default value: 0
    (0: disabled; 1: enabled).

    Table 71
    HR CTRL_REG7 (DCF [1:0]) LP cutoff freq. [Hz]
    1 0 ODR/50
    1 } 1 ODR/100
    1 2 ODR/9
    1 3 ODR/400
    DCF_XL

    Accelerometer digital filter (high pass and low pass) cutoff frequency selection: the bandwidth
    of the high-pass filter depends on the selected ODR. See Table 71

    FDS_XL

    Filtered data selection. Default value: 0
    (0: internal filter bypassed; 1: data from internal filter sent to output register and FIFO)

    HPIS1_XL

    High-pass filter enabled for acceleration sensor interrupt function on Interrupt. Default
    value: 0 (0: filter bypassed; 1: filter enabled)

    Temperature Bits

    FIFO_TEMP_EN

    Temperature data storage in FIFO enable. Default value: 0
    (0: temperature data not stored in FIFO; 1: temperature data stored in FIFO)

    FIFO Control Bits

    FIFO_EN

    FIFO memory enable. Default value: 0
    (0: disabled; 1: enabled)

    STOP_ON_FTH

    Enable FIFO threshold level use. Default value: 0
    (0: FIFO depth is not limited; 1: FIFO depth is limited to threshold level)

    FIFO_TEMP_EN

    Temperature data storage in FIFO enable. Default value: 0
    (0: temperature data not stored in FIFO; 1: temperature data stored in FIFO)

    FMODE

    FIFO mode selection bits. Default value: 000

    Table 84
    FMODE Mode
    0 Bypass mode. FIFO turned off
    1 FIFO mode. Stops collecting data when FIFO is full.
    2 Reserved
    3 Continuous mode until trigger is deasserted, then FIFO mode
    4 Bypass mode until trigger is deasserted, then Continuous mode.
    6 Continuous mode. If the FIFOis full, the new sample overwrites the older sample.
    5 No specification
    7 No specification
    FTH

    FIFO threshold level setting. Default value: 0 0000

    FIFO Status

    FTHstatus

    FIFO threshold status.
    (0: FIFO filling is lower than threshold level; 1: FIFO filling is equal or higher than
    threshold level

    OVRN 0

    FIFO overrun status.
    (0: FIFO is not completely filled; 1: FIFO is completely filled and at least one samples
    has been overwritten)

    FSS 0

    Number of unread samples stored into FIFO.
    (000000: FIFO empty; 100000: FIFO full, 32 unread samples)
    Table 87. FIFO_SRC example: OVR/FSS details

    FTH OVRN FSS5 FSS4 FSS3 FSS2 FSS1 FSS0 Description
    0 0 0 0 0 0 0 0 FIFO empty
    x 0 0 0 0 0 0 1 1
    x 0 1 0 0 0 0 0
    1 1 1 0 0 0 0 0 At least one sample has been overwritten

    Interrupt Control Bits

    SLEEP_ON_INACT_EN

    Gyroscope operating mode during inactivity. Default value: 0
    (0: gyroscope in power-down; 1: gyroscope in sleep mode)

    ACT_THS

    Inactivity threshold. Default value: 000 0000

    ACT_DUR

    Inactivity duration. Default value: 0000 0000

    AOI_XL

    AND/OR combination of accelerometer’s interrupt events. Default value: 0
    (0: OR combination; 1: AND combination)

    D6

    6-direction detection function for interrupt. Default value: 0
    (0: disabled; 1: enabled)

    ZHIE_XL

    Enable interrupt generation on accelerometer’s Z-axis high event. Default value: 0
    (0: disable interrupt request; 1: interrupt request on measured acceleration value
    higher than preset threshold)

    ZLIE_XL

    Enable interrupt generation on accelerometer’s Z-axis low event. Default value: 0
    (0: disable interrupt request; 1: interrupt request on measured acceleration value
    lower than preset threshold)

    YHIE_XL

    Enable interrupt generation on accelerometer’s Y-axis high event. Default value: 0
    (0: disable interrupt request; 1: interrupt request on measured acceleration value
    higher than preset threshold)

    YLIE_XL

    Enable interrupt generation on accelerometer’s Y-axis low event. Default value: 0
    (0: disable interrupt request; 1: interrupt request on measured acceleration value
    lower than preset threshold)

    XHIE_XL

    Enable interrupt generation on accelerometer’s X-axis high event. Default value: 0
    (0: disable interrupt request; 1: interrupt request on measured acceleration value
    higher than preset threshold)

    XLIE_XL

    Enable interrupt generation on accelerometer’s X-axis low event. Default value: 0
    (0: disable interrupt request; 1: interrupt request on measured acceleration value
    lower than preset threshold)

    THS_XL_X

    X-axis interrupt threshold. Default value: 0000 0000

    THS_XL_Y

    Y-axis interrupt threshold. Default value: 0000 0000

    THS_XL_Z

    Z-axis interrupt threshold. Default value: 0000 0000

    WAIT_XL

    Wait function enabled on duration counter. Default value: 0
    (0: wait function off; 1: wait for DUR_XL [6:0] samples before exiting interrupt)

    DUR_XL6

    Enter/exit interrupt duration value. Default value: 000 0000

    INT1_IG_G

    Gyroscope interrupt enable on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_IG_XL

    Accelerometer interrupt generator on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_FSS5

    FSS5 interrupt enable on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_OVR

    Overrun interrupt on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_FTH

    FIFO threshold interrupt on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_Boot

    Boot status available on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_DRDY_G

    Gyroscope data ready on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT1_DRDY_XL

    Accelerometer data ready on INT 1_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT2_INACT

    Inactivity interrupt output signal. Default value: 0
    (0: no interrupt has been generated; 1: one or more interrupt events have been
    generated)

    INT2_FSS5

    FSS5 interrupt enable on INT2_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT2_OVR

    Overrun interrupt on INT2_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT2_FTH

    FIFO threshold interrupt on INT2_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT2_DRDY_TEMP

    Temperature data ready on INT2_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT2_DRDY_G

    Gyroscope data ready on INT2_A/G pin. Default value: 0
    (0: disabled; 1: enabled)

    INT2_DRDY_XL

    Accelerometer data ready on INT2_A/G pin. Default value: 0
    (0: disabled; 1: enabled)
    //Status registers
    Xxxxxxxxxxxxxxxxxxxxx

    IG_XL 0, IG_XL2 0

    Accelerometer interrupt output signal. Default value: 0
    (0: no interrupt has been generated; 1: one or more interrupt events have been generated)

    IG_G 0, IG_G2 0

    Gyroscope interrupt output signal. Default value: 0
    (0: no interrupt has been generated; 1: one or more interrupt events have been generated

    INACT 0, INACT2 0

    Inactivity interrupt output signal. Default value: 0
    (0: no interrupt has been generated; 1: one or more interrupt events have been generated)

    BOOT_STATUS 0, BOOT_STATUS2 0

    Boot running flag signal. Default value: 0
    (0: no boot running; 1: boot running)

    TDA 0, TDA2 0

    Temperature sensor new data available. Default value: 0
    (0: new data is not yet available; 1: new data is available)

    GDA 0, GDA2 0

    Gyroscope new data available. Default value: 0
    (0: a new set of data is not yet available; 1: a new set of data is available)

    XLDA 0, XLDA2 0

    Accelerometer new data available. Default value: 0
    (0: a new set of data is not yet available; 1: a new set of data is available)

    IA_G

    Gyro Interrupt active. Default value: 0
    (0: no interrupt has been generated; 1: one or more interrupts have been generated)

    ZH_G

    Gyro Yaw (Z) high. Default value: 0
    (0: no interrupt, 1: Z high event has occurred)

    ZL_G

    Gyro Yaw (Z) low. Default value: 0
    (0: no interrupt; 1: Z low event has occurred)

    YH_G

    Gyro Roll (Y) high. Default value: 0
    (0: no interrupt, 1: Y high event has occurred)

    YL_G

    Gyro Roll (Y) low. Default value: 0
    (0: no interrupt, 1: Y low event has occurred)

    XH_G

    Gyro Pitch (X) high. Default value: 0
    (0: no interrupt, 1: X high event has occurred)

    XL_G

    Gyro Pitch (X) low. Default value: 0
    (0: no interrupt, 1: X low event has occurred)

    IA_XL

    Accelerometer Interrupt active. Default value: 0.
    (0: no interrupt has been generated; 1: one or more interrupts have been generated)

    ZH_XL

    Accelerometer's Z high event. Default value: 0
    (0: no interrupt, 1: Z high event has occurred)

    ZL_XL

    Accelerometer's Z low event. Default value: 0
    (0: no interrupt; 1: Z low event has occurred)

    YH_XL

    Accelerometer's Y high event. Default value: 0
    (0: no interrupt, 1: Y high event has occurred)

    YL_XL

    Accelerometer's Y low event. Default value: 0
    (0: no interrupt, 1: Y low event has occurred)

    XH_XL

    Accelerometer's X high event. Default value: 0
    (0: no interrupt, 1: X high event has occurred)

    XL_XL

    Accelerometer's X low. event. Default value: 0
    (0: no interrupt, 1: X low event has occurred)

    Latest version of the LSM9DS1.js module is attached.

    If you need a way to cool your coffee or tea and need to stretch here is a different kind of project.
    https://www.youtube.com/watch?v=5_50RuMcc28


    1 Attachment

  • Calibrating the Accelerometer

    The procedure is similar to the magnetometer calibration but several things are done differently.

    1. There are 3 axis, and if the accelerometer is attached to one face of a cube, we need to collect data with each of the six faces of the cube on the bottom. Data taken when the sensor is in motion may contain additional acceleration values that would distort the calibration.
    2. The magnetometer used the average R value, for the accelerometer we will calculate the counts needed to produce a 1G value, taking the range setting of the accelerometer as a factor.

      We need a way to signal the program that the sensor is positioned and ready to acquire some data.

      With WebIDE this can be achieved by reassigning the console with the setConsole() function.
      With a PICO this has its perils if you set the program up to run on boot and save the program.
      You need to provide a way to get the console back to the USB port. Preferably several ways!

      //ConsoleMenu.js 11 May 2017
      
      //How not to brick a PICO when reassigning the console
      // away from the USB port to do menu programs
      //Setup the button to light the LED and reset the Pico
      //Always include this code when a save() and oninit() functions are // used.
      setWatch(function(e) {
      digitalWrite(LED1, e.state);
      USB.setConsole();
      reset();
      }, BTN, { repeat: true });
      
      E.on('init', function() {
      console.log("Hello");
      startPgm();
      });
      //input cmd from terminal,send it to parsecmd()
      var sst=""; //The input buffer
      USB.on('data', function (data) {
      var i;
      sst+=data;
      // look for control c in input stream
      if(sst.indexOf(String.fromCharCode(3))>-1){
      USB.setConsole();
      reset();
      }//   console.log("control C seen");
      USB.print(data);
      if(sst.length>-1)
      if(sst.charAt(sst.length-1)==="\r")selectparser();
      });
      
      //Espruino replies here
      LoopbackA.on('data',function(data){
      USB.print(data); //sending data to terminal
      });
      console.log("In left pane type startPgm();");
      console.log("Use startPgm() first and make sure you can exit back to the console");
      console.log("Once you are sure that a proper exit works then type  save();");
      
      var menulevel=0;
      function startPgm(){
      setTimeout(function () {
      // start();
      LoopbackB.setConsole();
      mainmenu();
      }, 1000);
      }//end startPgm
      
      function mainmenu(){
      USB.print("\n\rMain Menu\n\rType somthing or a control C to Exit\n\r");
      //    USB.setConsole();
      }//end mainmenu
      
      function selectparser(){
      USB.print("\n\r"+sst+"\n\r");
      //    USB.setConsole();
        
      }//end select parser
      

    The attached file CollectAGdata.js combines the above menu program with AGtest1.js to produce a program that collects 32 samples and wait for a carriage return. The sensor can be repositioned and the next setting can be collected by pressing return. When finished type any character followed by a return to exit the program. The data are then copied from the WebIDE left pane and pasted nto a .CSV file. Some example output:

    G=, -716,194,-17, A=, -148,-780,16899
    G=, -711,193,-18, A=, -154,-787,16901
    G=, -718,190,-18, A=, -156,-781,16944
    G=, -719,193,-14, A=, -169,-762,16929
    G=, -714,194,-12, A=, -178,-765,16942
    G=, -714,194,-18, A=, -186,-730,16940
    G=, -716,190,-17, A=, -195,-737,16940
    G=, -725,191,-17, A=, -210,-736,16887
    G=, -711,187,-15, A=, -182,-746,16924
    G=, -713,190,-19, A=, -225,-741,16894
    G=, -712,192,-21, A=, -209,-760,16893
    G=, -704,194,-12, A=, -197,-743,16876
    G=, -719,198,-15, A=, -132,-754,16905
    G=, -717,217,-15, A=, -145,-706,16794
    G=, -713,184,-15, A=, -207,-902,16881
    G=, -718,204,-25, A=, -185,-817,16879
    G=, -722,193,-9, A=, -110,-743,16895
    G=, -721,196,-28, A=, -244,-878,16873
    G=, -735,190,-14, A=, -189,-795,16896
    G=, -720,214,-21, A=, -179,-749,16898
    G=, -714,176,-18, A=, -219,-867,16894
    G=, -726,199,-26, A=, -142,-731,16935
    G=, -730,190,-18, A=, -156,-772,16936
    G=, -719,190,-26, A=, -196,-758,16898
    G=, -714,188,-20, A=, -129,-745,16981
    G=, -719,193,-26, A=, -203,-777,16936
    G=, -726,182,-19, A=, -210,-741,16947
    G=, -723,199,-24, A=, -232,-722,16910
    G=, -735,187,-27, A=, -149,-738,16921
    G=, -729,188,-30, A=, -231,-760,16885
    G=, -723,190,-23, A=, -167,-756,16869
    G=, -715,192,-21, A=, -221,-760,17003
    <- LoopbackB
    -> USB
    

    The data are then pasted in a spreadsheet similar to the one used for the magnetometer and the twelve calibration values are calculated.


    2 Attachments

  • The Accelerometer and Gyroscope Calibrations

    Accelerometer May12 2017 2g scale using average R

    Culled Range = 2230.842

    Offset A B C
    X -987.2736361 0.995549457 0.140149893 2.11449E-05
    Y 272.0992136 -0.042589517 0.991729217 0.00020329
    Z 446.896968 -0.24363696 -0.291923385 1
    Accelerometer May12 2017 2g scale using target value

    Culled Range = 2159.087

    Offset A B C
    X -1068.093899 0.981090704 0.127644011 4.11128E-05
    Y 185.8088179 -0.013882693 0.989541404 3.62454E-05
    Z 193.6596862 -0.268744893 -0.20999626 1
    Gyroscope May12 2017 245deg per s.

    Offsets Only as I’m not set up to spin the sensor and collect data.
    Culled Range = 2506.073

    Offset A B C
    X -780.9157638 1 0 0
    Y 214.1401801 0 1 0
    Z -0.657364084 0 0 1

    See attached files


    3 Attachments

  • AGmagtest1.js

    The attached program AGmagtest.js uses the LSM9DS1.js module and the preceding calibrations to produce data in engineering units.

    The variable autocalc controls the type of units displayed.

    //autocalc 0=raw, 1=raw-offset, 2 Engineering Units
    var autocalc=2;

    In the function setupAG() the gyro ODR determines if gyro data are available.

    W.AGwrite(W.REGval.ODR_G,1);//do gyro and acceleration
    //W.AGwrite(W.REGval.ODR_G,0);//do acceleration only.

    Here is sample output with autocalc=2 and ODR_G=1

    M magnetometer values are in Gauss,
    G gyroscope values are in degrees per second
    A acceleration values are in g, (1= Earth gravity)

    M,0.0150,-0.0847,-0.1949,G,0.3582,-0.1057,-0.1222,A,0.0839,-0.0478,0.9993,
    M,0.0172,-0.0830,-0.1933,G,0.3358,-0.1581,-0.1297,A,0.0869,-0.0552,1.0039,
    M,0.0172,-0.0830,-0.1933,G,0.3433,-0.1655,-0.1297,A,0.0841,-0.0479,1.0000,
    M,0.0172,-0.0830,-0.1933,G,0.3807,-0.1057,-0.1222,A,0.0831,-0.0438,0.9987,
    M,0.0151,-0.0866,-0.1965,G,0.4031,-0.2104,-0.1222,A,0.0817,-0.0398,0.9956,
    M,0.0151,-0.0866,-0.1965,G,0.3807,-0.0833,-0.1222,A,0.0821,-0.0416,0.9960,
    M,0.0151,-0.0866,-0.1965,G,0.4031,-0.1879,-0.1147,A,0.0831,-0.0460,0.9991,
    M,0.0162,-0.0845,-0.1933,G,0.3732,-0.0982,-0.1446,A,0.0822,-0.0459,1.0009,
    M,0.0162,-0.0845,-0.1933,G,0.3059,-0.1132,-0.1297,A,0.0834,-0.0461,1.0031,
    >clearInterval();
    
    Changing ODR_G to 0 produces:
    M,0.0154,-0.0826,-0.1849,A,0.0864,-0.0516,1.0014,
    M,0.0154,-0.0826,-0.1849,A,0.0868,-0.0537,1.0017,
    M,0.0154,-0.0826,-0.1849,A,0.0858,-0.0549,1.0009,
    M,0.0154,-0.0826,-0.1849,A,0.0862,-0.0567,1.0032,
    M,0.0154,-0.0826,-0.1849,A,0.0861,-0.0594,1.0028,
    M,0.0154,-0.0826,-0.1849,A,0.0852,-0.0598,1.0009,
    M,0.0154,-0.0826,-0.1849,A,0.0883,-0.0601,0.9992,
    M,0.0154,-0.0826,-0.1849,A,0.0877,-0.0574,0.9964,
    >clearInterval();
    

    1 Attachment

  • ...I expected you to put the sensor onto the stirling engine fly wheel... a centered / balanced puck and sensor would work to measure the RPM. ;-)

  • Now that's an interesting idea. The flywheel shaft is 10mm long with a crank on each end, so there's not much room for the Puck. Perhaps a photo interrupter with a tab on the flywheel would be easier for RPM sensing.
    The LTD (Low Temperature Differential) Stirling is right up against the wall to work using hot coffee and small design changes can move the needed temperature differential.
    One idea to pursue is a pressure sensor tapped into the displacer chamber. This would allow one to study the differences in displacer designs such as size, materials and motion of the displacer.

  • Correction to AGMagtest1

    The index values were corrected in the displayAg() function when set to display Accelerometer and Magnetometer values. AGMagtest2.js is attached.

    Adding the Rodrigues.js module to level the compass

    The background for this module is described here:
    http://forum.espruino.com/conversations/298151/

    The basic idea is to take the accelerometer readings and determine the vector transformation needed to level the compass. This is done with the function A=FF.Setup(AA,[0,0,1]); where FF is an instance of Rodrigues. The tilt angle theta is then available and subsequent calls to the function BB=FF.RotVector(t) will transform other vectors (magnetometer) into level coordinates.
    The attached Rodrigues.js should be placed in the modules subdirectory of your WebIDE project.
    The attached program AGMagCompass1.js will then produce the following output when autocalc=2 and W.AGwrite(W.REGval.ODR_G,1);//do gyro and acceleration.

    M,0.0744,-0.1211,-0.1714,G,-0.1053,0.0413,-0.0948,A,0.1575,0.3104,0.8621,
    Tilt,21.98781027298,Heading,306.68106708380,Dip,72.16684715533
    M,0.0735,-0.1221,-0.1734,G,-0.1128,0.0563,-0.1072,A,0.1559,0.3118,0.8613,
    Tilt,22.03654082060,Heading,306.02287887278,Dip,72.48561573888
    M,0.0730,-0.1207,-0.1713,G,-0.1165,0.0251,-0.0998,A,0.1551,0.3114,0.8618,
    Tilt,21.98386692289,Heading,306.35699288042,Dip,72.36622200953
    M,0.0742,-0.1194,-0.1707,G,-0.0928,0.0712,-0.0823,A,0.1538,0.3145,0.8604,
    Tilt,22.14296350951,Heading,308.33309975404,Dip,72.42224117781
    M,0.0736,-0.1171,-0.1704,G,-0.0679,0.0313,-0.0674,A,0.1558,0.3134,0.8606,
    Tilt,22.13447453002,Heading,308.75984729535,Dip,72.83761339423
    >clearInterval();
    

    Changing W.AGwrite(W.REGval.ODR_G,0);//do acceleration only produces the following output:

    M,0.0747,-0.1202,-0.1713,A,0.1563,0.3096,0.8569,
    Tilt,22.03355580117,Heading,307.43511787449,Dip,72.28617992890
    M,0.0763,-0.1235,-0.1719,A,0.1563,0.3094,0.8568,
    Tilt,22.02918941321,Heading,306.91511852880,Dip,71.66990038608
    M,0.0741,-0.1204,-0.1721,A,0.1560,0.3093,0.8569,
    Tilt,22.01028842165,Heading,306.98156662314,Dip,72.44988041050
    M,0.0780,-0.1226,-0.1704,A,0.1559,0.3090,0.8568,
    Tilt,22.00089649394,Heading,308.38042963598,Dip,71.31936469986
    >clearInterval();
    

    ```


    3 Attachments

  • Why would you use a .connect() function / method to create a Rodrigues instance?

    I know hat this pattern is typical for connecting devices. But in this case, it is just a transformer, isn't?

    To return a constructor function in a require, just set the exported object to it: exports = Rodrigues;.

    If you want to stick with the pattern, you can return exports.getInstance = function() { return new Rodrigues(); } to provide a self documenting function.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

LSM9DS1 A 9 degree of freedom Inertial Navigation Chip

Posted by Avatar for ClearMemory041063 @ClearMemory041063

Actions