adxl345: Driver, initial commit for review.

pull/67/head
bpds 2 years ago
parent 931687b33a
commit 8778b8a954
No known key found for this signature in database
GPG Key ID: B666D5C1144EADD6

@ -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 package adxl345
import ( import (
@ -11,14 +7,11 @@ import (
"periph.io/x/conn/v3/spi" "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 ( const (
DeviceID = 0x00 // Device ID, expected to be 0xE5 when using ADXL345 DeviceID = 0x00 // Device ID, expected to be 0xE5 when using ADXL345
// 0x01 to 0x1C are reserved for future use
ThreshTap = 0x1D // Tap threshold ThreshTap = 0x1D // Tap threshold
OfsX = 0x1E // X-axis offset OfsX = 0x1E // X-axis offset
OfsY = 0x1F // Y-axis offset OfsY = 0x1F // Y-axis offset
@ -59,31 +52,21 @@ const (
) )
// Sensitivity constants represents the sensitivity of the ADXL345. // DefaultSpiFrequency is the default SPI frequency used to communicate with the device.
// 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
)
var ( 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. SpiMode = spi.Mode3 // Defines the base clock signal, along with the polarity and phase of the data signal.
SpiBits = 8 SpiBits = 8
) )
var DefaultOpts = Opts{ var DefaultOpts = Opts{
ExpectedDeviceID: 0xE5, TurnOnOnStart: true,
Sensitivity: S2G, ExpectedDevid: 0xE5,
} }
type Opts struct { type Opts struct {
ExpectedDeviceID byte // Expected device ID used to verify that the device is an ADXL345. TurnOnOnStart bool // Turn on the device in measurement mode on start.
Sensitivity Sensitivity // Sensitivity of the device (2G, 4G, 8G, 16G) ExpectedDevid byte // Expected device ID used to verify that the device is an ADXL345.
} }
// Dev is a driver for the ADXL345 accelerometer // Dev is a driver for the ADXL345 accelerometer
@ -91,21 +74,18 @@ type Opts struct {
type Dev struct { type Dev struct {
name string name string
s spi.Conn 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 { 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 bus and chip parameters define the SPI bus and chip select to use.
// The SPI s is configured. // The SPI s is configured.
// The device is turned on. // The device is turned on.
// The device is verified to be an ADXL345. // 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. // Convert the spi.Port into a spi.Conn so it can be used for communication.
c, err := p.Connect(SpiFrequency, SpiMode, SpiBits) c, err := p.Connect(SpiFrequency, SpiMode, SpiBits)
if err != nil { if err != nil {
@ -115,46 +95,29 @@ func NewSpi(p spi.Port, o *Opts) (*Dev, error) {
name: "ADXL345", name: "ADXL345",
s: c, s: c,
} }
if o.TurnOnOnStart {
err = d.TurnOn() err = d.TurnOn()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if o.Sensitivity != S2G { // default
err = d.setSensitivity(o.Sensitivity)
if err != nil {
return nil, err
}
} }
// Verify that the device is an ADXL345. // Verify that the device is an ADXL345.
rx, _ := d.ReadRaw(DeviceID) rx, _ := d.RawReadRegister(DeviceID)
if rx[1] != o.ExpectedDeviceID { if rx[1] != o.ExpectedDevid {
return nil, fmt.Errorf("wrong device connected should be an adxl345 should be\"%#x\" rx0=\"%#x\" rx1=\"%#x\"", o.ExpectedDeviceID, rx[0], rx[1]) 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 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. // TurnOn turns on the measurement mode of the ADXL345.
// This is required before reading data from the device. // This is required before reading data from the device.
func (d *Dev) TurnOn() error { func (d *Dev) TurnOn() error {
return d.Write(PowerCtl, 0x08) return d.WriteRegister(PowerCtl, 0x08)
} }
// TurnOff turns off the measurement mode of the ADXL345. // TurnOff turns off the measurement mode of the ADXL345.
func (d *Dev) TurnOff() error { func (d *Dev) TurnOff() error {
return d.Write(PowerCtl, 0x00) return d.WriteRegister(PowerCtl, 0x00)
} }
// Update reads the acceleration values from the ADXL345. // Update reads the acceleration values from the ADXL345.
@ -162,39 +125,38 @@ func (d *Dev) TurnOff() error {
// This is a simple synchronous implementation. // This is a simple synchronous implementation.
func (d *Dev) Update() Acceleration { func (d *Dev) Update() Acceleration {
return Acceleration{ return Acceleration{
X: d.ReadAndCombine(DataX0, DataX1), X: d.readAndCombine(DataX0, DataX1),
Y: d.ReadAndCombine(DataY0, DataY1), Y: d.readAndCombine(DataY0, DataY1),
Z: d.ReadAndCombine(DataZ0, DataZ1), Z: d.readAndCombine(DataZ0, DataZ1),
} }
} }
// ReadAndCombine combines two registers to form a 16-bit value. // 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. // Example:
// X := d.ReadAndCombine(DataX0, DataX1) where: // `DATAX0` is the address of the lower byte (LSB, least significant byte) of the X-axis data
// `DataX0` is the address of the lower byte (LSB, least significant byte) // `DATAX1` is the address of the upper byte (MSB, most significant byte) of the X-axis data
// `DataX1` is the address of the upper byte (MSB, most significant byte)
// The ADXL345 combines both registers to deliver 16-bit output for each acceleration axis. // 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. // 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 { func (d *Dev) readAndCombine(reg1, reg2 byte) int16 {
low, _ := d.Read(reg1) low, _ := d.ReadRegister(reg1)
high, _ := d.Read(reg2) high, _ := d.ReadRegister(reg2)
return int16(uint16(high)<<8) | int16(low) return int16(uint16(high)<<8) | int16(low)
} }
// Read reads a 16-bit value from the specified register address. // ReadRegister reads a 16-bit value from the specified register address.
func (d *Dev) Read(regAddress byte) (int16, error) { 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} tx := []byte{regAddress | 0x80, 0x00}
rx := make([]byte, len(tx)) rx := make([]byte, len(tx))
err := d.s.Tx(tx, rx) err := d.s.Tx(tx, rx)
if err != nil { r := int16(binary.LittleEndian.Uint16(rx))
return 0, err return r, err
}
return int16(binary.LittleEndian.Uint16(rx)), nil
} }
// ReadRaw reads a []byte value from the specified register address. // RawReadRegister reads a []byte value from the specified register address.
func (d *Dev) ReadRaw(regAddress byte) ([]byte, error) { func (d *Dev) RawReadRegister(regAddress byte) ([]byte, error) {
// Send a two-byte sequence: // Send a two-byte sequence:
// - The first byte contains the address with bit 7 set high to indicate read op // - 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 // - 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 return rx, err
} }
// Write writes a 1 byte value to the specified register address. // WriteRegister writes a 1 byte value to the specified register address.
func (d *Dev) Write(regAddress byte, value byte) error { func (d *Dev) WriteRegister(regAddress byte, value byte) error {
// Prepare a 2-byte buffer with the register address and the desired value. // Prepare a 2-byte buffer with the register address and the desired value.
tx := []byte{regAddress, value} tx := []byte{regAddress, value}
// Prepare a receiving buffer of the same size as the transmit buffer. // 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 return err
} }
// Acceleration represents the acceleration on the three axes X,Y,Z. // Acceleration represents the acceleration on the three axes
// 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`.
type Acceleration struct { type Acceleration struct {
X int16 X int16
Y int16 Y int16
Z int16 Z int16
} }
// String returns a string representation of the Acceleration // sum returns the sum of the accelerations on the three axes
func (a Acceleration) String() string { func (a Acceleration) sum() int64 {
return fmt.Sprintf("X:%d Y:%d Z:%d", a.X, a.Y, a.Z) return int64(a.X) + int64(a.Y) + int64(a.Z)
} }
// Sensitivity returns the sensitivity of the device as a human-readable string. // diff returns the absolute difference between the two Acceleration
func (s Sensitivity) String() string { func (a Acceleration) diff(b Acceleration) Acceleration {
switch s { diff := Acceleration{
case S2G: X: a.X - b.X,
return "+/-2g" Y: a.Y - b.Y,
case S4G: Z: a.Z - b.Z,
return "+/-4g" }
case S8G: if diff.X < 0 {
return "+/-8g" diff.X = -diff.X
case S16G: }
return "+/-16g" if diff.Y < 0 {
default: diff.Y = -diff.Y
return "unsupported" }
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)
} }

@ -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 // Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file. // that can be found in the LICENSE file.

@ -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 package example
import ( import (
@ -30,13 +26,12 @@ func Example() {
defer p.Close() defer p.Close()
d, err := adxl345.NewSpi(p, &adxl345.DefaultOpts) o := adxl345.DefaultOpts
d, err := adxl345.New(p, &o)
if err != nil { if err != nil {
panic(err) panic(err)
} }
fmt.Println(d.String())
// use a ticker to read the acceleration values every 200ms // use a ticker to read the acceleration values every 200ms
ticker := time.NewTicker(30 * time.Millisecond) ticker := time.NewTicker(30 * time.Millisecond)
defer ticker.Stop() defer ticker.Stop()

Loading…
Cancel
Save