diff --git a/as7262/as7262.go b/as7262/as7262.go new file mode 100644 index 000000000..ec354e46c --- /dev/null +++ b/as7262/as7262.go @@ -0,0 +1,161 @@ +package as7262 + +import ( + "encoding/binary" + "math" + "tinygo.org/x/drivers" + "tinygo.org/x/drivers/internal/legacy" +) + +type Error uint8 + +const ( + ErrInvalidID Error = 0x1 + TxValid byte = 0x02 + RxValid byte = 0x01 +) + +func (e Error) Error() string { + switch e { + case ErrInvalidID: + return "Invalid chip ID" + default: + return "Unknown error" + } +} + +type Device struct { + bus drivers.I2C + buf []byte + Address uint8 + vControlReg *vControlReg + vLedControlReg *vLedControlReg +} + +// New returns pointer of new as7262 device +func New(i2c drivers.I2C) *Device { + return &Device{ + bus: i2c, + buf: []byte{0}, + Address: DefaultAddress, + vControlReg: newVControlReg(), + vLedControlReg: newVLedControlReg(), + } +} + +// deviceStatus returns StatusReg of as7262 +func (d *Device) deviceStatus() byte { + d.buf[0] = 0b00000000 + legacy.ReadRegister(d.bus, DefaultAddress, StatusReg, d.buf) + return d.buf[0] +} + +// writeReady returns true if as7262 is ready to write write-register +func (d *Device) writeReady() bool { + return d.deviceStatus()&TxValid == 0 +} + +// readReady return true if as7262 is ready to read read-register +func (d *Device) readReady() bool { + return d.deviceStatus()&RxValid != 0 +} + +func (d *Device) readByte(reg byte) byte { + d.buf[0] = 0b00000000 + for { + if d.writeReady() { + break + } + } + + legacy.WriteRegister(d.bus, d.Address, WriteReg, []byte{reg}) + + for { + if d.readReady() { + break + } + } + + legacy.ReadRegister(d.bus, d.Address, ReadReg, d.buf) + return d.buf[0] +} + +func (d *Device) writeByte(reg byte, value byte) { + for { + if d.writeReady() { + break + } + } + + legacy.WriteRegister(d.bus, d.Address, WriteReg, []byte{reg | 0x80}) + + for { + if d.writeReady() { + break + } + } + + legacy.WriteRegister(d.bus, d.Address, WriteReg, []byte{value}) +} + +func (d *Device) read32Bit(reg byte) float32 { + var bytes [4]byte + + for i := 0; i < 4; i++ { + bytes[3-i] = d.readByte(reg + byte(i)) + } + floatValue := math.Float32frombits(binary.BigEndian.Uint32(bytes[:])) + return floatValue +} + +// Temperature returns sensor temperature +func (d *Device) Temperature() byte { + return d.readByte(TempRegister) +} + +// GetColors set pointer array: V, B, G, Y, O, R +func (d *Device) GetColors(arr *[6]float32) { + arr[0] = d.GetViolet() + arr[1] = d.GetBlue() + arr[2] = d.GetGreen() + arr[3] = d.GetYellow() + arr[4] = d.GetOrange() + arr[5] = d.GetRed() +} + +// GetRGB set pointer array: R, G, B +func (d *Device) GetRGB(arr *[3]float32) { + arr[0] = d.GetRed() + arr[1] = d.GetGreen() + arr[2] = d.GetBlue() +} + +// GetViolet returns violet value +func (d *Device) GetViolet() float32 { + return d.read32Bit(VCalReg) +} + +// GetBlue returns blue value +func (d *Device) GetBlue() float32 { + return d.read32Bit(BCalReg) +} + +// GetGreen returns green value +func (d *Device) GetGreen() float32 { + return d.read32Bit(GCalReg) +} + +// GetYellow returns yellow value +func (d *Device) GetYellow() float32 { + return d.read32Bit(YCalReg) +} + +// GetOrange returns orange value +func (d *Device) GetOrange() float32 { + return d.read32Bit(OCalReg) +} + +// GetRed returns red value +func (d *Device) GetRed() float32 { + return d.read32Bit(RCalReg) +} diff --git a/as7262/registers.go b/as7262/registers.go new file mode 100644 index 000000000..0719b8f93 --- /dev/null +++ b/as7262/registers.go @@ -0,0 +1,66 @@ +package as7262 + +const ( + // DefaultAddress Address is default I2C address of AS7262 + DefaultAddress = 0x49 + + // StatusReg + StatusReg = 0x00 + // WriteReg + WriteReg = 0x01 + // ReadReg + ReadReg = 0x02 + + // ControlReg + ControlReg = 0x04 + // IntegrationTimeReg + IntegrationTimeReg = 0x05 + // TempRegister + TempRegister = 0x06 + // LedRegister + LedRegister = 0x07 + + /* + Sensor Raw Data Registers + */ + // VHighRawReg Channel V High Data Byte + VHighRawReg = 0x08 + // VLowRawReg Channel V Low Data Byte + VLowRawReg = 0x09 + // BHighRawReg Channel B High Data Byte + BHighRawReg = 0x0A + // BLowRawReg Channel B Low Data Byte + BLowRawReg = 0x0B + // GHighRawReg Channel G High Data Byte + GHighRawReg = 0x0C + // GLowRawReg Channel G Low Data Byte + GLowRawReg = 0x0D + // YHighRawReg Channel Y High Data Byte + YHighRawReg = 0x0E + // YLowRawReg Channel Y Low Data Byte + YLowRawReg = 0x0F + // OHighRawReg Channel O High Data Byte + OHighRawReg = 0x10 + // OLowRawReg Channel O Low Data Byte + OLowRawReg = 0x11 + // RHighRawReg Channel R High Data Byte + RHighRawReg = 0x12 + // RLowRawReg Channel R Low Data Byte + RLowRawReg = 0x13 + + /* + Sensor Calibrated Data Registers + */ + // VCalReg address for Channel V Calibrated Data + VCalReg = 0x14 + // BCalReg address for Channel B Calibrated Data + BCalReg = 0x18 + // GCalReg address for Channel G Calibrated Data + GCalReg = 0x1C + // YCalReg address for Channel Y Calibrated Data + YCalReg = 0x20 + // OCalReg address for Channel O Calibrated Data + OCalReg = 0x24 + // RCalReg address for Channel R Calibrated Data + RCalReg = 0x28 +) diff --git a/as7262/vControlRegister.go b/as7262/vControlRegister.go new file mode 100644 index 000000000..51d895769 --- /dev/null +++ b/as7262/vControlRegister.go @@ -0,0 +1,97 @@ +package as7262 + +import "time" + +type vControlReg struct { + reset byte + interrupt byte + gain byte + bank byte + dataReady byte +} + +func newVControlReg() *vControlReg { + return &vControlReg{ + reset: 0b10000000, + interrupt: 0b01000000, + gain: 0b00110000, + bank: 0b00001100, // measurement mode + dataReady: 0b00000010, + } +} + +// encodeCReg register to a complete byte for writing +func (d *Device) encodeCReg() byte { + return d.vControlReg.reset | + d.vControlReg.interrupt | + d.vControlReg.gain | + d.vControlReg.bank | + d.vControlReg.dataReady +} + +// decodeCReg register to represent as7262 internal state +func (d *Device) decodeCReg(encoded byte) { + d.vControlReg.reset = encoded & 0b10000000 + d.vControlReg.interrupt = encoded & 0b01000000 + d.vControlReg.gain = encoded & 0b00110000 + d.vControlReg.bank = encoded & 0b00001100 + d.vControlReg.dataReady = encoded & 0b00000010 +} + +// setReset bit which will soft reset the as7262 sensor +func (d *Device) setReset(reset bool) { + if reset { + d.vControlReg.reset |= 0b10000000 + } else { + d.vControlReg.reset &= 0b01111111 + } +} + +// setGain sets bit 4:5 of VControlReg for gain +func (d *Device) setGain(gain float32) { + // set gain (defaults to 64) + // values: 1, 3.7, 16, 64 + var g byte + switch gain { + case 1: + g = 0b00 + case 3.7: + g = 0b01 + case 16: + g = 0b10 + default: + g = 0b11 + } + + // bitwise clear operation & setting bit 4:5 + d.vControlReg.gain &= 0b11001111 + d.vControlReg.gain |= g << 4 +} + +// setMode sets bit 2:3 of VControlReg for mode +func (d *Device) setMode(mode int) { + // set mode: 0, 1, 2, 3 + m := byte(mode) + + // bitwise clear operation & setting bit 4:5 + d.vControlReg.bank &= 0b11110011 + d.vControlReg.bank |= m << 2 +} + +// Configure as7262 behaviour +func (d *Device) Configure(reset bool, gain float32, integrationTime float32, mode int) { + d.setReset(reset) + d.setGain(gain) + d.setMode(mode) + crEncoded := d.encodeCReg() + + // write ControlReg and read full ControlReg + d.writeByte(ControlReg, crEncoded) + time.Sleep(time.Second) + d.readByte(ControlReg) + d.decodeCReg(d.buf[0]) + + // set integrationTime: float32 as ms + t := byte(int(integrationTime*2.8) & 0xff) + d.writeByte(IntegrationTimeReg, t) +} diff --git a/as7262/vLedRegister.go b/as7262/vLedRegister.go new file mode 100644 index 000000000..821015dd6 --- /dev/null +++ b/as7262/vLedRegister.go @@ -0,0 +1,105 @@ +package as7262 + +type vLedControlReg struct { + illuminationCurrentLimit byte + illuminationStatus byte + indicatorCurrentLimit byte + indicatorStatus byte +} + +func newVLedControlReg() *vLedControlReg { + return &vLedControlReg{ + illuminationCurrentLimit: 0b00110000, + illuminationStatus: 0b00001000, + indicatorCurrentLimit: 0b00000110, + indicatorStatus: 0b00000001, + } +} + +// encodeLedReg register to a complete byte for writing +func (d *Device) encodeLedReg() byte { + return d.vLedControlReg.illuminationCurrentLimit | + d.vLedControlReg.illuminationStatus | + d.vLedControlReg.indicatorCurrentLimit | + d.vLedControlReg.indicatorStatus +} + +// decodeLedReg register to represent as7262 internal state +func (d *Device) decodeLedReg(encoded byte) { + d.vLedControlReg.illuminationCurrentLimit = encoded & 0b00110000 + d.vLedControlReg.illuminationStatus = encoded & 0b00001000 + d.vLedControlReg.indicatorCurrentLimit = encoded & 0b00000110 + d.vLedControlReg.indicatorStatus = encoded & 0b00000001 +} + +// setIlCr +func (d *Device) setIlCr(ilCurLim float32) { + // values: 12.5, 25, 50, 100 (defaults to 50) + var cr byte + switch ilCurLim { + case 12.5: + cr = 0b00 + case 25: + cr = 0b01 + case 100: + cr = 0b11 + default: + cr = 0b10 + } + + //bitwise clear operation & setting bit 4:5 + d.vLedControlReg.illuminationCurrentLimit &= 0b11001111 + d.vLedControlReg.illuminationCurrentLimit |= cr << 4 +} + +// setIlOn +func (d *Device) setIlOn(ilOn bool) { + if ilOn { + d.vLedControlReg.illuminationStatus |= 0b00001000 + } else { + d.vLedControlReg.illuminationStatus &= 0b11110111 + } +} + +// setInCur +func (d *Device) setInCur(inCurLim float32) { + // values: 1, 2, 4, 8 (defaults to 8) + var cr byte + switch inCurLim { + case 1: + cr = 0b00 + case 2: + cr = 0b01 + case 4: + cr = 0b10 + default: + cr = 0b11 + } + + //bitwise clear operation & setting bit 4:5 + d.vLedControlReg.indicatorCurrentLimit &= 0b11111001 + d.vLedControlReg.indicatorCurrentLimit |= cr << 1 +} + +// setInOn +func (d *Device) setInOn(inOn bool) { + if inOn { + d.vLedControlReg.indicatorStatus |= 0b00000001 + } else { + d.vLedControlReg.indicatorStatus &= 0b11111110 + } +} + +// ConfigureLed with all possible configurations +func (d *Device) ConfigureLed(ilCurLim float32, ilOn bool, inCurLim float32, inOn bool) { + d.setIlCr(ilCurLim) + d.setIlOn(ilOn) + d.setInCur(inCurLim) + d.setInOn(inOn) + lrEncoded := d.encodeLedReg() + + // write ControlReg and read full ControlReg + d.writeByte(LedRegister, lrEncoded) + d.readByte(LedRegister) + d.decodeLedReg(d.buf[0]) +} diff --git a/examples/as7262/main.go b/examples/as7262/main.go new file mode 100644 index 000000000..7cb0b5b6e --- /dev/null +++ b/examples/as7262/main.go @@ -0,0 +1,27 @@ +// tinygo flash -target arduino examples/as7262/main.go && tinygo monitor -baudrate 9600 + +package main + +import ( + "machine" + "time" + "tinygo.org/x/drivers/as7262" +) + +var ( + i2c = machine.I2C0 + sensor = as7262.New(i2c) +) + +func main() { + i2c.Configure(machine.I2CConfig{Frequency: machine.KHz * 100}) + sensor.Configure(true, 64, 17.857, 2) + //sensor.ConfigureLed(12.5, true, 8, true) + + println("Starting ...") + + for { + println("Value: ", sensor.Temperature()) + time.Sleep(time.Millisecond * 800) + } +} diff --git a/smoketest.sh b/smoketest.sh index 54b7bdb74..4a6ad1954 100755 --- a/smoketest.sh +++ b/smoketest.sh @@ -132,4 +132,5 @@ tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/t tinygo build -size short -o ./build/test.hex -target=pico ./examples/ndir/main_ndir.go tinygo build -size short -o ./build/test.hex -target=microbit ./examples/ndir/main_ndir.go tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/ndir/main_ndir.go +tinygo build -size short -o ./build/test.hex -target=arduino ./examples/as7262/main.go tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/mpu9150/main.go