mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
173 lines
5.7 KiB
Go
173 lines
5.7 KiB
Go
// Product: https://ams.com/as5600
|
|
// Datasheet: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf
|
|
|
|
package as560x // import tinygo.org/x/drivers/ams560x
|
|
|
|
import (
|
|
"time"
|
|
|
|
"tinygo.org/x/drivers"
|
|
)
|
|
|
|
// AS5600 includes MPOS & MANG in addition to ZPOS to set a 'narrower angle range'
|
|
// ZPOS enables setting the 'zero position' of the device to any RAW_ANGLE value.
|
|
// MPOS ('max position') & MANG 'max angle' enable a 'partial range' on the AS5600.
|
|
// The value in ANGLE is scaled & adjusted by the device according to ZPOS and MPOS/MANG.
|
|
// The entire 12-bit range is 'compressed' into the RAW_ANGLE range of ZPOS->MPOS
|
|
// (or ZPOS->ZPOS+MANG) thus enabling a higher resolution for a partial range.
|
|
// if ZPOS > MPOS (or ZPOS + MANG > 4095) i.e. the incremental range 'crosses zero'
|
|
// then the device will automatically compensate for the correct range.
|
|
// For RAW_ANGLE values outside of the partial range, ANGLE will be 'capped' at either
|
|
// 0 or 4095, depending on 'which end of the partial range is closer.'
|
|
|
|
// AS5600Device represents an ams AS5600 device driver accessed over I2C
|
|
type AS5600Device struct {
|
|
// promote BaseDevice
|
|
BaseDevice
|
|
}
|
|
|
|
// NewAS5600 creates a new AS5600Device given an I2C bus
|
|
func NewAS5600(bus drivers.I2C) AS5600Device {
|
|
// Create base device
|
|
baseDev := newBaseDevice(bus)
|
|
// Add AS5600 specific registers
|
|
baseDev.registers[MPOS] = newI2CRegister(MPOS, 0, 0xfff, 2, reg_read|reg_write|reg_program)
|
|
baseDev.registers[MANG] = newI2CRegister(MANG, 0, 0xfff, 2, reg_read|reg_write|reg_program)
|
|
// Add AS5600 specific 'virtual registers'
|
|
conf, ok := baseDev.registers[CONF]
|
|
if ok {
|
|
baseDev.registers[PWMF] = newVirtualRegister(conf, 6, 0b11)
|
|
baseDev.registers[OUTS] = newVirtualRegister(conf, 4, 0b11)
|
|
}
|
|
// Return the device
|
|
return AS5600Device{baseDev}
|
|
}
|
|
|
|
// Configure sets up the AMS AS5600 sensor device with the given configuration.
|
|
func (d *AS5600Device) Configure(cfg Config) error {
|
|
// Call the BaseDevice method to do the actual Configure
|
|
d.BaseDevice.Configure(cfg)
|
|
// For AS5600 devices we need to calculate the maxAngle on startup from ZPOS/MPOS/MANG
|
|
// These could have been permanently BURN'ed (by writing BURN register with BURN_ANGLE/BURN_SETTING)
|
|
// or may have already been written in previous runs without a power cycle since.
|
|
mpos, err := d.ReadRegister(MPOS)
|
|
if nil != err {
|
|
return err
|
|
}
|
|
mang, err := d.ReadRegister(MANG)
|
|
if nil != err {
|
|
return err
|
|
}
|
|
// Read ZPOS for side effect of caching only so that next calculateEffectiveMaxAngle() can't fail
|
|
if _, err = d.ReadRegister(ZPOS); nil != err {
|
|
return err
|
|
}
|
|
if mpos != 0 {
|
|
// If MPOS is set, use MPOS regardless of MANG
|
|
err = d.calculateEffectiveMaxAngle(MPOS, mpos)
|
|
} else if mang != 0 {
|
|
// If MANG is set and MPOS == 0, use MANG
|
|
err = d.calculateEffectiveMaxAngle(MANG, mang)
|
|
} else {
|
|
// if neither is set, we have no narrow range
|
|
d.maxAngle = NATIVE_ANGLE_RANGE
|
|
}
|
|
return err
|
|
}
|
|
|
|
// calculateEffectiveMaxAngle calculates d.maxAngle after one of ZPOS/MPOS/MANG have been written
|
|
func (d *AS5600Device) calculateEffectiveMaxAngle(register uint8, value uint16) error {
|
|
|
|
var zpos, mpos uint16 = 0, 0
|
|
var err error = nil
|
|
|
|
switch register {
|
|
case MANG:
|
|
d.maxAngle = value // The easy case
|
|
return nil
|
|
case ZPOS:
|
|
zpos = value
|
|
mpos, err = d.ReadRegister(MPOS)
|
|
case MPOS:
|
|
mpos = value
|
|
zpos, err = d.ReadRegister(ZPOS)
|
|
default:
|
|
panic("calculateEffectiveMaxAngle() can only work from ZPOS, MPOS or MANG")
|
|
}
|
|
|
|
if nil != err {
|
|
return err
|
|
}
|
|
// MANG is effectively MPOS-ZPOS
|
|
mang := int(mpos) - int(zpos)
|
|
// correct for mpos < zpos
|
|
if mang < 0 {
|
|
mang += NATIVE_ANGLE_RANGE
|
|
}
|
|
d.maxAngle = uint16(mang)
|
|
return nil
|
|
}
|
|
|
|
// WriteRegister writes the given value for the given register to the AS560x device via I2C
|
|
func (d *AS5600Device) WriteRegister(address uint8, value uint16) error {
|
|
// Call the BaseDevice method to do the actual write
|
|
if err := d.BaseDevice.WriteRegister(address, value); err != nil {
|
|
return err
|
|
}
|
|
// When either ZPOS/MANG/MPOS are set we need to recalculate maxAngle
|
|
// We also may need to invalidate some cached values for the other two registers
|
|
recalc := false
|
|
switch address {
|
|
case ZPOS:
|
|
// Setting a new ZPOS invalidates MPOS but not MANG
|
|
d.registers[MPOS].invalidate()
|
|
recalc = true
|
|
case MPOS:
|
|
// Setting a new MPOS invalidates MANG but not ZPOS
|
|
d.registers[MANG].invalidate()
|
|
recalc = true
|
|
case MANG:
|
|
// Setting a new MANG invalidates MPOS but not ZPOS
|
|
d.registers[MPOS].invalidate()
|
|
recalc = true
|
|
}
|
|
if recalc {
|
|
// Datasheet tells us to wait at least 1ms before reading back
|
|
time.Sleep(time.Millisecond * 10) // conservative wait
|
|
return d.calculateEffectiveMaxAngle(address, value)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetMaxPosition returns the 'max position' (MPOS) in different units
|
|
func (d *AS5600Device) GetMaxPosition(units AngleUnit) (uint16, float32, error) {
|
|
mpos, err := d.ReadRegister(MPOS)
|
|
if nil != err {
|
|
return 0, 0.0, err
|
|
}
|
|
// Convert to requested units
|
|
i, f := convertFromNativeAngle(mpos, NATIVE_ANGLE_RANGE, units)
|
|
return i, f, nil
|
|
}
|
|
|
|
// SetMaxPosition sets the 'max position' (MPOS) in different units
|
|
func (d *AS5600Device) SetMaxPosition(mpos float32, units AngleUnit) error {
|
|
return d.WriteRegister(MPOS, convertToNativeAngle(mpos, units))
|
|
}
|
|
|
|
// GetMaxAngle returns the 'max position' (MANG) in different units
|
|
func (d *AS5600Device) GetMaxAngle(units AngleUnit) (uint16, float32, error) {
|
|
mang, err := d.ReadRegister(MANG)
|
|
if nil != err {
|
|
return 0, 0.0, err
|
|
}
|
|
// Convert to requested units
|
|
i, f := convertFromNativeAngle(mang, NATIVE_ANGLE_RANGE, units)
|
|
return i, f, nil
|
|
}
|
|
|
|
// SetMaxAngle sets the 'max angle' (MANG) in different units
|
|
func (d *AS5600Device) SetMaxAngle(mang float32, units AngleUnit) error {
|
|
return d.WriteRegister(MANG, convertToNativeAngle(mang, units))
|
|
}
|