CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

VL53L0X working example

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
PrinceNai



Joined: 31 Oct 2016
Posts: 452
Location: Montenegro

View user's profile Send private message

VL53L0X working example
PostPosted: Sun Jan 21, 2018 12:54 pm     Reply with quote

Hello,

This is the code for vl53l0x ToF sensor. All the files I used in the project are here, just cut them to separate pieces. It isn't polished, so far it just shows the result of a measurement on a LCD. Thanks to the original poster on GitHub here: https://github.com/LILCMU/vl53l0x-ccs

Best regards, Samo


Code:


//******************************************************************************
//   main.c
//******************************************************************************

/* This example shows how to get single-shot range
 measurements from the VL53L0X. The sensor can optionally be
 configured with different ranging profiles, as described in
 the VL53L0X API user manual, to get better performance for
 a certain application. This code is based on the four
 "SingleRanging" examples in the VL53L0X API.
 The range readings are in units of mm.
 
 The original code was found here: https://github.com/LILCMU/vl53l0x-ccs
 Thank you Arnan (Roger) Sipitakiat for porting to CCS.
 
 It is modified to work on 18f252, the result is displayed on LCD.
 

 
 */
//#include <18f252.h>
//#include <Wire.h>

#include <main.h>

#define millis()(msTimer)
int32 msTimer=0;

#include <VL53L0X.h>
#include <VL53L0X.c>
#include <flex_lcd.c>

#INT_TIMER2                   //1ms interrupt
void  TIMER2_isr(void) {
   msTimer++;
}

void pic_setup() {
   setup_timer_2(T2_DIV_BY_16,156,2);      //502 us overflow, 1,0 ms interrupt
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);
   lcd_init();
   lcd_putc('\f');            //CLEAR lcd   
   lcd_gotoxy(1,1);
   delay_ms(100);
}

//*****************************************************************************
// VL53L0X sensor;

// Uncomment this line to use long range mode. This
// increases the sensitivity of the sensor and extends its
// potential range, but increases the likelihood of getting
// an inaccurate reading because of reflections from objects
// other than the intended target. It works best in dark
// conditions.

//#define LONG_RANGE

// Uncomment ONE of these two lines to get
// - higher speed at the cost of lower accuracy OR
// - higher accuracy at the cost of lower speed

//#define HIGH_SPEED
#define HIGH_ACCURACY


void setup()
{

// C5 is connected to XSHUT on a module
   output_low(PIN_C5);          // reset module
   delay_ms(1000);
   output_high(PIN_C5);

//   printf("Starting\r\n");        //original
   
   pic_setup();
   printf(lcd_putc, "Starting...");
   delay_ms(1000);
   init();
   setTimeout(200);

#if defined LONG_RANGE
  // lower the return signal rate limit (default is 0.25 MCPS)
   setSignalRateLimit(0.1);
  // increase laser pulse periods (defaults are 14 and 10 PCLKs)
   setVcselPulsePeriod(VcselPeriodPreRange, 18);
   setVcselPulsePeriod(VcselPeriodFinalRange, 14);
#endif

#if defined HIGH_SPEED
  // reduce timing budget to 20 ms (default is about 33 ms)
   setMeasurementTimingBudget(20000);
#elif defined HIGH_ACCURACY
  // increase timing budget to 200 ms
   setMeasurementTimingBudget(200000);
#endif
}
//*****************************************************************************
void loop()
{
//   printf("%lu\r\n", readRangeSingleMillimeters());
//   if (timeoutOccurred()) { printf(" TIMEOUT\r\n"); }
   delay_us(1);
   printf(lcd_putc,"%lu", readRangeSingleMillimeters());
   if (timeoutOccurred()) {
      delay_us(1);                        // delays of 1us are for debugging purpose only
      printf(lcd_putc, " TIMEOUT....");   // to be done, so far it doesn't timeout
      delay_us(1);
      }
     
   delay_us(1);
}
//*****************************************************************************

void main() {

   setup();
   while(1) {
      delay_ms(200);   
      lcd_putc('\f');            //CLEAR lcd   
      lcd_gotoxy(1,1);
      loop();

   }

}

//******************************************************************************
//   main.h
//******************************************************************************
#include <18F252.h>
#device ADC=16

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES WDT128                   //Watch Dog Timer uses 1:128 Postscale
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O

#device ICD=TRUE
#use delay(crystal=20000000)
//#use i2c(Master,SLOW,sda=PIN_C4,scl=PIN_C3,force_hw)
#use i2c(Master,sda=PIN_C4,scl=PIN_C3,force_hw)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

#define RUN_BUTTON PIN_B7     // not used!!!

//******************************************************************************
//   VL53L0X.h
//******************************************************************************
#ifndef VL53L0X_h
#define VL53L0X_h

