tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/as560x/as5600.go (about) 1 // Product: https://ams.com/as5600 2 // Datasheet: https://ams.com/documents/20143/36005/AS5600_DS000365_5-00.pdf 3 4 package as560x // import tinygo.org/x/drivers/ams560x 5 6 import ( 7 "time" 8 9 "tinygo.org/x/drivers" 10 ) 11 12 // AS5600 includes MPOS & MANG in addition to ZPOS to set a 'narrower angle range' 13 // ZPOS enables setting the 'zero position' of the device to any RAW_ANGLE value. 14 // MPOS ('max position') & MANG 'max angle' enable a 'partial range' on the AS5600. 15 // The value in ANGLE is scaled & adjusted by the device according to ZPOS and MPOS/MANG. 16 // The entire 12-bit range is 'compressed' into the RAW_ANGLE range of ZPOS->MPOS 17 // (or ZPOS->ZPOS+MANG) thus enabling a higher resolution for a partial range. 18 // if ZPOS > MPOS (or ZPOS + MANG > 4095) i.e. the incremental range 'crosses zero' 19 // then the device will automatically compensate for the correct range. 20 // For RAW_ANGLE values outside of the partial range, ANGLE will be 'capped' at either 21 // 0 or 4095, depending on 'which end of the partial range is closer.' 22 23 // AS5600Device represents an ams AS5600 device driver accessed over I2C 24 type AS5600Device struct { 25 // promote BaseDevice 26 BaseDevice 27 } 28 29 // NewAS5600 creates a new AS5600Device given an I2C bus 30 func NewAS5600(bus drivers.I2C) AS5600Device { 31 // Create base device 32 baseDev := newBaseDevice(bus) 33 // Add AS5600 specific registers 34 baseDev.registers[MPOS] = newI2CRegister(MPOS, 0, 0xfff, 2, reg_read|reg_write|reg_program) 35 baseDev.registers[MANG] = newI2CRegister(MANG, 0, 0xfff, 2, reg_read|reg_write|reg_program) 36 // Add AS5600 specific 'virtual registers' 37 conf, ok := baseDev.registers[CONF] 38 if ok { 39 baseDev.registers[PWMF] = newVirtualRegister(conf, 6, 0b11) 40 baseDev.registers[OUTS] = newVirtualRegister(conf, 4, 0b11) 41 } 42 // Return the device 43 return AS5600Device{baseDev} 44 } 45 46 // Configure sets up the AMS AS5600 sensor device with the given configuration. 47 func (d *AS5600Device) Configure(cfg Config) error { 48 // Call the BaseDevice method to do the actual Configure 49 d.BaseDevice.Configure(cfg) 50 // For AS5600 devices we need to calculate the maxAngle on startup from ZPOS/MPOS/MANG 51 // These could have been permanently BURN'ed (by writing BURN register with BURN_ANGLE/BURN_SETTING) 52 // or may have already been written in previous runs without a power cycle since. 53 mpos, err := d.ReadRegister(MPOS) 54 if nil != err { 55 return err 56 } 57 mang, err := d.ReadRegister(MANG) 58 if nil != err { 59 return err 60 } 61 // Read ZPOS for side effect of caching only so that next calculateEffectiveMaxAngle() can't fail 62 if _, err = d.ReadRegister(ZPOS); nil != err { 63 return err 64 } 65 if mpos != 0 { 66 // If MPOS is set, use MPOS regardless of MANG 67 err = d.calculateEffectiveMaxAngle(MPOS, mpos) 68 } else if mang != 0 { 69 // If MANG is set and MPOS == 0, use MANG 70 err = d.calculateEffectiveMaxAngle(MANG, mang) 71 } else { 72 // if neither is set, we have no narrow range 73 d.maxAngle = NATIVE_ANGLE_RANGE 74 } 75 return err 76 } 77 78 // calculateEffectiveMaxAngle calculates d.maxAngle after one of ZPOS/MPOS/MANG have been written 79 func (d *AS5600Device) calculateEffectiveMaxAngle(register uint8, value uint16) error { 80 81 var zpos, mpos uint16 = 0, 0 82 var err error = nil 83 84 switch register { 85 case MANG: 86 d.maxAngle = value // The easy case 87 return nil 88 case ZPOS: 89 zpos = value 90 mpos, err = d.ReadRegister(MPOS) 91 case MPOS: 92 mpos = value 93 zpos, err = d.ReadRegister(ZPOS) 94 default: 95 panic("calculateEffectiveMaxAngle() can only work from ZPOS, MPOS or MANG") 96 } 97 98 if nil != err { 99 return err 100 } 101 // MANG is effectively MPOS-ZPOS 102 mang := int(mpos) - int(zpos) 103 // correct for mpos < zpos 104 if mang < 0 { 105 mang += NATIVE_ANGLE_RANGE 106 } 107 d.maxAngle = uint16(mang) 108 return nil 109 } 110 111 // WriteRegister writes the given value for the given register to the AS560x device via I2C 112 func (d *AS5600Device) WriteRegister(address uint8, value uint16) error { 113 // Call the BaseDevice method to do the actual write 114 if err := d.BaseDevice.WriteRegister(address, value); err != nil { 115 return err 116 } 117 // When either ZPOS/MANG/MPOS are set we need to recalculate maxAngle 118 // We also may need to invalidate some cached values for the other two registers 119 recalc := false 120 switch address { 121 case ZPOS: 122 // Setting a new ZPOS invalidates MPOS but not MANG 123 d.registers[MPOS].invalidate() 124 recalc = true 125 case MPOS: 126 // Setting a new MPOS invalidates MANG but not ZPOS 127 d.registers[MANG].invalidate() 128 recalc = true 129 case MANG: 130 // Setting a new MANG invalidates MPOS but not ZPOS 131 d.registers[MPOS].invalidate() 132 recalc = true 133 } 134 if recalc { 135 // Datasheet tells us to wait at least 1ms before reading back 136 time.Sleep(time.Millisecond * 10) // conservative wait 137 return d.calculateEffectiveMaxAngle(address, value) 138 } 139 return nil 140 } 141 142 // GetMaxPosition returns the 'max position' (MPOS) in different units 143 func (d *AS5600Device) GetMaxPosition(units AngleUnit) (uint16, float32, error) { 144 mpos, err := d.ReadRegister(MPOS) 145 if nil != err { 146 return 0, 0.0, err 147 } 148 // Convert to requested units 149 i, f := convertFromNativeAngle(mpos, NATIVE_ANGLE_RANGE, units) 150 return i, f, nil 151 } 152 153 // SetMaxPosition sets the 'max position' (MPOS) in different units 154 func (d *AS5600Device) SetMaxPosition(mpos float32, units AngleUnit) error { 155 return d.WriteRegister(MPOS, convertToNativeAngle(mpos, units)) 156 } 157 158 // GetMaxAngle returns the 'max position' (MANG) in different units 159 func (d *AS5600Device) GetMaxAngle(units AngleUnit) (uint16, float32, error) { 160 mang, err := d.ReadRegister(MANG) 161 if nil != err { 162 return 0, 0.0, err 163 } 164 // Convert to requested units 165 i, f := convertFromNativeAngle(mang, NATIVE_ANGLE_RANGE, units) 166 return i, f, nil 167 } 168 169 // SetMaxAngle sets the 'max angle' (MANG) in different units 170 func (d *AS5600Device) SetMaxAngle(mang float32, units AngleUnit) error { 171 return d.WriteRegister(MANG, convertToNativeAngle(mang, units)) 172 }