mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-26 10:38:41 +00:00
5b2c2f800d
Implement function to get the effective SPAD count. Signed-off-by: Michael Meister <michael.meister@bytesatwork.ch>
585 lines
18 KiB
Go
585 lines
18 KiB
Go
// Package vl53l1x provides a driver for the VL53L1X time-of-flight
|
|
// distance sensor
|
|
//
|
|
// Datasheet:
|
|
// https://www.st.com/resource/en/datasheet/vl53l1x.pdf
|
|
// This driver was based on the library https://github.com/pololu/vl53l1x-arduino
|
|
// and ST's VL53L1X API (STSW-IMG007)
|
|
// https://www.st.com/content/st_com/en/products/embedded-software/proximity-sensors-software/stsw-img007.html
|
|
package vl53l1x // import "tinygo.org/x/drivers/vl53l1x"
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"tinygo.org/x/drivers"
|
|
)
|
|
|
|
type DistanceMode uint8
|
|
type RangeStatus uint8
|
|
|
|
type rangingData struct {
|
|
mm uint16
|
|
status RangeStatus
|
|
signalRateMCPS int32 //MCPS : Mega Count Per Second
|
|
ambientRateMCPS int32
|
|
effectiveSPADCount uint16
|
|
}
|
|
|
|
type resultBuffer struct {
|
|
status uint8
|
|
streamCount uint8
|
|
effectiveSPADCount uint16
|
|
ambientRateMCPSSD0 uint16
|
|
mmCrosstalkSD0 uint16
|
|
signalRateCrosstalkMCPSSD0 uint16
|
|
}
|
|
|
|
// Device wraps an I2C connection to a VL53L1X device.
|
|
type Device struct {
|
|
bus drivers.I2C
|
|
Address uint16
|
|
mode DistanceMode
|
|
timeout uint32
|
|
fastOscillatorFreq uint16
|
|
oscillatorOffset uint16
|
|
calibrated bool
|
|
VHVInit uint8
|
|
VHVTimeout uint8
|
|
rangingData rangingData
|
|
results resultBuffer
|
|
}
|
|
|
|
// New creates a new VL53L1X connection. The I2C bus must already be
|
|
// configured.
|
|
//
|
|
// This function only creates the Device object, it does not touch the device.
|
|
func New(bus drivers.I2C) Device {
|
|
return Device{
|
|
bus: bus,
|
|
Address: Address,
|
|
mode: LONG,
|
|
timeout: 500,
|
|
}
|
|
}
|
|
|
|
// Connected returns whether a VL53L1X has been found.
|
|
// It does a "who am I" request and checks the response.
|
|
func (d *Device) Connected() bool {
|
|
return d.readReg16Bit(WHO_AM_I) == CHIP_ID
|
|
}
|
|
|
|
// Configure sets up the device for communication
|
|
func (d *Device) Configure(use2v8Mode bool) bool {
|
|
if !d.Connected() {
|
|
return false
|
|
}
|
|
d.writeReg(SOFT_RESET, 0x00)
|
|
time.Sleep(100 * time.Microsecond)
|
|
d.writeReg(SOFT_RESET, 0x01)
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
start := time.Now()
|
|
for (d.readReg(FIRMWARE_SYSTEM_STATUS) & 0x01) == 0 {
|
|
elapsed := time.Since(start)
|
|
if d.timeout > 0 && uint32(elapsed.Seconds()*1000) > d.timeout {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if use2v8Mode {
|
|
d.writeReg(PAD_I2C_HV_EXTSUP_CONFIG, d.readReg(PAD_I2C_HV_EXTSUP_CONFIG)|0x01)
|
|
}
|
|
|
|
d.fastOscillatorFreq = d.readReg16Bit(OSC_MEASURED_FAST_OSC_FREQUENCY)
|
|
d.oscillatorOffset = d.readReg16Bit(RESULT_OSC_CALIBRATE_VAL)
|
|
|
|
// static config
|
|
d.writeReg16Bit(DSS_CONFIG_TARGET_TOTAL_RATE_MCPS, TARGETRATE)
|
|
d.writeReg(GPIO_TIO_HV_STATUS, 0x02)
|
|
d.writeReg(SIGMA_ESTIMATOR_EFFECTIVE_PULSE_WIDTH_NS, 8)
|
|
d.writeReg(SIGMA_ESTIMATOR_EFFECTIVE_AMBIENT_WIDTH_NS, 16)
|
|
d.writeReg(ALGO_CROSSTALK_COMPENSATION_VALID_HEIGHT_MM, 0xFF)
|
|
d.writeReg(ALGO_RANGE_MIN_CLIP, 0)
|
|
d.writeReg(ALGO_CONSISTENCY_CHECK_TOLERANCE, 2)
|
|
|
|
// general config
|
|
d.writeReg16Bit(SYSTEM_THRESH_RATE_HIGH, 0x0000)
|
|
d.writeReg16Bit(SYSTEM_THRESH_RATE_LOW, 0x0000)
|
|
d.writeReg(DSS_CONFIG_APERTURE_ATTENUATION, 0x38)
|
|
|
|
// timing config
|
|
d.writeReg16Bit(RANGE_CONFIG_SIGMA_THRESH, 360)
|
|
d.writeReg16Bit(RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT_MCPS, 192)
|
|
|
|
// dynamic config
|
|
d.writeReg(SYSTEM_GROUPED_PARAMETER_HOLD_0, 0x01)
|
|
d.writeReg(SYSTEM_GROUPED_PARAMETER_HOLD_1, 0x01)
|
|
d.writeReg(SD_CONFIG_QUANTIFIER, 2)
|
|
|
|
d.writeReg(SYSTEM_GROUPED_PARAMETER_HOLD, 0x00)
|
|
d.writeReg(SYSTEM_SEED_CONFIG, 1)
|
|
|
|
// Low power auto mode
|
|
d.writeReg(SYSTEM_SEQUENCE_CONFIG, 0x8B) // VHV, PHASECAL, DSS1, RANGE
|
|
d.writeReg16Bit(DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT, 200<<8)
|
|
d.writeReg(DSS_CONFIG_ROI_MODE_CONTROL, 2) // REQUESTED_EFFFECTIVE_SPADS
|
|
|
|
d.SetDistanceMode(d.mode)
|
|
d.SetMeasurementTimingBudget(50000)
|
|
|
|
d.writeReg16Bit(ALGO_PART_TO_PART_RANGE_OFFSET_MM, d.readReg16Bit(MM_CONFIG_OUTER_OFFSET_MM)*4)
|
|
|
|
return true
|
|
}
|
|
|
|
// SetAddress sets the I2C address which this device listens to.
|
|
func (d *Device) SetAddress(address uint8) {
|
|
d.writeReg(I2C_SLAVE_DEVICE_ADDRESS, address)
|
|
d.Address = uint16(address)
|
|
}
|
|
|
|
// GetAddress returns the I2C address which this device listens to.
|
|
func (d *Device) GetAddress() uint8 {
|
|
return uint8(d.Address)
|
|
}
|
|
|
|
// SetTimeout configures the timeout
|
|
func (d *Device) SetTimeout(timeout uint32) {
|
|
d.timeout = timeout
|
|
}
|
|
|
|
// SetDistanceMode sets the mode for calculating the distance.
|
|
// Distance mode vs. max. distance
|
|
// SHORT: 136cm (dark) - 135cm (strong ambient light)
|
|
// MEDIUM: 290cm (dark) - 76cm (strong ambient light)
|
|
// LONG: 360cm (dark) - 73cm (strong ambient light)
|
|
// It returns false if an invalid mode is provided
|
|
func (d *Device) SetDistanceMode(mode DistanceMode) bool {
|
|
budgetMicroseconds := d.GetMeasurementTimingBudget()
|
|
switch mode {
|
|
case SHORT:
|
|
// timing config
|
|
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_A, 0x07)
|
|
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_B, 0x05)
|
|
d.writeReg(RANGE_CONFIG_VALID_PHASE_HIGH, 0x38)
|
|
|
|
// dynamic config
|
|
d.writeReg(SD_CONFIG_WOI_SD0, 0x07)
|
|
d.writeReg(SD_CONFIG_WOI_SD1, 0x05)
|
|
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD0, 6)
|
|
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD1, 6)
|
|
case MEDIUM:
|
|
// timing config
|
|
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_A, 0x0B)
|
|
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_B, 0x09)
|
|
d.writeReg(RANGE_CONFIG_VALID_PHASE_HIGH, 0x78)
|
|
|
|
// dynamic config
|
|
d.writeReg(SD_CONFIG_WOI_SD0, 0x0B)
|
|
d.writeReg(SD_CONFIG_WOI_SD1, 0x09)
|
|
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD0, 10)
|
|
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD1, 10)
|
|
case LONG:
|
|
// timing config
|
|
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_A, 0x0F)
|
|
d.writeReg(RANGE_CONFIG_VCSEL_PERIOD_B, 0x0D)
|
|
d.writeReg(RANGE_CONFIG_VALID_PHASE_HIGH, 0xB8)
|
|
|
|
// dynamic config
|
|
d.writeReg(SD_CONFIG_WOI_SD0, 0x0F)
|
|
d.writeReg(SD_CONFIG_WOI_SD1, 0x0D)
|
|
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD0, 14)
|
|
d.writeReg(SD_CONFIG_INITIAL_PHASE_SD1, 14)
|
|
default:
|
|
return false
|
|
}
|
|
|
|
d.SetMeasurementTimingBudget(budgetMicroseconds)
|
|
d.mode = mode
|
|
return true
|
|
}
|
|
|
|
// GetMeasurementTimingBudget returns the timing budget in microseconds
|
|
func (d *Device) GetMeasurementTimingBudget() uint32 {
|
|
macroPeriod := d.calculateMacroPeriod(uint32(d.readReg(RANGE_CONFIG_VCSEL_PERIOD_A)))
|
|
rangeConfigTimeout := timeoutMclksToMicroseconds(decodeTimeout(d.readReg16Bit(RANGE_CONFIG_TIMEOUT_MACROP_A)), macroPeriod)
|
|
return 2 * uint32(rangeConfigTimeout) * TIMING_GUARD
|
|
}
|
|
|
|
// SetMeasurementTimingBudget configures the timing budget in microseconds
|
|
// It returns false if an invalid timing budget is provided
|
|
func (d *Device) SetMeasurementTimingBudget(budgetMicroseconds uint32) bool {
|
|
if budgetMicroseconds <= TIMING_GUARD {
|
|
return false
|
|
}
|
|
budgetMicroseconds -= TIMING_GUARD
|
|
if budgetMicroseconds > 1100000 {
|
|
return false
|
|
}
|
|
rangeConfigTimeout := budgetMicroseconds / 2
|
|
// Update Macro Period for Range A VCSEL Period
|
|
macroPeriod := d.calculateMacroPeriod(uint32(d.readReg(RANGE_CONFIG_VCSEL_PERIOD_A)))
|
|
|
|
// Update Phase timeout - uses Timing A
|
|
phasecalTimeoutMclks := timeoutMicrosecondsToMclks(1000, macroPeriod)
|
|
if phasecalTimeoutMclks > 0xFF {
|
|
phasecalTimeoutMclks = 0xFF
|
|
}
|
|
d.writeReg(PHASECAL_CONFIG_TIMEOUT_MACROP, uint8(phasecalTimeoutMclks))
|
|
|
|
// Update MM Timing A timeout
|
|
d.writeReg16Bit(MM_CONFIG_TIMEOUT_MACROP_A, encodeTimeout(timeoutMicrosecondsToMclks(1, macroPeriod)))
|
|
// Update Range Timing A timeout
|
|
d.writeReg16Bit(RANGE_CONFIG_TIMEOUT_MACROP_A, encodeTimeout(timeoutMicrosecondsToMclks(rangeConfigTimeout, macroPeriod)))
|
|
|
|
macroPeriod = d.calculateMacroPeriod(uint32(d.readReg(RANGE_CONFIG_VCSEL_PERIOD_B)))
|
|
// Update MM Timing B timeout
|
|
d.writeReg16Bit(MM_CONFIG_TIMEOUT_MACROP_B, encodeTimeout(timeoutMicrosecondsToMclks(1, macroPeriod)))
|
|
// Update Range Timing B timeout
|
|
d.writeReg16Bit(RANGE_CONFIG_TIMEOUT_MACROP_B, encodeTimeout(timeoutMicrosecondsToMclks(rangeConfigTimeout, macroPeriod)))
|
|
|
|
return true
|
|
}
|
|
|
|
// Read stores in the buffer the values of the sensor and returns
|
|
// the current distance in mm
|
|
func (d *Device) Read(blocking bool) uint16 {
|
|
if blocking {
|
|
start := time.Now()
|
|
|
|
for !d.dataReady() {
|
|
elapsed := time.Since(start)
|
|
if d.timeout > 0 && uint32(elapsed.Seconds()*1000) > d.timeout {
|
|
d.rangingData.status = None
|
|
d.rangingData.mm = 0
|
|
d.rangingData.signalRateMCPS = 0
|
|
d.rangingData.ambientRateMCPS = 0
|
|
return d.rangingData.mm
|
|
}
|
|
}
|
|
}
|
|
d.readResults()
|
|
|
|
if !d.calibrated {
|
|
d.setupManualCalibration()
|
|
d.calibrated = true
|
|
}
|
|
|
|
d.updateDSS()
|
|
d.getRangingData()
|
|
d.writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01) //sys_interrupt_clear_range
|
|
|
|
return d.rangingData.mm
|
|
}
|
|
|
|
// updateDSS updates the DSS
|
|
func (d *Device) updateDSS() {
|
|
spadCount := d.results.effectiveSPADCount
|
|
if spadCount != 0 {
|
|
totalRatePerSpad := uint32(d.results.signalRateCrosstalkMCPSSD0) + uint32(d.results.ambientRateMCPSSD0)
|
|
if totalRatePerSpad > 0xFFFF {
|
|
totalRatePerSpad = 0xFFFF
|
|
}
|
|
totalRatePerSpad <<= 16
|
|
totalRatePerSpad /= uint32(spadCount)
|
|
if totalRatePerSpad != 0 {
|
|
requireSpads := (uint32(TARGETRATE) << 16) / totalRatePerSpad
|
|
if requireSpads > 0xFFFF {
|
|
requireSpads = 0xFFFF
|
|
}
|
|
d.writeReg16Bit(DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT, uint16(requireSpads))
|
|
return
|
|
}
|
|
}
|
|
d.writeReg16Bit(DSS_CONFIG_MANUAL_EFFECTIVE_SPADS_SELECT, 0x8000)
|
|
}
|
|
|
|
// readResults read the register and stores the data in the results buffer
|
|
func (d *Device) readResults() {
|
|
data := make([]byte, 17)
|
|
msb := byte((RESULT_RANGE_STATUS >> 8) & 0xFF)
|
|
lsb := byte(RESULT_RANGE_STATUS & 0xFF)
|
|
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
|
|
d.results.status = data[0]
|
|
// data[1] report_status : not used
|
|
d.results.streamCount = data[2]
|
|
d.results.effectiveSPADCount = readUint(data[3], data[4])
|
|
// data[5] , data[6] peak signal count rate mcps sd0 : not used
|
|
d.results.ambientRateMCPSSD0 = readUint(data[7], data[8])
|
|
// data[9] , data[10] sigma_sd0 : not used
|
|
// data[11] , data[12] phase_sd0 : not used
|
|
d.results.mmCrosstalkSD0 = readUint(data[13], data[14])
|
|
d.results.signalRateCrosstalkMCPSSD0 = readUint(data[15], data[16])
|
|
}
|
|
|
|
// dataReady returns true when the data is ready to be read
|
|
func (d *Device) dataReady() bool {
|
|
return (d.readReg(GPIO_TIO_HV_STATUS) & 0x01) == 0
|
|
}
|
|
|
|
// Distance returns the distance in mm
|
|
func (d *Device) Distance() int32 {
|
|
return int32(d.rangingData.mm)
|
|
}
|
|
|
|
// Status returns the status of the sensor
|
|
func (d *Device) Status() RangeStatus {
|
|
return d.rangingData.status
|
|
}
|
|
|
|
// SignalRate returns the peak signal rate in count per second (cps)
|
|
func (d *Device) SignalRate() int32 {
|
|
return d.rangingData.signalRateMCPS
|
|
}
|
|
|
|
// AmbientRate returns the ambient rate in count per second (cps)
|
|
func (d *Device) AmbientRate() int32 {
|
|
return d.rangingData.ambientRateMCPS
|
|
}
|
|
|
|
// EffectiveSPADCount returns the effective number of SPADs
|
|
func (d *Device) EffectiveSPADCount() uint16 {
|
|
return d.rangingData.effectiveSPADCount
|
|
}
|
|
|
|
// getRangingData stores in the buffer the ranging data
|
|
func (d *Device) getRangingData() {
|
|
d.rangingData.mm = uint16((uint32(d.results.mmCrosstalkSD0)*2011 + 0x0400) / 0x0800)
|
|
switch d.results.status {
|
|
case 1, // VCSELCONTINUITYTESTFAILURE
|
|
2, // VCSELWATCHDOGTESTFAILURE
|
|
3, // NOVHVVALUEFOUND
|
|
17: // MULTCLIPFAIL
|
|
d.rangingData.status = HardwareFail
|
|
|
|
case 13: // USERROICLIP
|
|
d.rangingData.status = MinRangeFail
|
|
|
|
case 18: // GPHSTREAMCOUNT0READY
|
|
d.rangingData.status = SynchronizationInt
|
|
|
|
case 5: // RANGEPHASECHECK
|
|
d.rangingData.status = OutOfBoundsFail
|
|
|
|
case 4: // MSRCNOTARGET
|
|
d.rangingData.status = SignalFail
|
|
|
|
case 6: // SIGMATHRESHOLDCHECK
|
|
d.rangingData.status = SignalFail
|
|
|
|
case 7: // PHASECONSISTENCY
|
|
d.rangingData.status = WrapTargetFail
|
|
|
|
case 12: // RANGEIGNORETHRESHOLD
|
|
d.rangingData.status = XtalkSignalFail
|
|
|
|
case 8: // MINCLIP
|
|
d.rangingData.status = RangeValidMinRangeClipped
|
|
|
|
case 9: // RANGECOMPLETE
|
|
if d.results.streamCount == 0 {
|
|
d.rangingData.status = RangeValidNoWrapCheckFail
|
|
} else {
|
|
d.rangingData.status = RangeValid
|
|
}
|
|
|
|
default:
|
|
d.rangingData.status = None
|
|
}
|
|
|
|
d.rangingData.signalRateMCPS = 1000000 * int32(d.results.signalRateCrosstalkMCPSSD0) / (1 << 7)
|
|
d.rangingData.ambientRateMCPS = 1000000 * int32(d.results.ambientRateMCPSSD0) / (1 << 7)
|
|
d.rangingData.effectiveSPADCount = d.results.effectiveSPADCount
|
|
}
|
|
|
|
// setupManualCalibration configures the manual calibration
|
|
func (d *Device) setupManualCalibration() {
|
|
// save original VHV configs
|
|
d.VHVInit = d.readReg(VHV_CONFIG_INIT)
|
|
d.VHVTimeout = d.readReg(VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND)
|
|
|
|
// disable VHV init
|
|
d.writeReg(VHV_CONFIG_INIT, d.VHVInit&0x7F)
|
|
|
|
// set loop bound to tuning param
|
|
d.writeReg(VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, (d.VHVTimeout&0x03)+(3<<2))
|
|
|
|
// override phasecal
|
|
d.writeReg(PHASECAL_CONFIG_OVERRIDE, 0x01)
|
|
d.writeReg(CAL_CONFIG_VCSEL_START, d.readReg(PHASECAL_RESULT_VCSEL_START))
|
|
}
|
|
|
|
// StartContinuous starts the continuous sensing mode
|
|
func (d *Device) StartContinuous(periodMs uint32) {
|
|
d.writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, periodMs*uint32(d.oscillatorOffset))
|
|
d.writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01) // sys_interrupt_clear_range
|
|
d.writeReg(SYSTEM_MODE_START, 0x40) // mode_range_timed
|
|
}
|
|
|
|
// StopContinuous stops the continuous sensing mode
|
|
func (d *Device) StopContinuous() {
|
|
d.writeReg(SYSTEM_MODE_START, 0x80) // mode_range_abort
|
|
|
|
d.calibrated = false
|
|
|
|
// restore vhv configs
|
|
if d.VHVInit != 0 {
|
|
d.writeReg(VHV_CONFIG_INIT, d.VHVInit)
|
|
}
|
|
if d.VHVTimeout != 0 {
|
|
d.writeReg(VHV_CONFIG_TIMEOUT_MACROP_LOOP_BOUND, d.VHVTimeout)
|
|
}
|
|
|
|
// remove phasecal override
|
|
d.writeReg(PHASECAL_CONFIG_OVERRIDE, 0x00)
|
|
}
|
|
|
|
// SetROI sets the 'region of interest' for x and y coordinates. Valid ranges are from 4/4 to 16/16.
|
|
func (d *Device) SetROI(x, y uint8) error {
|
|
if !validROIRange(x, y) {
|
|
return errors.New("ROI value out of range")
|
|
}
|
|
|
|
if x > 10 || y > 10 {
|
|
d.writeReg(ROI_CONFIG_USER_ROI_CENTRE_SPAD, 199)
|
|
}
|
|
|
|
d.writeReg(ROI_CONFIG_USER_ROI_REQUESTED_GLOBAL_XY_SIZE, (y-1)<<4|(x-1))
|
|
return nil
|
|
}
|
|
|
|
// GetROI returns the currently configured 'region of interest' for x and y coordinates.
|
|
func (d *Device) GetROI() (x, y uint8, err error) {
|
|
reg := d.readReg(ROI_CONFIG_USER_ROI_REQUESTED_GLOBAL_XY_SIZE)
|
|
|
|
x = (reg & 0x0f) + 1
|
|
y = ((reg & 0xf0) >> 4) + 1
|
|
|
|
if !validROIRange(x, y) {
|
|
err = errors.New("ROI value out of range")
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func validROIRange(x, y uint8) bool {
|
|
return x >= 4 && x <= 16 && y >= 4 && y <= 16
|
|
}
|
|
|
|
// writeReg sends a single byte to the specified register address
|
|
func (d *Device) writeReg(reg uint16, value uint8) {
|
|
msb := byte((reg >> 8) & 0xFF)
|
|
lsb := byte(reg & 0xFF)
|
|
d.bus.Tx(d.Address, []byte{msb, lsb, value}, nil)
|
|
}
|
|
|
|
// writeReg16Bit sends two bytes to the specified register address
|
|
func (d *Device) writeReg16Bit(reg uint16, value uint16) {
|
|
data := make([]byte, 4)
|
|
data[0] = byte((reg >> 8) & 0xFF)
|
|
data[1] = byte(reg & 0xFF)
|
|
data[2] = byte((value >> 8) & 0xFF)
|
|
data[3] = byte(value & 0xFF)
|
|
d.bus.Tx(d.Address, data, nil)
|
|
}
|
|
|
|
// writeReg32Bit sends four bytes to the specified register address
|
|
func (d *Device) writeReg32Bit(reg uint16, value uint32) {
|
|
data := make([]byte, 6)
|
|
data[0] = byte((reg >> 8) & 0xFF)
|
|
data[1] = byte(reg & 0xFF)
|
|
data[2] = byte((value >> 24) & 0xFF)
|
|
data[3] = byte((value >> 16) & 0xFF)
|
|
data[4] = byte((value >> 8) & 0xFF)
|
|
data[5] = byte(value & 0xFF)
|
|
d.bus.Tx(d.Address, data, nil)
|
|
}
|
|
|
|
// readReg reads a single byte from the specified address
|
|
func (d *Device) readReg(reg uint16) uint8 {
|
|
data := []byte{0}
|
|
msb := byte((reg >> 8) & 0xFF)
|
|
lsb := byte(reg & 0xFF)
|
|
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
|
|
return data[0]
|
|
}
|
|
|
|
// readReg16Bit reads two bytes from the specified address
|
|
// and returns it as a uint16
|
|
func (d *Device) readReg16Bit(reg uint16) uint16 {
|
|
data := []byte{0, 0}
|
|
msb := byte((reg >> 8) & 0xFF)
|
|
lsb := byte(reg & 0xFF)
|
|
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
|
|
return readUint(data[0], data[1])
|
|
}
|
|
|
|
// readReg32Bit reads four bytes from the specified address
|
|
// and returns it as a uint32
|
|
func (d *Device) readReg32Bit(reg uint16) uint32 {
|
|
data := make([]byte, 4)
|
|
msb := byte((reg >> 8) & 0xFF)
|
|
lsb := byte(reg & 0xFF)
|
|
d.bus.Tx(d.Address, []byte{msb, lsb}, data)
|
|
return readUint32(data)
|
|
}
|
|
|
|
// readUint converts two bytes to uint16
|
|
func readUint(msb byte, lsb byte) uint16 {
|
|
return (uint16(msb) << 8) | uint16(lsb)
|
|
}
|
|
|
|
// readUint converts four bytes to uint32
|
|
func readUint32(data []byte) uint32 {
|
|
if len(data) != 4 {
|
|
return 0
|
|
}
|
|
var value uint32
|
|
value = uint32(data[0]) << 24
|
|
value |= uint32(data[1]) << 16
|
|
value |= uint32(data[2]) << 8
|
|
value |= uint32(data[3])
|
|
return value
|
|
}
|
|
|
|
// encodeTimeout encodes the timeout in the correct format: (LSByte * 2^MSByte) + 1
|
|
func encodeTimeout(timeoutMclks uint32) uint16 {
|
|
if timeoutMclks == 0 {
|
|
return 0
|
|
}
|
|
msb := 0
|
|
lsb := timeoutMclks - 1
|
|
for (lsb & 0xFFFFFF00) > 0 {
|
|
lsb >>= 1
|
|
msb++
|
|
}
|
|
return uint16(msb<<8) | uint16(lsb&0xFF)
|
|
}
|
|
|
|
// decodeTimeout decodes the timeout from the format: (LSByte * 2^MSByte) + 1
|
|
func decodeTimeout(regVal uint16) uint32 {
|
|
return (uint32(regVal&0xFF) << (regVal >> 8)) + 1
|
|
}
|
|
|
|
// timeoutMclksToMicroseconds transform from mclks to microseconds
|
|
func timeoutMclksToMicroseconds(timeoutMclks uint32, macroPeriodMicroseconds uint32) uint32 {
|
|
return uint32((uint64(timeoutMclks)*uint64(macroPeriodMicroseconds) + 0x800) >> 12)
|
|
}
|
|
|
|
// timeoutMicrosecondsToMclks transform from microseconds to mclks
|
|
func timeoutMicrosecondsToMclks(timeoutMicroseconds uint32, macroPeriodMicroseconds uint32) uint32 {
|
|
return ((timeoutMicroseconds << 12) + (macroPeriodMicroseconds >> 1)) / macroPeriodMicroseconds
|
|
}
|
|
|
|
// calculateMacroPerios calculates the macro period in microsendos from the vcsel period
|
|
func (d *Device) calculateMacroPeriod(vcselPeriod uint32) uint32 {
|
|
pplPeriodMicroseconds := (uint32(1) << 30) / uint32(d.fastOscillatorFreq)
|
|
vcselPeriodPclks := (vcselPeriod + 1) << 1
|
|
macroPeriodMicroseconds := 2304 * pplPeriodMicroseconds
|
|
macroPeriodMicroseconds >>= 6
|
|
macroPeriodMicroseconds *= vcselPeriodPclks
|
|
macroPeriodMicroseconds >>= 6
|
|
return macroPeriodMicroseconds
|
|
}
|