//#include <Arduino.h>


    // register addresses from API vl53l0x_device.h (ordered as listed there)
    enum regAddr
    {
      SYSRANGE_START                              = 0x00,

      SYSTEM_THRESH_HIGH                          = 0x0C,
      SYSTEM_THRESH_LOW                           = 0x0E,

      SYSTEM_SEQUENCE_CONFIG                      = 0x01,
      SYSTEM_RANGE_CONFIG                         = 0x09,
      SYSTEM_INTERMEASUREMENT_PERIOD              = 0x04,

      SYSTEM_INTERRUPT_CONFIG_GPIO                = 0x0A,

      GPIO_HV_MUX_ACTIVE_HIGH                     = 0x84,

      SYSTEM_INTERRUPT_CLEAR                      = 0x0B,

      RESULT_INTERRUPT_STATUS                     = 0x13,
      RESULT_RANGE_STATUS                         = 0x14,

      RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN       = 0xBC,
      RESULT_CORE_RANGING_TOTAL_EVENTS_RTN        = 0xC0,
      RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF       = 0xD0,
      RESULT_CORE_RANGING_TOTAL_EVENTS_REF        = 0xD4,
      RESULT_PEAK_SIGNAL_RATE_REF                 = 0xB6,

      ALGO_PART_TO_PART_RANGE_OFFSET_MM           = 0x28,

      I2C_SLAVE_DEVICE_ADDRESS                    = 0x8A,

      MSRC_CONFIG_CONTROL                         = 0x60,

      PRE_RANGE_CONFIG_MIN_SNR                    = 0x27,
      PRE_RANGE_CONFIG_VALID_PHASE_LOW            = 0x56,
      PRE_RANGE_CONFIG_VALID_PHASE_HIGH           = 0x57,
      PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT          = 0x64,

      FINAL_RANGE_CONFIG_MIN_SNR                  = 0x67,
      FINAL_RANGE_CONFIG_VALID_PHASE_LOW          = 0x47,
      FINAL_RANGE_CONFIG_VALID_PHASE_HIGH         = 0x48,
      FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT = 0x44,

      PRE_RANGE_CONFIG_SIGMA_THRESH_HI            = 0x61,
      PRE_RANGE_CONFIG_SIGMA_THRESH_LO            = 0x62,

      PRE_RANGE_CONFIG_VCSEL_PERIOD               = 0x50,
      PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI          = 0x51,
      PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO          = 0x52,

      SYSTEM_HISTOGRAM_BIN                        = 0x81,
      HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT       = 0x33,
      HISTOGRAM_CONFIG_READOUT_CTRL               = 0x55,

      FINAL_RANGE_CONFIG_VCSEL_PERIOD             = 0x70,
      FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI        = 0x71,
      FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO        = 0x72,
      CROSSTALK_COMPENSATION_PEAK_RATE_MCPS       = 0x20,

      MSRC_CONFIG_TIMEOUT_MACROP                  = 0x46,

      SOFT_RESET_GO2_SOFT_RESET_N                 = 0xBF,
      IDENTIFICATION_MODEL_ID                     = 0xC0,
      IDENTIFICATION_REVISION_ID                  = 0xC2,

      OSC_CALIBRATE_VAL                           = 0xF8,

      GLOBAL_CONFIG_VCSEL_WIDTH                   = 0x32,
      GLOBAL_CONFIG_SPAD_ENABLES_REF_0            = 0xB0,
      GLOBAL_CONFIG_SPAD_ENABLES_REF_1            = 0xB1,
      GLOBAL_CONFIG_SPAD_ENABLES_REF_2            = 0xB2,
      GLOBAL_CONFIG_SPAD_ENABLES_REF_3            = 0xB3,
      GLOBAL_CONFIG_SPAD_ENABLES_REF_4            = 0xB4,
      GLOBAL_CONFIG_SPAD_ENABLES_REF_5            = 0xB5,

      GLOBAL_CONFIG_REF_EN_START_SELECT           = 0xB6,
      DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD         = 0x4E,
      DYNAMIC_SPAD_REF_EN_START_OFFSET            = 0x4F,
      POWER_MANAGEMENT_GO1_POWER_FORCE            = 0x80,

      VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV           = 0x89,

      ALGO_PHASECAL_LIM                           = 0x30,
      ALGO_PHASECAL_CONFIG_TIMEOUT                = 0x30,
    };

    // private variables

    typedef struct
    {
      boolean tcc, msrc, dss, pre_range, final_range;
    } SequenceStepEnables;



    typedef struct
    {
      int16 pre_range_vcsel_period_pclks, final_range_vcsel_period_pclks;

      int16 msrc_dss_tcc_mclks, pre_range_mclks, final_range_mclks;
      int32 msrc_dss_tcc_us,    pre_range_us,    final_range_us;
    } SequenceStepTimeouts;

    void getSequenceStepEnables(SequenceStepEnables * enables);
    void getSequenceStepTimeouts(SequenceStepEnables * enables, SequenceStepTimeouts * timeouts);
   
   
    enum vcselPeriodType { VcselPeriodPreRange, VcselPeriodFinalRange };

    int8 address;
    int16 io_timeout;
    boolean did_timeout;
    int16 timeout_start_ms;

    int8 stop_variable; // read by init and used when starting measurement; is StopVariable field of VL53L0X_DevData_t structure in API
    int32 measurement_timing_budget_us;

    boolean getSpadInfo(int8 * count, boolean * type_is_aperture);

    int8 last_status; // status of last I2C transmission

    VL53L0X(void);

    void setAddress(int8 new_addr);
    int8 getAddress(void) { return address; }

    boolean init(boolean io_2v8 = TRUE);
    #separate boolean init2();

    void writeReg(int8 reg, int8 value);
    void writeReg16Bit(int8 reg, int16 value);
    void writeReg32Bit(int8 reg, int32 value);
    int8 readReg(int8 reg);
    int16 readReg16Bit(int8 reg);
    int32 readReg32Bit(int8 reg);

    void writeMulti(int8 reg, int8  * src, int8 count);
    void readMulti(int8 reg, int8 * dst, int8 count);

    boolean setSignalRateLimit(float limit_Mcps);
    float getSignalRateLimit(void);

    boolean setMeasurementTimingBudget(int32 budget_us);
    int32 getMeasurementTimingBudget(void);

    boolean setVcselPulsePeriod(vcselPeriodType type, int8 period_pclks);
    int8 getVcselPulsePeriod(vcselPeriodType type);

    void startContinuous(int32 period_ms = 0);
    void stopContinuous(void);
    int16 readRangeContinuousMillimeters(void);
    int16 readRangeSingleMillimeters(void);

    void setTimeout(int16 timeout) { io_timeout = timeout; }
    int16 getTimeout(void) { return io_timeout; }
    boolean timeoutOccurred(void);



    boolean performSingleRefCalibration(int8 vhv_init_byte);

    static int16 decodeTimeout(int16 value);
    static int16 encodeTimeout(int16 timeout_mclks);
    static int32 timeoutMclksToMicroseconds(int16 timeout_period_mclks, int8 vcsel_period_pclks);
    static int32 timeoutMicrosecondsToMclks(int32 timeout_period_us, int8 vcsel_period_pclks);


#endif

//******************************************************************************
//   VL53L0X.c
//******************************************************************************

// Most of the functionality of this library is based on the VL53L0X API
// provided by ST (STSW-IMG005), and some of the explanatory comments are quoted
// or paraphrased from the API source code, API user manual (UM2039), and the
// VL53L0X datasheet.

#include <VL53L0X.h>
// #include <Wire.h>

// Defines /////////////////////////////////////////////////////////////////////

// The Arduino two-wire interface uses a 7-bit number for the address,
// and sets the last bit correctly based on reads and writes
//#define ADDRESS_DEFAULT 0b0101001

// 8 bit i2c address
#define ADDRESS_DEFAULT 0b01010010              //0x52


// Record the current time to check an upcoming timeout against
#define startTimeout() (timeout_start_ms = millis())
//#define startTimeout() (timeout_start_ms = 0)

// Check if timeout is enabled (set to nonzero value) and has expired
#define checkTimeoutExpired() (io_timeout > 0 && ((int16)millis() - timeout_start_ms) > io_timeout)

// Decode VCSEL (vertical cavity surface emitting laser) pulse period in PCLKs
// from register value
// based on VL53L0X_decode_vcsel_period()
#define decodeVcselPeriod(reg_val)      (((reg_val) + 1) << 1)

// Encode VCSEL pulse period register value from period in PCLKs
// based on VL53L0X_encode_vcsel_period()
#define encodeVcselPeriod(period_pclks) (((period_pclks) >> 1) - 1)

// Calculate macro period in *nanoseconds* from VCSEL period in PCLKs
// based on VL53L0X_calc_macro_period_ps()
// PLL_period_ps = 1655; macro_period_vclks = 2304
#define calcMacroPeriod(vcsel_period_pclks) ((((int32)2304 * (vcsel_period_pclks) * 1655) + 500) / 1000)

// Constructors ////////////////////////////////////////////////////////////////

// VL53L0X(void)
//   : address(ADDRESS_DEFAULT)
//   , io_timeout(0) // no timeout
//   , did_timeout(false)
// {
// }
//!VL53L0X(void) {
//!  address = (ADDRESS_DEFAULT);
//!  io_timeout =0 ; // no timeout
//!  did_timeout = false;
//!}
// Public Methods //////////////////////////////////////////////////////////////

