#include "vl53_board.h" #include "FreeRTOS.h" #include "task.h" #include "robot_params.h" #include "vl53_calibration_config.h" /* ================= EMA滤波底层实现 ================= */ static void vl53_ema_init(Vl53EMA_t *ema, float alpha) { ema->x = 0.0f; ema->alpha = alpha; ema->initialized = 0; } static float vl53_ema_update(Vl53EMA_t *ema, float measurement) { if (ema->initialized == 0) { ema->x = measurement; ema->initialized = 1; return ema->x; } // EMA公式: x_new = alpha * measurement + (1 - alpha) * x_old ema->x = ema->alpha * measurement + (1.0f - ema->alpha) * ema->x; return ema->x; } static const Vl53RuntimeCalibration_t *vl53_get_runtime_calibration(uint8_t id) { switch (id) { case 0: return &k_vl53_left_calibration[0]; case 1: return &k_vl53_left_calibration[1]; case 2: return &k_vl53_right_calibration[0]; case 3: return &k_vl53_right_calibration[1]; default: return NULL; } } static VL53L0X_Error vl53_apply_runtime_calibration(VL53L0X_DEV dev, uint8_t id) { const Vl53RuntimeCalibration_t *cal = vl53_get_runtime_calibration(id); VL53L0X_Error status; if (cal == NULL) return VL53L0X_ERROR_NONE; if (cal->offset_calibrated != 0U) { status = VL53L0X_SetOffsetCalibrationDataMicroMeter(dev, cal->offset_micro_meters); if (status != VL53L0X_ERROR_NONE) return status; } if (cal->xtalk_calibrated != 0U) { status = VL53L0X_SetXTalkCompensationRateMegaCps(dev, cal->xtalk_compensation_rate_mcps); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_SetXTalkCompensationEnable(dev, 1u); if (status != VL53L0X_ERROR_NONE) return status; } else { status = VL53L0X_SetXTalkCompensationEnable(dev, 0u); if (status != VL53L0X_ERROR_NONE) return status; } return VL53L0X_ERROR_NONE; } /* ================= ST 官方配置序列 ================= */ static VL53L0X_Error vl53_do_static_init(VL53L0X_DEV dev, uint32_t timing_budget_us) { VL53L0X_Error status; uint32_t ref_spad_count = 0u; uint8_t is_aperture_spads = 0u; uint8_t vhv_settings = 0u; uint8_t phase_cal = 0u; status = VL53L0X_StaticInit(dev); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_PerformRefSpadManagement(dev, &ref_spad_count, &is_aperture_spads); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_PerformRefCalibration(dev, &vhv_settings, &phase_cal); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_SetDeviceMode(dev, VL53L0X_DEVICEMODE_CONTINUOUS_RANGING); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_SetMeasurementTimingBudgetMicroSeconds(dev, timing_budget_us); if (status != VL53L0X_ERROR_NONE) return status; /* 连续测量采用 back-to-back 模式,让下一次测量在上一帧完成后立即开始。 * 这样任务轮询周期变快时,能尽可能拿到最新帧,而不是被固定间隔再次拖慢。 */ status = VL53L0X_SetInterMeasurementPeriodMilliSeconds(dev, 0u); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_SetLimitCheckEnable(dev, VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, 1u); if (status != VL53L0X_ERROR_NONE) return status; status = VL53L0X_SetLimitCheckEnable(dev, VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, 1u); if (status != VL53L0X_ERROR_NONE) return status; return VL53L0X_ERROR_NONE; } /* ================= 核心管理器 API ================= */ VL53L0X_Error Vl53Board_Init(Vl53Board_t *board, const Vl53BoardHwCfg_t *hw_cfgs, uint8_t count, uint32_t timing_budget_us) { if ((board == NULL) || (hw_cfgs == NULL) || (count == 0) || (count > VL53_MAX_DEVS_PER_BOARD)) { return VL53L0X_ERROR_INVALID_PARAMS; } memset(board, 0, sizeof(Vl53Board_t)); board->dev_count = count; board->timing_budget_us = (timing_budget_us == 0u) ? 33000u : timing_budget_us; for (uint8_t i = 0; i < count; i++) { board->dev[i].name = hw_cfgs[i].name; board->dev[i].id = hw_cfgs[i].id; board->dev[i].comms_type = 1u; board->dev[i].comms_speed_khz = 400u; board->dev[i].is_present = 0u; VL53L0X_PlatformAttachBus(&board->dev[i], hw_cfgs[i].hi2c, VL53L0X_DEFAULT_ADDR_8BIT, 100u, NULL); VL53L0X_PlatformAttachPins(&board->dev[i], hw_cfgs[i].xshut_port, hw_cfgs[i].xshut_pin, NULL, 0); VL53L0X_PlatformSetXShut(&board->dev[i], GPIO_PIN_RESET); /* 初始化EMA滤波器:alpha 从 robot_params.h 读取 */ vl53_ema_init(&board->ema[i], PARAM_VL53_EMA_ALPHA); } vTaskDelay(pdMS_TO_TICKS(10)); for (uint8_t i = 0; i < count; i++) { if (VL53L0X_PlatformSetXShut(&board->dev[i], GPIO_PIN_SET) != VL53L0X_ERROR_NONE) continue; vTaskDelay(pdMS_TO_TICKS(20)); board->dev[i].I2cDevAddr = VL53L0X_DEFAULT_ADDR_8BIT; if (VL53L0X_PlatformChangeAddress(&board->dev[i], hw_cfgs[i].runtime_addr_8bit) != VL53L0X_ERROR_NONE) continue; if (VL53L0X_DataInit(&board->dev[i]) != VL53L0X_ERROR_NONE) continue; if (vl53_do_static_init(&board->dev[i], board->timing_budget_us) != VL53L0X_ERROR_NONE) continue; if (vl53_apply_runtime_calibration(&board->dev[i], hw_cfgs[i].id) != VL53L0X_ERROR_NONE) continue; board->init_mask |= (uint8_t)(1u << i); board->dev[i].is_present = 1u; } return VL53L0X_ERROR_NONE; } VL53L0X_Error Vl53Board_StartContinuous(Vl53Board_t *board) { if (board == NULL) return VL53L0X_ERROR_INVALID_PARAMS; for (uint8_t i = 0; i < board->dev_count; i++) { if (board->init_mask & (1u << i)) VL53L0X_StartMeasurement(&board->dev[i]); } return VL53L0X_ERROR_NONE; } VL53L0X_Error Vl53Board_StopContinuous(Vl53Board_t *board) { if (board == NULL) return VL53L0X_ERROR_INVALID_PARAMS; for (uint8_t i = 0; i < board->dev_count; i++) { if (board->init_mask & (1u << i)) VL53L0X_StopMeasurement(&board->dev[i]); } return VL53L0X_ERROR_NONE; } VL53L0X_Error Vl53Board_ReadAll(Vl53Board_t *board, Vl53BoardSnapshot_t *snapshot) { if (board == NULL || snapshot == NULL) return VL53L0X_ERROR_INVALID_PARAMS; memset(snapshot, 0, sizeof(Vl53BoardSnapshot_t)); snapshot->tick_ms = xTaskGetTickCount() * portTICK_PERIOD_MS; for (uint8_t i = 0; i < board->dev_count; i++) { if ((board->init_mask & (1u << i)) == 0u) { snapshot->range_status[i] = 255u; continue; } uint8_t ready = 0u; if (VL53L0X_GetMeasurementDataReady(&board->dev[i], &ready) != VL53L0X_ERROR_NONE) continue; if (ready) { VL53L0X_RangingMeasurementData_t data; memset(&data, 0, sizeof(data)); if (VL53L0X_GetRangingMeasurementData(&board->dev[i], &data) == VL53L0X_ERROR_NONE) { /* 1. 写入原始数据 */ snapshot->range_mm[i] = data.RangeMilliMeter; snapshot->range_status[i] = data.RangeStatus; if (data.RangeStatus == 0u) { /* 2. 标记有效并按开关决定是否应用EMA滤波 */ snapshot->valid_mask |= (1u << i); #if PARAM_VL53_USE_EMA_FILTER snapshot->range_mm_filtered[i] = vl53_ema_update(&board->ema[i], (float)data.RangeMilliMeter); #else snapshot->range_mm_filtered[i] = (float)data.RangeMilliMeter; board->ema[i].x = (float)data.RangeMilliMeter; board->ema[i].initialized = 1u; #endif } else { /* 测距失败时,滤波值维持上一次的历史最佳估计不变 */ snapshot->range_mm_filtered[i] = board->ema[i].x; } VL53L0X_ClearInterruptMask(&board->dev[i], 0u); } } else { /* 如果没准备好,把上一帧的历史值顺延下来,防止读到 0 */ snapshot->range_mm_filtered[i] = board->ema[i].x; } } return VL53L0X_ERROR_NONE; }