tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/as560x/i2c_register.go (about) 1 package as560x // import tinygo.org/x/drivers/ams560x 2 3 import ( 4 "encoding/binary" 5 "errors" 6 7 "tinygo.org/x/drivers" 8 "tinygo.org/x/drivers/internal/legacy" 9 ) 10 11 // registerAttributes is a bitfield of attributes for a register 12 type registerAttributes uint8 13 14 const ( 15 // reg_read indicates that the register is readable 16 reg_read registerAttributes = 1 << iota 17 // reg_write indicates that the register is writeable 18 reg_write 19 // reg_program indicates that the register can be permanently programmed ('BURNed') 20 reg_program 21 ) 22 23 var ( 24 errRegisterNotReadable = errors.New("Register is not readable") 25 errRegisterNotWriteable = errors.New("Register is not writeable") 26 ) 27 28 // i2cRegister encapsulates the address, structure and read/write logic for a register on a AS560x device 29 type i2cRegister struct { 30 // host is the 'host register' for virtual registers. Physical/root registers have this set to self 31 host *i2cRegister 32 // address is the i2c address of the register. For 2-byte (word) addresses it's the low byte which holds the MSBs 33 address uint8 34 // shift is the number of bits the value is 'left shifted' into the register byte/word (0-15) 35 shift uint16 36 // mask is a bitwise mask applied to the register AFTER 'right shifting' to mask the register value 37 mask uint16 38 // num_bytes is the width of the register in bytes, 1 or 2. 39 num_bytes uint8 40 // attributes holds the register attributes. A bitfield of REG_xyz constants 41 attributes registerAttributes 42 // cached indicates whether we are holding a cached value of the register in value 43 cached bool 44 // value can be used as a 'cache' of the register's value for writeable registers. 45 value uint16 46 } 47 48 // newI2CRegister returns a pointer to a new i2cRegister with no cached value 49 func newI2CRegister(address uint8, shift uint16, mask uint16, num_bytes uint8, attributes registerAttributes) *i2cRegister { 50 reg := &i2cRegister{ 51 address: address, 52 shift: shift, 53 mask: mask, 54 num_bytes: num_bytes, 55 attributes: attributes, 56 } 57 // root registers host themselves 58 reg.host = reg 59 return reg 60 } 61 62 // newVirtualRegister returns a pointer to a new i2cRegister with the given host register and shift/mask. 63 func newVirtualRegister(host *i2cRegister, shift uint16, mask uint16) *i2cRegister { 64 return &i2cRegister{ 65 host: host, 66 address: host.address, 67 shift: shift, 68 mask: mask, 69 num_bytes: host.num_bytes, 70 attributes: host.attributes, 71 } 72 } 73 74 // invalidate invalidates any cached value for the register and forces an I2C read on the next read() 75 func (r *i2cRegister) invalidate() { 76 r.host.cached = false 77 r.host.value = 0 78 } 79 80 // readShiftAndMask is an internal method to read a value for the register over the given I2C bus from the device with the given address applying the given shift and mask 81 func (r *i2cRegister) readShiftAndMask(bus drivers.I2C, deviceAddress uint8, shift uint16, mask uint16) (uint16, error) { 82 if r.host.attributes®_read == 0 { 83 return 0, errRegisterNotReadable 84 } 85 86 // Only read over I2C if we don't have the host register value cached 87 var val uint16 = r.host.value 88 if !r.host.cached { 89 // To avoid an alloc we always use an array of 2 bytes 90 var buffer [2]byte 91 var buf []byte 92 if r.host.num_bytes < 2 { 93 buf = buffer[:1] 94 } else { 95 buf = buffer[:] 96 } 97 // Read the host register over I2C 98 err := legacy.ReadRegister(bus, deviceAddress, r.host.address, buf) 99 if nil != err { 100 return 0, err 101 } 102 // Unpack data from I2C 103 if r.host.num_bytes > 1 { 104 val = binary.BigEndian.Uint16(buf) 105 } else { 106 val = uint16(buf[0]) 107 } 108 // cache this value if the host register is writeable. Note we cache the entire buffer without applying shift/mask 109 if r.host.attributes®_write != 0 { 110 r.host.value = val 111 r.host.cached = true 112 } 113 } 114 // Shift and mask the value before returning 115 val >>= shift 116 val &= mask 117 return val, nil 118 } 119 120 // read reads a value for the register over the given I2C bus from the device with the given address. 121 func (r *i2cRegister) read(bus drivers.I2C, deviceAddress uint8) (uint16, error) { 122 return r.readShiftAndMask(bus, deviceAddress, r.shift, r.mask) 123 } 124 125 // write writes a value for the register over the given I2C bus to the device with the given address. 126 func (r *i2cRegister) write(bus drivers.I2C, deviceAddress uint8, value uint16) error { 127 if r.host.attributes®_write == 0 { 128 return errRegisterNotWriteable 129 } 130 var newValue uint16 = 0 131 // Data sheet tells us to do a read first, modify only the desired bits and then write back 132 // since (quote:) 'Blank fields may contain factory settings' 133 // We will also need to do this anyway to support virtualRegister mappings on some registers 134 // (e.g. CONF/STATUS) 135 if (r.host.attributes & reg_read) > 0 { // not all registers are readable, e.g. BURN 136 // read the host register's entire host byte/word, regardless of shift & mask 137 readValue, error := r.readShiftAndMask(bus, deviceAddress, 0, 0xffff) 138 if error != nil { 139 return error 140 } 141 // Zero-out ONLY the relevant bits in newValue we just read 142 readValue &= (0xffff ^ (r.mask << r.shift)) 143 newValue = readValue 144 } 145 // Mask the new value and shift it into place 146 value &= r.mask 147 value <<= r.shift 148 // OR the masked & shifted value back into newValue to be written 149 newValue |= value 150 // Pack newValue into a byte buffer to write. To avoid an alloc we always use an array of 2 bytes 151 var buffer [2]byte 152 var buf []byte 153 if r.host.num_bytes < 2 { 154 buf = buffer[:1] 155 buf[0] = uint8(newValue & 0xff) 156 } else { 157 buf = buffer[:] 158 binary.BigEndian.PutUint16(buf, newValue) 159 } 160 161 // Write the register from the buffer over I2C 162 err := legacy.WriteRegister(bus, deviceAddress, r.host.address, buf) 163 // after successful I2C write, cache this value if the host register (if also readable) 164 // Note we cache the entire buffer without applying shift/mask 165 if nil == err && r.host.attributes®_read != 0 { 166 r.host.value = newValue 167 r.host.cached = true 168 } 169 return err 170 }