void setAddress(int8 new_addr)
{
  writeReg(I2C_SLAVE_DEVICE_ADDRESS, new_addr & 0x7F);
  address = new_addr;
}

// Initialize sensor using sequence based on VL53L0X_DataInit(),
// VL53L0X_StaticInit(), and VL53L0X_PerformRefCalibration().
// This function does not perform reference SPAD calibration
// (VL53L0X_PerformRefSpadManagement()), since the API user manual says that it
// is performed by ST on the bare modules; it seems like that should work well
// enough unless a cover glass is added.
// If io_2v8 (optional) is true or not given, the sensor is configured for 2V8
// mode.
boolean init(boolean io_2v8)
{
  // VL53L0X_DataInit() begin
  address = (ADDRESS_DEFAULT);
  io_timeout =0 ; // no timeout
  did_timeout = false;
 
  // sensor uses 1V8 mode for I/O by default; switch to 2V8 mode if necessary
  if (io_2v8)
  {
    writeReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV,
      readReg(VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV) | 0x01); // set bit 0
  }

  // "Set I2C standard mode"
  writeReg(0x88, 0x00);

  writeReg(0x80, 0x01);
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);
  stop_variable = readReg(0x91);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  // disable SIGNAL_RATE_MSRC (bit 1) and SIGNAL_RATE_PRE_RANGE (bit 4) limit checks
  writeReg(MSRC_CONFIG_CONTROL, readReg(MSRC_CONFIG_CONTROL) | 0x12);

  // set final range signal rate limit to 0.25 MCPS (million counts per second)
  setSignalRateLimit(0.25);

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0xFF);

  // VL53L0X_DataInit() end

  // VL53L0X_StaticInit() begin

  int8 spad_count;
  //boolean spad_type_is_aperture;    // boolean can't be used as a pointer
  int8 spad_type_is_aperture;
 
 
 
  if (!getSpadInfo(&spad_count, &spad_type_is_aperture)) { return false; }

  // The SPAD map (RefGoodSpadMap) is read by VL53L0X_get_info_from_device() in
  // the API, but the same data seems to be more easily readable from
  // GLOBAL_CONFIG_SPAD_ENABLES_REF_0 through _6, so read it from there
  int8 ref_spad_map[6];
  readMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);

  // -- VL53L0X_set_reference_spads() begin (assume NVM values are valid)

  writeReg(0xFF, 0x01);
  writeReg(DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00);
  writeReg(DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C);
  writeReg(0xFF, 0x00);
  writeReg(GLOBAL_CONFIG_REF_EN_START_SELECT, 0xB4);

  int8 first_spad_to_enable = spad_type_is_aperture ? 12 : 0; // 12 is the first aperture spad
  int8 spads_enabled = 0;

  for (int8 i = 0; i < 48; i++)
  {
    if (i < first_spad_to_enable || spads_enabled == spad_count)
    {
      // This bit is lower than the first one that should be enabled, or
      // (reference_spad_count) bits have already been enabled, so zero this bit
      ref_spad_map[i / 8] &= ~(1 << (i % 8));
    }
    else if ((ref_spad_map[i / 8] >> (i % 8)) & 0x1)
    {
      spads_enabled++;
    }
  }

  writeMulti(GLOBAL_CONFIG_SPAD_ENABLES_REF_0, ref_spad_map, 6);

  // -- VL53L0X_set_reference_spads() end

  // -- VL53L0X_load_tuning_settings() begin
  // DefaultTuningSettings from vl53l0x_tuning.h

  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);

  writeReg(0xFF, 0x00);
  writeReg(0x09, 0x00);
  writeReg(0x10, 0x00);
  writeReg(0x11, 0x00);

  writeReg(0x24, 0x01);
  writeReg(0x25, 0xFF);
  writeReg(0x75, 0x00);

  writeReg(0xFF, 0x01);
  writeReg(0x4E, 0x2C);
  writeReg(0x48, 0x00);
  writeReg(0x30, 0x20);

  writeReg(0xFF, 0x00);
  writeReg(0x30, 0x09);
  writeReg(0x54, 0x00);
  writeReg(0x31, 0x04);
  writeReg(0x32, 0x03);
  writeReg(0x40, 0x83);
  writeReg(0x46, 0x25);
  writeReg(0x60, 0x00);
  writeReg(0x27, 0x00);
  writeReg(0x50, 0x06);
  writeReg(0x51, 0x00);
  writeReg(0x52, 0x96);
  writeReg(0x56, 0x08);
  writeReg(0x57, 0x30);
  writeReg(0x61, 0x00);
  writeReg(0x62, 0x00);
  writeReg(0x64, 0x00);
  writeReg(0x65, 0x00);
  writeReg(0x66, 0xA0);

  writeReg(0xFF, 0x01);
  writeReg(0x22, 0x32);
  writeReg(0x47, 0x14);
  writeReg(0x49, 0xFF);
  writeReg(0x4A, 0x00);

  writeReg(0xFF, 0x00);
  writeReg(0x7A, 0x0A);
  writeReg(0x7B, 0x00);
  writeReg(0x78, 0x21);

  writeReg(0xFF, 0x01);
  writeReg(0x23, 0x34);
  writeReg(0x42, 0x00);
  writeReg(0x44, 0xFF);
  writeReg(0x45, 0x26);
  writeReg(0x46, 0x05);
  writeReg(0x40, 0x40);
  writeReg(0x0E, 0x06);
  writeReg(0x20, 0x1A);
  writeReg(0x43, 0x40);

  writeReg(0xFF, 0x00);
  writeReg(0x34, 0x03);
  writeReg(0x35, 0x44);

  writeReg(0xFF, 0x01);
  writeReg(0x31, 0x04);
  writeReg(0x4B, 0x09);
  writeReg(0x4C, 0x05);
  writeReg(0x4D, 0x04);

  writeReg(0xFF, 0x00);
  writeReg(0x44, 0x00);
  writeReg(0x45, 0x20);
  writeReg(0x47, 0x08);
  writeReg(0x48, 0x28);
  writeReg(0x67, 0x00);
  writeReg(0x70, 0x04);
  writeReg(0x71, 0x01);
  writeReg(0x72, 0xFE);
  writeReg(0x76, 0x00);
  writeReg(0x77, 0x00);

  writeReg(0xFF, 0x01);
  writeReg(0x0D, 0x01);

  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x01);
  writeReg(0x01, 0xF8);

  writeReg(0xFF, 0x01);
  writeReg(0x8E, 0x01);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);
 
  return init2();


}


