tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/lis2mdl/lis2mdl.go (about)

     1  // Package lis2mdl implements a driver for the LIS2MDL,
     2  // a magnetic sensor which is included on BBC micro:bit v1.5.
     3  //
     4  // Datasheet: https://www.st.com/resource/en/datasheet/lis2mdl.pdf
     5  package lis2mdl // import "tinygo.org/x/drivers/lis2mdl"
     6  
     7  import (
     8  	"math"
     9  	"time"
    10  
    11  	"tinygo.org/x/drivers"
    12  	"tinygo.org/x/drivers/internal/legacy"
    13  )
    14  
    15  // Device wraps an I2C connection to a LIS2MDL device.
    16  type Device struct {
    17  	bus        drivers.I2C
    18  	Address    uint8
    19  	PowerMode  uint8
    20  	SystemMode uint8
    21  	DataRate   uint8
    22  }
    23  
    24  // Configuration for LIS2MDL device.
    25  type Configuration struct {
    26  	PowerMode  uint8
    27  	SystemMode uint8
    28  	DataRate   uint8
    29  }
    30  
    31  // New creates a new LIS2MDL connection. The I2C bus must already be
    32  // configured.
    33  //
    34  // This function only creates the Device object, it does not touch the device.
    35  func New(bus drivers.I2C) Device {
    36  	return Device{bus: bus, Address: ADDRESS}
    37  }
    38  
    39  // Connected returns whether LIS2MDL sensor has been found.
    40  func (d *Device) Connected() bool {
    41  	data := []byte{0}
    42  	legacy.ReadRegister(d.bus, uint8(d.Address), WHO_AM_I, data)
    43  	return data[0] == 0x40
    44  }
    45  
    46  // Configure sets up the LIS2MDL device for communication.
    47  func (d *Device) Configure(cfg Configuration) {
    48  	if cfg.PowerMode != 0 {
    49  		d.PowerMode = cfg.PowerMode
    50  	} else {
    51  		d.PowerMode = POWER_NORMAL
    52  	}
    53  
    54  	if cfg.DataRate != 0 {
    55  		d.DataRate = cfg.DataRate
    56  	} else {
    57  		d.DataRate = DATARATE_100HZ
    58  	}
    59  
    60  	if cfg.SystemMode != 0 {
    61  		d.SystemMode = cfg.SystemMode
    62  	} else {
    63  		d.SystemMode = SYSTEM_CONTINUOUS
    64  	}
    65  
    66  	cmd := []byte{0}
    67  
    68  	// reset
    69  	cmd[0] = byte(1 << 5)
    70  	legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
    71  	time.Sleep(100 * time.Millisecond)
    72  
    73  	// reboot
    74  	cmd[0] = byte(1 << 6)
    75  	legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
    76  	time.Sleep(100 * time.Millisecond)
    77  
    78  	// bdu
    79  	cmd[0] = byte(1 << 4)
    80  	legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_C, cmd)
    81  
    82  	// Temperature compensation is on for magnetic sensor (0x80)
    83  	cmd[0] = byte(0x80)
    84  	legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
    85  
    86  	// speed
    87  	cmd[0] = byte(0x80 | d.DataRate)
    88  	legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
    89  }
    90  
    91  // ReadMagneticField reads the current magnetic field from the device and returns
    92  // it in mG (milligauss). 1 mG = 0.1 µT (microtesla).
    93  func (d *Device) ReadMagneticField() (x int32, y int32, z int32) {
    94  	// turn back on read mode, even though it is supposed to be continuous?
    95  	cmd := []byte{0}
    96  	cmd[0] = byte(0x80 | d.PowerMode<<4 | d.DataRate<<2 | d.SystemMode)
    97  	legacy.WriteRegister(d.bus, uint8(d.Address), CFG_REG_A, cmd)
    98  	time.Sleep(10 * time.Millisecond)
    99  
   100  	data := make([]byte, 6)
   101  	legacy.ReadRegister(d.bus, uint8(d.Address), OUTX_L_REG, data)
   102  
   103  	x = int32(int16((uint16(data[0]) << 8) | uint16(data[1])))
   104  	y = int32(int16((uint16(data[2]) << 8) | uint16(data[3])))
   105  	z = int32(int16((uint16(data[4]) << 8) | uint16(data[5])))
   106  
   107  	return
   108  }
   109  
   110  // ReadCompass reads the current compass heading from the device and returns
   111  // it in degrees. When the z axis is pointing straight to Earth and
   112  // the y axis is pointing to North, the heading would be zero.
   113  //
   114  // However, the heading may be off due to electronic compasses would be effected
   115  // by strong magnetic fields and require constant calibration.
   116  func (d *Device) ReadCompass() (h int32) {
   117  	x, y, _ := d.ReadMagneticField()
   118  	xf, yf := float64(x)*0.15, float64(y)*0.15
   119  
   120  	rh := (math.Atan2(yf, xf) * 180) / math.Pi
   121  	if rh < 0 {
   122  		rh = 360 + rh
   123  	}
   124  
   125  	return int32(rh)
   126  }