From af4efceac1efa9bd8bb11d2a4427ca4b41df16a2 Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Thu, 20 Aug 2020 13:21:39 +0200 Subject: [PATCH] lis2mdl: add LIS2MDL magnetometer (#187) Signed-off-by: deadprogram --- Makefile | 2 + README.md | 3 +- examples/lis2mdl/main.go | 29 ++++++++++ lis2mdl/lis2mdl.go | 119 +++++++++++++++++++++++++++++++++++++++ lis2mdl/registers.go | 32 +++++++++++ 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 examples/lis2mdl/main.go create mode 100644 lis2mdl/lis2mdl.go create mode 100644 lis2mdl/registers.go diff --git a/Makefile b/Makefile index 0678544..56ce277 100644 --- a/Makefile +++ b/Makefile @@ -151,5 +151,7 @@ smoke-test: @md5sum ./build/test.hex tinygo build -size short -o ./build/test.hex -target=hifive1b ./examples/ssd1351/main.go @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go + @md5sum ./build/test.hex test: clean fmt-check smoke-test diff --git a/README.md b/README.md index ea122c7..7d0f44b 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ func main() { ## Currently supported devices -The following 51 devices are supported. +The following 52 devices are supported. | Device Name | Interface Type | |----------|-------------| @@ -80,6 +80,7 @@ The following 51 devices are supported. | [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI | | [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM | | [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM | +| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C | | [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C | | [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C | | [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C | diff --git a/examples/lis2mdl/main.go b/examples/lis2mdl/main.go new file mode 100644 index 0000000..55601fc --- /dev/null +++ b/examples/lis2mdl/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/lis2mdl" +) + +func main() { + machine.I2C0.Configure(machine.I2CConfig{}) + compass := lis2mdl.New(machine.I2C0) + + if !compass.Connected() { + for { + println("LIS2MDL not connected!") + time.Sleep(1 * time.Second) + } + } + + compass.Configure(lis2mdl.Configuration{}) //default settings + + for { + heading := compass.ReadCompass() + println("Heading:", heading) + + time.Sleep(time.Millisecond * 100) + } +} diff --git a/lis2mdl/lis2mdl.go b/lis2mdl/lis2mdl.go new file mode 100644 index 0000000..2686ca6 --- /dev/null +++ b/lis2mdl/lis2mdl.go @@ -0,0 +1,119 @@ +// Package lis2mdl implements a driver for the LIS2MDL, +// a magnetic sensor which is included on BBC micro:bit v1.5. +// +// Datasheet: https://www.st.com/resource/en/datasheet/lsm303agr.pdf +// +package lis2mdl // import "tinygo.org/x/drivers/lis2mdl" + +import ( + "machine" + "math" + "time" +) + +// Device wraps an I2C connection to a LIS2MDL device. +type Device struct { + bus machine.I2C + Address uint8 + PowerMode uint8 + SystemMode uint8 + DataRate uint8 +} + +// Configuration for LIS2MDL device. +type Configuration struct { + PowerMode uint8 + SystemMode uint8 + DataRate uint8 +} + +// New creates a new LIS2MDL connection. The I2C bus must already be +// configured. +// +// This function only creates the Device object, it does not touch the device. +func New(bus machine.I2C) Device { + return Device{bus: bus, Address: MAG_ADDRESS} +} + +// Connected returns whether LIS2MDL sensor has been found. +func (d *Device) Connected() bool { + data := []byte{0} + d.bus.ReadRegister(uint8(d.Address), MAG_WHO_AM_I, data) + return data[0] == 0x40 +} + +// Configure sets up the LIS2MDL device for communication. +func (d *Device) Configure(cfg Configuration) { + if cfg.PowerMode != 0 { + d.PowerMode = cfg.PowerMode + } else { + d.PowerMode = MAG_POWER_NORMAL + } + + if cfg.DataRate != 0 { + d.DataRate = cfg.DataRate + } else { + d.DataRate = MAG_DATARATE_100HZ + } + + if cfg.SystemMode != 0 { + d.SystemMode = cfg.SystemMode + } else { + d.SystemMode = MAG_SYSTEM_CONTINUOUS + } + + cmd := []byte{0} + + // reset + cmd[0] = byte(1 << 5) + d.bus.WriteRegister(uint8(d.Address), MAG_MR_CFG_REG_A, cmd) + time.Sleep(100 * time.Millisecond) + + // reboot + cmd[0] = byte(1 << 6) + d.bus.WriteRegister(uint8(d.Address), MAG_MR_CFG_REG_A, cmd) + time.Sleep(100 * time.Millisecond) + + // bdu + cmd[0] = byte(1 << 4) + d.bus.WriteRegister(uint8(d.Address), MAG_MR_CFG_REG_C, cmd) + + // Temperature compensation is on for magnetic sensor (0x80) + cmd[0] = byte(0x80) + d.bus.WriteRegister(uint8(d.Address), MAG_MR_CFG_REG_A, cmd) + + // speed + cmd[0] = byte(0x80 | d.DataRate) + d.bus.WriteRegister(uint8(d.Address), MAG_MR_CFG_REG_A, cmd) +} + +// ReadMagneticField reads the current magnetic field from the device and returns +// it in mG (milligauss). 1 mG = 0.1 µT (microtesla). +func (d *Device) ReadMagneticField() (x int32, y int32, z int32) { + data := make([]byte, 6) + d.bus.ReadRegister(uint8(d.Address), MAG_OUT_X_L_M, data) + + x = int32(int16((uint16(data[0]) << 8) | uint16(data[1]))) + y = int32(int16((uint16(data[2]) << 8) | uint16(data[3]))) + z = int32(int16((uint16(data[4]) << 8) | uint16(data[5]))) + + return +} + +// ReadCompass reads the current compass heading from the device and returns +// it in degrees. When the z axis is pointing straight to Earth and +// the y axis is pointing to North, the heading would be zero. +// +// However, the heading may be off due to electronic compasses would be effected +// by strong magnetic fields and require constant calibration. +func (d *Device) ReadCompass() (h int32) { + x, y, _ := d.ReadMagneticField() + xf, yf := float64(x)*0.15, float64(y)*0.15 + + rh := (math.Atan2(yf, xf) * 180) / math.Pi + if rh < 0 { + rh = 360 + rh + } + + return int32(rh) +} diff --git a/lis2mdl/registers.go b/lis2mdl/registers.go new file mode 100644 index 0000000..2294408 --- /dev/null +++ b/lis2mdl/registers.go @@ -0,0 +1,32 @@ +package lis2mdl + +const ( + // Constants/addresses used for I2C. + MAG_ADDRESS = 0x1E + + // magnetic sensor registers. + MAG_WHO_AM_I = 0x4F + MAG_MR_CFG_REG_A = 0x60 + MAG_MR_CFG_REG_B = 0x61 + MAG_MR_CFG_REG_C = 0x62 + MAG_OUT_X_L_M = 0x68 + MAG_OUT_X_H_M = 0x69 + MAG_OUT_Y_L_M = 0x6A + MAG_OUT_Y_H_M = 0x6B + MAG_OUT_Z_L_M = 0x6C + MAG_OUT_Z_H_M = 0x6D + + // magnetic sensor power mode. + MAG_POWER_NORMAL = 0x00 // default + MAG_POWER_LOW = 0x01 + + // magnetic sensor operate mode. + MAG_SYSTEM_CONTINUOUS = 0x00 // default + MAG_SYSTEM_SINGLE = 0x01 + + // magnetic sensor data rate + MAG_DATARATE_10HZ = 0x00 // default + MAG_DATARATE_20HZ = 0x01 + MAG_DATARATE_50HZ = 0x02 + MAG_DATARATE_100HZ = 0x03 +)