/**
* @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 *********************************/