#separate boolean init2() {
  // -- VL53L0X_load_tuning_settings() end

  // "Set interrupt config to new sample ready"
  // -- VL53L0X_SetGpioConfig() begin

  writeReg(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04);
  writeReg(GPIO_HV_MUX_ACTIVE_HIGH, readReg(GPIO_HV_MUX_ACTIVE_HIGH) & ~0x10); // active low
  writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);

  // -- VL53L0X_SetGpioConfig() end

  measurement_timing_budget_us = getMeasurementTimingBudget();

  // "Disable MSRC and TCC by default"
  // MSRC = Minimum Signal Rate Check
  // TCC = Target CentreCheck
  // -- VL53L0X_SetSequenceStepEnable() begin

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);

  // -- VL53L0X_SetSequenceStepEnable() end

  // "Recalculate timing budget"
  setMeasurementTimingBudget(measurement_timing_budget_us);

  // VL53L0X_StaticInit() end

  // VL53L0X_PerformRefCalibration() begin (VL53L0X_perform_ref_calibration())

  // -- VL53L0X_perform_vhv_calibration() begin

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0x01);
  if (!performSingleRefCalibration(0x40)) { return false; }

  // -- VL53L0X_perform_vhv_calibration() end

  // -- VL53L0X_perform_phase_calibration() begin

  writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
  if (!performSingleRefCalibration(0x00)) { return false; }

  // -- VL53L0X_perform_phase_calibration() end

  // "restore the previous Sequence Config"
  writeReg(SYSTEM_SEQUENCE_CONFIG, 0xE8);

  // VL53L0X_PerformRefCalibration() end

  return true;
}

// Write an 8-bit register
void writeReg(int8 reg, int8 value)
{
//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  Wire.write(value);
//!  last_status = Wire.endTransmission();

// IN the Arduino, last_status has the following values
//!0:success
//!1:data too long to fit in transmit buffer
//!2:received NACK on transmit of address
//!3:received NACK on transmit of data
//!4:other error

// In our current port, last_status will only depend on the
// success or failure of the last transmitted byte
// 0 = success, got an ack
// 1 =  no ack (NACK)
// 2 = collision

i2c_start();
i2c_write(address);
i2c_write(reg);
last_status = i2c_write(value);
i2c_stop();

}

// Write a 16-bit register
void writeReg16Bit(int8 reg, int16 value)
{
//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  Wire.write((value >> 8) & 0xFF); // value high byte
//!  Wire.write( value       & 0xFF); // value low byte
//!  last_status = Wire.endTransmission();
i2c_start();
i2c_write(address);
i2c_write(reg);
i2c_write((value >> 8) & 0xFF);
last_status = i2c_write(value & 0xFF);
i2c_stop();

}

// Write a 32-bit register
void writeReg32Bit(int8 reg, int32 value)
{
//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  Wire.write((value >> 24) & 0xFF); // value highest byte
//!  Wire.write((value >> 16) & 0xFF);
//!  Wire.write((value >>  8) & 0xFF);
//!  Wire.write( value        & 0xFF); // value lowest byte
//!  last_status = Wire.endTransmission();

i2c_start();
i2c_write(address);
i2c_write(reg);
i2c_write((value >> 24) & 0xFF);
i2c_write((value >> 16) & 0xFF);
i2c_write((value >>  8) & 0xFF);
last_status = i2c_write(value & 0xFF);
i2c_stop();

}

// Read an 8-bit register
int8 readReg(int8 reg)
{
  int8 value;

//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  last_status = Wire.endTransmission();
//!
//!  Wire.requestFrom(address, (int8)1);
//!  value = Wire.read();
//!

  i2c_start();
  i2c_write(address);
  last_status = i2c_write(reg);
 
  i2c_start();
  i2c_write(address | 1);
  value = i2c_read(0);
 
  return value;

}

// Read a 16-bit register
int16 readReg16Bit(int8 reg)
{
  int16 value;

//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  last_status = Wire.endTransmission();
//!
//!  Wire.requestFrom(address, (int8)2);
//!  value  = (int16)Wire.read() << 8; // value high byte
//!  value |=           Wire.read();      // value low byte

  i2c_start();
  i2c_write(address);
  last_status = i2c_write(reg);
 
  i2c_start();
  i2c_write(address | 1);
  value = (int16)i2c_read() << 8;
  value |= i2c_read(0);

  return value;
}

// Read a 32-bit register
int32 readReg32Bit(int8 reg)
{
  int32 value;

//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  last_status = Wire.endTransmission();
//!
//!  Wire.requestFrom(address, (int8)4);
//!  value  = (int32)Wire.read() << 24; // value highest byte
//!  value |= (int32)Wire.read() << 16;
//!  value |= (int16)Wire.read() <<  8;
//!  value |=           Wire.read();       // value lowest byte

  i2c_start();
  i2c_write(address);
  last_status = i2c_write(reg);
 
  i2c_start();
  i2c_write(address | 1);
  value =  (int32)i2c_read() << 24;
  value |= (int32)i2c_read() << 16;
  value |= (int32)i2c_read() << 8;
  value |= i2c_read(0);


  return value;
}

// Write an arbitrary number of bytes from the given array to the sensor,
// starting at the given register
void writeMulti(int8 reg, int8 * src, int8 count)
{
//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!
//!  while (count-- > 0)
//!  {
//!    Wire.write(*(src++));
//!  }
//!
//!  last_status = Wire.endTransmission();

   i2c_start();
   i2c_write(address);
   i2c_write(reg);
   
   while (count-- > 0) {
      last_status = i2c_write(*src++);
   }
   
   i2c_stop();

}

// Read an arbitrary number of bytes from the sensor, starting at the given
// register, into the given array
void readMulti(int8 reg, int8 * dst, int8 count)
{
//!  Wire.beginTransmission(address);
//!  Wire.write(reg);
//!  last_status = Wire.endTransmission();
//!
//!  Wire.requestFrom(address, count);
//!
//!  while (count-- > 0)
//!  {
//!    *(dst++) = Wire.read();
//!  }

   i2c_start();
   i2c_write(address);
   last_status = i2c_write(reg);
   
   i2c_start();
   i2c_write(address|1);
   while (count-- > 1) {
      *(dst++) = i2c_read();
   }
   *(dst++) = i2c_read(0);

   i2c_stop();


}

// Set the return signal rate limit check value in units of MCPS (mega counts
// per second). "This represents the amplitude of the signal reflected from the
// target and detected by the device"; setting this limit presumably determines
// the minimum measurement necessary for the sensor to report a valid reading.
// Setting a lower limit increases the potential range of the sensor but also
// seems to increase the likelihood of getting an inaccurate reading because of
// unwanted reflections from objects other than the intended target.
// Defaults to 0.25 MCPS as initialized by the ST API and this library.
boolean setSignalRateLimit(float limit_Mcps)
{
  if (limit_Mcps < 0 || limit_Mcps > 511.99) { return false; }

  // Q9.7 fixed point format (9 integer bits, 7 fractional bits)
  writeReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, limit_Mcps * (1 << 7));
  return true;
}

// Get the return signal rate limit check value in MCPS
float getSignalRateLimit(void)
{
  return (float)readReg16Bit(FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT) / (1 << 7);
}

