mpu9250: Add i2c interface support (#101)

* Refactor SPI transport to be dual transport. 
* Implement I2C support.
pull/102/head v3.7.4
gsexton 1 year ago committed by GitHub
parent 38cb3394bc
commit bc9678aa18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -2,7 +2,12 @@
// 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.
// Package mpu9250 MPU-9250 is a 9-axis MotionTracking device that combines a 3-axis gyroscope, 3-axis accelerometer, 3-axis magnetometer and a Digital Motion Processor™ (DMP) // Package mpu9250 MPU-9250 is a 9-axis MotionTracking device that combines a
// 3-axis gyroscope, 3-axis accelerometer, 3-axis magnetometer and a Digital
// Motion Processor™ (DMP). Connections via SPI or I2C are supported.
//
// There is a sample program in periph.io/x/cmd/mpu9250 that you can run to
// test using the sensor.
// //
// # Datasheet // # Datasheet
// //
@ -24,16 +29,6 @@ const (
accelsenSitivity = 16384 accelsenSitivity = 16384
) )
// Proto defines the low-level methods used by different transports.
type Proto interface {
writeMaskedReg(address byte, mask byte, value byte) error
readMaskedReg(address byte, mask byte) (byte, error)
readByte(address byte) (byte, error)
writeByte(address byte, value byte) error
readUint16(address ...byte) (uint16, error)
writeMagReg(address byte, value byte) error
}
// AccelerometerData the values for x/y/z axises. // AccelerometerData the values for x/y/z axises.
type AccelerometerData struct { type AccelerometerData struct {
X, Y, Z int16 X, Y, Z int16
@ -62,14 +57,14 @@ type SelfTestResult struct {
// MPU9250 defines the structure to keep reference to the transport. // MPU9250 defines the structure to keep reference to the transport.
type MPU9250 struct { type MPU9250 struct {
transport Proto transport Transport
debug func(string, ...interface{}) debug func(string, ...interface{})
} }
// New creates the new instance of the driver. // New creates the new instance of the driver.
// //
// transport the transport interface. // transport the transport interface.
func New(transport Proto) (*MPU9250, error) { func New(transport Transport) (*MPU9250, error) {
return &MPU9250{transport: transport, debug: noop}, nil return &MPU9250{transport: transport, debug: noop}, nil
} }

@ -1,123 +0,0 @@
// Copyright 2020 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 mpu9250
import (
"fmt"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
"periph.io/x/conn/v3/spi/spireg"
)
// DebugF the debug function type.
type DebugF func(string, ...interface{})
// SpiTransport Encapsulates the SPI transport parameters.
type SpiTransport struct {
device spi.Conn
cs gpio.PinOut
debug DebugF
}
// NewSpiTransport Creates the SPI transport using the provided device path and chip select pin reference.
func NewSpiTransport(path string, cs gpio.PinOut) (*SpiTransport, error) {
dev, err := spireg.Open(path)
if err != nil {
return nil, wrapf("can't open SPI %v", err)
}
conn, err := dev.Connect(1*physic.MegaHertz, spi.Mode0, 8)
if err != nil {
return nil, wrapf("can't initialize SPI %v", err)
}
return &SpiTransport{device: conn, cs: cs, debug: noop}, nil
}
// EnableDebug Sets the debugging output using the local print function.
func (s *SpiTransport) EnableDebug(f DebugF) {
s.debug = f
}
func (s *SpiTransport) writeByte(address byte, value byte) error {
s.debug("write register %x value %x", address, value)
var (
buf = [...]byte{address, value}
res [2]byte
)
if err := s.cs.Out(gpio.Low); err != nil {
return err
}
if err := s.device.Tx(buf[:], res[:]); err != nil {
return err
}
return s.cs.Out(gpio.High)
}
func (s *SpiTransport) writeMagReg(address byte, value byte) error {
return s.writeByte(address, value)
}
func (s *SpiTransport) writeMaskedReg(address byte, mask byte, value byte) error {
s.debug("write masked %x, mask %x, value %x", address, mask, value)
maskedValue := mask & value
s.debug("masked value %x", maskedValue)
regVal, err := s.readByte(address)
if err != nil {
return err
}
s.debug("current register %x", regVal)
regVal = (regVal &^ maskedValue) | maskedValue
s.debug("new value %x", regVal)
return s.writeByte(address, regVal)
}
func (s *SpiTransport) readMaskedReg(address byte, mask byte) (byte, error) {
s.debug("read masked %x, mask %x", address, mask)
reg, err := s.readByte(address)
if err != nil {
return 0, err
}
s.debug("masked value %x", reg)
return reg & mask, nil
}
func (s *SpiTransport) readByte(address byte) (byte, error) {
s.debug("read register %x", address)
var (
buf = [...]byte{0x80 | address, 0}
res [2]byte
)
if err := s.cs.Out(gpio.Low); err != nil {
return 0, err
}
if err := s.device.Tx(buf[:], res[:]); err != nil {
return 0, err
}
s.debug("register content %x:%x", res[0], res[1])
if err := s.cs.Out(gpio.High); err != nil {
return 0, err
}
return res[1], nil
}
func (s *SpiTransport) readUint16(address ...byte) (uint16, error) {
if len(address) != 2 {
return 0, fmt.Errorf("only 2 bytes per read")
}
h, err := s.readByte(address[0])
if err != nil {
return 0, err
}
l, err := s.readByte(address[1])
if err != nil {
return 0, err
}
return uint16(h)<<8 | uint16(l), nil
}
func noop(string, ...interface{}) {}
var _ Proto = &SpiTransport{}

@ -0,0 +1,148 @@
// Copyright 2020 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 mpu9250
import (
"fmt"
"periph.io/x/conn/v3/gpio"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/physic"
"periph.io/x/conn/v3/spi"
"periph.io/x/conn/v3/spi/spireg"
)
// DebugF the debug function type.
type DebugF func(string, ...interface{})
// Transport Encapsulates the SPI transport parameters.
type Transport struct {
device spi.Conn
d *i2c.Dev
cs gpio.PinOut
debug DebugF
}
// NewSpiTransport Creates the SPI transport using the provided device path and chip select pin reference.
func NewSpiTransport(path string, cs gpio.PinOut) (*Transport, error) {
dev, err := spireg.Open(path)
if err != nil {
return nil, wrapf("can't open SPI %v", err)
}
conn, err := dev.Connect(1*physic.MegaHertz, spi.Mode0, 8)
if err != nil {
return nil, wrapf("can't initialize SPI %v", err)
}
return &Transport{device: conn, cs: cs, debug: noop}, nil
}
func NewI2cTransport(bus i2c.Bus, address uint16) (*Transport, error) {
return &Transport{d: &i2c.Dev{Bus: bus, Addr: address}, debug: noop}, nil
}
// EnableDebug Sets the debugging output using the local print function.
func (t *Transport) EnableDebug(f DebugF) {
t.debug = f
}
func (t *Transport) writeByte(address byte, value byte) error {
if t.d == nil {
return t.writeByteSPI(address, value)
}
return t.writeByteI2C(address, value)
}
func (t *Transport) writeByteSPI(address, value byte) error {
t.debug("write register %x value %x", address, value)
var (
buf = [...]byte{address, value}
res [2]byte
)
if err := t.cs.Out(gpio.Low); err != nil {
return err
}
if err := t.device.Tx(buf[:], res[:]); err != nil {
return err
}
return t.cs.Out(gpio.High)
}
func (t *Transport) writeByteI2C(address, value byte) error {
w := []byte{address, value}
return t.d.Tx(w, nil)
}
func (t *Transport) writeMaskedReg(address byte, mask byte, value byte) error {
t.debug("write masked %x, mask %x, value %x", address, mask, value)
maskedValue := mask & value
t.debug("masked value %x", maskedValue)
regVal, err := t.readByte(address)
if err != nil {
return err
}
t.debug("current register %x", regVal)
regVal = (regVal &^ maskedValue) | maskedValue
t.debug("new value %x", regVal)
return t.writeByte(address, regVal)
}
func (t *Transport) readMaskedReg(address byte, mask byte) (byte, error) {
t.debug("read masked %x, mask %x", address, mask)
reg, err := t.readByte(address)
if err != nil {
return 0, err
}
t.debug("masked value %x", reg)
return reg & mask, nil
}
func (t *Transport) readByte(address byte) (byte, error) {
if t.d == nil {
return t.readByteSPI(address)
}
return t.readByteI2C(address)
}
func (t *Transport) readByteSPI(address byte) (byte, error) {
t.debug("read register %x", address)
var (
buf = [...]byte{0x80 | address, 0}
res [2]byte
)
if err := t.cs.Out(gpio.Low); err != nil {
return 0, err
}
if err := t.device.Tx(buf[:], res[:]); err != nil {
return 0, err
}
t.debug("register content %x:%x", res[0], res[1])
if err := t.cs.Out(gpio.High); err != nil {
return 0, err
}
return res[1], nil
}
func (t *Transport) readByteI2C(address byte) (byte, error) {
r := make([]byte, 1)
err := t.d.Tx([]byte{address}, r)
return r[0], err
}
func (t *Transport) readUint16(address ...byte) (uint16, error) {
if len(address) != 2 {
return 0, fmt.Errorf("only 2 bytes per read")
}
h, err := t.readByte(address[0])
if err != nil {
return 0, err
}
l, err := t.readByte(address[1])
if err != nil {
return 0, err
}
return uint16(h)<<8 | uint16(l), nil
}
func noop(string, ...interface{}) {}
Loading…
Cancel
Save