@ -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 ")
}
// New Spi 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 New Spi ( 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 . R eadRaw ( DeviceID )
if rx [ 1 ] != o . ExpectedDevi ceID {
return nil , fmt . Errorf ( "wrong device connected should be an adxl345 should be\"%#x\" rx0=\"%#x\" rx1=\"%#x\"" , o . ExpectedDevi ceID , rx [ 0 ] , rx [ 1 ] )
rx , _ := d . R awReadRegister ( DeviceID )
if rx [ 1 ] != o . ExpectedDevi d {
return nil , fmt . Errorf ( "wrong device connected should be an adxl345 should be\"%#x\" rx0=\"%#x\" rx1=\"%#x\"" , o . ExpectedDevi d , 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 . R eadAndCombine( DataX0 , DataX1 ) ,
Y : d . R eadAndCombine( DataY0 , DataY1 ) ,
Z : d . R eadAndCombine( DataZ0 , DataZ1 ) ,
X : d . r eadAndCombine( DataX0 , DataX1 ) ,
Y : d . r eadAndCombine( DataY0 , DataY1 ) ,
Z : d . r eadAndCombine( 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
}
// R eadRaw reads a []byte value from the specified register address.
func ( d * Dev ) R eadRaw ( regAddress byte ) ( [ ] byte , error ) {
// R awReadRegister reads a []byte value from the specified register address.
func ( d * Dev ) R awReadRegister ( 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 {
// Write Register writes a 1 byte value to the specified register address.
func ( d * Dev ) Write Register ( 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 )
}