// Set the measurement timing budget in microseconds, which is the time allowed
// for one measurement; the ST API and this library take care of splitting the
// timing budget among the sub-steps in the ranging sequence. A longer timing
// budget allows for more accurate measurements. Increasing the budget by a
// factor of N decreases the range measurement standard deviation by a factor of
// sqrt(N). Defaults to about 33 milliseconds; the minimum is 20 ms.
// based on VL53L0X_set_measurement_timing_budget_micro_seconds()
boolean setMeasurementTimingBudget(int32 budget_us)
{
  SequenceStepEnables enables;
  SequenceStepTimeouts timeouts;

  int16 const StartOverhead      = 1320; // note that this is different than the value in get_
  int16 const EndOverhead        = 960;
  int16 const MsrcOverhead       = 660;
  int16 const TccOverhead        = 590;
  int16 const DssOverhead        = 690;
  int16 const PreRangeOverhead   = 660;
  int16 const FinalRangeOverhead = 550;

  int32 const MinTimingBudget = 20000;

  if (budget_us < MinTimingBudget) { return false; }

  int32 used_budget_us = StartOverhead + EndOverhead;

  getSequenceStepEnables(&enables);
  getSequenceStepTimeouts(&enables, &timeouts);

  if (enables.tcc)
  {
    used_budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead);
  }

  if (enables.dss)
  {
    used_budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead);
  }
  else if (enables.msrc)
  {
    used_budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead);
  }

  if (enables.pre_range)
  {
    used_budget_us += (timeouts.pre_range_us + PreRangeOverhead);
  }

  if (enables.final_range)
  {
    used_budget_us += FinalRangeOverhead;

    // "Note that the final range timeout is determined by the timing
    // budget and the sum of all other timeouts within the sequence.
    // If there is no room for the final range timeout, then an error
    // will be set. Otherwise the remaining time will be applied to
    // the final range."

    if (used_budget_us > budget_us)
    {
      // "Requested timeout too big."
      return false;
    }

    int32 final_range_timeout_us = budget_us - used_budget_us;

    // set_sequence_step_timeout() begin
    // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)

    // "For the final range timeout, the pre-range timeout
    //  must be added. To do this both final and pre-range
    //  timeouts must be expressed in macro periods MClks
    //  because they have different vcsel periods."

    int16 final_range_timeout_mclks =
      timeoutMicrosecondsToMclks(final_range_timeout_us,
                                 timeouts.final_range_vcsel_period_pclks);

    if (enables.pre_range)
    {
      final_range_timeout_mclks += timeouts.pre_range_mclks;
    }

    writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
      encodeTimeout(final_range_timeout_mclks));

    // set_sequence_step_timeout() end

    measurement_timing_budget_us = budget_us; // store for internal reuse
  }
  return true;
}

// Get the measurement timing budget in microseconds
// based on VL53L0X_get_measurement_timing_budget_micro_seconds()
// in us
int32 getMeasurementTimingBudget(void)
{
  SequenceStepEnables enables;
  SequenceStepTimeouts timeouts;

  int16 const StartOverhead     = 1910; // note that this is different than the value in set_
  int16 const EndOverhead        = 960;
  int16 const MsrcOverhead       = 660;
  int16 const TccOverhead        = 590;
  int16 const DssOverhead        = 690;
  int16 const PreRangeOverhead   = 660;
  int16 const FinalRangeOverhead = 550;

  // "Start and end overhead times always present"
  int32 budget_us = StartOverhead + EndOverhead;

  getSequenceStepEnables(&enables);
  getSequenceStepTimeouts(&enables, &timeouts);

  if (enables.tcc)
  {
    budget_us += (timeouts.msrc_dss_tcc_us + TccOverhead);
  }

  if (enables.dss)
  {
    budget_us += 2 * (timeouts.msrc_dss_tcc_us + DssOverhead);
  }
  else if (enables.msrc)
  {
    budget_us += (timeouts.msrc_dss_tcc_us + MsrcOverhead);
  }

  if (enables.pre_range)
  {
    budget_us += (timeouts.pre_range_us + PreRangeOverhead);
  }

  if (enables.final_range)
  {
    budget_us += (timeouts.final_range_us + FinalRangeOverhead);
  }

  measurement_timing_budget_us = budget_us; // store for internal reuse
  return budget_us;
}

// Set the VCSEL (vertical cavity surface emitting laser) pulse period for the
// given period type (pre-range or final range) to the given value in PCLKs.
// Longer periods seem to increase the potential range of the sensor.
// Valid values are (even numbers only):
//  pre:  12 to 18 (initialized default: 14)
//  final: 8 to 14 (initialized default: 10)
// based on VL53L0X_set_vcsel_pulse_period()
boolean setVcselPulsePeriod(vcselPeriodType type, int8 period_pclks)
{
  int8 vcsel_period_reg = encodeVcselPeriod(period_pclks);

  SequenceStepEnables enables;
  SequenceStepTimeouts timeouts;

  getSequenceStepEnables(&enables);
  getSequenceStepTimeouts(&enables, &timeouts);

  // "Apply specific settings for the requested clock period"
  // "Re-calculate and apply timeouts, in macro periods"

  // "When the VCSEL period for the pre or final range is changed,
  // the corresponding timeout must be read from the device using
  // the current VCSEL period, then the new VCSEL period can be
  // applied. The timeout then must be written back to the device
  // using the new VCSEL period.
  //
  // For the MSRC timeout, the same applies - this timeout being
  // dependant on the pre-range vcsel period."


  if (type == VcselPeriodPreRange)
  {
    // "Set phase check limits"
    switch (period_pclks)
    {
      case 12:
        writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x18);
        break;

      case 14:
        writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x30);
        break;

      case 16:
        writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x40);
        break;

      case 18:
        writeReg(PRE_RANGE_CONFIG_VALID_PHASE_HIGH, 0x50);
        break;

      default:
        // invalid period
        return false;
    }
    writeReg(PRE_RANGE_CONFIG_VALID_PHASE_LOW, 0x08);

    // apply new VCSEL period
    writeReg(PRE_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg);

    // update timeouts

    // set_sequence_step_timeout() begin
    // (SequenceStepId == VL53L0X_SEQUENCESTEP_PRE_RANGE)

    int16 new_pre_range_timeout_mclks =
      timeoutMicrosecondsToMclks(timeouts.pre_range_us, period_pclks);

    writeReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI,
      encodeTimeout(new_pre_range_timeout_mclks));

    // set_sequence_step_timeout() end

    // set_sequence_step_timeout() begin
    // (SequenceStepId == VL53L0X_SEQUENCESTEP_MSRC)

    int16 new_msrc_timeout_mclks =
      timeoutMicrosecondsToMclks(timeouts.msrc_dss_tcc_us, period_pclks);

    writeReg(MSRC_CONFIG_TIMEOUT_MACROP,
      (new_msrc_timeout_mclks > 256) ? 255 : (new_msrc_timeout_mclks - 1));

    // set_sequence_step_timeout() end
  }
  else if (type == VcselPeriodFinalRange)
  {
    switch (period_pclks)
    {
      case 8:
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x10);
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW,  0x08);
        writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x02);
        writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C);
        writeReg(0xFF, 0x01);
        writeReg(ALGO_PHASECAL_LIM, 0x30);
        writeReg(0xFF, 0x00);
        break;

      case 10:
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x28);
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW,  0x08);
        writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
        writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09);
        writeReg(0xFF, 0x01);
        writeReg(ALGO_PHASECAL_LIM, 0x20);
        writeReg(0xFF, 0x00);
        break;

      case 12:
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x38);
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW,  0x08);
        writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
        writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08);
        writeReg(0xFF, 0x01);
        writeReg(ALGO_PHASECAL_LIM, 0x20);
        writeReg(0xFF, 0x00);
        break;

      case 14:
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, 0x48);
        writeReg(FINAL_RANGE_CONFIG_VALID_PHASE_LOW,  0x08);
        writeReg(GLOBAL_CONFIG_VCSEL_WIDTH, 0x03);
        writeReg(ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07);
        writeReg(0xFF, 0x01);
        writeReg(ALGO_PHASECAL_LIM, 0x20);
        writeReg(0xFF, 0x00);
        break;

      default:
        // invalid period
        return false;
    }

    // apply new VCSEL period
    writeReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD, vcsel_period_reg);

    // update timeouts

    // set_sequence_step_timeout() begin
    // (SequenceStepId == VL53L0X_SEQUENCESTEP_FINAL_RANGE)

    // "For the final range timeout, the pre-range timeout
    //  must be added. To do this both final and pre-range
    //  timeouts must be expressed in macro periods MClks
    //  because they have different vcsel periods."

    int16 new_final_range_timeout_mclks =
      timeoutMicrosecondsToMclks(timeouts.final_range_us, period_pclks);

    if (enables.pre_range)
    {
      new_final_range_timeout_mclks += timeouts.pre_range_mclks;
    }

    writeReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI,
      encodeTimeout(new_final_range_timeout_mclks));

    // set_sequence_step_timeout end
  }
  else
  {
    // invalid type
    return false;
  }

  // "Finally, the timing budget must be re-applied"

  setMeasurementTimingBudget(measurement_timing_budget_us);

  // "Perform the phase calibration. This is needed after changing on vcsel period."
  // VL53L0X_perform_phase_calibration() begin

  int8 sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG);
  writeReg(SYSTEM_SEQUENCE_CONFIG, 0x02);
  performSingleRefCalibration(0x0);
  writeReg(SYSTEM_SEQUENCE_CONFIG, sequence_config);

  // VL53L0X_perform_phase_calibration() end

  return true;
}

