From 8c80439e669f5218d782549692df19df63a84b13 Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 29 Apr 2023 12:32:48 -0300 Subject: [PATCH 1/7] rewrite mpu6050 driver implementation to be much more functional --- examples/mpu6050/main.go | 7 +- mpu6050/mpu6050.go | 318 +++++++++++++++++++++++++++++---------- mpu6050/registers.go | 177 +++++++--------------- 3 files changed, 297 insertions(+), 205 deletions(-) diff --git a/examples/mpu6050/main.go b/examples/mpu6050/main.go index 90c224039..c57e69e76 100644 --- a/examples/mpu6050/main.go +++ b/examples/mpu6050/main.go @@ -11,8 +11,11 @@ import ( func main() { machine.I2C0.Configure(machine.I2CConfig{}) - accel := mpu6050.New(machine.I2C0) - accel.Configure() + mpuDevice := mpu6050.New(machine.I2C0) + mpuDevice.Init(mpu6050.IMUConfig{ + AccRange: mpu6050.ACCEL_RANGE_16, + GyroRange: mpu6050.GYRO_RANGE_2000, + }) for { x, y, z := accel.ReadAcceleration() diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go index c6e945da2..0254bf94b 100644 --- a/mpu6050/mpu6050.go +++ b/mpu6050/mpu6050.go @@ -1,95 +1,251 @@ -// Package mpu6050 provides a driver for the MPU6050 accelerometer and gyroscope -// made by InvenSense. -// -// Datasheets: -// https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf -// https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf -package mpu6050 // import "tinygo.org/x/drivers/mpu6050" +package mpu6050 + +import ( + "encoding/binary" + "fmt" + + "tinygo.org/x/drivers" +) -import "tinygo.org/x/drivers" +const DefaultAddress = 0x68 -// Device wraps an I2C connection to a MPU6050 device. -type Device struct { - bus drivers.I2C - Address uint16 +type IMUConfig struct { + // Use ACCEL_RANGE_2 through ACCEL_RANGE_16. + AccRange byte + // Use GYRO_RANGE_250 through GYRO_RANGE_2000 + GyroRange byte + sampleRatio byte // TODO(soypat): expose these as configurable. + clkSel byte } -// New creates a new MPU6050 connection. The I2C bus must already be -// configured. -// -// This function only creates the Device object, it does not touch the device. -func New(bus drivers.I2C) Device { - return Device{bus, Address} +// Dev contains MPU board abstraction for usage +type Dev struct { + conn drivers.I2C + aRange int32 //Gyroscope FSR acording to SetAccelRange input + gRange int32 //Gyroscope FSR acording to SetGyroRange input + data [14]byte + address byte +} + +// New instantiates and initializes a MPU6050 struct without writing/reading +// i2c bus. Typical I2C MPU6050 address is 0x68. +func New(bus drivers.I2C, addr uint16) *Dev { + p := &Dev{} + p.address = uint8(addr) + p.conn = bus + return p +} + +// Init configures the necessary registers for using the +// MPU6050. It sets the range of both the accelerometer +// and the gyroscope, the sample rate, the clock source +// and wakes up the peripheral. +func (p *Dev) Init(data IMUConfig) (err error) { + + // setSleep + if err = p.SetSleep(false); err != nil { + return fmt.Errorf("set sleep: %w", err) + } + // setClockSource + if err = p.SetClockSource(data.clkSel); err != nil { + return fmt.Errorf("set clksrc: %w", err) + } + // setSampleRate + if err = p.SetSampleRate(data.sampleRatio); err != nil { + return fmt.Errorf("set sample: %w", err) + } + // setFullScaleGyroRange + if err = p.SetGyroRange(data.GyroRange); err != nil { + return fmt.Errorf("set gyro: %w", err) + } + // setFullScaleAccelRange + if err = p.SetAccelRange(data.AccRange); err != nil { + return fmt.Errorf("set accelrange: %w", err) + } + return nil } -// Connected returns whether a MPU6050 has been found. -// It does a "who am I" request and checks the response. -func (d Device) Connected() bool { - data := []byte{0} - d.bus.ReadRegister(uint8(d.Address), WHO_AM_I, data) - return data[0] == 0x68 +// Update fetches the latest data from the MPU6050 +func (p *Dev) Update() (err error) { + if err = p.read(ACCEL_XOUT_H, p.data[:]); err != nil { + return err + } + return nil } -// Configure sets up the device for communication. -func (d Device) Configure() error { - return d.SetClockSource(CLOCK_INTERNAL) +// Get treats the MPU6050 like an ADC and returns the raw reading of the channel. +// Channels 0-2 are acceleration, 3-5 are gyroscope and 6 is the temperature. +func (d *Dev) RawReading(channel int) int16 { + if channel > 6 { + // Bad value. + return 0 + } + return convertWord(d.data[channel*2:]) } -// ReadAcceleration reads the current acceleration from the device and returns -// it in µg (micro-gravity). When one of the axes is pointing straight to Earth +func convertWord(buf []byte) int16 { + return int16(binary.BigEndian.Uint16(buf)) +} + +func (d *Dev) ChannelLen() int { return 7 } + +// Acceleration returns last read acceleration in µg (micro-gravity). +// When one of the axes is pointing straight to Earth // and the sensor is not moving the returned value will be around 1000000 or // -1000000. -func (d Device) ReadAcceleration() (x int32, y int32, z int32) { - data := make([]byte, 6) - d.bus.ReadRegister(uint8(d.Address), ACCEL_XOUT_H, data) - // Now do two things: - // 1. merge the two values to a 16-bit number (and cast to a 32-bit integer) - // 2. scale the value to bring it in the -1000000..1000000 range. - // This is done with a trick. What we do here is essentially multiply by - // 1000000 and divide by 16384 to get the original scale, but to avoid - // overflow we do it at 1/64 of the value: - // 1000000 / 64 = 15625 - // 16384 / 64 = 256 - x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / 256 - y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / 256 - z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 256 - return -} - -// ReadRotation reads the current rotation from the device and returns it in -// µ°/s (micro-degrees/sec). This means that if you were to do a complete +func (d *Dev) Acceleration() (ax, ay, az int32) { + const accelOffset = 0 + ax = int32(convertWord(d.data[accelOffset+0:])) * 15625 / 512 * int32(d.aRange) + ay = int32(convertWord(d.data[accelOffset+2:])) * 15625 / 512 * int32(d.aRange) + az = int32(convertWord(d.data[accelOffset+4:])) * 15625 / 512 * int32(d.aRange) + return ax, ay, az +} + +// Rotations reads the current rotation from the device and returns it in +// µ°/rad (micro-radians/sec). This means that if you were to do a complete // rotation along one axis and while doing so integrate all values over time, -// you would get a value close to 360000000. -func (d Device) ReadRotation() (x int32, y int32, z int32) { - data := make([]byte, 6) - d.bus.ReadRegister(uint8(d.Address), GYRO_XOUT_H, data) - // First the value is converted from a pair of bytes to a signed 16-bit - // value and then to a signed 32-bit value to avoid integer overflow. - // Then the value is scaled to µ°/s (micro-degrees per second). - // This is done in the following steps: - // 1. Multiply by 250 * 1000_000 - // 2. Divide by 32768 - // The following calculation (x * 15625 / 2048 * 1000) is essentially the - // same but avoids overflow. First both operations are divided by 16 leading - // to multiply by 15625000 and divide by 2048, and then part of the multiply - // is done after the divide instead of before. - x = int32(int16((uint16(data[0])<<8)|uint16(data[1]))) * 15625 / 2048 * 1000 - y = int32(int16((uint16(data[2])<<8)|uint16(data[3]))) * 15625 / 2048 * 1000 - z = int32(int16((uint16(data[4])<<8)|uint16(data[5]))) * 15625 / 2048 * 1000 - return -} - -// SetClockSource allows the user to configure the clock source. -func (d Device) SetClockSource(source uint8) error { - return d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{source}) -} - -// SetFullScaleGyroRange allows the user to configure the scale range for the gyroscope. -func (d Device) SetFullScaleGyroRange(rng uint8) error { - return d.bus.WriteRegister(uint8(d.Address), GYRO_CONFIG, []uint8{rng}) -} - -// SetFullScaleAccelRange allows the user to configure the scale range for the accelerometer. -func (d Device) SetFullScaleAccelRange(rng uint8) error { - return d.bus.WriteRegister(uint8(d.Address), ACCEL_CONFIG, []uint8{rng}) +// you would get a value close to 6.3 radians (360 degrees). +func (d *Dev) AngularVelocity() (gx, gy, gz int32) { + const angvelOffset = 8 + gx = int32(convertWord(d.data[angvelOffset+0:])) * 2663 / 5000 * d.gRange + gy = int32(convertWord(d.data[angvelOffset+2:])) * 2663 / 5000 * d.gRange + gz = int32(convertWord(d.data[angvelOffset+4:])) * 2663 / 5000 * d.gRange + return gx, gy, gz +} + +// Temperature returns the temperature of the device in milli-centigrade. +func (d *Dev) Temperature() (Celsius int32) { + const tempOffset = 6 + return 1000*int32(convertWord(d.data[tempOffset:]))/340 + 37*1000 +} + +// SetSampleRate sets the sample rate for the FIFO, +// register ouput and DMP. The sample rate is determined +// by: +// +// SR = Gyroscope Output Rate / (1 + srDiv) +// +// The Gyroscope Output Rate is 8kHz when the DLPF is +// disabled and 1kHz otherwise. The maximum sample rate +// for the accelerometer is 1kHz, if a higher sample rate +// is chosen, the same accelerometer sample will be output. +func (p *Dev) SetSampleRate(srDiv byte) (err error) { + // setSampleRate + var sr [1]byte + sr[0] = srDiv + if err = p.write8(SMPRT_DIV, sr[0]); err != nil { + return err + } + return nil +} + +// SetClockSource configures the source of the clock +// for the peripheral. +func (p *Dev) SetClockSource(clkSel byte) (err error) { + // setClockSource + var pwrMgt [1]byte + + if err = p.read(PWR_MGMT_1, pwrMgt[:]); err != nil { + return err + } + pwrMgt[0] = (pwrMgt[0] & (^CLK_SEL_MASK)) | clkSel // Escribo solo el campo de clk_sel + if err = p.write8(PWR_MGMT_1, pwrMgt[0]); err != nil { + return err + } + return nil +} + +// SetGyroRange configures the full scale range of the gyroscope. +// It has four possible values +- 250°/s, 500°/s, 1000°/s, 2000°/s. +// The function takes values of gyroRange from 0-3 where 0 means the +// lowest FSR (250°/s) and 3 is the highest FSR (2000°/s). +func (p *Dev) SetGyroRange(gyroRange byte) (err error) { + switch gyroRange { + case GYRO_RANGE_250: + p.gRange = 250 + case GYRO_RANGE_500: + p.gRange = 500 + case GYRO_RANGE_1000: + p.gRange = 1000 + case GYRO_RANGE_2000: + p.gRange = 2000 + default: + return fmt.Errorf("invalid gyroscope FSR input") + } + // setFullScaleGyroRange + var gConfig [1]byte + + if err = p.read(GYRO_CONFIG, gConfig[:]); err != nil { + return err + } + gConfig[0] = (gConfig[0] & (^G_FS_SEL)) | (gyroRange << G_FS_SHIFT) // Escribo solo el campo de FS_sel + + if err = p.write8(GYRO_CONFIG, gConfig[0]); err != nil { + return err + } + return nil +} + +// SetAccelRange configures the full scale range of the accelerometer. +// It has four possible values +- 2g, 4g, 8g, 16g. +// The function takes values of accRange from 0-3 where 0 means the +// lowest FSR (2g) and 3 is the highest FSR (16g) +func (p *Dev) SetAccelRange(accRange byte) (err error) { + switch accRange { + case ACCEL_RANGE_2: + p.aRange = 2 + case ACCEL_RANGE_4: + p.aRange = 4 + case ACCEL_RANGE_8: + p.aRange = 8 + case ACCEL_RANGE_16: + p.aRange = 16 + default: + return fmt.Errorf("invalid accelerometer FSR input") + } + // setFullScaleAccelRange + var aConfig [1]byte + + if err = p.read(ACCEL_CONFIG, aConfig[:]); err != nil { + return err + } + aConfig[0] = (aConfig[0] & (^AFS_SEL)) | (accRange << AFS_SHIFT) // Escribo solo el campo de FS_sel + + if err = p.write8(ACCEL_CONFIG, aConfig[0]); err != nil { + return err + } + return nil +} + +// SetSleep sets the sleep bit on the power managment 1 field. +// When the recieved bool is true, it sets the bit to 1 thus putting +// the peripheral in sleep mode. +// When false is recieved the bit is set to 0 and the peripheral wakes +// up. +func (p *Dev) SetSleep(sleep bool) (err error) { + // setSleepBit + var pwrMgt [1]byte + + if err = p.read(PWR_MGMT_1, pwrMgt[:]); err != nil { + return err + } + if sleep { + pwrMgt[0] = (pwrMgt[0] & (^SLEEP_MASK)) | (1 << SLEEP_SHIFT) // Escribo solo el campo de clk_sel + } else { + pwrMgt[0] = (pwrMgt[0] & (^SLEEP_MASK)) + } + //Envio el registro modificado al periferico + if err = p.write8(PWR_MGMT_1, pwrMgt[0]); err != nil { + return err + } + return nil +} + +func DefaultConfig() IMUConfig { + return IMUConfig{ + AccRange: ACCEL_RANGE_16, + GyroRange: GYRO_RANGE_2000, + sampleRatio: 0, // TODO add const values. + clkSel: 0, + } } diff --git a/mpu6050/registers.go b/mpu6050/registers.go index 4b74369f5..b7e4883c0 100644 --- a/mpu6050/registers.go +++ b/mpu6050/registers.go @@ -1,132 +1,65 @@ package mpu6050 -// Constants/addresses used for I2C. +// read reads register reg and writes n bytes to b where +// n is the length of b. +func (p *Dev) read(reg uint8, buff []byte) error { + return p.conn.Tx(uint16(p.address), []byte{reg}, buff) +} -// The I2C address which this device listens to. -const Address = 0x68 +// write8 writes a registry byte +func (p *Dev) write8(reg uint8, datum byte) error { + err := p.write(reg, []byte{datum}) + return err +} -// Registers. Names, addresses and comments copied from the datasheet. -const ( - // Self test registers - SELF_TEST_X = 0x0D - SELF_TEST_Y = 0x0E - SELF_TEST_Z = 0x0F - SELF_TEST_A = 0x10 - - SMPLRT_DIV = 0x19 // Sample rate divider - CONFIG = 0x1A // Configuration - GYRO_CONFIG = 0x1B // Gyroscope configuration - ACCEL_CONFIG = 0x1C // Accelerometer configuration - FIFO_EN = 0x23 // FIFO enable - - // I2C pass-through configuration - I2C_MST_CTRL = 0x24 - I2C_SLV0_ADDR = 0x25 - I2C_SLV0_REG = 0x26 - I2C_SLV0_CTRL = 0x27 - I2C_SLV1_ADDR = 0x28 - I2C_SLV1_REG = 0x29 - I2C_SLV1_CTRL = 0x2A - I2C_SLV2_ADDR = 0x2B - I2C_SLV2_REG = 0x2C - I2C_SLV2_CTRL = 0x2D - I2C_SLV3_ADDR = 0x2E - I2C_SLV3_REG = 0x2F - I2C_SLV3_CTRL = 0x30 - I2C_SLV4_ADDR = 0x31 - I2C_SLV4_REG = 0x32 - I2C_SLV4_DO = 0x33 - I2C_SLV4_CTRL = 0x34 - I2C_SLV4_DI = 0x35 - I2C_MST_STATUS = 0x36 - - // Interrupt configuration - INT_PIN_CFG = 0x37 // Interrupt pin/bypass enable configuration - INT_ENABLE = 0x38 // Interrupt enable - INT_STATUS = 0x3A // Interrupt status - - // Accelerometer measurements - ACCEL_XOUT_H = 0x3B - ACCEL_XOUT_L = 0x3C - ACCEL_YOUT_H = 0x3D - ACCEL_YOUT_L = 0x3E - ACCEL_ZOUT_H = 0x3F - ACCEL_ZOUT_L = 0x40 +// write write a byte buffer to sequential incrementing adresses (1 byte per address) in individual +// write operations. +func (p *Dev) write(addr uint8, buff []byte) error { + p.conn.Tx(uint16(p.address), append([]byte{addr}, buff...), nil) + return nil +} - // Temperature measurement - TEMP_OUT_H = 0x41 - TEMP_OUT_L = 0x42 - - // Gyroscope measurements - GYRO_XOUT_H = 0x43 - GYRO_XOUT_L = 0x44 - GYRO_YOUT_H = 0x45 - GYRO_YOUT_L = 0x46 - GYRO_ZOUT_H = 0x47 - GYRO_ZOUT_L = 0x48 - - // External sensor data - EXT_SENS_DATA_00 = 0x49 - EXT_SENS_DATA_01 = 0x4A - EXT_SENS_DATA_02 = 0x4B - EXT_SENS_DATA_03 = 0x4C - EXT_SENS_DATA_04 = 0x4D - EXT_SENS_DATA_05 = 0x4E - EXT_SENS_DATA_06 = 0x4F - EXT_SENS_DATA_07 = 0x50 - EXT_SENS_DATA_08 = 0x51 - EXT_SENS_DATA_09 = 0x52 - EXT_SENS_DATA_10 = 0x53 - EXT_SENS_DATA_11 = 0x54 - EXT_SENS_DATA_12 = 0x55 - EXT_SENS_DATA_13 = 0x56 - EXT_SENS_DATA_14 = 0x57 - EXT_SENS_DATA_15 = 0x58 - EXT_SENS_DATA_16 = 0x59 - EXT_SENS_DATA_17 = 0x5A - EXT_SENS_DATA_18 = 0x5B - EXT_SENS_DATA_19 = 0x5C - EXT_SENS_DATA_20 = 0x5D - EXT_SENS_DATA_21 = 0x5E - EXT_SENS_DATA_22 = 0x5F - EXT_SENS_DATA_23 = 0x60 - - // I2C peripheral data out - I2C_PER0_DO = 0x63 - I2C_PER1_DO = 0x64 - I2C_PER2_DO = 0x65 - I2C_PER3_DO = 0x66 - I2C_MST_DELAY_CT = 0x67 +// MPU 6050 REGISTER ADRESSES +const ( + SMPRT_DIV uint8 = 0x19 + CONFIG uint8 = 0x1A + GYRO_CONFIG uint8 = 0x1B + ACCEL_CONFIG uint8 = 0x1C + FIFO_EN uint8 = 0x23 + I2C_MST_CTRL uint8 = 0x24 + PWR_MGMT_1 uint8 = 0x6B + ACCEL_XOUT_H uint8 = 0x3B + TEMP_OUT_H uint8 = 0x41 + GYRO_XOUT_H uint8 = 0x43 +) - // Clock settings - CLOCK_INTERNAL = 0x00 - CLOCK_PLL_XGYRO = 0x01 - CLOCK_PLL_YGYRO = 0x02 - CLOCK_PLL_ZGYRO = 0x03 - CLOCK_PLL_EXTERNAL_32_768_KZ = 0x04 - CLOCK_PLL_EXTERNAL_19_2_MHZ = 0x05 - CLOCK_RESERVED = 0x06 - CLOCK_STOP = 0x07 +// MPU 6050 MASKS +const ( + G_FS_SEL uint8 = 0x18 + AFS_SEL uint8 = 0x18 + CLK_SEL_MASK uint8 = 0x07 + SLEEP_MASK uint8 = 0x40 +) - // Accelerometer settings - AFS_RANGE_2G = 0x00 - AFS_RANGE_4G = 0x01 - AFS_RANGE_8G = 0x02 - AFS_RANGE_16G = 0x03 +// MPU 6050 SHIFTS +const ( + AFS_SHIFT uint8 = 3 + G_FS_SHIFT uint8 = 3 + SLEEP_SHIFT uint8 = 6 +) - // Gyroscope settings - FS_RANGE_250 = 0x00 - FS_RANGE_500 = 0x01 - FS_RANGE_1000 = 0x02 - FS_RANGE_2000 = 0x03 +// Gyroscope ranges for Init configuration +const ( + GYRO_RANGE_250 byte = 0x00 + GYRO_RANGE_500 byte = 0x01 + GYRO_RANGE_1000 byte = 0x02 + GYRO_RANGE_2000 byte = 0x03 +) - // other registers - SIGNAL_PATH_RES = 0x68 // Signal path reset - USER_CTRL = 0x6A // User control - PWR_MGMT_1 = 0x6B // Power Management 1 - PWR_MGMT_2 = 0x6C // Power Management 2 - FIFO_COUNTH = 0x72 // FIFO count registers (high bits) - FIFO_COUNTL = 0x73 // FIFO count registers (low bits) - FIFO_R_W = 0x74 // FIFO read/write - WHO_AM_I = 0x75 // Who am I +// Accelerometer ranges for Init configuration +const ( + ACCEL_RANGE_2 byte = 0x00 + ACCEL_RANGE_4 byte = 0x01 + ACCEL_RANGE_8 byte = 0x02 + ACCEL_RANGE_16 byte = 0x03 ) From 44b049c6823cd74d37dec0309a0bad28e86f9fbe Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 29 Apr 2023 12:39:07 -0300 Subject: [PATCH 2/7] fix example --- examples/mpu6050/main.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/mpu6050/main.go b/examples/mpu6050/main.go index c57e69e76..53d1b1f32 100644 --- a/examples/mpu6050/main.go +++ b/examples/mpu6050/main.go @@ -18,8 +18,17 @@ func main() { }) for { - x, y, z := accel.ReadAcceleration() - println(x, y, z) time.Sleep(time.Millisecond * 100) + err := mpuDevice.Update() + if err != nil { + println("error reading from mpu6050:", err.Error()) + continue + } + print("acceleration: ") + println(mpuDevice.Acceleration()) + print("angular velocity:") + println(mpuDevice.AngularVelocity()) + print("temperature centigrade:") + println(mpuDevice.Temperature() / 1000) } } From ae9ac8a9681df43de62b2bfd2438fd690a6da299 Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 29 Apr 2023 12:42:00 -0300 Subject: [PATCH 3/7] remove useless int32 cast --- mpu6050/mpu6050.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go index 0254bf94b..08bbc8bf4 100644 --- a/mpu6050/mpu6050.go +++ b/mpu6050/mpu6050.go @@ -95,9 +95,9 @@ func (d *Dev) ChannelLen() int { return 7 } // -1000000. func (d *Dev) Acceleration() (ax, ay, az int32) { const accelOffset = 0 - ax = int32(convertWord(d.data[accelOffset+0:])) * 15625 / 512 * int32(d.aRange) - ay = int32(convertWord(d.data[accelOffset+2:])) * 15625 / 512 * int32(d.aRange) - az = int32(convertWord(d.data[accelOffset+4:])) * 15625 / 512 * int32(d.aRange) + ax = int32(convertWord(d.data[accelOffset+0:])) * 15625 / 512 * d.aRange + ay = int32(convertWord(d.data[accelOffset+2:])) * 15625 / 512 * d.aRange + az = int32(convertWord(d.data[accelOffset+4:])) * 15625 / 512 * d.aRange return ax, ay, az } From a2124d3a449243c8a33f9a23abe5a840ab7d6e32 Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 30 Apr 2023 10:27:48 -0300 Subject: [PATCH 4/7] apply Ayke suggestions; rename SetRange* methods --- examples/mpu6050/main.go | 9 +++-- mpu6050/mpu6050.go | 86 ++++++++++++++++++++-------------------- mpu6050/registers.go | 40 +++++++++---------- 3 files changed, 70 insertions(+), 65 deletions(-) diff --git a/examples/mpu6050/main.go b/examples/mpu6050/main.go index 53d1b1f32..7e76ef40c 100644 --- a/examples/mpu6050/main.go +++ b/examples/mpu6050/main.go @@ -11,12 +11,15 @@ import ( func main() { machine.I2C0.Configure(machine.I2CConfig{}) - mpuDevice := mpu6050.New(machine.I2C0) - mpuDevice.Init(mpu6050.IMUConfig{ + mpuDevice := mpu6050.New(machine.I2C0, mpu6050.DefaultAddress) + + err := mpuDevice.Configure(mpu6050.Config{ AccRange: mpu6050.ACCEL_RANGE_16, GyroRange: mpu6050.GYRO_RANGE_2000, }) - + if err != nil { + panic(err.Error()) + } for { time.Sleep(time.Millisecond * 100) err := mpuDevice.Update() diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go index 08bbc8bf4..29c492077 100644 --- a/mpu6050/mpu6050.go +++ b/mpu6050/mpu6050.go @@ -3,13 +3,14 @@ package mpu6050 import ( "encoding/binary" "fmt" + "math" "tinygo.org/x/drivers" ) const DefaultAddress = 0x68 -type IMUConfig struct { +type Config struct { // Use ACCEL_RANGE_2 through ACCEL_RANGE_16. AccRange byte // Use GYRO_RANGE_250 through GYRO_RANGE_2000 @@ -18,8 +19,8 @@ type IMUConfig struct { clkSel byte } -// Dev contains MPU board abstraction for usage -type Dev struct { +// Device contains MPU board abstraction for usage +type Device struct { conn drivers.I2C aRange int32 //Gyroscope FSR acording to SetAccelRange input gRange int32 //Gyroscope FSR acording to SetGyroRange input @@ -29,8 +30,8 @@ type Dev struct { // New instantiates and initializes a MPU6050 struct without writing/reading // i2c bus. Typical I2C MPU6050 address is 0x68. -func New(bus drivers.I2C, addr uint16) *Dev { - p := &Dev{} +func New(bus drivers.I2C, addr uint16) *Device { + p := &Device{} p.address = uint8(addr) p.conn = bus return p @@ -40,7 +41,7 @@ func New(bus drivers.I2C, addr uint16) *Dev { // MPU6050. It sets the range of both the accelerometer // and the gyroscope, the sample rate, the clock source // and wakes up the peripheral. -func (p *Dev) Init(data IMUConfig) (err error) { +func (p *Device) Configure(data Config) (err error) { // setSleep if err = p.SetSleep(false); err != nil { @@ -55,19 +56,19 @@ func (p *Dev) Init(data IMUConfig) (err error) { return fmt.Errorf("set sample: %w", err) } // setFullScaleGyroRange - if err = p.SetGyroRange(data.GyroRange); err != nil { + if err = p.SetRangeGyro(data.GyroRange); err != nil { return fmt.Errorf("set gyro: %w", err) } // setFullScaleAccelRange - if err = p.SetAccelRange(data.AccRange); err != nil { + if err = p.SetRangeAccel(data.AccRange); err != nil { return fmt.Errorf("set accelrange: %w", err) } return nil } // Update fetches the latest data from the MPU6050 -func (p *Dev) Update() (err error) { - if err = p.read(ACCEL_XOUT_H, p.data[:]); err != nil { +func (p *Device) Update() (err error) { + if err = p.read(_ACCEL_XOUT_H, p.data[:]); err != nil { return err } return nil @@ -75,7 +76,7 @@ func (p *Dev) Update() (err error) { // Get treats the MPU6050 like an ADC and returns the raw reading of the channel. // Channels 0-2 are acceleration, 3-5 are gyroscope and 6 is the temperature. -func (d *Dev) RawReading(channel int) int16 { +func (d *Device) RawReading(channel int) int16 { if channel > 6 { // Bad value. return 0 @@ -87,13 +88,13 @@ func convertWord(buf []byte) int16 { return int16(binary.BigEndian.Uint16(buf)) } -func (d *Dev) ChannelLen() int { return 7 } +func (d *Device) ChannelLen() int { return 7 } // Acceleration returns last read acceleration in µg (micro-gravity). // When one of the axes is pointing straight to Earth // and the sensor is not moving the returned value will be around 1000000 or // -1000000. -func (d *Dev) Acceleration() (ax, ay, az int32) { +func (d *Device) Acceleration() (ax, ay, az int32) { const accelOffset = 0 ax = int32(convertWord(d.data[accelOffset+0:])) * 15625 / 512 * d.aRange ay = int32(convertWord(d.data[accelOffset+2:])) * 15625 / 512 * d.aRange @@ -105,18 +106,19 @@ func (d *Dev) Acceleration() (ax, ay, az int32) { // µ°/rad (micro-radians/sec). This means that if you were to do a complete // rotation along one axis and while doing so integrate all values over time, // you would get a value close to 6.3 radians (360 degrees). -func (d *Dev) AngularVelocity() (gx, gy, gz int32) { +func (d *Device) AngularVelocity() (gx, gy, gz int32) { + const a = math.MaxInt16*8192 > math.MaxInt32 const angvelOffset = 8 - gx = int32(convertWord(d.data[angvelOffset+0:])) * 2663 / 5000 * d.gRange - gy = int32(convertWord(d.data[angvelOffset+2:])) * 2663 / 5000 * d.gRange - gz = int32(convertWord(d.data[angvelOffset+4:])) * 2663 / 5000 * d.gRange + gx = int32(convertWord(d.data[angvelOffset+0:])) * 4363 / 8192 * d.gRange + gy = int32(convertWord(d.data[angvelOffset+2:])) * 4363 / 8192 * d.gRange + gz = int32(convertWord(d.data[angvelOffset+4:])) * 4363 / 8192 * d.gRange return gx, gy, gz } // Temperature returns the temperature of the device in milli-centigrade. -func (d *Dev) Temperature() (Celsius int32) { +func (d *Device) Temperature() (Celsius int32) { const tempOffset = 6 - return 1000*int32(convertWord(d.data[tempOffset:]))/340 + 37*1000 + return 1506*int32(convertWord(d.data[tempOffset:]))/512 + 37*1000 } // SetSampleRate sets the sample rate for the FIFO, @@ -129,11 +131,11 @@ func (d *Dev) Temperature() (Celsius int32) { // disabled and 1kHz otherwise. The maximum sample rate // for the accelerometer is 1kHz, if a higher sample rate // is chosen, the same accelerometer sample will be output. -func (p *Dev) SetSampleRate(srDiv byte) (err error) { +func (p *Device) SetSampleRate(srDiv byte) (err error) { // setSampleRate var sr [1]byte sr[0] = srDiv - if err = p.write8(SMPRT_DIV, sr[0]); err != nil { + if err = p.write8(_SMPRT_DIV, sr[0]); err != nil { return err } return nil @@ -141,25 +143,25 @@ func (p *Dev) SetSampleRate(srDiv byte) (err error) { // SetClockSource configures the source of the clock // for the peripheral. -func (p *Dev) SetClockSource(clkSel byte) (err error) { +func (p *Device) SetClockSource(clkSel byte) (err error) { // setClockSource var pwrMgt [1]byte - if err = p.read(PWR_MGMT_1, pwrMgt[:]); err != nil { + if err = p.read(_PWR_MGMT_1, pwrMgt[:]); err != nil { return err } - pwrMgt[0] = (pwrMgt[0] & (^CLK_SEL_MASK)) | clkSel // Escribo solo el campo de clk_sel - if err = p.write8(PWR_MGMT_1, pwrMgt[0]); err != nil { + pwrMgt[0] = (pwrMgt[0] & (^_CLK_SEL_MASK)) | clkSel // Escribo solo el campo de clk_sel + if err = p.write8(_PWR_MGMT_1, pwrMgt[0]); err != nil { return err } return nil } -// SetGyroRange configures the full scale range of the gyroscope. +// SetRangeGyro configures the full scale range of the gyroscope. // It has four possible values +- 250°/s, 500°/s, 1000°/s, 2000°/s. // The function takes values of gyroRange from 0-3 where 0 means the // lowest FSR (250°/s) and 3 is the highest FSR (2000°/s). -func (p *Dev) SetGyroRange(gyroRange byte) (err error) { +func (p *Device) SetRangeGyro(gyroRange byte) (err error) { switch gyroRange { case GYRO_RANGE_250: p.gRange = 250 @@ -175,22 +177,22 @@ func (p *Dev) SetGyroRange(gyroRange byte) (err error) { // setFullScaleGyroRange var gConfig [1]byte - if err = p.read(GYRO_CONFIG, gConfig[:]); err != nil { + if err = p.read(_GYRO_CONFIG, gConfig[:]); err != nil { return err } - gConfig[0] = (gConfig[0] & (^G_FS_SEL)) | (gyroRange << G_FS_SHIFT) // Escribo solo el campo de FS_sel + gConfig[0] = (gConfig[0] & (^_G_FS_SEL)) | (gyroRange << _G_FS_SHIFT) // Escribo solo el campo de FS_sel - if err = p.write8(GYRO_CONFIG, gConfig[0]); err != nil { + if err = p.write8(_GYRO_CONFIG, gConfig[0]); err != nil { return err } return nil } -// SetAccelRange configures the full scale range of the accelerometer. +// SetRangeAccel configures the full scale range of the accelerometer. // It has four possible values +- 2g, 4g, 8g, 16g. // The function takes values of accRange from 0-3 where 0 means the // lowest FSR (2g) and 3 is the highest FSR (16g) -func (p *Dev) SetAccelRange(accRange byte) (err error) { +func (p *Device) SetRangeAccel(accRange byte) (err error) { switch accRange { case ACCEL_RANGE_2: p.aRange = 2 @@ -206,12 +208,12 @@ func (p *Dev) SetAccelRange(accRange byte) (err error) { // setFullScaleAccelRange var aConfig [1]byte - if err = p.read(ACCEL_CONFIG, aConfig[:]); err != nil { + if err = p.read(_ACCEL_CONFIG, aConfig[:]); err != nil { return err } - aConfig[0] = (aConfig[0] & (^AFS_SEL)) | (accRange << AFS_SHIFT) // Escribo solo el campo de FS_sel + aConfig[0] = (aConfig[0] & (^_AFS_SEL)) | (accRange << _AFS_SHIFT) - if err = p.write8(ACCEL_CONFIG, aConfig[0]); err != nil { + if err = p.write8(_ACCEL_CONFIG, aConfig[0]); err != nil { return err } return nil @@ -222,27 +224,27 @@ func (p *Dev) SetAccelRange(accRange byte) (err error) { // the peripheral in sleep mode. // When false is recieved the bit is set to 0 and the peripheral wakes // up. -func (p *Dev) SetSleep(sleep bool) (err error) { +func (p *Device) SetSleep(sleep bool) (err error) { // setSleepBit var pwrMgt [1]byte - if err = p.read(PWR_MGMT_1, pwrMgt[:]); err != nil { + if err = p.read(_PWR_MGMT_1, pwrMgt[:]); err != nil { return err } if sleep { - pwrMgt[0] = (pwrMgt[0] & (^SLEEP_MASK)) | (1 << SLEEP_SHIFT) // Escribo solo el campo de clk_sel + pwrMgt[0] = (pwrMgt[0] & (^_SLEEP_MASK)) | (1 << _SLEEP_SHIFT) // Overwrite only Sleep } else { - pwrMgt[0] = (pwrMgt[0] & (^SLEEP_MASK)) + pwrMgt[0] = (pwrMgt[0] & (^_SLEEP_MASK)) } //Envio el registro modificado al periferico - if err = p.write8(PWR_MGMT_1, pwrMgt[0]); err != nil { + if err = p.write8(_PWR_MGMT_1, pwrMgt[0]); err != nil { return err } return nil } -func DefaultConfig() IMUConfig { - return IMUConfig{ +func DefaultConfig() Config { + return Config{ AccRange: ACCEL_RANGE_16, GyroRange: GYRO_RANGE_2000, sampleRatio: 0, // TODO add const values. diff --git a/mpu6050/registers.go b/mpu6050/registers.go index b7e4883c0..0b923beeb 100644 --- a/mpu6050/registers.go +++ b/mpu6050/registers.go @@ -2,50 +2,50 @@ package mpu6050 // read reads register reg and writes n bytes to b where // n is the length of b. -func (p *Dev) read(reg uint8, buff []byte) error { +func (p *Device) read(reg uint8, buff []byte) error { return p.conn.Tx(uint16(p.address), []byte{reg}, buff) } // write8 writes a registry byte -func (p *Dev) write8(reg uint8, datum byte) error { +func (p *Device) write8(reg uint8, datum byte) error { err := p.write(reg, []byte{datum}) return err } // write write a byte buffer to sequential incrementing adresses (1 byte per address) in individual // write operations. -func (p *Dev) write(addr uint8, buff []byte) error { +func (p *Device) write(addr uint8, buff []byte) error { p.conn.Tx(uint16(p.address), append([]byte{addr}, buff...), nil) return nil } // MPU 6050 REGISTER ADRESSES const ( - SMPRT_DIV uint8 = 0x19 - CONFIG uint8 = 0x1A - GYRO_CONFIG uint8 = 0x1B - ACCEL_CONFIG uint8 = 0x1C - FIFO_EN uint8 = 0x23 - I2C_MST_CTRL uint8 = 0x24 - PWR_MGMT_1 uint8 = 0x6B - ACCEL_XOUT_H uint8 = 0x3B - TEMP_OUT_H uint8 = 0x41 - GYRO_XOUT_H uint8 = 0x43 + _SMPRT_DIV uint8 = 0x19 + _CONFIG uint8 = 0x1A + _GYRO_CONFIG uint8 = 0x1B + _ACCEL_CONFIG uint8 = 0x1C + _FIFO_EN uint8 = 0x23 + _I2C_MST_CTRL uint8 = 0x24 + _PWR_MGMT_1 uint8 = 0x6B + _ACCEL_XOUT_H uint8 = 0x3B + _TEMP_OUT_H uint8 = 0x41 + _GYRO_XOUT_H uint8 = 0x43 ) // MPU 6050 MASKS const ( - G_FS_SEL uint8 = 0x18 - AFS_SEL uint8 = 0x18 - CLK_SEL_MASK uint8 = 0x07 - SLEEP_MASK uint8 = 0x40 + _G_FS_SEL uint8 = 0x18 + _AFS_SEL uint8 = 0x18 + _CLK_SEL_MASK uint8 = 0x07 + _SLEEP_MASK uint8 = 0x40 ) // MPU 6050 SHIFTS const ( - AFS_SHIFT uint8 = 3 - G_FS_SHIFT uint8 = 3 - SLEEP_SHIFT uint8 = 6 + _AFS_SHIFT uint8 = 3 + _G_FS_SHIFT uint8 = 3 + _SLEEP_SHIFT uint8 = 6 ) // Gyroscope ranges for Init configuration From c4c9bdb54323bcfe124bad645e89b63d23fbbe82 Mon Sep 17 00:00:00 2001 From: soypat Date: Sun, 30 Apr 2023 10:37:07 -0300 Subject: [PATCH 5/7] tidy up; remove RawReading; expose RawData field --- mpu6050/mpu6050.go | 87 ++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go index 29c492077..cb8f7deca 100644 --- a/mpu6050/mpu6050.go +++ b/mpu6050/mpu6050.go @@ -2,8 +2,7 @@ package mpu6050 import ( "encoding/binary" - "fmt" - "math" + "errors" "tinygo.org/x/drivers" ) @@ -21,10 +20,12 @@ type Config struct { // Device contains MPU board abstraction for usage type Device struct { - conn drivers.I2C - aRange int32 //Gyroscope FSR acording to SetAccelRange input - gRange int32 //Gyroscope FSR acording to SetGyroRange input - data [14]byte + conn drivers.I2C + aRange int32 //Gyroscope FSR acording to SetAccelRange input + gRange int32 //Gyroscope FSR acording to SetGyroRange input + // RawData contains the accelerometer, gyroscope and temperature RawData read + // in the last call via the Update method. + RawData [14]byte address byte } @@ -42,63 +43,41 @@ func New(bus drivers.I2C, addr uint16) *Device { // and the gyroscope, the sample rate, the clock source // and wakes up the peripheral. func (p *Device) Configure(data Config) (err error) { - - // setSleep - if err = p.SetSleep(false); err != nil { - return fmt.Errorf("set sleep: %w", err) + if err = p.Sleep(false); err != nil { + return errors.New("set sleep: " + err.Error()) } - // setClockSource if err = p.SetClockSource(data.clkSel); err != nil { - return fmt.Errorf("set clksrc: %w", err) + return errors.New("set clksrc: " + err.Error()) } - // setSampleRate if err = p.SetSampleRate(data.sampleRatio); err != nil { - return fmt.Errorf("set sample: %w", err) + return errors.New("set sampleratio: " + err.Error()) } - // setFullScaleGyroRange if err = p.SetRangeGyro(data.GyroRange); err != nil { - return fmt.Errorf("set gyro: %w", err) + return errors.New("set gyrorange: " + err.Error()) } - // setFullScaleAccelRange if err = p.SetRangeAccel(data.AccRange); err != nil { - return fmt.Errorf("set accelrange: %w", err) + return errors.New("set accelrange: " + err.Error()) } return nil } // Update fetches the latest data from the MPU6050 func (p *Device) Update() (err error) { - if err = p.read(_ACCEL_XOUT_H, p.data[:]); err != nil { + if err = p.read(_ACCEL_XOUT_H, p.RawData[:]); err != nil { return err } return nil } -// Get treats the MPU6050 like an ADC and returns the raw reading of the channel. -// Channels 0-2 are acceleration, 3-5 are gyroscope and 6 is the temperature. -func (d *Device) RawReading(channel int) int16 { - if channel > 6 { - // Bad value. - return 0 - } - return convertWord(d.data[channel*2:]) -} - -func convertWord(buf []byte) int16 { - return int16(binary.BigEndian.Uint16(buf)) -} - -func (d *Device) ChannelLen() int { return 7 } - // Acceleration returns last read acceleration in µg (micro-gravity). // When one of the axes is pointing straight to Earth // and the sensor is not moving the returned value will be around 1000000 or // -1000000. func (d *Device) Acceleration() (ax, ay, az int32) { const accelOffset = 0 - ax = int32(convertWord(d.data[accelOffset+0:])) * 15625 / 512 * d.aRange - ay = int32(convertWord(d.data[accelOffset+2:])) * 15625 / 512 * d.aRange - az = int32(convertWord(d.data[accelOffset+4:])) * 15625 / 512 * d.aRange + ax = int32(convertWord(d.RawData[accelOffset+0:])) * 15625 / 512 * d.aRange + ay = int32(convertWord(d.RawData[accelOffset+2:])) * 15625 / 512 * d.aRange + az = int32(convertWord(d.RawData[accelOffset+4:])) * 15625 / 512 * d.aRange return ax, ay, az } @@ -107,18 +86,22 @@ func (d *Device) Acceleration() (ax, ay, az int32) { // rotation along one axis and while doing so integrate all values over time, // you would get a value close to 6.3 radians (360 degrees). func (d *Device) AngularVelocity() (gx, gy, gz int32) { - const a = math.MaxInt16*8192 > math.MaxInt32 const angvelOffset = 8 - gx = int32(convertWord(d.data[angvelOffset+0:])) * 4363 / 8192 * d.gRange - gy = int32(convertWord(d.data[angvelOffset+2:])) * 4363 / 8192 * d.gRange - gz = int32(convertWord(d.data[angvelOffset+4:])) * 4363 / 8192 * d.gRange + _ = d.RawData[angvelOffset+5] // This line fails to compile if RawData is too short. + gx = int32(convertWord(d.RawData[angvelOffset+0:])) * 4363 / 8192 * d.gRange + gy = int32(convertWord(d.RawData[angvelOffset+2:])) * 4363 / 8192 * d.gRange + gz = int32(convertWord(d.RawData[angvelOffset+4:])) * 4363 / 8192 * d.gRange return gx, gy, gz } // Temperature returns the temperature of the device in milli-centigrade. func (d *Device) Temperature() (Celsius int32) { const tempOffset = 6 - return 1506*int32(convertWord(d.data[tempOffset:]))/512 + 37*1000 + return 1506*int32(convertWord(d.RawData[tempOffset:]))/512 + 37*1000 +} + +func convertWord(buf []byte) int16 { + return int16(binary.BigEndian.Uint16(buf)) } // SetSampleRate sets the sample rate for the FIFO, @@ -172,7 +155,7 @@ func (p *Device) SetRangeGyro(gyroRange byte) (err error) { case GYRO_RANGE_2000: p.gRange = 2000 default: - return fmt.Errorf("invalid gyroscope FSR input") + return errors.New("invalid gyroscope FSR input") } // setFullScaleGyroRange var gConfig [1]byte @@ -203,11 +186,10 @@ func (p *Device) SetRangeAccel(accRange byte) (err error) { case ACCEL_RANGE_16: p.aRange = 16 default: - return fmt.Errorf("invalid accelerometer FSR input") + return errors.New("invalid accelerometer FSR input") } - // setFullScaleAccelRange - var aConfig [1]byte + var aConfig [1]byte if err = p.read(_ACCEL_CONFIG, aConfig[:]); err != nil { return err } @@ -219,24 +201,21 @@ func (p *Device) SetRangeAccel(accRange byte) (err error) { return nil } -// SetSleep sets the sleep bit on the power managment 1 field. +// Sleep sets the sleep bit on the power managment 1 field. // When the recieved bool is true, it sets the bit to 1 thus putting // the peripheral in sleep mode. -// When false is recieved the bit is set to 0 and the peripheral wakes -// up. -func (p *Device) SetSleep(sleep bool) (err error) { +// When false is recieved the bit is set to 0 and the peripheral wakes up. +func (p *Device) Sleep(sleepEnabled bool) (err error) { // setSleepBit var pwrMgt [1]byte - if err = p.read(_PWR_MGMT_1, pwrMgt[:]); err != nil { return err } - if sleep { + if sleepEnabled { pwrMgt[0] = (pwrMgt[0] & (^_SLEEP_MASK)) | (1 << _SLEEP_SHIFT) // Overwrite only Sleep } else { pwrMgt[0] = (pwrMgt[0] & (^_SLEEP_MASK)) } - //Envio el registro modificado al periferico if err = p.write8(_PWR_MGMT_1, pwrMgt[0]); err != nil { return err } From d675bd4cbc1225c7c233ca458ee39491628fd629 Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 20 May 2023 15:40:49 -0300 Subject: [PATCH 6/7] add writeMasked; add range types; unexport config methods; less allocs; less spanish --- examples/mpu6050/main.go | 6 +- mpu6050/mpu6050.go | 187 +++++++++++++++++++-------------------- mpu6050/registers.go | 167 +++++++++++++++++++++++++++++----- 3 files changed, 239 insertions(+), 121 deletions(-) diff --git a/examples/mpu6050/main.go b/examples/mpu6050/main.go index 7e76ef40c..b64fe3be6 100644 --- a/examples/mpu6050/main.go +++ b/examples/mpu6050/main.go @@ -14,8 +14,8 @@ func main() { mpuDevice := mpu6050.New(machine.I2C0, mpu6050.DefaultAddress) err := mpuDevice.Configure(mpu6050.Config{ - AccRange: mpu6050.ACCEL_RANGE_16, - GyroRange: mpu6050.GYRO_RANGE_2000, + AccelRange: mpu6050.ACCEL_RANGE_16, + GyroRange: mpu6050.GYRO_RANGE_2000, }) if err != nil { panic(err.Error()) @@ -31,7 +31,7 @@ func main() { println(mpuDevice.Acceleration()) print("angular velocity:") println(mpuDevice.AngularVelocity()) - print("temperature centigrade:") + print("temperature celsius:") println(mpuDevice.Temperature() / 1000) } } diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go index cb8f7deca..b7f9d3698 100644 --- a/mpu6050/mpu6050.go +++ b/mpu6050/mpu6050.go @@ -1,4 +1,10 @@ -package mpu6050 +// Package mpu6050 provides a driver for the MPU6050 accelerometer and gyroscope +// made by InvenSense. +// +// Datasheets: +// https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf +// https://www.invensense.com/wp-content/uploads/2015/02/MPU-6000-Register-Map1.pdf +package mpu6050 // import "tinygo.org/x/drivers/mpu6050" import ( "encoding/binary" @@ -9,11 +15,24 @@ import ( const DefaultAddress = 0x68 +// RangeAccel defines the range of the accelerometer. +// Allowed values are 2, 4, 8 and 16 with the unit g (gravity). +type RangeAccel uint8 + +// RangeGyro defines the range of the gyroscope. +// Allowed values are 250, 500, 1000 and 2000 with the unit °/s (degree per second). +type RangeGyro uint8 + +var ( + errInvalidRangeAccel = errors.New("mpu6050: invalid range for accelerometer") + errInvalidRangeGyro = errors.New("mpu6050: invalid range for gyroscope") +) + type Config struct { // Use ACCEL_RANGE_2 through ACCEL_RANGE_16. - AccRange byte + AccelRange RangeAccel // Use GYRO_RANGE_250 through GYRO_RANGE_2000 - GyroRange byte + GyroRange RangeGyro sampleRatio byte // TODO(soypat): expose these as configurable. clkSel byte } @@ -23,9 +42,12 @@ type Device struct { conn drivers.I2C aRange int32 //Gyroscope FSR acording to SetAccelRange input gRange int32 //Gyroscope FSR acording to SetGyroRange input - // RawData contains the accelerometer, gyroscope and temperature RawData read - // in the last call via the Update method. - RawData [14]byte + // data contains the accelerometer, gyroscope and temperature data read + // in the last call via the Update method. The data is stored as seven 16bit unsigned + // integers in big endian format: + // + // | ax | ay | az | temp | gx | gy | gz | + data [14]byte address byte } @@ -44,67 +66,72 @@ func New(bus drivers.I2C, addr uint16) *Device { // and wakes up the peripheral. func (p *Device) Configure(data Config) (err error) { if err = p.Sleep(false); err != nil { - return errors.New("set sleep: " + err.Error()) + return err } - if err = p.SetClockSource(data.clkSel); err != nil { - return errors.New("set clksrc: " + err.Error()) + if err = p.setClockSource(data.clkSel); err != nil { + return err } - if err = p.SetSampleRate(data.sampleRatio); err != nil { - return errors.New("set sampleratio: " + err.Error()) + if err = p.setSampleRate(data.sampleRatio); err != nil { + return err } - if err = p.SetRangeGyro(data.GyroRange); err != nil { - return errors.New("set gyrorange: " + err.Error()) + if err = p.setRangeGyro(data.GyroRange); err != nil { + return err } - if err = p.SetRangeAccel(data.AccRange); err != nil { - return errors.New("set accelrange: " + err.Error()) + if err = p.setRangeAccel(data.AccelRange); err != nil { + return err } return nil } +func (d Device) Connected() bool { + data := []byte{0} + d.read(_WHO_AM_I, data) + return data[0] == 0x68 +} + // Update fetches the latest data from the MPU6050 func (p *Device) Update() (err error) { - if err = p.read(_ACCEL_XOUT_H, p.RawData[:]); err != nil { + if err = p.read(_ACCEL_XOUT_H, p.data[:]); err != nil { return err } return nil } // Acceleration returns last read acceleration in µg (micro-gravity). -// When one of the axes is pointing straight to Earth -// and the sensor is not moving the returned value will be around 1000000 or -// -1000000. +// When one of the axes is pointing straight to Earth and the sensor is not +// moving the returned value will be around 1000000 or -1000000. func (d *Device) Acceleration() (ax, ay, az int32) { const accelOffset = 0 - ax = int32(convertWord(d.RawData[accelOffset+0:])) * 15625 / 512 * d.aRange - ay = int32(convertWord(d.RawData[accelOffset+2:])) * 15625 / 512 * d.aRange - az = int32(convertWord(d.RawData[accelOffset+4:])) * 15625 / 512 * d.aRange + ax = int32(convertWord(d.data[accelOffset+0:])) * 15625 / 512 * d.aRange + ay = int32(convertWord(d.data[accelOffset+2:])) * 15625 / 512 * d.aRange + az = int32(convertWord(d.data[accelOffset+4:])) * 15625 / 512 * d.aRange return ax, ay, az } -// Rotations reads the current rotation from the device and returns it in +// AngularVelocity reads the current angular velocity from the device and returns it in // µ°/rad (micro-radians/sec). This means that if you were to do a complete // rotation along one axis and while doing so integrate all values over time, // you would get a value close to 6.3 radians (360 degrees). func (d *Device) AngularVelocity() (gx, gy, gz int32) { const angvelOffset = 8 - _ = d.RawData[angvelOffset+5] // This line fails to compile if RawData is too short. - gx = int32(convertWord(d.RawData[angvelOffset+0:])) * 4363 / 8192 * d.gRange - gy = int32(convertWord(d.RawData[angvelOffset+2:])) * 4363 / 8192 * d.gRange - gz = int32(convertWord(d.RawData[angvelOffset+4:])) * 4363 / 8192 * d.gRange + _ = d.data[angvelOffset+5] // This line fails to compile if RawData is too short. + gx = int32(convertWord(d.data[angvelOffset+0:])) * 4363 / 8192 * d.gRange + gy = int32(convertWord(d.data[angvelOffset+2:])) * 4363 / 8192 * d.gRange + gz = int32(convertWord(d.data[angvelOffset+4:])) * 4363 / 8192 * d.gRange return gx, gy, gz } // Temperature returns the temperature of the device in milli-centigrade. func (d *Device) Temperature() (Celsius int32) { const tempOffset = 6 - return 1506*int32(convertWord(d.RawData[tempOffset:]))/512 + 37*1000 + return 1506*int32(convertWord(d.data[tempOffset:]))/512 + 37*1000 } func convertWord(buf []byte) int16 { return int16(binary.BigEndian.Uint16(buf)) } -// SetSampleRate sets the sample rate for the FIFO, +// setSampleRate sets the sample rate for the FIFO, // register ouput and DMP. The sample rate is determined // by: // @@ -114,7 +141,7 @@ func convertWord(buf []byte) int16 { // disabled and 1kHz otherwise. The maximum sample rate // for the accelerometer is 1kHz, if a higher sample rate // is chosen, the same accelerometer sample will be output. -func (p *Device) SetSampleRate(srDiv byte) (err error) { +func (p *Device) setSampleRate(srDiv byte) (err error) { // setSampleRate var sr [1]byte sr[0] = srDiv @@ -124,81 +151,48 @@ func (p *Device) SetSampleRate(srDiv byte) (err error) { return nil } -// SetClockSource configures the source of the clock +// setClockSource configures the source of the clock // for the peripheral. -func (p *Device) SetClockSource(clkSel byte) (err error) { - // setClockSource - var pwrMgt [1]byte - - if err = p.read(_PWR_MGMT_1, pwrMgt[:]); err != nil { - return err - } - pwrMgt[0] = (pwrMgt[0] & (^_CLK_SEL_MASK)) | clkSel // Escribo solo el campo de clk_sel - if err = p.write8(_PWR_MGMT_1, pwrMgt[0]); err != nil { - return err - } - return nil +func (p *Device) setClockSource(clkSel byte) (err error) { + return p.writeMasked(_PWR_MGMT_1, _CLK_SEL_MASK, clkSel) } -// SetRangeGyro configures the full scale range of the gyroscope. +// setRangeGyro configures the full scale range of the gyroscope. // It has four possible values +- 250°/s, 500°/s, 1000°/s, 2000°/s. -// The function takes values of gyroRange from 0-3 where 0 means the -// lowest FSR (250°/s) and 3 is the highest FSR (2000°/s). -func (p *Device) SetRangeGyro(gyroRange byte) (err error) { +func (p *Device) setRangeGyro(gyroRange RangeGyro) (err error) { switch gyroRange { - case GYRO_RANGE_250: + case RangeGyro250: p.gRange = 250 - case GYRO_RANGE_500: + case RangeGyro500: p.gRange = 500 - case GYRO_RANGE_1000: + case RangeGyro1000: p.gRange = 1000 - case GYRO_RANGE_2000: + case RangeGyro2000: p.gRange = 2000 default: - return errors.New("invalid gyroscope FSR input") + return errInvalidRangeGyro } - // setFullScaleGyroRange - var gConfig [1]byte - - if err = p.read(_GYRO_CONFIG, gConfig[:]); err != nil { - return err - } - gConfig[0] = (gConfig[0] & (^_G_FS_SEL)) | (gyroRange << _G_FS_SHIFT) // Escribo solo el campo de FS_sel - - if err = p.write8(_GYRO_CONFIG, gConfig[0]); err != nil { - return err - } - return nil + return p.writeMasked(_GYRO_CONFIG, _G_FS_SEL, uint8(gyroRange)<<_G_FS_SHIFT) } -// SetRangeAccel configures the full scale range of the accelerometer. +// setRangeAccel configures the full scale range of the accelerometer. // It has four possible values +- 2g, 4g, 8g, 16g. // The function takes values of accRange from 0-3 where 0 means the // lowest FSR (2g) and 3 is the highest FSR (16g) -func (p *Device) SetRangeAccel(accRange byte) (err error) { +func (p *Device) setRangeAccel(accRange RangeAccel) (err error) { switch accRange { - case ACCEL_RANGE_2: + case RangeAccel2: p.aRange = 2 - case ACCEL_RANGE_4: + case RangeAccel4: p.aRange = 4 - case ACCEL_RANGE_8: + case RangeAccel8: p.aRange = 8 - case ACCEL_RANGE_16: + case RangeAccel16: p.aRange = 16 default: - return errors.New("invalid accelerometer FSR input") + return errInvalidRangeAccel } - - var aConfig [1]byte - if err = p.read(_ACCEL_CONFIG, aConfig[:]); err != nil { - return err - } - aConfig[0] = (aConfig[0] & (^_AFS_SEL)) | (accRange << _AFS_SHIFT) - - if err = p.write8(_ACCEL_CONFIG, aConfig[0]); err != nil { - return err - } - return nil + return p.writeMasked(_ACCEL_CONFIG, _AFS_SEL, uint8(accRange)<<_AFS_SHIFT) } // Sleep sets the sleep bit on the power managment 1 field. @@ -206,26 +200,29 @@ func (p *Device) SetRangeAccel(accRange byte) (err error) { // the peripheral in sleep mode. // When false is recieved the bit is set to 0 and the peripheral wakes up. func (p *Device) Sleep(sleepEnabled bool) (err error) { - // setSleepBit - var pwrMgt [1]byte - if err = p.read(_PWR_MGMT_1, pwrMgt[:]); err != nil { + return p.writeMasked(_PWR_MGMT_1, _SLEEP_MASK, b2u8(sleepEnabled)<<_SLEEP_SHIFT) +} + +func (d *Device) writeMasked(reg byte, mask byte, value byte) error { + var b [1]byte + if err := d.read(reg, b[:]); err != nil { return err } - if sleepEnabled { - pwrMgt[0] = (pwrMgt[0] & (^_SLEEP_MASK)) | (1 << _SLEEP_SHIFT) // Overwrite only Sleep - } else { - pwrMgt[0] = (pwrMgt[0] & (^_SLEEP_MASK)) - } - if err = p.write8(_PWR_MGMT_1, pwrMgt[0]); err != nil { - return err + b[0] = (b[0] &^ mask) | value&mask + return d.write8(reg, b[0]) +} + +func b2u8(b bool) byte { + if b { + return 1 } - return nil + return 0 } func DefaultConfig() Config { return Config{ - AccRange: ACCEL_RANGE_16, - GyroRange: GYRO_RANGE_2000, + AccelRange: RangeAccel16, + GyroRange: RangeGyro2000, sampleRatio: 0, // TODO add const values. clkSel: 0, } diff --git a/mpu6050/registers.go b/mpu6050/registers.go index 0b923beeb..e06bf36b6 100644 --- a/mpu6050/registers.go +++ b/mpu6050/registers.go @@ -3,20 +3,16 @@ package mpu6050 // read reads register reg and writes n bytes to b where // n is the length of b. func (p *Device) read(reg uint8, buff []byte) error { - return p.conn.Tx(uint16(p.address), []byte{reg}, buff) + buf := [1]byte{reg} + return p.conn.Tx(uint16(p.address), buf[:1], buff) } // write8 writes a registry byte func (p *Device) write8(reg uint8, datum byte) error { - err := p.write(reg, []byte{datum}) - return err -} - -// write write a byte buffer to sequential incrementing adresses (1 byte per address) in individual -// write operations. -func (p *Device) write(addr uint8, buff []byte) error { - p.conn.Tx(uint16(p.address), append([]byte{addr}, buff...), nil) - return nil + var buff [2]byte + buff[0] = reg + buff[1] = datum + return p.conn.Tx(uint16(p.address), buff[:], nil) } // MPU 6050 REGISTER ADRESSES @@ -26,11 +22,6 @@ const ( _GYRO_CONFIG uint8 = 0x1B _ACCEL_CONFIG uint8 = 0x1C _FIFO_EN uint8 = 0x23 - _I2C_MST_CTRL uint8 = 0x24 - _PWR_MGMT_1 uint8 = 0x6B - _ACCEL_XOUT_H uint8 = 0x3B - _TEMP_OUT_H uint8 = 0x41 - _GYRO_XOUT_H uint8 = 0x43 ) // MPU 6050 MASKS @@ -50,16 +41,146 @@ const ( // Gyroscope ranges for Init configuration const ( - GYRO_RANGE_250 byte = 0x00 - GYRO_RANGE_500 byte = 0x01 - GYRO_RANGE_1000 byte = 0x02 - GYRO_RANGE_2000 byte = 0x03 + // 250°/s + RangeGyro250 RangeGyro = iota + // 500°/s + RangeGyro500 + // 1000°/s + RangeGyro1000 + // 2000°/s + RangeGyro2000 ) // Accelerometer ranges for Init configuration const ( - ACCEL_RANGE_2 byte = 0x00 - ACCEL_RANGE_4 byte = 0x01 - ACCEL_RANGE_8 byte = 0x02 - ACCEL_RANGE_16 byte = 0x03 + // 2g + RangeAccel2 RangeAccel = iota + // 4g + RangeAccel4 + // 8g + RangeAccel8 + // 16g + RangeAccel16 +) + +// Registers. Names, addresses and comments copied from the datasheet. +const ( + // Self test registers + _SELF_TEST_X = 0x0D + _SELF_TEST_Y = 0x0E + _SELF_TEST_Z = 0x0F + _SELF_TEST_A = 0x10 + + _SMPLRT_DIV = 0x19 // Sample rate divider + + // I2C pass-through configuration + _I2C_MST_CTRL = 0x24 + _I2C_SLV0_ADDR = 0x25 + _I2C_SLV0_REG = 0x26 + _I2C_SLV0_CTRL = 0x27 + _I2C_SLV1_ADDR = 0x28 + _I2C_SLV1_REG = 0x29 + _I2C_SLV1_CTRL = 0x2A + _I2C_SLV2_ADDR = 0x2B + _I2C_SLV2_REG = 0x2C + _I2C_SLV2_CTRL = 0x2D + _I2C_SLV3_ADDR = 0x2E + _I2C_SLV3_REG = 0x2F + _I2C_SLV3_CTRL = 0x30 + _I2C_SLV4_ADDR = 0x31 + _I2C_SLV4_REG = 0x32 + _I2C_SLV4_DO = 0x33 + _I2C_SLV4_CTRL = 0x34 + _I2C_SLV4_DI = 0x35 + _I2C_MST_STATUS = 0x36 + + // Interrupt configuration + _INT_PIN_CFG = 0x37 // Interrupt pin/bypass enable configuration + _INT_ENABLE = 0x38 // Interrupt enable + _INT_STATUS = 0x3A // Interrupt status + + // Accelerometer measurements + _ACCEL_XOUT_H = 0x3B + _ACCEL_XOUT_L = 0x3C + _ACCEL_YOUT_H = 0x3D + _ACCEL_YOUT_L = 0x3E + _ACCEL_ZOUT_H = 0x3F + _ACCEL_ZOUT_L = 0x40 + + // Temperature measurement + _TEMP_OUT_H = 0x41 + _TEMP_OUT_L = 0x42 + + // Gyroscope measurements + _GYRO_XOUT_H = 0x43 + _GYRO_XOUT_L = 0x44 + _GYRO_YOUT_H = 0x45 + _GYRO_YOUT_L = 0x46 + _GYRO_ZOUT_H = 0x47 + _GYRO_ZOUT_L = 0x48 + + // External sensor data + _EXT_SENS_DATA_00 = 0x49 + _EXT_SENS_DATA_01 = 0x4A + _EXT_SENS_DATA_02 = 0x4B + _EXT_SENS_DATA_03 = 0x4C + _EXT_SENS_DATA_04 = 0x4D + _EXT_SENS_DATA_05 = 0x4E + _EXT_SENS_DATA_06 = 0x4F + _EXT_SENS_DATA_07 = 0x50 + _EXT_SENS_DATA_08 = 0x51 + _EXT_SENS_DATA_09 = 0x52 + _EXT_SENS_DATA_10 = 0x53 + _EXT_SENS_DATA_11 = 0x54 + _EXT_SENS_DATA_12 = 0x55 + _EXT_SENS_DATA_13 = 0x56 + _EXT_SENS_DATA_14 = 0x57 + _EXT_SENS_DATA_15 = 0x58 + _EXT_SENS_DATA_16 = 0x59 + _EXT_SENS_DATA_17 = 0x5A + _EXT_SENS_DATA_18 = 0x5B + _EXT_SENS_DATA_19 = 0x5C + _EXT_SENS_DATA_20 = 0x5D + _EXT_SENS_DATA_21 = 0x5E + _EXT_SENS_DATA_22 = 0x5F + _EXT_SENS_DATA_23 = 0x60 + + // I2C peripheral data out + _I2C_PER0_DO = 0x63 + _I2C_PER1_DO = 0x64 + _I2C_PER2_DO = 0x65 + _I2C_PER3_DO = 0x66 + _I2C_MST_DELAY_CT = 0x67 + + // Clock settings + _CLOCK_INTERNAL = 0x00 + _CLOCK_PLL_XGYRO = 0x01 + _CLOCK_PLL_YGYRO = 0x02 + _CLOCK_PLL_ZGYRO = 0x03 + _CLOCK_PLL_EXTERNAL_32_768_KZ = 0x04 + _CLOCK_PLL_EXTERNAL_19_2_MHZ = 0x05 + _CLOCK_RESERVED = 0x06 + _CLOCK_STOP = 0x07 + + // Accelerometer settings + _AFS_RANGE_2G = 0x00 + _AFS_RANGE_4G = 0x01 + _AFS_RANGE_8G = 0x02 + _AFS_RANGE_16G = 0x03 + + // Gyroscope settings + _FS_RANGE_250 = 0x00 + _FS_RANGE_500 = 0x01 + _FS_RANGE_1000 = 0x02 + _FS_RANGE_2000 = 0x03 + + // other registers + _SIGNAL_PATH_RES = 0x68 // Signal path reset + _USER_CTRL = 0x6A // User control + _PWR_MGMT_1 = 0x6B // Power Management 1 + _PWR_MGMT_2 = 0x6C // Power Management 2 + _FIFO_COUNTH = 0x72 // FIFO count registers (high bits) + _FIFO_COUNTL = 0x73 // FIFO count registers (low bits) + _FIFO_R_W = 0x74 // FIFO read/write + _WHO_AM_I = 0x75 // Who am I register ) From 6288ba5ae99b157ca13e3f3076c052519c49d2fd Mon Sep 17 00:00:00 2001 From: soypat Date: Sat, 27 May 2023 14:44:16 -0300 Subject: [PATCH 7/7] make zero value useful for configuration --- examples/mpu6050/main.go | 6 ++---- mpu6050/mpu6050.go | 19 ++++++------------- mpu6050/registers.go | 6 ++++-- 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/examples/mpu6050/main.go b/examples/mpu6050/main.go index b64fe3be6..a80c2aa27 100644 --- a/examples/mpu6050/main.go +++ b/examples/mpu6050/main.go @@ -13,10 +13,8 @@ func main() { mpuDevice := mpu6050.New(machine.I2C0, mpu6050.DefaultAddress) - err := mpuDevice.Configure(mpu6050.Config{ - AccelRange: mpu6050.ACCEL_RANGE_16, - GyroRange: mpu6050.GYRO_RANGE_2000, - }) + // Configure the device with default configuration. + err := mpuDevice.Configure(mpu6050.Config{}) if err != nil { panic(err.Error()) } diff --git a/mpu6050/mpu6050.go b/mpu6050/mpu6050.go index b7f9d3698..9004814ad 100644 --- a/mpu6050/mpu6050.go +++ b/mpu6050/mpu6050.go @@ -167,12 +167,13 @@ func (p *Device) setRangeGyro(gyroRange RangeGyro) (err error) { p.gRange = 500 case RangeGyro1000: p.gRange = 1000 - case RangeGyro2000: + case RangeGyro2000, rangeGyroDefault: + gyroRange = RangeGyro2000 p.gRange = 2000 default: return errInvalidRangeGyro } - return p.writeMasked(_GYRO_CONFIG, _G_FS_SEL, uint8(gyroRange)<<_G_FS_SHIFT) + return p.writeMasked(_GYRO_CONFIG, _G_FS_SEL, uint8(gyroRange-1)<<_G_FS_SHIFT) } // setRangeAccel configures the full scale range of the accelerometer. @@ -187,12 +188,13 @@ func (p *Device) setRangeAccel(accRange RangeAccel) (err error) { p.aRange = 4 case RangeAccel8: p.aRange = 8 - case RangeAccel16: + case RangeAccel16, rangeAccelDefault: + accRange = RangeAccel16 p.aRange = 16 default: return errInvalidRangeAccel } - return p.writeMasked(_ACCEL_CONFIG, _AFS_SEL, uint8(accRange)<<_AFS_SHIFT) + return p.writeMasked(_ACCEL_CONFIG, _AFS_SEL, uint8(accRange-1)<<_AFS_SHIFT) } // Sleep sets the sleep bit on the power managment 1 field. @@ -218,12 +220,3 @@ func b2u8(b bool) byte { } return 0 } - -func DefaultConfig() Config { - return Config{ - AccelRange: RangeAccel16, - GyroRange: RangeGyro2000, - sampleRatio: 0, // TODO add const values. - clkSel: 0, - } -} diff --git a/mpu6050/registers.go b/mpu6050/registers.go index e06bf36b6..ca6c5e0ae 100644 --- a/mpu6050/registers.go +++ b/mpu6050/registers.go @@ -41,8 +41,9 @@ const ( // Gyroscope ranges for Init configuration const ( + rangeGyroDefault = iota // 250°/s - RangeGyro250 RangeGyro = iota + RangeGyro250 // 500°/s RangeGyro500 // 1000°/s @@ -53,8 +54,9 @@ const ( // Accelerometer ranges for Init configuration const ( + rangeAccelDefault RangeAccel = iota // 2g - RangeAccel2 RangeAccel = iota + RangeAccel2 // 4g RangeAccel4 // 8g