/** * @file VL6180_ATY.c * * @param Project DEVICE_GENERAL_ATY_LIB * * @author ATY * * @copyright * - Copyright 2017 - 2026 MZ-ATY * - This code follows: * - MZ-ATY Various Contents Joint Statement - * * https://mengze.top/MZ-ATY_VCJS * - CC 4.0 BY-NC-SA - * * https://creativecommons.org/licenses/by-nc-sa/4.0/ * - Your use will be deemed to have accepted the terms of this statement. * * @brief functions of VL6180 proximity and ambient light sensor for all embedded device * * @version * - 1_00_250101 > ATY * -# Initial version, support I2C communication, range and ALS measurement ******************************************************************************** */ #include "VL6180_ATY.h" /******************************* Private Functions *****************************/ /** * @brief Check if device is initialized, auto-initialize if not * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ static uint8_t VL6180_CheckAndAutoInit(struct VL6180_ATY_Dev *dev) { if (!dev->initialized) { if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Auto-initializing device...\n"); } return VL6180_Init(dev); } return VL6180_STATUS_OK; } /** * @brief VL6180 default initialization settings * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ static uint8_t VL6180_ApplyDefaultSettings(struct VL6180_ATY_Dev *dev) { // Mandatory: Private registers VL6180_WriteRegister8(dev, 0x0207, 0x01); VL6180_WriteRegister8(dev, 0x0208, 0x01); VL6180_WriteRegister8(dev, 0x0096, 0x00); VL6180_WriteRegister8(dev, 0x0097, 0xfd); VL6180_WriteRegister8(dev, 0x00e3, 0x00); VL6180_WriteRegister8(dev, 0x00e4, 0x04); VL6180_WriteRegister8(dev, 0x00e5, 0x02); VL6180_WriteRegister8(dev, 0x00e6, 0x01); VL6180_WriteRegister8(dev, 0x00e7, 0x03); VL6180_WriteRegister8(dev, 0x00f5, 0x02); VL6180_WriteRegister8(dev, 0x00d9, 0x05); VL6180_WriteRegister8(dev, 0x00db, 0xce); VL6180_WriteRegister8(dev, 0x00dc, 0x03); VL6180_WriteRegister8(dev, 0x00dd, 0xf8); VL6180_WriteRegister8(dev, 0x009f, 0x00); VL6180_WriteRegister8(dev, 0x00a3, 0x3c); VL6180_WriteRegister8(dev, 0x00b7, 0x00); VL6180_WriteRegister8(dev, 0x00bb, 0x3c); VL6180_WriteRegister8(dev, 0x00b2, 0x09); VL6180_WriteRegister8(dev, 0x00ca, 0x09); VL6180_WriteRegister8(dev, 0x0198, 0x01); VL6180_WriteRegister8(dev, 0x01b0, 0x17); VL6180_WriteRegister8(dev, 0x01ad, 0x00); VL6180_WriteRegister8(dev, 0x00ff, 0x05); VL6180_WriteRegister8(dev, 0x0100, 0x05); VL6180_WriteRegister8(dev, 0x0199, 0x05); VL6180_WriteRegister8(dev, 0x01a6, 0x1b); VL6180_WriteRegister8(dev, 0x01ac, 0x3e); VL6180_WriteRegister8(dev, 0x01a7, 0x1f); VL6180_WriteRegister8(dev, 0x0030, 0x00); // Recommended: Public registers - See data sheet for more detail VL6180_WriteRegister8(dev, VL6180_REG_SYSTEM_MODE_GPIO1, 0x10); // Enables polling for 'New Sample ready' VL6180_WriteRegister8(dev, VL6180_REG_READOUT_AVERAGING_SAMPLE_PERIOD, 0x30); // Set the averaging sample period VL6180_WriteRegister8(dev, VL6180_REG_SYSALS_ANALOGUE_GAIN, 0x46); // Light sensor gain settings VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_VHV_REPEAT_RATE, 0xFF); // Set auto calibration period VL6180_WriteRegister8(dev, VL6180_REG_SYSALS_INTEGRATION_PERIOD, 0x63); // ALS integration time VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_VHV_RECALIBRATE, 0x01); // perform a single temperature calibration VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_INTERMEASUREMENT_PERIOD, 0x09); // Set default ranging inter-measurement period to 100ms VL6180_WriteRegister8(dev, VL6180_REG_SYSALS_INTERMEASUREMENT_PERIOD, 0x31); // Set default ALS inter-measurement period to 500ms VL6180_WriteRegister8(dev, VL6180_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, 0x24); // Configures interrupt on 'New Sample Ready threshold event' // Clear fresh out of reset flag VL6180_WriteRegister8(dev, VL6180_REG_SYSTEM_FRESH_OUT_OF_RESET, 0x00); return VL6180_STATUS_OK; } /******************************* Public Functions *****************************/ /** * @brief Initialize VL6180 device * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_Init(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Initializing device...\n"); } // Set default I2C address if not set if (dev->i2cAddress == 0) { dev->i2cAddress = VL6180_I2C_ADDR_DEFAULT; } // Check if device is present uint8_t modelId; if (VL6180_ReadRegister8(dev, VL6180_REG_IDENTIFICATION_MODEL_ID, &modelId) != VL6180_STATUS_OK) { dev->devicePresent = 0; dev->lastStatus = VL6180_STATUS_NO_DEVICE; if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Device not found!\n"); } return VL6180_STATUS_NO_DEVICE; } if (modelId != 0xB4) { dev->devicePresent = 0; dev->lastStatus = VL6180_STATUS_NO_DEVICE; if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Invalid model ID: 0x%02X\n", modelId); } return VL6180_STATUS_NO_DEVICE; } dev->devicePresent = 1; dev->modelId = modelId; // Read device identification VL6180_ReadDeviceID(dev); // Check if fresh out of reset uint8_t freshOutOfReset; VL6180_ReadRegister8(dev, VL6180_REG_SYSTEM_FRESH_OUT_OF_RESET, &freshOutOfReset); dev->freshOutOfReset = freshOutOfReset; if (freshOutOfReset == 0x01) { // Apply default settings VL6180_ApplyDefaultSettings(dev); if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Applied default settings\n"); } } // Set default configuration dev->alsGain = VL6180_ALS_GAIN_1; dev->alsIntegrationPeriod = 100; dev->rangeMaxConvergenceTime = 50; dev->rangeOffset = 0; dev->crosstalkCompensationRate = 0; dev->initialized = 1; dev->lastStatus = VL6180_STATUS_OK; if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Initialization completed successfully\n"); } return VL6180_STATUS_OK; } /** * @brief Reset VL6180 device * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_Reset(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Resetting device...\n"); } // Clear all interrupt flags VL6180_WriteRegister8(dev, VL6180_REG_SYSTEM_INTERRUPT_CLEAR, 0x07); // Reset device state dev->initialized = 0; dev->devicePresent = 0; dev->rangeValue = 0; dev->alsValue = 0; dev->rangeStatus = 0; dev->alsStatus = 0; dev->lastStatus = VL6180_STATUS_OK; return VL6180_STATUS_OK; } /** * @brief Read device identification information * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ReadDeviceID(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } VL6180_ReadRegister8(dev, VL6180_REG_IDENTIFICATION_MODEL_ID, &dev->modelId); VL6180_ReadRegister8(dev, VL6180_REG_IDENTIFICATION_MODEL_REV_MAJOR, &dev->modelRevMajor); VL6180_ReadRegister8(dev, VL6180_REG_IDENTIFICATION_MODEL_REV_MINOR, &dev->modelRevMinor); VL6180_ReadRegister8(dev, VL6180_REG_IDENTIFICATION_MODULE_REV_MAJOR, &dev->moduleRevMajor); VL6180_ReadRegister8(dev, VL6180_REG_IDENTIFICATION_MODULE_REV_MINOR, &dev->moduleRevMinor); if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Model ID: 0x%02X, Rev: %d.%d, Module Rev: %d.%d\n", dev->modelId, dev->modelRevMajor, dev->modelRevMinor, dev->moduleRevMajor, dev->moduleRevMinor); } return VL6180_STATUS_OK; } /** * @brief Load default settings to VL6180 * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_LoadDefaultSettings(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } return VL6180_ApplyDefaultSettings(dev); } /******************************* Range Measurement Functions *****************/ /** * @brief Start range measurement * @param dev Pointer to VL6180 device structure * @param mode Measurement mode (single shot or continuous) * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_StartRangeMeasurement(struct VL6180_ATY_Dev *dev, uint8_t mode) { if (!dev) { return VL6180_STATUS_ERROR; } // Auto-initialize if not initialized uint8_t status = VL6180_CheckAndAutoInit(dev); if (status != VL6180_STATUS_OK) { return status; } if (dev->lock) { if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Device is locked\n"); } return VL6180_STATUS_ERROR; } dev->lock = 1; if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Starting range measurement (mode: %d)\n", mode); } // Clear any existing interrupts VL6180_ClearRangeInterrupt(dev); // Start measurement status = VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_START, mode); dev->lock = 0; dev->lastStatus = status; return status; } /** * @brief Read range measurement result * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ReadRangeResult(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } // Auto-initialize if not initialized uint8_t status = VL6180_CheckAndAutoInit(dev); if (status != VL6180_STATUS_OK) { return status; } if (dev->lock) { return VL6180_STATUS_ERROR; } dev->lock = 1; // Read range value VL6180_ReadRegister8(dev, VL6180_REG_RESULT_RANGE_VAL, &dev->rangeValue); // Read range status VL6180_ReadRegister8(dev, VL6180_REG_RESULT_RANGE_STATUS, &dev->rangeStatus); // Read additional range data VL6180_ReadRegister16(dev, VL6180_REG_RESULT_RANGE_RAW, &dev->rangeRaw); VL6180_ReadRegister16(dev, VL6180_REG_RESULT_RANGE_RETURN_RATE, &dev->rangeRate); VL6180_ReadRegister32(dev, VL6180_REG_RESULT_RANGE_RETURN_SIGNAL_COUNT, &dev->rangeSignalCount); VL6180_ReadRegister32(dev, VL6180_REG_RESULT_RANGE_RETURN_AMB_COUNT, &dev->rangeAmbientCount); VL6180_ReadRegister16(dev, VL6180_REG_RESULT_RANGE_RETURN_CONV_TIME, &dev->rangeConvergenceTime); if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Range: %dmm, Status: 0x%02X, Rate: %d\n", dev->rangeValue, dev->rangeStatus, dev->rangeRate); } dev->lock = 0; dev->lastStatus = VL6180_STATUS_OK; return VL6180_STATUS_OK; } /** * @brief Get range measurement value * @param dev Pointer to VL6180 device structure * @return Range value in millimeters */ uint8_t VL6180_GetRangeValue(struct VL6180_ATY_Dev *dev) { if (!dev) { return 0; } return dev->rangeValue; } /** * @brief Check if range measurement is ready * @param dev Pointer to VL6180 device structure * @return 1 if ready, 0 if not ready */ uint8_t VL6180_IsRangeReady(struct VL6180_ATY_Dev *dev) { if (!dev) { return 0; } uint8_t status; VL6180_ReadRegister8(dev, VL6180_REG_RESULT_INTERRUPT_STATUS_GPIO, &status); return (status & 0x04) ? 1 : 0; } /** * @brief Clear range interrupt * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ClearRangeInterrupt(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } return VL6180_WriteRegister8(dev, VL6180_REG_SYSTEM_INTERRUPT_CLEAR, 0x01); } /******************************* ALS Measurement Functions *******************/ /** * @brief Start ALS measurement * @param dev Pointer to VL6180 device structure * @param mode Measurement mode (single shot or continuous) * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_StartALSMeasurement(struct VL6180_ATY_Dev *dev, uint8_t mode) { if (!dev) { return VL6180_STATUS_ERROR; } // Auto-initialize if not initialized uint8_t status = VL6180_CheckAndAutoInit(dev); if (status != VL6180_STATUS_OK) { return status; } if (dev->lock) { if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Device is locked\n"); } return VL6180_STATUS_ERROR; } dev->lock = 1; if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: Starting ALS measurement (mode: %d)\n", mode); } // Clear any existing interrupts VL6180_ClearALSInterrupt(dev); // Start measurement status = VL6180_WriteRegister8(dev, VL6180_REG_SYSALS_START, mode); dev->lock = 0; dev->lastStatus = status; return status; } /** * @brief Read ALS measurement result * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ReadALSResult(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } // Auto-initialize if not initialized uint8_t status = VL6180_CheckAndAutoInit(dev); if (status != VL6180_STATUS_OK) { return status; } if (dev->lock) { return VL6180_STATUS_ERROR; } dev->lock = 1; // Read ALS value VL6180_ReadRegister16(dev, VL6180_REG_RESULT_ALS_VAL, &dev->alsValue); // Read ALS status VL6180_ReadRegister8(dev, VL6180_REG_RESULT_ALS_STATUS, &dev->alsStatus); if (dev->debugEnable && dev->LOG) { dev->LOG("VL6180: ALS: %d counts, Status: 0x%02X\n", dev->alsValue, dev->alsStatus); } dev->lock = 0; dev->lastStatus = VL6180_STATUS_OK; return VL6180_STATUS_OK; } /** * @brief Get ALS measurement value * @param dev Pointer to VL6180 device structure * @return ALS value in counts */ uint16_t VL6180_GetALSValue(struct VL6180_ATY_Dev *dev) { if (!dev) { return 0; } return dev->alsValue; } /** * @brief Check if ALS measurement is ready * @param dev Pointer to VL6180 device structure * @return 1 if ready, 0 if not ready */ uint8_t VL6180_IsALSReady(struct VL6180_ATY_Dev *dev) { if (!dev) { return 0; } uint8_t status; VL6180_ReadRegister8(dev, VL6180_REG_RESULT_INTERRUPT_STATUS_GPIO, &status); return (status & 0x20) ? 1 : 0; } /** * @brief Clear ALS interrupt * @param dev Pointer to VL6180 device structure * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ClearALSInterrupt(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } return VL6180_WriteRegister8(dev, VL6180_REG_SYSTEM_INTERRUPT_CLEAR, 0x02); } /******************************* Configuration Functions *********************/ /** * @brief Set range measurement thresholds * @param dev Pointer to VL6180 device structure * @param low Low threshold in mm * @param high High threshold in mm * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_SetRangeThresholds(struct VL6180_ATY_Dev *dev, uint8_t low, uint8_t high) { if (!dev) { return VL6180_STATUS_ERROR; } dev->rangeThresholdLow = low; dev->rangeThresholdHigh = high; VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_THRESH_LOW, low); VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_THRESH_HIGH, high); return VL6180_STATUS_OK; } /** * @brief Set ALS measurement thresholds * @param dev Pointer to VL6180 device structure * @param low Low threshold in counts * @param high High threshold in counts * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_SetALSThresholds(struct VL6180_ATY_Dev *dev, uint16_t low, uint16_t high) { if (!dev) { return VL6180_STATUS_ERROR; } dev->alsThresholdLow = low; dev->alsThresholdHigh = high; VL6180_WriteRegister16(dev, VL6180_REG_SYSALS_THRESH_LOW, low); VL6180_WriteRegister16(dev, VL6180_REG_SYSALS_THRESH_HIGH, high); return VL6180_STATUS_OK; } /** * @brief Set ALS gain * @param dev Pointer to VL6180 device structure * @param gain ALS gain setting * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_SetALSGain(struct VL6180_ATY_Dev *dev, uint8_t gain) { if (!dev) { return VL6180_STATUS_ERROR; } if (gain > VL6180_ALS_GAIN_40) { return VL6180_STATUS_ERROR; } dev->alsGain = gain; return VL6180_WriteRegister8(dev, VL6180_REG_SYSALS_ANALOGUE_GAIN, (0x40 | gain)); } /** * @brief Set ALS integration period * @param dev Pointer to VL6180 device structure * @param period Integration period in ms (1-512ms) * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_SetALSIntegrationPeriod(struct VL6180_ATY_Dev *dev, uint16_t period) { if (!dev) { return VL6180_STATUS_ERROR; } if (period < 1 || period > 512) { return VL6180_STATUS_ERROR; } dev->alsIntegrationPeriod = period; return VL6180_WriteRegister16(dev, VL6180_REG_SYSALS_INTEGRATION_PERIOD, period - 1); } /** * @brief Set range offset calibration * @param dev Pointer to VL6180 device structure * @param offset Offset value in mm (-128 to +127) * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_SetRangeOffset(struct VL6180_ATY_Dev *dev, int8_t offset) { if (!dev) { return VL6180_STATUS_ERROR; } dev->rangeOffset = offset; return VL6180_WriteRegister8(dev, VL6180_REG_SYSRANGE_PART_TO_PART_RANGE_OFFSET, (uint8_t)offset); } /** * @brief Set crosstalk compensation * @param dev Pointer to VL6180 device structure * @param rate Crosstalk compensation rate * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_SetCrosstalkCompensation(struct VL6180_ATY_Dev *dev, uint16_t rate) { if (!dev) { return VL6180_STATUS_ERROR; } dev->crosstalkCompensationRate = rate; return VL6180_WriteRegister16(dev, VL6180_REG_SYSRANGE_CROSSTALK_COMPENSATION_RATE, rate); } /******************************* I2C Register Access Functions ***************/ /** * @brief Write 8-bit register * @param dev Pointer to VL6180 device structure * @param reg Register address * @param data Data to write * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_WriteRegister8(struct VL6180_ATY_Dev *dev, uint16_t reg, uint8_t data) { if (!dev || !dev->i2cStart || !dev->i2cStop || !dev->i2cWriteByte) { return VL6180_STATUS_ERROR; } // Start I2C transaction if (dev->i2cStart() != 0) { return VL6180_STATUS_ERROR; } // Send device address with write bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x00) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send register address (16-bit, MSB first) if (dev->i2cWriteByte((reg >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(reg & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send data if (dev->i2cWriteByte(data) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Stop I2C transaction dev->i2cStop(); return VL6180_STATUS_OK; } /** * @brief Write 16-bit register * @param dev Pointer to VL6180 device structure * @param reg Register address * @param data Data to write * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_WriteRegister16(struct VL6180_ATY_Dev *dev, uint16_t reg, uint16_t data) { if (!dev || !dev->i2cStart || !dev->i2cStop || !dev->i2cWriteByte) { return VL6180_STATUS_ERROR; } // Start I2C transaction if (dev->i2cStart() != 0) { return VL6180_STATUS_ERROR; } // Send device address with write bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x00) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send register address (16-bit, MSB first) if (dev->i2cWriteByte((reg >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(reg & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send data (16-bit, MSB first) if (dev->i2cWriteByte((data >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(data & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Stop I2C transaction dev->i2cStop(); return VL6180_STATUS_OK; } /** * @brief Write 32-bit register * @param dev Pointer to VL6180 device structure * @param reg Register address * @param data Data to write * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_WriteRegister32(struct VL6180_ATY_Dev *dev, uint16_t reg, uint32_t data) { if (!dev || !dev->i2cStart || !dev->i2cStop || !dev->i2cWriteByte) { return VL6180_STATUS_ERROR; } // Start I2C transaction if (dev->i2cStart() != 0) { return VL6180_STATUS_ERROR; } // Send device address with write bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x00) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send register address (16-bit, MSB first) if (dev->i2cWriteByte((reg >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(reg & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send data (32-bit, MSB first) if (dev->i2cWriteByte((data >> 24) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte((data >> 16) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte((data >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(data & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Stop I2C transaction dev->i2cStop(); return VL6180_STATUS_OK; } /** * @brief Read 8-bit register * @param dev Pointer to VL6180 device structure * @param reg Register address * @param data Pointer to store read data * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ReadRegister8(struct VL6180_ATY_Dev *dev, uint16_t reg, uint8_t *data) { if (!dev || !data || !dev->i2cStart || !dev->i2cStop || !dev->i2cWriteByte || !dev->i2cReadByte) { return VL6180_STATUS_ERROR; } // Start I2C transaction for write if (dev->i2cStart() != 0) { return VL6180_STATUS_ERROR; } // Send device address with write bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x00) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send register address (16-bit, MSB first) if (dev->i2cWriteByte((reg >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(reg & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Restart for read if (dev->i2cStart() != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send device address with read bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x01) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Read data *data = dev->i2cReadByte(0); // NACK for last byte // Stop I2C transaction dev->i2cStop(); return VL6180_STATUS_OK; } /** * @brief Read 16-bit register * @param dev Pointer to VL6180 device structure * @param reg Register address * @param data Pointer to store read data * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ReadRegister16(struct VL6180_ATY_Dev *dev, uint16_t reg, uint16_t *data) { if (!dev || !data || !dev->i2cStart || !dev->i2cStop || !dev->i2cWriteByte || !dev->i2cReadByte) { return VL6180_STATUS_ERROR; } // Start I2C transaction for write if (dev->i2cStart() != 0) { return VL6180_STATUS_ERROR; } // Send device address with write bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x00) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send register address (16-bit, MSB first) if (dev->i2cWriteByte((reg >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(reg & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Restart for read if (dev->i2cStart() != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send device address with read bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x01) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Read data (16-bit, MSB first) uint8_t msb = dev->i2cReadByte(1); // ACK for first byte uint8_t lsb = dev->i2cReadByte(0); // NACK for last byte *data = (msb << 8) | lsb; // Stop I2C transaction dev->i2cStop(); return VL6180_STATUS_OK; } /** * @brief Read 32-bit register * @param dev Pointer to VL6180 device structure * @param reg Register address * @param data Pointer to store read data * @return VL6180_STATUS_OK if successful */ uint8_t VL6180_ReadRegister32(struct VL6180_ATY_Dev *dev, uint16_t reg, uint32_t *data) { if (!dev || !data || !dev->i2cStart || !dev->i2cStop || !dev->i2cWriteByte || !dev->i2cReadByte) { return VL6180_STATUS_ERROR; } // Start I2C transaction for write if (dev->i2cStart() != 0) { return VL6180_STATUS_ERROR; } // Send device address with write bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x00) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send register address (16-bit, MSB first) if (dev->i2cWriteByte((reg >> 8) & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } if (dev->i2cWriteByte(reg & 0xFF) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Restart for read if (dev->i2cStart() != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Send device address with read bit if (dev->i2cWriteByte((dev->i2cAddress << 1) | 0x01) != 0) { dev->i2cStop(); return VL6180_STATUS_ERROR; } // Read data (32-bit, MSB first) uint8_t byte3 = dev->i2cReadByte(1); // ACK uint8_t byte2 = dev->i2cReadByte(1); // ACK uint8_t byte1 = dev->i2cReadByte(1); // ACK uint8_t byte0 = dev->i2cReadByte(0); // NACK for last byte *data = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0; // Stop I2C transaction dev->i2cStop(); return VL6180_STATUS_OK; } /******************************* Utility Functions ***************************/ /** * @brief Get device status * @param dev Pointer to VL6180 device structure * @return Device status */ uint8_t VL6180_GetDeviceStatus(struct VL6180_ATY_Dev *dev) { if (!dev) { return VL6180_STATUS_ERROR; } return dev->lastStatus; } /** * @brief Set I2C address * @param dev Pointer to VL6180 device structure * @param address New I2C address */ void VL6180_SetI2CAddress(struct VL6180_ATY_Dev *dev, uint8_t address) { if (dev) { dev->i2cAddress = address; } } /** * @brief Convert ALS counts to lux * @param dev Pointer to VL6180 device structure * @param alsValue ALS value in counts * @return Lux value */ float VL6180_ConvertALSToLux(struct VL6180_ATY_Dev *dev, uint16_t alsValue) { if (!dev) { return 0.0f; } // Gain factors for different ALS gain settings float gainFactors[] = {20.0f, 10.32f, 5.21f, 2.60f, 1.72f, 1.28f, 1.01f, 40.0f}; if (dev->alsGain > VL6180_ALS_GAIN_40) { return 0.0f; } float gain = gainFactors[dev->alsGain]; float integrationTime = dev->alsIntegrationPeriod / 100.0f; // Convert to seconds // Calculate lux using the formula from datasheet float lux = (float)alsValue * 0.32f / (gain * integrationTime); return lux; } /** * @brief Get error string description * @param errorCode Error code * @return Error string */ const char* VL6180_GetErrorString(uint8_t errorCode) { switch (errorCode) { case VL6180_ERROR_NONE: return "No error"; case VL6180_ERROR_SYSERR_1: return "System error 1"; case VL6180_ERROR_SYSERR_5: return "System error 5"; case VL6180_ERROR_ECEFAIL: return "ECE failure"; case VL6180_ERROR_NOCONVERGE: return "No convergence"; case VL6180_ERROR_RANGEIGNORE: return "Range ignore"; case VL6180_ERROR_SNR: return "SNR error"; case VL6180_ERROR_RAWUFLOW: return "Raw underflow"; case VL6180_ERROR_RAWOFLOW: return "Raw overflow"; case VL6180_ERROR_RANGEUFLOW: return "Range underflow"; case VL6180_ERROR_RANGEOFLOW: return "Range overflow"; default: return "Unknown error"; } } /******************************* Usage Examples ******************************/ /* // Example 1: Basic VL6180 usage for range measurement #include "VL6180_ATY.h" // I2C function implementations (platform specific) uint8_t i2c_start(void) { // Your I2C start implementation } uint8_t i2c_stop(void) { // Your I2C stop implementation } uint8_t i2c_write_byte(uint8_t data) { // Your I2C write implementation } uint8_t i2c_read_byte(uint8_t ack) { // Your I2C read implementation } void delay_ms(uint32_t ms) { // Your delay implementation } void debug_log(const char* format, ...) { // Your log implementation } int main(void) { // Initialize VL6180 device structure struct VL6180_ATY_Dev vl6180_dev = { .i2cStart = i2c_start, .i2cStop = i2c_stop, .i2cWriteByte = i2c_write_byte, .i2cReadByte = i2c_read_byte, .delayMs = delay_ms, .i2cAddress = VL6180_I2C_ADDR_DEFAULT, .debugEnable = 1, .LOG = debug_log, .initialized = 0 // Auto-initialization will be triggered }; // VL6180_Init will be called automatically in VL6180_StartRangeMeasurement while (1) { // Start single-shot range measurement if (VL6180_StartRangeMeasurement(&vl6180_dev, VL6180_MODE_RANGE_SINGLE_SHOT) == VL6180_STATUS_OK) { // Wait for measurement to complete while (!VL6180_IsRangeReady(&vl6180_dev)) { delay_ms(1); } // Read range result if (VL6180_ReadRangeResult(&vl6180_dev) == VL6180_STATUS_OK) { uint8_t range = VL6180_GetRangeValue(&vl6180_dev); debug_log("Range: %d mm\n", range); } // Clear interrupt VL6180_ClearRangeInterrupt(&vl6180_dev); } delay_ms(100); } } // Example 2: VL6180 ALS (Ambient Light Sensor) usage int als_example(void) { struct VL6180_ATY_Dev vl6180_dev = { .i2cStart = i2c_start, .i2cStop = i2c_stop, .i2cWriteByte = i2c_write_byte, .i2cReadByte = i2c_read_byte, .delayMs = delay_ms, .i2cAddress = VL6180_I2C_ADDR_DEFAULT, .debugEnable = 1, .LOG = debug_log, .initialized = 0 // Auto-initialization will be triggered }; // Configure ALS settings VL6180_SetALSGain(&vl6180_dev, VL6180_ALS_GAIN_1); VL6180_SetALSIntegrationPeriod(&vl6180_dev, 100); while (1) { // Start single-shot ALS measurement if (VL6180_StartALSMeasurement(&vl6180_dev, VL6180_MODE_ALS_SINGLE_SHOT) == VL6180_STATUS_OK) { // Wait for measurement to complete while (!VL6180_IsALSReady(&vl6180_dev)) { delay_ms(1); } // Read ALS result if (VL6180_ReadALSResult(&vl6180_dev) == VL6180_STATUS_OK) { uint16_t als_counts = VL6180_GetALSValue(&vl6180_dev); float lux = VL6180_ConvertALSToLux(&vl6180_dev, als_counts); debug_log("ALS: %d counts, %.2f lux\n", als_counts, lux); } // Clear interrupt VL6180_ClearALSInterrupt(&vl6180_dev); } delay_ms(500); } } */ /******************************** End Of File *********************************/