本文章 以VL53L0X API為基礎,實驗在RP2040下測試距離量測:
- polling mode: Single ranging or Continuous ranging。
- interrupt mode: ready for new new measurement。
接線圖:
一、實作步驟:
- download VL53L0X API from ST website: https://www.st.com/en/embedded-software/stsw-img005.html
- make a folder vl53l0x_api_rp2040 in project folder
- copy api/core to project folder vl53l0x_api_rp2040/core
- copy platform/inc/vl53l0x_platform.h, vl53l0x_platform_log.h, vl53l0x_types.h to vl53l0x_api_rp2040/platform/inc folder.
- copy platform/src/vl53lx_platform.c to vl53l0x_api_rp2040/platform/src folder
資料夾結構如下圖所示:
- 實作src/vl53l0x_rp2040.c and inc/vl53l0x_rp2040.h, 詳細程式碼附於文章末尾。
- vl53l0x_platform.h 與 vl53l0x_platform.c稍微修改。
- vl53l0x_platform.h:
#include "vl53l0x_def.h"
#include "vl53l0x_platform_log.h"
//#include "vl53l0x_i2c_platform.h" - vl53l0x_platform.c:
include files:
#include "vl53l0x_platform.h"
//#include "vl53l0x_i2c_platform.h"
#include "vl53l0x_rp2040.h"
#include "vl53l0x_api.h"
//#include <Windows.h>
#include "pico/stdlib.h"
檔案末尾, 修改VL53L0X_PollingDelay():
VL53L0X_Error VL53L0X_PollingDelay(VL53L0X_DEV Dev){ VL53L0X_Error status = VL53L0X_ERROR_NONE; busy_wait_ms(1); /* LOG_FUNCTION_START(""); const DWORD cTimeout_ms = 1; HANDLE hEvent = CreateEvent(0, TRUE, FALSE, 0); if(hEvent != NULL) { WaitForSingleObject(hEvent,cTimeout_ms); } LOG_FUNCTION_END(status); */ return status; }
三、實測影片:
四、程式碼:
- vl43l0x_rp2040.c
#include "stdio.h" #include "pico/stdlib.h" #include "vl53l0x_rp2040.h" #include "string.h" #include "vl53l0x_api.h" #define STATUS_OK 0x00 #define STATUS_FAIL 0x01 i2c_inst_t* vl53l0x_i2c_port = i2c_default; uint vl53l0x_i2c_sda = PICO_DEFAULT_I2C_SDA_PIN; uint vl53l0x_i2c_scl = PICO_DEFAULT_I2C_SCL_PIN; VL53L0X_Error VL53L0X_dev_i2c_default_initialise(VL53L0X_Dev_t *pDevice, uint32_t RangeProfile) { VL53L0X_Error Status; i2c_init(vl53l0x_i2c_port, 400 * 1000); gpio_set_function(vl53l0x_i2c_sda, GPIO_FUNC_I2C); gpio_set_function(vl53l0x_i2c_scl, GPIO_FUNC_I2C); gpio_pull_up(vl53l0x_i2c_sda); gpio_pull_up(vl53l0x_i2c_scl); Status = VL53L0X_device_initizlise(pDevice, RangeProfile); return Status; } VL53L0X_Error VL53L0X_dev_i2c_initialise(VL53L0X_Dev_t *pDevice, i2c_inst_t* i2c_port, uint sda, uint scl, uint16_t i2c_speed_k, uint32_t RangeProfile) { VL53L0X_Error Status; vl53l0x_i2c_port = i2c_port; vl53l0x_i2c_sda = sda; vl53l0x_i2c_scl = scl; i2c_init(vl53l0x_i2c_port, i2c_speed_k * 1000); gpio_set_function(vl53l0x_i2c_sda, GPIO_FUNC_I2C); gpio_set_function(vl53l0x_i2c_scl, GPIO_FUNC_I2C); gpio_pull_up(vl53l0x_i2c_sda); gpio_pull_up(vl53l0x_i2c_scl); Status = VL53L0X_device_initizlise(pDevice, RangeProfile); return Status; } VL53L0X_Error VL53L0X_device_initizlise(VL53L0X_Dev_t *pDevice, uint32_t RangeProfile) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; int i; uint32_t refSpadCount; uint8_t isApertureSpads; uint8_t VhvSettings; uint8_t PhaseCal; Status = VL53L0X_DataInit(pDevice); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_StaticInit(pDevice); // Device Initialization if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_PerformRefCalibration(pDevice, &VhvSettings, &PhaseCal); // Device Initialization if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_PerformRefSpadManagement(pDevice, &refSpadCount, &isApertureSpads); // Device Initialization if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetLimitCheckEnable(pDevice, VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, 1); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetLimitCheckEnable(pDevice, VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, 1); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetLimitCheckValue(pDevice, VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, (FixPoint1616_t)(0.1*65536)); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetLimitCheckValue(pDevice, VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, (FixPoint1616_t)(60*65536)); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetMeasurementTimingBudgetMicroSeconds(pDevice, RangeProfile); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetVcselPulsePeriod(pDevice, VL53L0X_VCSEL_PERIOD_PRE_RANGE, 18); if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_SetVcselPulsePeriod(pDevice, VL53L0X_VCSEL_PERIOD_FINAL_RANGE, 14); if(Status != VL53L0X_ERROR_NONE) return Status; return Status; } int32_t VL53L0X_write_multi(uint8_t address, uint8_t index, uint8_t *pdata, int32_t count) { int32_t status = STATUS_OK; uint8_t i2c_buff[count+1]; i2c_buff[0] = index; memcpy(i2c_buff+1, pdata, count); if ( i2c_write_blocking(vl53l0x_i2c_port, address, i2c_buff, count+1, false) == PICO_ERROR_GENERIC) { status = STATUS_FAIL; } return status; } int32_t VL53L0X_read_multi(uint8_t address, uint8_t index, uint8_t *pdata, int32_t count) { int32_t status = STATUS_OK; int i2c_ret = i2c_write_blocking(vl53l0x_i2c_port, address, &index, 1, true); if (i2c_ret == PICO_ERROR_GENERIC) return STATUS_FAIL; i2c_ret = i2c_read_blocking(vl53l0x_i2c_port, address, pdata, count, false); if (i2c_ret == PICO_ERROR_GENERIC) return STATUS_FAIL; return status; } int32_t VL53L0X_write_byte(uint8_t address, uint8_t index, uint8_t data) { int32_t status = STATUS_OK; status = VL53L0X_write_multi(address, index, &data, 1); return status; } int32_t VL53L0X_write_word(uint8_t address, uint8_t index, uint16_t data) { int32_t status = STATUS_OK; uint8_t buffer[BYTES_PER_WORD]; // Split 16-bit word into MS and LS uint8_t buffer[0] = (uint8_t)(data >> 8); buffer[1] = (uint8_t)(data & 0x00FF); status = VL53L0X_write_multi(address, index, buffer, BYTES_PER_WORD); return status; } int32_t VL53L0X_write_dword(uint8_t address, uint8_t index, uint32_t data) { int32_t status = STATUS_OK; uint8_t buffer[BYTES_PER_DWORD]; // Split 32-bit word into MS ... LS bytes buffer[0] = (uint8_t) (data >> 24); buffer[1] = (uint8_t)((data & 0x00FF0000) >> 16); buffer[2] = (uint8_t)((data & 0x0000FF00) >> 8); buffer[3] = (uint8_t) (data & 0x000000FF); status = VL53L0X_write_multi(address, index, buffer, BYTES_PER_DWORD); return status; } int32_t VL53L0X_read_byte(uint8_t address, uint8_t index, uint8_t *pdata) { int32_t status = STATUS_OK; int32_t cbyte_count = 1; status = VL53L0X_read_multi(address, index, pdata, cbyte_count); return status; } int32_t VL53L0X_read_word(uint8_t address, uint8_t index, uint16_t *pdata) { int32_t status = STATUS_OK; uint8_t buffer[BYTES_PER_WORD]; status = VL53L0X_read_multi(address, index, buffer, BYTES_PER_WORD); *pdata = ((uint16_t)buffer[0] << 8) + (uint16_t)buffer[1]; return status; } int32_t VL53L0X_read_dword(uint8_t address, uint8_t index, uint32_t *pdata) { int32_t status = STATUS_OK; uint8_t buffer[BYTES_PER_DWORD]; status = VL53L0X_read_multi(address, index, buffer, BYTES_PER_DWORD); *pdata = ((uint32_t)buffer[0] << 24) + ((uint32_t)buffer[1] << 16) + ((uint32_t)buffer[2] << 8) + (uint32_t)buffer[3]; return status; } VL53L0X_Error VL53L0X_SingleRanging(VL53L0X_Dev_t *pDevice, uint16_t *MeasuredData) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; VL53L0X_RangingMeasurementData_t RangingMeasurementData; *MeasuredData=0; Status = VL53L0X_SetDeviceMode(pDevice, VL53L0X_DEVICEMODE_SINGLE_RANGING); // Setup in single ranging mode if(Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_PerformSingleRangingMeasurement(pDevice, &RangingMeasurementData); if (Status == VL53L0X_ERROR_NONE && RangingMeasurementData.RangeStatus == 0) { *MeasuredData = RangingMeasurementData.RangeMilliMeter; } else { Status = VL53L0X_ERROR_RANGE_ERROR; } /* for accuracy average several samples uint32_t ranging=0; uint32_t valid_count=0; int i; for(i=0; i<10; i++){ Status = VL53L0X_PerformSingleRangingMeasurement(pDevice, &RangingMeasurementData); if (Status == VL53L0X_ERROR_NONE && RangingMeasurementData.RangeStatus == 0) { ranging += RangingMeasurementData.RangeMilliMeter; valid_count++; } if (Status != VL53L0X_ERROR_NONE) break; } if (valid_count == 0) { Status = VL53L0X_ERROR_RANGE_ERROR; } else { *MeasuredData = ranging/valid_count; } */ return Status; } VL53L0X_Error WaitMeasurementDataReady(VL53L0X_Dev_t *pDevice) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; uint8_t dataReady=0; absolute_time_t timeout = make_timeout_time_ms(200); do { Status = VL53L0X_GetMeasurementDataReady(pDevice, &dataReady); if ((dataReady == 0x01) || Status != VL53L0X_ERROR_NONE) { break; } } while (absolute_time_diff_us(get_absolute_time(), timeout) > 0); if (!dataReady) Status = VL53L0X_ERROR_TIME_OUT; return Status; } VL53L0X_Error WaitStopCompleted(VL53L0X_Dev_t *pDevice) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; uint32_t StopCompleted=1; absolute_time_t timeout = make_timeout_time_ms(200); do { Status = VL53L0X_GetStopCompletedStatus(pDevice, &StopCompleted); if ((StopCompleted == 0x00) || Status != VL53L0X_ERROR_NONE) { break; } } while (absolute_time_diff_us(get_absolute_time(), timeout) > 0); if (StopCompleted) { Status = VL53L0X_ERROR_TIME_OUT; } return Status; } VL53L0X_Error VL53L0X_ContinuousRanging(VL53L0X_Dev_t *pDevice, uint16_t *MeasuredData, uint16_t RangeCount, uint16_t *validCount){ VL53L0X_Error Status = VL53L0X_ERROR_NONE; VL53L0X_RangingMeasurementData_t RangingMeasurementData; Status = VL53L0X_SetDeviceMode(pDevice, VL53L0X_DEVICEMODE_CONTINUOUS_RANGING); if (Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_StartMeasurement(pDevice); if (Status != VL53L0X_ERROR_NONE) return Status; *validCount=0; uint16_t vCount=0; for (int i=0; i < RangeCount; i++) { Status = WaitMeasurementDataReady(pDevice); if (Status != VL53L0X_ERROR_NONE) break; Status = VL53L0X_GetRangingMeasurementData(pDevice, &RangingMeasurementData); if (Status == VL53L0X_ERROR_NONE ) { if (RangingMeasurementData.RangeStatus == 0) { MeasuredData[vCount++] = RangingMeasurementData.RangeMilliMeter; //printf("valid:%d, m:%d \n",vCount, RangingMeasurementData.RangeMilliMeter); // Clear the interrupt } VL53L0X_ClearInterruptMask(pDevice, VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); VL53L0X_PollingDelay(pDevice); } } *validCount = vCount; Status = VL53L0X_StopMeasurement(pDevice); if (Status != VL53L0X_ERROR_NONE) return Status; Status = WaitStopCompleted(pDevice); if (Status != VL53L0X_ERROR_NONE) return Status; Status = VL53L0X_ClearInterruptMask(pDevice, VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); return Status; }
- vl43l0x_rp2040.h
#ifndef _VL53L0X_RP2040_H_ #define _VL53L0X_RP2040_H_ #ifdef __cplusplus extern "C" { #endif #include "hardware/i2c.h" #include "vl53l0x_platform.h" #define BYTES_PER_WORD 2 #define BYTES_PER_DWORD 4 enum { VL53L0X_DEFAULT_MODE = 30000, VL53L0X_HIGHT_ACCURACY = 200000, VL53L0X_LONG_RANGE = 33000, VL53L0X_HIGH_SPEED = 20000 } RANGE_PROFILE; int32_t VL53L0X_write_multi(uint8_t address, uint8_t index, uint8_t *pdata, int32_t count); int32_t VL53L0X_read_multi(uint8_t address, uint8_t index, uint8_t *pdata, int32_t count); int32_t VL53L0X_write_byte(uint8_t address, uint8_t index, uint8_t data); int32_t VL53L0X_write_word(uint8_t address, uint8_t index, uint16_t data); int32_t VL53L0X_write_dword(uint8_t address, uint8_t index, uint32_t data); int32_t VL53L0X_read_byte(uint8_t address, uint8_t index, uint8_t *pdata); int32_t VL53L0X_read_word(uint8_t address, uint8_t index, uint16_t *pdata); int32_t VL53L0X_read_dword(uint8_t address, uint8_t index, uint32_t *pdata); VL53L0X_Error VL53L0X_device_initizlise(VL53L0X_Dev_t *pDevice, uint32_t RangeProfile); VL53L0X_Error VL53L0X_dev_i2c_default_initialise(VL53L0X_Dev_t *pDevice, uint32_t RangeProfile); VL53L0X_Error VL53L0X_dev_i2c_initialise(VL53L0X_Dev_t *pDevice, i2c_inst_t* i2c_port, uint sda, uint scl, uint16_t i2c_speed_k, uint32_t RangeProfile); void vl53l0x_print_device_info(VL53L0X_Dev_t *pDevice); VL53L0X_Error VL53L0X_SingleRanging(VL53L0X_Dev_t *pDevice, uint16_t *MeasureData); VL53L0X_Error VL53L0X_ContinuousRanging(VL53L0X_Dev_t *pDevice, uint16_t *MeasuredData, uint16_t RangeCount, uint16_t *validCount); #ifdef __cplusplus } #endif #endif //_VL53L0X_RP2040_H_
- CMakeLists.txt(vl53l0x_api_rp2040)
add_library(vl53l0x_api_rp2040 INTERFACE) target_sources(vl53l0x_api_rp2040 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/core/src/vl53l0x_api_calibration.c ${CMAKE_CURRENT_LIST_DIR}/core/src/vl53l0x_api_core.c ${CMAKE_CURRENT_LIST_DIR}/core/src/vl53l0x_api_ranging.c ${CMAKE_CURRENT_LIST_DIR}/core/src/vl53l0x_api_strings.c ${CMAKE_CURRENT_LIST_DIR}/core/src/vl53l0x_api.c ${CMAKE_CURRENT_LIST_DIR}/platform/src/vl53l0x_rp2040.c ${CMAKE_CURRENT_LIST_DIR}/platform/src/vl53l0x_platform.c ) target_include_directories(vl53l0x_api_rp2040 INTERFACE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/core/inc ${CMAKE_CURRENT_LIST_DIR}/core/src ${CMAKE_CURRENT_LIST_DIR}/platform/inc ${CMAKE_CURRENT_LIST_DIR}/platform/crc ) target_link_libraries(vl53l0x_api_rp2040 INTERFACE pico_stdlib hardware_i2c )
- pico_vl53l0x.c(main test file)
#include "stdio.h" #include "stdlib.h" #include "pico/stdlib.h" #include "vl53l0x_api.h" #include "vl53l0x_rp2040.h" #include "pico/multicore.h" VL53L0X_RangingMeasurementData_t gRangingData; VL53L0X_Dev_t gVL53L0XDevice; #define VL53L0X_GPIO_IRQ 16 #define RED_LED_PIN 15 #define GREEN_LED_PIN 14 #define BLUE_LED_PIN 13 volatile bool vl53l0x_irq_data_ready=false; void gpioirq_cb(uint gpio, uint32_t event_mask) { VL53L0X_Error Status; uint8_t data_ready=0; if (gpio == VL53L0X_GPIO_IRQ) { if (event_mask & GPIO_IRQ_EDGE_FALL) { gpio_acknowledge_irq(VL53L0X_GPIO_IRQ, GPIO_IRQ_EDGE_RISE); vl53l0x_irq_data_ready=true; } } } void core1__interrupt_task() { VL53L0X_Error Status; gpio_init(VL53L0X_GPIO_IRQ); gpio_set_irq_enabled_with_callback(VL53L0X_GPIO_IRQ, GPIO_IRQ_EDGE_FALL, true, gpioirq_cb); Status = VL53L0X_SetDeviceMode(&gVL53L0XDevice, VL53L0X_DEVICEMODE_CONTINUOUS_RANGING); if (Status != VL53L0X_ERROR_NONE) { printf("VL52L01 Device mode error\n"); return; } Status = VL53L0X_StartMeasurement(&gVL53L0XDevice); while(1) { if (vl53l0x_irq_data_ready) { vl53l0x_irq_data_ready=false; Status=VL53L0X_GetRangingMeasurementData(&gVL53L0XDevice, &gRangingData); if (Status == VL53L0X_ERROR_NONE && gRangingData.RangeStatus == VL53L0X_ERROR_NONE) { printf("Ranging data:%4d mm\n", gRangingData.RangeMilliMeter); } sleep_ms(10); // VL53L0X_ClearInterruptMask(&gVL53L0XDevice, VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY); } } VL53L0X_StopMeasurement(&gVL53L0XDevice); } VL53L0X_Error singleRanging(VL53L0X_Dev_t *pDevice, uint16_t *MeasuredData) { VL53L0X_Error Status; Status = VL53L0X_SingleRanging(pDevice, MeasuredData); /* if (Status == VL53L0X_ERROR_NONE) printf("Measured distance: %d\n",*MeasuredData); else printf("measure error\n"); */ return Status; } VL53L0X_Error continuousRanging(VL53L0X_Dev_t *pDevice, uint16_t *ContinuousData, uint16_t *validCount) { uint32_t sum=0; uint16_t MeasuredData=0; VL53L0X_Error Status; sum=0; Status = VL53L0X_ContinuousRanging(pDevice, ContinuousData, 16, validCount); for (int i = 0; i < *validCount; i++) { sum += ContinuousData[i]; } if (*validCount > 0) { MeasuredData = sum/(*validCount); printf("Average continuous measured distance: %4d,\n" "\tmeasuerd count: %d, valid count: %d\n\n",MeasuredData, 16, *validCount); } else { printf("measure error\n"); } return Status; } int main(void) { VL53L0X_Error Status = VL53L0X_ERROR_NONE; VL53L0X_Dev_t *pDevice = &gVL53L0XDevice; stdio_init_all(); gpio_init(RED_LED_PIN); gpio_init(GREEN_LED_PIN); gpio_init(BLUE_LED_PIN); gpio_set_dir_out_masked(1 << RED_LED_PIN|1 << GREEN_LED_PIN|1 << BLUE_LED_PIN); pDevice->I2cDevAddr = 0x29; pDevice->comms_type = 1; pDevice->comms_speed_khz = 400; Status = VL53L0X_dev_i2c_default_initialise(pDevice, VL53L0X_DEFAULT_MODE); // interrupt ranging test //multicore_launch_core1(core1__interrupt_task); //while (1); uint16_t continuousRingingValue[32]; uint16_t validCount; uint16_t ranging_value=32; while(1) { //continuousRanging(pDevice, continuousRingingValue, &validCount); Status = singleRanging(pDevice, &ranging_value); if (Status == VL53L0X_ERROR_NONE) { gpio_put_masked(1 << RED_LED_PIN|1 << GREEN_LED_PIN|1 << BLUE_LED_PIN, 0); if (ranging_value < 200) gpio_put(RED_LED_PIN, true); else if (ranging_value < 400) gpio_put(BLUE_LED_PIN, true); else gpio_put(GREEN_LED_PIN, true); } } return 0; }
- CMakeLists.txt(root)
# Generated Cmake Pico project file cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # Initialise pico_sdk from installed location # (note this can come from environment, CMake cache etc) set(PICO_SDK_PATH "/home/duser/pico/pico-sdk") set(PICO_BOARD pico CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") endif() project(pico_vl53l0x C CXX ASM) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(pico_vl53l0x pico_vl53l0x.c ) pico_set_program_name(pico_vl53l0x "pico_vl53l0x") pico_set_program_version(pico_vl53l0x "0.1") pico_enable_stdio_uart(pico_vl53l0x 0) pico_enable_stdio_usb(pico_vl53l0x 1) # Add the standard library to the build target_link_libraries(pico_vl53l0x pico_stdlib) # Add the standard include files to the build target_include_directories(pico_vl53l0x PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required ) # Add any user requested libraries target_link_libraries(pico_vl53l0x hardware_i2c pico_multicore ) add_subdirectory(vl53l0x_api_rp2040) target_link_libraries(pico_vl53l0x vl53l0x_api_rp2040 ) pico_add_extra_outputs(pico_vl53l0x)
沒有留言:
張貼留言