Added I²C support.

- Removed useless ReadRaw
- Changed the scope of ReadAndCombine to private readAndCombine
pull/67/head
bpds 2 years ago
parent 9ddc05227d
commit 5eba749b5f
No known key found for this signature in database
GPG Key ID: B666D5C1144EADD6

@ -7,6 +7,8 @@ package adxl345
import ( import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"periph.io/x/conn/v3"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/physic" "periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi" "periph.io/x/conn/v3/spi"
) )
@ -74,7 +76,7 @@ const (
// Currently tested devices // Currently tested devices
const ( const (
AdxlXXX = 0x00 // No specific expectation AdxlXXX = 0x01 // No specific expectation. For non-detected devices the response is 0x00
Adxl345 = 0xE5 // Expecting an Adxl345 Adxl345 = 0xE5 // Expecting an Adxl345
) )
@ -97,22 +99,44 @@ type Opts struct {
// Dev is a driver for the ADXL345 accelerometer // Dev is a driver for the ADXL345 accelerometer
// It uses the SPI interface to communicate with the device. // It uses the SPI interface to communicate with the device.
type Dev struct { type Dev struct {
name string c conn.Conn
s spi.Conn name string
isSPI bool
// The sensitivity of the device (2G, 4G, 8G, 16G) // The sensitivity of the device (2G, 4G, 8G, 16G)
// Set to 2G by default, can be changed in the Opts at initialization. // Set to 2G by default, can be changed in the Opts at initialization.
sensitivity Sensitivity sensitivity Sensitivity
} }
func (d *Dev) Mode() string {
if d.isSPI {
return "SPI"
} else {
return "I²C"
}
}
func (d *Dev) String() string { func (d *Dev) String() string {
return fmt.Sprintf("%s{Sensitivity:%s}", d.name, d.sensitivity) return fmt.Sprintf("%s{Sensitivity:%s, Mode:%s}", d.name, d.sensitivity, d.Mode())
} }
// NewSpi creates a new ADXL345 Dev with a spi connection or returns an error. // NewI2C returns an object that communicates over I²C to ADXL345
// The bus and chip parameters define the SPI bus and chip select to use. // accelerometer.
// The SPI s is configured. //
// The device is turned on. // The device is automatically turned on and the sensitivity is set to the Opts.Sensitivity.
// The device is verified to be an ADXL345. func NewI2C(b i2c.Bus, addr uint16, opts *Opts) (*Dev, error) {
d := &Dev{
c: &i2c.Dev{Bus: b, Addr: addr},
isSPI: false}
if err := d.makeDev(opts); err != nil {
return nil, err
}
return d, nil
}
// NewSpi returns an object that communicates over spi to ADXL345
// accelerometer.
//
// The device is automatically turned on and the sensitivity is set to the Opts.Sensitivity.
func NewSpi(p spi.Port, o *Opts) (*Dev, error) { func NewSpi(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)
@ -120,29 +144,42 @@ func NewSpi(p spi.Port, o *Opts) (*Dev, error) {
return nil, err return nil, err
} }
d := &Dev{ d := &Dev{
s: c, c: c,
isSPI: true,
} }
err = d.TurnOn() err = d.makeDev(o)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return d, nil
}
// makeDev turns on with the expected sensitivity and verifies if it is a supported device.
func (d *Dev) makeDev(o *Opts) error {
err := d.TurnOn()
if err != nil {
return err
}
if o.Sensitivity != S2G { // default if o.Sensitivity != S2G { // default
err = d.setSensitivity(o.Sensitivity) err = d.setSensitivity(o.Sensitivity)
if err != nil { if err != nil {
return nil, err return err
} }
} }
// Verify that the device Id // Verify that the device Id
rx, _ := d.ReadRaw(DeviceID) rx, err := d.Read(DeviceID)
switch rx[1] { if err != nil {
return fmt.Errorf("unable to read the deviceID \"%s\"", err.Error())
}
switch byte(rx & 0xff) {
case Adxl345: case Adxl345:
d.name = "adxl345" d.name = "adxl345"
return d, nil return nil
case o.ExpectedDeviceID: case o.ExpectedDeviceID:
d.name = fmt.Sprintf("expected%#x", o.ExpectedDeviceID) d.name = fmt.Sprintf("expected%#x", o.ExpectedDeviceID)
return d, nil return nil
default: default:
return nil, fmt.Errorf("unrecognized device expected=\"%#x\" found=\"%#x\" ", o.ExpectedDeviceID, rx[0]) return fmt.Errorf("unrecognized device expected=\"%#02x\" or \"%#02x\"found=\"%#02x\" ", o.ExpectedDeviceID, Adxl345, rx)
} }
} }
@ -175,57 +212,42 @@ 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. // The ADXL345 uses two 8-bit registers to store the output data for each axis.
// X := d.ReadAndCombine(DataX0, DataX1) where: // X := d.readAndCombine(DataX0, DataX1) where:
// `DataX0` is the address of the lower byte (LSB, least significant byte) // `DataX0` is the address of the lower byte (LSB, least significant byte)
// `DataX1` is the address of the upper byte (MSB, most significant byte) // `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.Read(reg1)
high, _ := d.Read(reg2) high, _ := d.Read(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. // Read reads a 16-bit value from the specified register address.
func (d *Dev) Read(regAddress byte) (int16, error) { func (d *Dev) Read(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.c.Tx(tx, rx)
if err != nil { if err != nil {
return 0, err return 0, err
} }
return int16(binary.LittleEndian.Uint16(rx)), nil return int16(binary.LittleEndian.Uint16(rx)), nil
} }
// ReadRaw reads a []byte value from the specified register address.
func (d *Dev) ReadRaw(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
tx := []byte{regAddress | 0x80, 0x00}
rx := make([]byte, len(tx))
err := d.s.Tx(tx, rx)
return rx, err
}
// Write writes a 1 byte value to the specified register address. // Write writes a 1 byte value to the specified register address.
func (d *Dev) Write(regAddress byte, value byte) error { func (d *Dev) Write(regAddress byte, value byte) error {
// Prepare a 2-byte buffer with the register address and the desired value. return d.c.Tx([]byte{regAddress, value}, nil)
tx := []byte{regAddress, value}
// Prepare a receiving buffer of the same size as the transmit buffer.
rx := make([]byte, len(tx))
// Perform the transfer. We expect the SPI device to write back an acknowledgement.
err := d.s.Tx(tx, rx)
return err
} }
// Acceleration represents the acceleration on the three axes X,Y,Z. // Acceleration represents the acceleration on the three axes X,Y,Z.

@ -1,56 +0,0 @@
// 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 (
"fmt"
"log"
"periph.io/x/conn/v3/spi/spireg"
"periph.io/x/devices/v3/adxl345"
"periph.io/x/host/v3"
"time"
)
// Example reads the acceleration values every 30ms for 3 seconds.
func Example() {
// Initialize the host
// Make sure periph is initialized.
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
// Use spireg SPI port registry to find the first available SPI bus.
p, err := spireg.Open("")
if err != nil {
log.Fatal(err)
}
defer p.Close()
d, err := adxl345.NewSpi(p, &adxl345.DefaultOpts)
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()
// stop after 3 seconds
stop := time.After(3 * time.Second)
for {
select {
case <-stop:
return
case <-ticker.C:
a := d.Update()
fmt.Println(a)
}
}
}

