/** ****************************************************************************** * File Name : app_tof_vl53l1x_range.c * Description : Main program body ****************************************************************************** * * COPYRIGHT(c) 2015 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "stm32xxx_hal.h" /* USER CODE BEGIN Includes */ #include <string.h> #include "X-NUCLEO-53L1A1.h" #include "vl53l1x_api.h" #include <limits.h> /** * @defgroup Configuration Static configuration * @{ */ #define HAVE_ALARM_DEMO 0 /** Time the initial 53L0 message is shown at power up */ #define WelcomeTime 660 /** Time the initial 53L0 message is shown at power up */ #define ModeChangeDispTime 500 /** * Time considered as a "long push" on push button */ #define PressBPSwicthTime 1000 /** @} */ /* config group */ #ifndef MIN # define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif #define B1_Pin GPIO_PIN_13 #define B1_GPIO_Port GPIOC #ifndef ARRAY_SIZE # define ARRAY_SIZE(x) (sizeof((x))/sizeof((x)[0])) #endif /** * @defgroup ErrCode Errors code shown on display * @{ */ #define ERR_DETECT -1 #define ERR_DEMO_RANGE_ONE 1 #define ERR_DEMO_RANGE_MULTI 2 /** }@} */ /* defgroup ErrCode */ /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ typedef enum { LONG_RANGE = 0, /*!< Long range mode */ HIGH_SPEED = 1, /*!< High speed mode */ HIGH_ACCURACY = 2, /*!< High accuracy mode */ } RangingConfig_e; /** * Global ranging struct */ VL53L0X_RangingMeasurementData_t RangingMeasurementData; /** leaky factor for filtered range * * r(n) = averaged_r(n-1)*leaky +r(n)(1-leaky) * * */ int LeakyFactorFix8 = (int)( 0.6 *256); /** How many device detect set by @a DetectSensors()*/ int nDevPresent=0; int Distance_data; VL53L0X_Dev_t VL53L0XDevs={ //.Id=XNUCLEO53L0A1_DEV_CENTER, //.DevLetter='c', .I2cHandle=&XNUCLEO53L0A1_hi2c, .I2cDevAddr=0x52}; /** Timer * * Used to get time stamp for UART logging */ TIM_HandleTypeDef htim5; /* TIM5 init function */ void MX_TIM5_Init(void) { TIM_MasterConfigTypeDef sMasterConfig; TIM_OC_InitTypeDef sConfigOC; htim5.Instance = TIM5; htim5.Init.Prescaler = 83; htim5.Init.CounterMode = TIM_COUNTERMODE_UP; htim5.Init.Period = 0xFFFFFFFF; htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_OC_Init(&htim5); sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig); sConfigOC.OCMode = TIM_OCMODE_TIMING; sConfigOC.Pulse = 0; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_OC_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_1); } void TimeStamp_Init(){ MX_TIM5_Init(); } void TimeStamp_Reset(){ HAL_TIM_Base_Start(&htim5); htim5.Instance->CNT=0; } uint32_t TimeStamp_Get(){ return htim5.Instance->CNT; } /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); /* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ void ResetAndDetectSensor(int SetDisplay); /* USER CODE END PFP */ /* USER CODE BEGIN 0 */ #define debug_printf trace_printf char WelcomeMsg[]="Hi I am Ranging VL53L0X mcu " MCU_NAME "\n"; #define BSP_BP_PORT GPIOC #define BSP_BP_PIN GPIO_PIN_13 int BSP_GetPushButton(void){ GPIO_PinState state ; state = HAL_GPIO_ReadPin(BSP_BP_PORT, BSP_BP_PIN); return state; } /** * When button is already pressed it waits for user to release it. * if button remains pressed for a given time it returns true. * This is used to detect mode switch by long press on blue Push Button * * As soon as time is elapsed -rb- is displayed to let user know the mode * switch is taken into account * * @return True if button remains pressed more than specified time */ int PusbButton_WaitUnPress(void){ uint32_t TimeStarted; TimeStarted = HAL_GetTick(); while( !BSP_GetPushButton() ){ ; /* debounce */ if(HAL_GetTick()- TimeStarted> PressBPSwicthTime){ // XNUCLEO53L0A1_SetDisplayString (" rb "); } } return HAL_GetTick() - TimeStarted>PressBPSwicthTime; } /** * Handle Error * * Set err on display and loop forever * @param err Error case code */ void HandleError(int err){ char msg[16]; sprintf(msg,"Er%d", err); //XNUCLEO53L0A1_SetDisplayString(msg); while(1){}; } /** * Reset all sensor then goes true presence detection * * All present devices are data initiated and assigned with their final address * @return */ int sts_tof_vl53l0x_DetectSensors(void) { int i; uint16_t Id; int status; int FinalAddress; /* detect all sensors (even on-board)*/ VL53L0X_Dev_t *pDev; pDev = &VL53L0XDevs; pDev->I2cDevAddr = 0x52; pDev->Present = 0; //status = XNUCLEO53L0A1_ResetId(pDev->Id, 1); //HAL_Delay(2); FinalAddress=0x52; do { /* Set I2C standard mode (400 KHz) before doing the first register access */ //if (status == VL53L0X_ERROR_NONE) status = VL53L0X_WrByte(pDev, 0x88, 0x00); /* Try to read one register using default 0x52 address */ status = VL53L0X_RdWord(pDev, VL53L0X_REG_IDENTIFICATION_MODEL_ID, &Id); if (status) { debug_printf("#%d Read id fail\n", i); break; } if (Id == 0xEEAA) { /* Sensor is found => Change its I2C address to final one */ status = VL53L0X_SetDeviceAddress(pDev,FinalAddress); if (status != 0) { debug_printf("#i VL53L0X_SetDeviceAddress fail\n", i); break; } pDev->I2cDevAddr = FinalAddress; /* Check all is OK with the new I2C address and initialize the sensor */ status = VL53L0X_RdWord(pDev, VL53L0X_REG_IDENTIFICATION_MODEL_ID, &Id); status = VL53L0X_DataInit(pDev); if( status == 0 ){ pDev->Present = 1; } else{ debug_printf("VL53L0X_DataInit %d fail\n", i); break; } debug_printf("VL53L0X %d Present and initiated to final 0x%x\n", i, pDev->I2cDevAddr); pDev->Present = 1; } else { debug_printf("#%d unknown ID %x\n", i, Id); status = 1; } /* if fail r can't use for any reason then put the device back to reset */ //if (status) { //XNUCLEO53L0A1_ResetId(i, 0); //} } while(0); /* Display detected sensor(s) */ return 1; } /** * Setup all detected sensors for single shot mode and setup ranging configuration */ void sts_tof_vl53l0x_SetupSingleShot(RangingConfig_e rangingConfig) { int status=VL53L0X_ERROR_NONE; uint8_t VhvSettings; uint8_t PhaseCal; uint32_t refSpadCount; uint8_t isApertureSpads; FixPoint1616_t signalLimit = (FixPoint1616_t)(0.25*65536); FixPoint1616_t sigmaLimit = (FixPoint1616_t)(18*65536); uint32_t timingBudget = 33000; uint8_t preRangeVcselPeriod = 14; uint8_t finalRangeVcselPeriod = 10; // uart_printf("\r\n######### start setup for single shot \r\n"); status=VL53L0X_StaticInit(&VL53L0XDevs); if( status ){ debug_printf("VL53L0X_StaticInit failed\n"); } status = VL53L0X_PerformRefCalibration(&VL53L0XDevs, &VhvSettings, &PhaseCal); if( status ){ debug_printf("VL53L0X_PerformRefCalibration failed\n"); } status = VL53L0X_PerformRefSpadManagement(&VL53L0XDevs, &refSpadCount, &isApertureSpads); if( status ){ debug_printf("VL53L0X_PerformRefSpadManagement failed\n"); } status = VL53L0X_SetDeviceMode(&VL53L0XDevs, VL53L0X_DEVICEMODE_SINGLE_RANGING); // Setup in single ranging mode if( status ){ debug_printf("VL53L0X_SetDeviceMode failed\n"); } status = VL53L0X_SetLimitCheckEnable(&VL53L0XDevs, VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, 1); // Enable Sigma limit if( status ){ debug_printf("VL53L0X_SetLimitCheckEnable failed\n"); } status = VL53L0X_SetLimitCheckEnable(&VL53L0XDevs, VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, 1); // Enable Signa limit if( status ){ debug_printf("VL53L0X_SetLimitCheckEnable failed\n"); } /* Ranging configuration */ switch(rangingConfig) { case LONG_RANGE: signalLimit = (FixPoint1616_t)(0.1*65536); sigmaLimit = (FixPoint1616_t)(60*65536); timingBudget = 33000; preRangeVcselPeriod = 18; finalRangeVcselPeriod = 14; break; case HIGH_ACCURACY: signalLimit = (FixPoint1616_t)(0.25*65536); sigmaLimit = (FixPoint1616_t)(18*65536); timingBudget = 200000; preRangeVcselPeriod = 14; finalRangeVcselPeriod = 10; break; case HIGH_SPEED: signalLimit = (FixPoint1616_t)(0.25*65536); sigmaLimit = (FixPoint1616_t)(32*65536); timingBudget = 20000; preRangeVcselPeriod = 14; finalRangeVcselPeriod = 10; break; default: debug_printf("Not Supported"); } status = VL53L0X_SetLimitCheckValue(&VL53L0XDevs, VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, signalLimit); if( status ){ debug_printf("VL53L0X_SetLimitCheckValue failed\n"); } status = VL53L0X_SetLimitCheckValue(&VL53L0XDevs, VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, sigmaLimit); if( status ){ debug_printf("VL53L0X_SetLimitCheckValue failed\n"); } status = VL53L0X_SetMeasurementTimingBudgetMicroSeconds(&VL53L0XDevs, timingBudget); if( status ){ debug_printf("VL53L0X_SetMeasurementTimingBudgetMicroSeconds failed\n"); } status = VL53L0X_SetVcselPulsePeriod(&VL53L0XDevs, VL53L0X_VCSEL_PERIOD_PRE_RANGE, preRangeVcselPeriod); if( status ){ debug_printf("VL53L0X_SetVcselPulsePeriod failed\n"); } status = VL53L0X_SetVcselPulsePeriod(&VL53L0XDevs, VL53L0X_VCSEL_PERIOD_FINAL_RANGE, finalRangeVcselPeriod); if( status ){ debug_printf("VL53L0X_SetVcselPulsePeriod failed\n"); } status = VL53L0X_PerformRefCalibration(&VL53L0XDevs, &VhvSettings, &PhaseCal); if( status ){ debug_printf("VL53L0X_PerformRefCalibration failed\n"); } VL53L0XDevs.LeakyFirst =1; // uart_printf("\r\n######### end of setup for single shot \r\n"); } /* Store new ranging data into the device structure, apply leaky integrator if needed */ void sts_tof_vl53l0x_Sensor_SetNewRange(VL53L0X_Dev_t *pDev, VL53L0X_RangingMeasurementData_t *pRange){ if( pRange->RangeStatus == 0 ){ if( pDev->LeakyFirst ){ pDev->LeakyFirst = 0; pDev->LeakyRange = pRange->RangeMilliMeter; } else{ pDev->LeakyRange = (pDev->LeakyRange*LeakyFactorFix8 + (256-LeakyFactorFix8)*pRange->RangeMilliMeter)>>8; } } else{ pDev->LeakyFirst = 1; } } /** * Implement the ranging function with all modes managed through the blue button (short and long press) * This function implements a while loop until the blue button is pressed * @param UseSensorsMask Mask of any sensors to use if not only one present * @param rangingConfig Ranging configuration to be used (same for all sensors) */ int RangeDemo(int UseSensorsMask, RangingConfig_e rangingConfig){ int over=0; int status; /* Setup all sensors in Single Shot mode */ sts_tof_vl53l0x_SetupSingleShot(rangingConfig); /* Start ranging until blue button is pressed */ do{ /* Call All-In-One blocking API function */ status = VL53L0X_PerformSingleRangingMeasurement(&VL53L0XDevs, &RangingMeasurementData); if( status ==0 ){ /* Push data logging to UART */ //trace_printf("%d,%u,%d,%d,%d\n", VL53L0XDevs[SingleSensorNo].Id, TimeStamp_Get(), RangingMeasurementData.RangeStatus, RangingMeasurementData.RangeMilliMeter, RangingMeasurementData.SignalRateRtnMegaCps); sts_tof_vl53l0x_Sensor_SetNewRange(&VL53L0XDevs,&RangingMeasurementData); /* Display distance in cm */ if( RangingMeasurementData.RangeStatus == 0 ){ uart_printf("########## LeakyRange Distance = %4d mm \r\n", (int)VL53L0XDevs.LeakyRange); } } else{ HandleError(ERR_DEMO_RANGE_ONE); } /* Check blue button */ if( !BSP_GetPushButton() ){ over=1; break; } }while( !over); /* Wait button to be un-pressed to decide if it is a short or long press */ status=PusbButton_WaitUnPress(); return status; } VL53L0X_Error sts_tof_vl53l0x_range(VL53L0X_Dev_t *dev,VL53L0X_RangingMeasurementData_t *pdata, RangingConfig_e rangingConfig) { int over=0; VL53L0X_Error status = VL53L0X_ERROR_NONE; /* Setup all sensors in Single Shot mode */ sts_tof_vl53l0x_SetupSingleShot(rangingConfig); /* Call All-In-One blocking API function */ status = VL53L0X_PerformSingleRangingMeasurement(&VL53L0XDevs, &RangingMeasurementData); if( status ==0 ){ sts_tof_vl53l0x_Sensor_SetNewRange(&VL53L0XDevs,&RangingMeasurementData); /* Display distance in mm */ if( RangingMeasurementData.RangeStatus == 0 ){ Distance_data = (int)VL53L0XDevs.LeakyRange; //uart_printf("## MeasureData Distance = %4d mm \r\n", (int)Distance_data,(int) VL53L0XDevs.LeakyRange); } } else{ HandleError(ERR_DEMO_RANGE_ONE); } return status; } /* USER CODE END 0 */ void STS_TOF_VL53L1X_Range_Process(void) { /* USER CODE BEGIN 1 */ int ExitWithLongPress=0; RangingConfig_e RangingConfig = HIGH_ACCURACY; //LONG_RANGE; //DemoMode_e DemoMode = RANGE_VALUE; int UseSensorsMask = 1<<XNUCLEO53L0A1_DEV_CENTER; static char buf[VL53L0X_MAX_STRING_LENGTH]; /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); /* Initialize timestamping for UART logging */ TimeStamp_Init(); /* USER CODE BEGIN 2 */ XNUCLEO53L0A1_Init(); //i2c, usart uart_printf(WelcomeMsg); uart_printf("\n##### 53L0\r\n"); sts_tof_vl53l0x_DetectSensors(); // confirm sensor online VL53L0X_trace_config(NULL, TRACE_MODULE_NONE, TRACE_LEVEL_NONE, TRACE_FUNCTION_NONE); // No Trace do { int Status=0; // RangingConfig == HIGH ACCURACY, LONG RANGE, HIGH SPEED Status = sts_tof_vl53l0x_range(&VL53L0XDevs,&RangingMeasurementData, RangingConfig); // status ==0, and ranging status for valid ranging value !!!!!!!!!!!!!!!!! if ((Status == 0) && (RangingMeasurementData.RangeStatus == 0 )) { //Distance_data = (int) VL53L0XDevs.LeakyRange; uart_printf("## LeakyRange = %4d mm\r\n", (int)Distance_data); } HAL_Delay(WelcomeTime/2); } while (1); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* Reset and Detect all sensors */ uart_printf("Reset and Detect Sensor 0 \r\n"); /* Reset Timestamping */ TimeStamp_Reset(); /* Start Ranging demo */ uart_printf("Start Ranging demo\r\n"); // ExitWithLongPress = RangeDemo(UseSensorsMask, RangingConfig); /* Blue button has been pressed (long or short press) */ if(ExitWithLongPress){ /* Short press : change ranging config */ RangingConfig = (RangingConfig == LONG_RANGE) ? HIGH_SPEED : ((RangingConfig == HIGH_SPEED) ? HIGH_ACCURACY : LONG_RANGE); } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } void STS_TOF_VL53L1X_Range_Process(void) { uint8_t vl53lx_model = STS_TOF_VL53L1X; uint8_t range_mode = STS_TOF_SHORT_RANGE; uint16_t i_distance_threshold_mm = 800; uint16_t i_inter_measurement_ms, i_macro_timing; uint16_t i_roi_width = 16, i_sigma_mm = 30, i_signal_kcps = 2000; while (1) { switch (range_mode) { case STS_TOF_SHORT_RANGE: // STS ---002 for short distance /* Example for robust and short distance measurements. Max distance reduced * but very low number of false-positives */ // status |= VL53L1X_ULP_SetSigmaThreshold(dev, 30); // status |= VL53L1X_ULP_SetSignalThreshold(dev, 2000); i_sigma_mm = 30; // increase this for longer distance, reduce for shorter distance i_signal_kcps = 2000; // 1000- 6000 kcps i_distance_threshold_mm = 900; i_inter_measurement_ms = 100; // 100 - 1000 ms i_macro_timing = 100; // i_roi_width = 16; break; case STS_TOF_LONG_RANGE: // STS --- 003 for long range /* Relax some limits. Be careful, it can create false-positives !*/ // status |= VL53L1X_ULP_SetSigmaThreshold(dev, 60); // status |= VL53L1X_ULP_SetSignalThreshold(dev, 1200); i_sigma_mm = 85; // increase this for longer distance, reduce for short distance i_signal_kcps = 1000; // 1000- 6000 kcps i_distance_threshold_mm = 4000; // 4000; i_inter_measurement_ms = 200; // 100 - 1000 ms i_macro_timing = 30; // 1 - 100 ms i_roi_width = 8; break; case STS_TOF_LOW_POWER_RANGE: // STS---001 for ultra low power /* Reduce the macro timing to minimum. This is equivalent as reducing the integration time */ // status = VL53L1X_ULP_SetMacroTiming(dev, 1); i_distance_threshold_mm = 4000; i_inter_measurement_ms = 100; // 100 - 1000 ms i_macro_timing = 1; /* Reduce at maximum the SPADS */ // status = VL53L1X_ULP_SetROI(dev, 4); i_roi_width = 4; break; default: break; } sts_vl53lx_ranging(vl53lx_model, range_mode, i_distance_threshold_mm, i_inter_measurement_ms, i_macro_timing, i_roi_width, i_sigma_mm, i_signal_kcps); range_mode++; range_mode %= 3; } } void BSP_PB_Callback(Button_TypeDef Button) { // PushButtonDetected = 1; } int sts_vl53l1x_ranging(uint8_t vl_model, uint8_t range_mode, uint16_t distance_threshold_mm, uint16_t inter_measurement_ms, uint16_t macro_timing, uint16_t roi_width, uint16_t sigma_mm, uint16_t signal_kcps) { /*********************************/ /* VL53L1X ranging variables */ /*********************************/ uint8_t status, loop; uint8_t dev; uint16_t sensor_id; uint8_t measurement_status; uint16_t estimated_distance_mm, r_signal_kcps, r_sigma_mm, r_ambient_kcps; /*********************************/ /* Customer platform */ /*********************************/ /* Default VL53L1X Ultra Low Power I2C address */ dev = 0x52; /* (Optional) Change I2C address */ // status = VL53L1X_ULP_SetI2CAddress(dev, 0x52); // dev = 0x20; /*********************************/ /* Power on sensor and init */ /*********************************/ APP_LOG(TS_OFF, VLEVEL_L, "Range Mode =%d \r\n", range_mode); /* (Optional) Check if there is a VL53L1X sensor connected */ status = VL53L1X_ULP_GetSensorId(dev, &sensor_id); APP_LOG(TS_OFF, VLEVEL_L, "VL53L1X address =%X\r\n", sensor_id); if (status || (sensor_id != 0xEACC)) { APP_LOG(TS_OFF, VLEVEL_L, "VL53L1X not detected at requested address\n"); return status; } /* (Mandatory) Init VL53L1X sensor */ status = VL53L1X_ULP_SensorInit(dev); if (status) { APP_LOG(TS_OFF, VLEVEL_L, "VL53L1X ultra low power Loading failed\n"); // HAL_Delay(100); return status; } APP_LOG(TS_OFF, VLEVEL_L, "VL53L1X ultra low power ready ! \r\n"); /*********************************/ /* Sensor configuration */ /*********************************/ /* (Optional) Program sensor to raise an interrupt ONLY below 300mm */ status = VL53L1X_ULP_SetInterruptConfiguration(dev, distance_threshold_mm, 1); // i_distance_threshold_mm /* (Optional) Program a 10Hz ranging frequency */ status = VL53L1X_ULP_SetInterMeasurementInMs(dev, inter_measurement_ms); // range_interval_ms /* Increase the macro timing. This is equivalent as increasing the integration time */ status = VL53L1X_ULP_SetMacroTiming(dev, macro_timing); // micro_timing_ms /* Enable all the SPADS */ status = VL53L1X_ULP_SetROI(dev, roi_width); // SPADS { 1 -- 16 } if (range_mode != STS_TOF_LOW_POWER_RANGE) { /* Example for robust and short distance measurements. Max distance reduced * but very low number of false-positives */ status |= VL53L1X_ULP_SetSigmaThreshold(dev, sigma_mm); status |= VL53L1X_ULP_SetSignalThreshold(dev, signal_kcps); } /*********************************/ /* Ranging loop */ /*********************************/ status = VL53L1X_ULP_StartRanging(dev); if (status) { APP_LOG(TS_OFF, VLEVEL_L, "VL53L1X_ULP_StartRanging failed with status %u\n", status); return status; } APP_LOG(TS_OFF, VLEVEL_L, "Ranging started. Put your hand close to the sensor to generate an interrupt...\n"); loop = 0; while (loop < 20) { /* Use this external function to detect when a hardware interrupt is generated on PIN 7 (GPIO1). It means that a new measurement is ready. */ if (IsInterruptDetected(dev)) { /* (Mandatory) Clear HW interrupt to restart measurements */ VL53L1X_ULP_ClearInterrupt(dev); /* Dump debug data */ status = VL53L1X_ULP_DumpDebugData(dev, &measurement_status, &estimated_distance_mm, &r_sigma_mm, &r_signal_kcps, &r_ambient_kcps); APP_LOG(TS_OFF, VLEVEL_L, "Target detected! Interrupt raised by sensor, Distance =%d mm \r\n", estimated_distance_mm); loop++; } } status = VL53L1X_ULP_StopRanging(dev); APP_LOG(TS_OFF, VLEVEL_L, "End of VL53L1X ultra low power demo\n"); return status; } /** System Clock Configuration */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/