// Get the VCSEL pulse period in PCLKs for the given period type.
// based on VL53L0X_get_vcsel_pulse_period()
int8 getVcselPulsePeriod(vcselPeriodType type)
{
  if (type == VcselPeriodPreRange)
  {
    return decodeVcselPeriod(readReg(PRE_RANGE_CONFIG_VCSEL_PERIOD));
  }
  else if (type == VcselPeriodFinalRange)
  {
    return decodeVcselPeriod(readReg(FINAL_RANGE_CONFIG_VCSEL_PERIOD));
  }
  else { return 255; }
}

// Start continuous ranging measurements. If period_ms (optional) is 0 or not
// given, continuous back-to-back mode is used (the sensor takes measurements as
// often as possible); otherwise, continuous timed mode is used, with the given
// inter-measurement period in milliseconds determining how often the sensor
// takes a measurement.
// based on VL53L0X_StartMeasurement()
void startContinuous(int32 period_ms)
{
  writeReg(0x80, 0x01);
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);
  writeReg(0x91, stop_variable);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  if (period_ms != 0)
  {
    // continuous timed mode

    // VL53L0X_SetInterMeasurementPeriodMilliSeconds() begin

    int16 osc_calibrate_val = readReg16Bit(OSC_CALIBRATE_VAL);

    if (osc_calibrate_val != 0)
    {
      period_ms *= osc_calibrate_val;
    }

    writeReg32Bit(SYSTEM_INTERMEASUREMENT_PERIOD, period_ms);

    // VL53L0X_SetInterMeasurementPeriodMilliSeconds() end

    writeReg(SYSRANGE_START, 0x04); // VL53L0X_REG_SYSRANGE_MODE_TIMED
  }
  else
  {
    // continuous back-to-back mode
    writeReg(SYSRANGE_START, 0x02); // VL53L0X_REG_SYSRANGE_MODE_BACKTOBACK
  }
}

// Stop continuous measurements
// based on VL53L0X_StopMeasurement()
void stopContinuous(void)
{
  writeReg(SYSRANGE_START, 0x01); // VL53L0X_REG_SYSRANGE_MODE_SINGLESHOT

  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);
  writeReg(0x91, 0x00);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
}

// Returns a range reading in millimeters when continuous mode is active
// (readRangeSingleMillimeters() also calls this function after starting a
// single-shot range measurement)
int16 readRangeContinuousMillimeters(void)
{
  startTimeout();
  while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
  {
    if (checkTimeoutExpired())
    {
      did_timeout = true;
      return 65535;
    }
  }

  // assumptions: Linearity Corrective Gain is 1000 (default);
  // fractional ranging is not enabled
  int16 range = readReg16Bit(RESULT_RANGE_STATUS + 10);

  writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);

  return range;
}

// Performs a single-shot range measurement and returns the reading in
// millimeters
// based on VL53L0X_PerformSingleRangingMeasurement()
int16 readRangeSingleMillimeters(void)
{
  writeReg(0x80, 0x01);
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);
  writeReg(0x91, stop_variable);
  writeReg(0x00, 0x01);
  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  writeReg(SYSRANGE_START, 0x01);

  // "Wait until start bit has been cleared"
  startTimeout();
  while (readReg(SYSRANGE_START) & 0x01)
  {
    if (checkTimeoutExpired())
    {
      did_timeout = true;
      return 65535;
    }
  }

  return readRangeContinuousMillimeters();
}

// Did a timeout occur in one of the read functions since the last call to
// timeoutOccurred()?
boolean timeoutOccurred()
{
  boolean tmp = did_timeout;
  did_timeout = false;
  return tmp;
}

// Private Methods /////////////////////////////////////////////////////////////

// Get reference SPAD (single photon avalanche diode) count and type
// based on VL53L0X_get_info_from_device(),
// but only gets reference SPAD count and type
boolean getSpadInfo(int8 * count, boolean * type_is_aperture)
{
  int8 tmp;

  writeReg(0x80, 0x01);
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x00);

  writeReg(0xFF, 0x06);
  writeReg(0x83, readReg(0x83) | 0x04);
  writeReg(0xFF, 0x07);
  writeReg(0x81, 0x01);

  writeReg(0x80, 0x01);

  writeReg(0x94, 0x6b);
  writeReg(0x83, 0x00);
  startTimeout();
  while (readReg(0x83) == 0x00)
  {
    if (checkTimeoutExpired()) { return false; }
  }
  writeReg(0x83, 0x01);
  tmp = readReg(0x92);

  *count = tmp & 0x7f;
  *type_is_aperture = (tmp >> 7) & 0x01;

  writeReg(0x81, 0x00);
  writeReg(0xFF, 0x06);
  writeReg(0x83, readReg( 0x83  & ~0x04));
  writeReg(0xFF, 0x01);
  writeReg(0x00, 0x01);

  writeReg(0xFF, 0x00);
  writeReg(0x80, 0x00);

  return true;
}

