|
|
View previous topic :: View next topic |
Author |
Message |
PrinceNai
Joined: 31 Oct 2016 Posts: 480 Location: Montenegro
|
VL53L0X working example |
Posted: Sun Jan 21, 2018 12:54 pm |
|
|
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
|
Thank you! |
Posted: Wed Jan 22, 2020 2:51 pm |
|
|
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: 480 Location: Montenegro
|
|
Posted: Mon Jan 27, 2020 9:13 am |
|
|
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
|
|
Posted: Tue Oct 13, 2020 7:47 am |
|
|
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????? |
|
|
cbarberis
Joined: 01 Oct 2003 Posts: 172 Location: Punta Gorda, Florida USA
|
|
Posted: Tue Oct 13, 2020 8:37 am |
|
|
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: 357 Location: South Africa
|
|
Posted: Tue Oct 13, 2020 12:50 pm |
|
|
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
|
|
Posted: Wed Oct 14, 2020 7:22 am |
|
|
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: 19520
|
|
Posted: Wed Oct 21, 2020 7:22 am |
|
|
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: 480 Location: Montenegro
|
|
Posted: Thu Oct 22, 2020 12:09 pm |
|
|
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 |
|
|
|
|
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
|