diff --git a/adxl345/adxl345.go b/adxl345/adxl345.go index 54bf23b..3d5c129 100644 --- a/adxl345/adxl345.go +++ b/adxl345/adxl345.go @@ -1,7 +1,3 @@ -// Copyright 2023 The Periph Authors. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - package adxl345 import ( @@ -11,14 +7,11 @@ import ( "periph.io/x/conn/v3/spi" ) -type Sensitivity byte - -// The following constants are register used by the ADXL345 -// Check the table 19 of the datasheet for more details. -// https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf const ( DeviceID = 0x00 // Device ID, expected to be 0xE5 when using ADXL345 + // 0x01 to 0x1C are reserved for future use + ThreshTap = 0x1D // Tap threshold OfsX = 0x1E // X-axis offset OfsY = 0x1F // Y-axis offset @@ -59,31 +52,21 @@ const ( ) -// Sensitivity constants represents the sensitivity of the ADXL345. -// The ADXL345 supports 4 sensitivity settings, ±2g, ±4g, ±8g, and ±16g, with the default being ±2g. -// Sensitivity is an option that can be set at initialization in Opts. -// You can set the sensitivity of the ADXL345 with the DataFormat register. -const ( - S2G Sensitivity = 0x00 // Sensitivity at 2g - S4G Sensitivity = 0x01 // Sensitivity at 4g - S8G Sensitivity = 0x02 // Sensitivity at 8g - S16G Sensitivity = 0x03 // Sensitivity at 16g -) - +// DefaultSpiFrequency is the default SPI frequency used to communicate with the device. var ( - SpiFrequency = physic.MegaHertz * 2 + SpiFrequency = physic.KiloHertz * 50 SpiMode = spi.Mode3 // Defines the base clock signal, along with the polarity and phase of the data signal. SpiBits = 8 ) var DefaultOpts = Opts{ - ExpectedDeviceID: 0xE5, - Sensitivity: S2G, + TurnOnOnStart: true, + ExpectedDevid: 0xE5, } type Opts struct { - ExpectedDeviceID byte // Expected device ID used to verify that the device is an ADXL345. - Sensitivity Sensitivity // Sensitivity of the device (2G, 4G, 8G, 16G) + TurnOnOnStart bool // Turn on the device in measurement mode on start. + ExpectedDevid byte // Expected device ID used to verify that the device is an ADXL345. } // Dev is a driver for the ADXL345 accelerometer @@ -91,21 +74,18 @@ type Opts struct { type Dev struct { name string s spi.Conn - // The sensitivity of the device (2G, 4G, 8G, 16G) - // Set to 2G by default, can be changed in the Opts at initialization. - sensitivity Sensitivity } func (d *Dev) String() string { - return fmt.Sprintf("ADXL345{Sensitivity:%s}", d.sensitivity) + return fmt.Sprintf("ADXL345") } -// NewSpi creates a new ADXL345 Dev with a spi connection or returns an error. +// New creates a new ADXL345 Dev or returns an error. // The bus and chip parameters define the SPI bus and chip select to use. // The SPI s is configured. // The device is turned on. // The device is verified to be an ADXL345. -func NewSpi(p spi.Port, o *Opts) (*Dev, error) { +func New(p spi.Port, o *Opts) (*Dev, error) { // Convert the spi.Port into a spi.Conn so it can be used for communication. c, err := p.Connect(SpiFrequency, SpiMode, SpiBits) if err != nil { @@ -115,46 +95,29 @@ func NewSpi(p spi.Port, o *Opts) (*Dev, error) { name: "ADXL345", s: c, } - err = d.TurnOn() - if err != nil { - return nil, err - } - if o.Sensitivity != S2G { // default - err = d.setSensitivity(o.Sensitivity) + if o.TurnOnOnStart { + err = d.TurnOn() if err != nil { return nil, err } } // Verify that the device is an ADXL345. - rx, _ := d.ReadRaw(DeviceID) - if rx[1] != o.ExpectedDeviceID { - return nil, fmt.Errorf("wrong device connected should be an adxl345 should be\"%#x\" rx0=\"%#x\" rx1=\"%#x\"", o.ExpectedDeviceID, rx[0], rx[1]) + rx, _ := d.RawReadRegister(DeviceID) + if rx[1] != o.ExpectedDevid { + return nil, fmt.Errorf("wrong device connected should be an adxl345 should be\"%#x\" rx0=\"%#x\" rx1=\"%#x\"", o.ExpectedDevid, rx[0], rx[1]) } return d, nil } -// SetSensitivity sets the sensitivity of the ADXL345. -// The sensitivity parameter should be one of 2, 4, 8, or 16, representing ±2g, ±4g, ±8g, or ±16g respectively. -func (d *Dev) setSensitivity(sensitivity Sensitivity) error { - switch sensitivity { - case S2G, S4G, S8G, S16G: - // Write to the DataFormat register - d.sensitivity = sensitivity - return d.Write(DataFormat, byte(sensitivity)) - default: - return fmt.Errorf("invalid sensitivity: %d. Valid values are 2, 4, 8, 16", sensitivity) - } -} - // TurnOn turns on the measurement mode of the ADXL345. // This is required before reading data from the device. func (d *Dev) TurnOn() error { - return d.Write(PowerCtl, 0x08) + return d.WriteRegister(PowerCtl, 0x08) } // TurnOff turns off the measurement mode of the ADXL345. func (d *Dev) TurnOff() error { - return d.Write(PowerCtl, 0x00) + return d.WriteRegister(PowerCtl, 0x00) } // Update reads the acceleration values from the ADXL345. @@ -162,39 +125,38 @@ func (d *Dev) TurnOff() error { // This is a simple synchronous implementation. func (d *Dev) Update() Acceleration { return Acceleration{ - X: d.ReadAndCombine(DataX0, DataX1), - Y: d.ReadAndCombine(DataY0, DataY1), - Z: d.ReadAndCombine(DataZ0, DataZ1), + X: d.readAndCombine(DataX0, DataX1), + Y: d.readAndCombine(DataY0, DataY1), + Z: d.readAndCombine(DataZ0, DataZ1), } } -// ReadAndCombine combines two registers to form a 16-bit value. -// The ADXL345 uses two 8-bit registers to store the output data for each axis. -// X := d.ReadAndCombine(DataX0, DataX1) where: -// `DataX0` is the address of the lower byte (LSB, least significant byte) -// `DataX1` is the address of the upper byte (MSB, most significant byte) +// readAndCombine combines two registers to form a 16-bit value. +// Example: +// `DATAX0` is the address of the lower byte (LSB, least significant byte) of the X-axis data +// `DATAX1` is the address of the upper byte (MSB, most significant byte) of the X-axis data // The ADXL345 combines both registers to deliver 16-bit output for each acceleration axis. // A similar approach is used for the Y and Z axes. This technique provides higher precision in the measurements. -func (d *Dev) ReadAndCombine(reg1, reg2 byte) int16 { - low, _ := d.Read(reg1) - high, _ := d.Read(reg2) - +func (d *Dev) readAndCombine(reg1, reg2 byte) int16 { + low, _ := d.ReadRegister(reg1) + high, _ := d.ReadRegister(reg2) return int16(uint16(high)<<8) | int16(low) } -// Read reads a 16-bit value from the specified register address. -func (d *Dev) Read(regAddress byte) (int16, error) { +// ReadRegister reads a 16-bit value from the specified register address. +func (d *Dev) ReadRegister(regAddress byte) (int16, error) { + // Send a two-byte sequence: + // - The first byte contains the address with bit 7 set high to indicate read op + // - The second byte is a "don't care" value, usually zero tx := []byte{regAddress | 0x80, 0x00} rx := make([]byte, len(tx)) err := d.s.Tx(tx, rx) - if err != nil { - return 0, err - } - return int16(binary.LittleEndian.Uint16(rx)), nil + r := int16(binary.LittleEndian.Uint16(rx)) + return r, err } -// ReadRaw reads a []byte value from the specified register address. -func (d *Dev) ReadRaw(regAddress byte) ([]byte, error) { +// RawReadRegister reads a []byte value from the specified register address. +func (d *Dev) RawReadRegister(regAddress byte) ([]byte, error) { // Send a two-byte sequence: // - The first byte contains the address with bit 7 set high to indicate read op // - The second byte is a "don't care" value, usually zero @@ -204,8 +166,8 @@ func (d *Dev) ReadRaw(regAddress byte) ([]byte, error) { return rx, err } -// Write writes a 1 byte value to the specified register address. -func (d *Dev) Write(regAddress byte, value byte) error { +// WriteRegister writes a 1 byte value to the specified register address. +func (d *Dev) WriteRegister(regAddress byte, value byte) error { // Prepare a 2-byte buffer with the register address and the desired value. tx := []byte{regAddress, value} // Prepare a receiving buffer of the same size as the transmit buffer. @@ -215,36 +177,43 @@ func (d *Dev) Write(regAddress byte, value byte) error { return err } -// Acceleration represents the acceleration on the three axes X,Y,Z. -// The sensitivity can be set to different levels: ±2g, ±4g, ±8g, or ±16g. (S2G, S4G, S8G, S16G) -// The output are 16-bit integers, so the device measures between -32768 and +32767 for each axis. -// For example, if the sensitivity is set to ±2g and you're getting a reading of 16384 on the X axis, that would correspond to 1g of acceleration along the X axis. -// To convert the raw values to a physical unit (like g or m/s²), you would need to know the sensitivity setting of your device. -// For instance, if your sensitivity is set to ±2g, the conversion factor would be 2 / 32768 = 0.000061g per count. -// So, you would multiply the raw acceleration values by this factor to get the acceleration in `g`. +// Acceleration represents the acceleration on the three axes type Acceleration struct { X int16 Y int16 Z int16 } -// String returns a string representation of the Acceleration -func (a Acceleration) String() string { - return fmt.Sprintf("X:%d Y:%d Z:%d", a.X, a.Y, a.Z) +// sum returns the sum of the accelerations on the three axes +func (a Acceleration) sum() int64 { + return int64(a.X) + int64(a.Y) + int64(a.Z) } -// Sensitivity returns the sensitivity of the device as a human-readable string. -func (s Sensitivity) String() string { - switch s { - case S2G: - return "+/-2g" - case S4G: - return "+/-4g" - case S8G: - return "+/-8g" - case S16G: - return "+/-16g" - default: - return "unsupported" +// diff returns the absolute difference between the two Acceleration +func (a Acceleration) diff(b Acceleration) Acceleration { + diff := Acceleration{ + X: a.X - b.X, + Y: a.Y - b.Y, + Z: a.Z - b.Z, + } + if diff.X < 0 { + diff.X = -diff.X + } + if diff.Y < 0 { + diff.Y = -diff.Y + } + if diff.Z < 0 { + diff.Z = -diff.Z } + return diff +} + +// isZero returns true if the Acceleration is zero +func (a Acceleration) isZero() bool { + return a.X == 0 && a.Y == 0 && a.Z == 0 +} + +// String returns a string representation of the Acceleration +func (a Acceleration) String() string { + return fmt.Sprintf("X:%d Y:%d Z:%d", a.X, a.Y, a.Z) } diff --git a/adxl345/doc.go b/adxl345/doc.go index 2fa3231..9c2e32e 100644 --- a/adxl345/doc.go +++ b/adxl345/doc.go @@ -1,4 +1,4 @@ -// Copyright 2032 The Periph Authors. All rights reserved. +// Copyright 2018 The Periph Authors. All rights reserved. // Use of this source code is governed under the Apache License, Version 2.0 // that can be found in the LICENSE file. diff --git a/adxl345/example/example.go b/adxl345/example/example.go index 595a676..b87341a 100644 --- a/adxl345/example/example.go +++ b/adxl345/example/example.go @@ -1,7 +1,3 @@ -// Copyright 2023 The Periph Authors. All rights reserved. -// Use of this source code is governed under the Apache License, Version 2.0 -// that can be found in the LICENSE file. - package example import ( @@ -30,13 +26,12 @@ func Example() { defer p.Close() - d, err := adxl345.NewSpi(p, &adxl345.DefaultOpts) + o := adxl345.DefaultOpts + d, err := adxl345.New(p, &o) if err != nil { panic(err) } - fmt.Println(d.String()) - // use a ticker to read the acceleration values every 200ms ticker := time.NewTicker(30 * time.Millisecond) defer ticker.Stop()