// Get sequence step enables
// based on VL53L0X_GetSequenceStepEnables()
void getSequenceStepEnables(SequenceStepEnables * enables)
{
  int8 sequence_config = readReg(SYSTEM_SEQUENCE_CONFIG);

  enables->tcc          = (sequence_config >> 4) & 0x1;
  enables->dss          = (sequence_config >> 3) & 0x1;
  enables->msrc         = (sequence_config >> 2) & 0x1;
  enables->pre_range    = (sequence_config >> 6) & 0x1;
  enables->final_range  = (sequence_config >> 7) & 0x1;
}

// Get sequence step timeouts
// based on get_sequence_step_timeout(),
// but gets all timeouts instead of just the requested one, and also stores
// intermediate values
void getSequenceStepTimeouts(SequenceStepEnables  * enables, SequenceStepTimeouts * timeouts)
{
  timeouts->pre_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodPreRange);

  timeouts->msrc_dss_tcc_mclks = readReg(MSRC_CONFIG_TIMEOUT_MACROP) + 1;
  timeouts->msrc_dss_tcc_us =
    timeoutMclksToMicroseconds(timeouts->msrc_dss_tcc_mclks,
                               timeouts->pre_range_vcsel_period_pclks);

  timeouts->pre_range_mclks =
    decodeTimeout(readReg16Bit(PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI));
  timeouts->pre_range_us =
    timeoutMclksToMicroseconds(timeouts->pre_range_mclks,
                               timeouts->pre_range_vcsel_period_pclks);

  timeouts->final_range_vcsel_period_pclks = getVcselPulsePeriod(VcselPeriodFinalRange);

  timeouts->final_range_mclks =
    decodeTimeout(readReg16Bit(FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI));

  if (enables->pre_range)
  {
    timeouts->final_range_mclks -= timeouts->pre_range_mclks;
  }

  timeouts->final_range_us =
    timeoutMclksToMicroseconds(timeouts->final_range_mclks,
                               timeouts->final_range_vcsel_period_pclks);
}

// Decode sequence step timeout in MCLKs from register value
// based on VL53L0X_decode_timeout()
// Note: the original function returned a int32, but the return value is
// always stored in a int16.
int16 decodeTimeout(int16 reg_val)
{
  // format: "(LSByte * 2^MSByte) + 1"
  return (int16)((reg_val & 0x00FF) <<
         (int16)((reg_val & 0xFF00) >> 8)) + 1;
}

// Encode sequence step timeout register value from timeout in MCLKs
// based on VL53L0X_encode_timeout()
// Note: the original function took a int16, but the argument passed to it
// is always a int16.
int16 encodeTimeout(int16 timeout_mclks)
{
  // format: "(LSByte * 2^MSByte) + 1"

  int32 ls_byte = 0;
  int16 ms_byte = 0;

  if (timeout_mclks > 0)
  {
    ls_byte = timeout_mclks - 1;

    while ((ls_byte & 0xFFFFFF00) > 0)
    {
      ls_byte >>= 1;
      ms_byte++;
    }

    return (ms_byte << 8) | (ls_byte & 0xFF);
  }
  else { return 0; }
}

// Convert sequence step timeout from MCLKs to microseconds with given VCSEL period in PCLKs
// based on VL53L0X_calc_timeout_us()
int32 timeoutMclksToMicroseconds(int16 timeout_period_mclks, int8 vcsel_period_pclks)
{
  int32 macro_period_ns = calcMacroPeriod(vcsel_period_pclks);

  return ((timeout_period_mclks * macro_period_ns) + (macro_period_ns / 2)) / 1000;
}

// Convert sequence step timeout from microseconds to MCLKs with given VCSEL period in PCLKs
// based on VL53L0X_calc_timeout_mclks()
int32 timeoutMicrosecondsToMclks(int32 timeout_period_us, int8 vcsel_period_pclks)
{
  int32 macro_period_ns = calcMacroPeriod(vcsel_period_pclks);

  return (((timeout_period_us * 1000) + (macro_period_ns / 2)) / macro_period_ns);
}


// based on VL53L0X_perform_single_ref_calibration()
boolean performSingleRefCalibration(int8 vhv_init_byte)
{
  writeReg(SYSRANGE_START, 0x01 | vhv_init_byte); // VL53L0X_REG_SYSRANGE_MODE_START_STOP

  startTimeout();
  while ((readReg(RESULT_INTERRUPT_STATUS) & 0x07) == 0)
  {
    if (checkTimeoutExpired()) { return false; }
  }

  writeReg(SYSTEM_INTERRUPT_CLEAR, 0x01);

  writeReg(SYSRANGE_START, 0x00);

  return true;
}


//!void main() {
//!   delay_ms(1000);
//!   init();
//!
//!}

//******************************************************************************
//   flex_lcd.c
//******************************************************************************


// flex_lcd.c

// These pins are for the Microchip PicDem2-Plus board,
// which is what I used to test the driver.  Change these
// pins to fit your own board.
// changed for my setup
#define LCD_DB4   PIN_B2
#define LCD_DB5   PIN_B3
#define LCD_DB6   PIN_B4
#define LCD_DB7   PIN_B5

#define LCD_E     PIN_A3
#define LCD_RS    PIN_B1
//#define LCD_RW    PIN_A2

// If you only want a 6-pin interface to your LCD, then
// connect the R/W pin on the LCD to ground, and comment
// out the following line.

//#define USE_LCD_RW   1     

//========================================
//#device PIC18F252
#define lcd_type 2        // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the 2nd line


int8 const LCD_INIT_STRING[4] =
{
 0x20 | (lcd_type << 2), // Func set: 4-bit, 2 lines, 5x8 dots
 0xc,                    // Display on
 1,                      // Clear display
 6                       // Increment cursor
 };


//-------------------------------------
void lcd_send_nibble(int8 nibble)
{
// Note:  !! converts an integer expression
// to a boolean (1 or 0).
 output_bit(LCD_DB4, !!(nibble & 1));
 output_bit(LCD_DB5, !!(nibble & 2));
 output_bit(LCD_DB6, !!(nibble & 4));   
 output_bit(LCD_DB7, !!(nibble & 8));   

 delay_cycles(1);
 output_high(LCD_E);
 delay_us(2);
 output_low(LCD_E);
}

//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine.  For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.     

#ifdef USE_LCD_RW
int8 lcd_read_nibble(void)
{
int8 retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3

retval = 0;
   
output_high(LCD_E);
delay_cycles(1);

retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);
 
output_low(LCD_E);
   
return(retval);   
}   
#endif

//---------------------------------------
// Read a byte from the LCD and return it.

#ifdef USE_LCD_RW
int8 lcd_read_byte(void)
{
int8 low;
int8 high;

output_high(LCD_RW);
delay_cycles(1);

high = lcd_read_nibble();

low = lcd_read_nibble();

return( (high<<4) | low);
}
#endif