@ -0,0 +1,96 @@
// 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 (
"fmt"
"log"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/conn/v3/spi/spireg"
"periph.io/x/devices/v3/adxl345"
"periph.io/x/host/v3"
"time"
)
// I2C uses an adxl345 device connected by I²C.
// it reads the acceleration values every 30ms for 30 seconds.
// You can i use `i2dctools` to find the I²C bus number
// e.g : sudo apt-get install i2c-tools
//
// sudo i2cdetect -y 1
func I2C(addr uint16) {
mustInitHost()
// Use i2creg to find the first available I²C bus.
// Generally I2C1 on raspberry pi.
p, err := i2creg.Open("")
if err != nil {
log.Fatal(err)
}
fmt.Print(p.String())
defer p.Close()
d, err := adxl345.NewI2C(p, addr, &adxl345.DefaultOpts)
if err != nil {
panic(err)
}
measure(d, 30*time.Second)
}
// Spi uses an adxl345 device connected by SPI.
// it reads the acceleration values every 30ms for 30 seconds.
func Spi() {
mustInitHost()
// Use spireg SPI port registry to find the first available SPI bus.
p, err := spireg.Open("")
if err != nil {
log.Fatal(err)
}
defer p.Close()
d, err := adxl345.NewSpi(p, &adxl345.DefaultOpts)
if err != nil {
panic(err)
}
measure(d, 30*time.Second)
}
// mustInitHost Make sure host is initialized.
func mustInitHost() {
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
}
// measure reads the acceleration values every 30ms for <duration> seconds
func measure(d *adxl345.Dev, duration time.Duration) {
fmt.Println(d.String())
mode := d.Mode()
// use a ticker to read the acceleration values every 200ms
ticker := time.NewTicker(30 * time.Millisecond)
defer ticker.Stop()
// stop after 3 seconds
stop := time.After(duration)
for {
select {
case <-stop:
return
case <-ticker.C:
a := d.Update()
fmt.Println(mode, a)
}
}
}
Loading…
Cancel
Save