//----------------------------------------
// Send a byte to the LCD.
void lcd_send_byte(int8 address, int8 n)
{
output_low(LCD_RS);

#ifdef USE_LCD_RW
while(bit_test(lcd_read_byte(),7)) ;
#else
delay_us(60);
#endif

if(address)
   output_high(LCD_RS);
else
   output_low(LCD_RS);
     
 delay_cycles(1);

#ifdef USE_LCD_RW
output_low(LCD_RW);
delay_cycles(1);
#endif

output_low(LCD_E);

lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}

//----------------------------
void lcd_init(void)
{
int8 i;

output_low(LCD_RS);

#ifdef USE_LCD_RW
output_low(LCD_RW);
#endif

output_low(LCD_E);

delay_ms(15);

for(i=0 ;i < 3; i++)
   {
    lcd_send_nibble(0x03);
    delay_ms(5);
   }

lcd_send_nibble(0x02);

for(i=0; i < sizeof(LCD_INIT_STRING); i++)
   {
    lcd_send_byte(0, LCD_INIT_STRING[i]);
   
    // If the R/W signal is not used, then
    // the busy bit can't be polled.  One of
    // the init commands takes longer than
    // the hard-coded delay of 60 us, so in
    // that case, lets just do a 5 ms delay
    // after all four of them.
    #ifndef USE_LCD_RW
    delay_ms(5);
    #endif
   }

}

//----------------------------

void lcd_gotoxy(int8 x, int8 y)
{
int8 address;

if(y != 1)
   address = lcd_line_two;
else
   address=0;

address += x-1;
lcd_send_byte(0, 0x80 | address);
}

//-----------------------------
void lcd_putc(char c)
{
 switch(c)
   {
    case '\f':
      lcd_send_byte(0,1);
      delay_ms(2);
      break;
   
    case '\n':
       lcd_gotoxy(1,2);
       break;
   
    case '\b':
       lcd_send_byte(0,0x10);
       break;
   
    default:
       lcd_send_byte(1,c);
       break;
   }
}

//------------------------------
#ifdef USE_LCD_RW
char lcd_getc(int8 x, int8 y)
{
char value;

lcd_gotoxy(x,y);

// Wait until busy flag is low.
while(bit_test(lcd_read_byte(),7));

output_high(LCD_RS);
value = lcd_read_byte();
output_low(lcd_RS);

return(value);
}
#endif
DOSManDan



Joined: 22 Jan 2020
Posts: 1

View user's profile Send private message

Thank you!
PostPosted: Wed Jan 22, 2020 2:51 pm     Reply with quote

I can't tell you how much I appreciate you posting this. The original code from STI isn't written to port easily and their examples aren't very clear on what needs to be done.

Thanks!
PrinceNai



Joined: 31 Oct 2016
Posts: 452
Location: Montenegro

View user's profile Send private message

PostPosted: Mon Jan 27, 2020 9:13 am     Reply with quote

I'm glad you find it helpful. There were times when I wanted to close my laptop the other way around from sheer frustration :-). I couldn't have it done without the help of other members here, not even remotely. If you manage to do something useful with this sensor, be sure to post it.

With kindest regards,

Samo
cbarberis



Joined: 01 Oct 2003
Posts: 172
Location: Punta Gorda, Florida USA

View user's profile Send private message Send e-mail

PostPosted: Tue Oct 13, 2020 7:47 am     Reply with quote

I split the various sections of the code you posted as you indicated and during compilation I get a million errors. I am using the latest version of the CCS compiler. There is so many compilation errors that when you clean-up one you just keep getting more.........Did this ever work????? Rolling Eyes
cbarberis



Joined: 01 Oct 2003
Posts: 172
Location: Punta Gorda, Florida USA

View user's profile Send private message Send e-mail

PostPosted: Tue Oct 13, 2020 8:37 am     Reply with quote

I don't think this piece of code ever worked on the CCS compiler at least not as written. It is a poorly interpreted copy of Arduino code.

For example:
Code:

// Record the current time to check an upcoming timeout against
#define startTimeout() (timeout_start_ms = millis())
//#define startTimeout() (timeout_start_ms = 0)

// Check if timeout is enabled (set to nonzero value) and has expired
#define checkTimeoutExpired() (io_timeout > 0 && ((int16)millis() - timeout_start_ms) > io_timeout)


Will always be flagged as an error, the function millis() is a defined function in Arduino but not in the CCS compiler and I certainly don't see your own defined function in this program. This should NOT be posted as a working program, because it is NOT and it wastes a lot of time for those that try to use it.
alan



Joined: 12 Nov 2012
Posts: 349
Location: South Africa

View user's profile Send private message

PostPosted: Tue Oct 13, 2020 12:50 pm     Reply with quote

millis are indeed defined.

Code:
#define millis()(msTimer)
int32 msTimer=0;

#include <VL53L0X.h>
#include <VL53L0X.c>
#include <flex_lcd.c>

#INT_TIMER2                   //1ms interrupt
void  TIMER2_isr(void) {
   msTimer++;
}
cbarberis



Joined: 01 Oct 2003
Posts: 172
Location: Punta Gorda, Florida USA

View user's profile Send private message Send e-mail

PostPosted: Wed Oct 14, 2020 7:22 am     Reply with quote

I stand corrected and my statements were incorrect.

The problem was related to the fact that I also downloaded the CCS translation file that PrinceNai got his code from. https://github.com/LILCMU/vl53l0x-ccs
So I copied both PrinceNai and the above file from Github and put each into a separate folder. I started compiling PrinceNai's original version and got quite a few errors. So I decided to work on the files from Github and here is where I found: Lots of issues like the one I described previously. After trying to fix some of the problems I lost track on which program I was working with. The original Github program had a load of issues and the aforementioned missing definitions, so I basically just gave up until Alan replied to my last comment. He was correct, there is no missing definition for what I stated on PrinceNai's translation. However, PrinceNai's translation would not compile without errors as well. I would get an error at line #260 #separate boolean init(2() as an "invalid overload function". I was able to fix that and it re-compiled just fine without any errors. I have not actually tested the actual code on a device but when I get some time, I will recompile for a different PIC and try it out with the VL53L0X module and I will post my results.
Ttelmah



Joined: 11 Mar 2010
Posts: 19195

View user's profile Send private message

PostPosted: Wed Oct 21, 2020 7:22 am     Reply with quote

Er. You are still suffering from mis-loading/mistyping. The line reads:

Code:

#separate boolean init2() {


The code _as posted_, compiles directly for me.
PrinceNai



Joined: 31 Oct 2016
Posts: 452
Location: Montenegro

View user's profile Send private message

PostPosted: Thu Oct 22, 2020 12:09 pm     Reply with quote

Dear Ttelmah,

Thank you for your answer. I lost all the original work on this when my PC crashed and in this particular case, good riddance. This sensor brought nothing but grief, way, way too complicated. It was from a very early stage a grudge project, will I be able to do it. 10 minutes after something showed on the LCD, I threw away all the sensors I had just to prevent losing one more minute dealing with it. I must be sick, can't leave something unfinished if I have a feeling that I'm too thick to get it. If I knew how not to lose my time on those things, I'd still be 35 instead of 50 :-). The only good thing that came out of it is that I moved all the projects I could find on other PC's to Dropbox.

Regards,
Samo
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group