LTR-390UV-01是一款集成了环境光传感(ALS)和紫外线传感(UVS)功能的微型数字传感器,采用I2C接口与主控通信。我在最近的一个智能家居项目中使用了这款传感器,用于实现根据环境光线自动调节LED亮度的功能。相比传统的光敏电阻,LTR-390UV-01具有更高的精度、更宽的动态范围以及更低的功耗,特别适合电池供电的嵌入式设备。
这款传感器最吸引我的几个特点:
LTR-390UV-01采用6引脚DFN封装,各引脚功能如下:
| 引脚编号 | 引脚名称 | 功能描述 |
|---|---|---|
| 1 | SDA | I2C数据线 |
| 2 | SCL | I2C时钟线 |
| 3 | INT | 中断输出 |
| 4 | GND | 地线 |
| 5 | VDD | 电源(1.7-3.6V) |
| 6 | NC | 不连接 |
在我的项目中,使用的是STM32L476RG Nucleo开发板,连接方式如下:
code复制LTR-390UV-01 STM32L476RG
---------------------------
VDD -> 3.3V
GND -> GND
SCL -> PB8(I2C1_SCL)
SDA -> PB9(I2C1_SDA)
INT -> PC13(可配置为外部中断)
注意:虽然传感器支持1.8V逻辑电平,但STM32的I2C接口是3.3V兼容的,因此可以直接连接。如果使用其他电压等级的MCU,需要注意电平转换。
首先需要在STM32上初始化I2C外设。我使用的是HAL库,配置如下:
c复制I2C_HandleTypeDef hi2c1;
void I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00707CBB; // 400kHz @ 80MHz PCLK1
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
// 配置模拟滤波器
if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
{
Error_Handler();
}
// 配置数字滤波器
if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
{
Error_Handler();
}
}
为了方便操作,我首先定义了传感器的主要寄存器地址和配置位:
c复制#define LTR390_ADDR 0x53 // 7位I2C地址
// 寄存器地址
#define LTR390_REG_MAIN_CTRL 0x00 // 主控制寄存器
#define LTR390_REG_MEAS_RATE 0x04 // 测量速率寄存器
#define LTR390_REG_ALS_DATA_0 0x0D // ALS数据低字节
#define LTR390_REG_UVS_DATA_0 0x10 // UVS数据低字节
#define LTR390_REG_INT_CFG 0x19 // 中断配置
#define LTR390_REG_INT_PST 0x1A // 中断持久化设置
#define LTR390_REG_THRESH_UP 0x21 // 上限阈值
// 主控制寄存器位定义
#define LTR390_CTRL_UVS_EN (1 << 3) // UVS模式使能
#define LTR390_CTRL_ALS_EN (1 << 4) // ALS模式使能
#define LTR390_CTRL_MODE_ACTIVE 0x01 // 主动模式
#define LTR390_CTRL_MODE_STANDBY 0x00 // 待机模式
// 测量速率设置
#define LTR390_MEAS_RATE_100MS 0x02 // 100ms积分时间
初始化函数需要配置传感器的工作模式、测量速率等参数:
c复制uint8_t LTR390_Init(void)
{
uint8_t reg_data;
// 1. 验证设备ID
if(HAL_I2C_Mem_Read(&hi2c1, LTR390_ADDR, 0x06, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100) != HAL_OK)
return 0;
if(reg_data != 0xB2) // 检查设备ID是否正确
return 0;
// 2. 重置传感器
reg_data = 0x01;
if(HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, 0x07, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100) != HAL_OK)
return 0;
HAL_Delay(10); // 等待复位完成
// 3. 配置测量速率(100ms, 18位分辨率)
reg_data = (LTR390_MEAS_RATE_100MS << 4) | 0x02;
if(HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_MEAS_RATE, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100) != HAL_OK)
return 0;
// 4. 使能ALS模式
reg_data = LTR390_CTRL_ALS_EN | LTR390_CTRL_MODE_ACTIVE;
if(HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_MAIN_CTRL, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100) != HAL_OK)
return 0;
return 1;
}
LTR-390UV-01的环境光数据是18位的,分布在3个寄存器中:
c复制uint32_t LTR390_Read_ALS(void)
{
uint8_t data[3];
uint32_t als_value;
// 读取3个数据寄存器
if(HAL_I2C_Mem_Read(&hi2c1, LTR390_ADDR, LTR390_REG_ALS_DATA_0, I2C_MEMADD_SIZE_8BIT, data, 3, 100) != HAL_OK)
return 0;
// 组合18位数据
als_value = ((uint32_t)data[2] << 16) | ((uint32_t)data[1] << 8) | data[0];
return als_value;
}
传感器输出的原始数据需要转换为实际的勒克斯值。根据数据手册,转换公式如下:
code复制ALS_Data = (ALS_ADC_DATA) / (Gain × Integration_Time)
在我的实现中,我使用了以下函数进行转换:
c复制float LTR390_Convert_To_Lux(uint32_t als_raw)
{
// 根据配置的增益和积分时间计算
// 本例中使用的是默认增益(1x)和100ms积分时间
const float gain = 1.0f;
const float integration_time = 0.1f; // 100ms
// 灵敏度因子,来自数据手册
const float sensitivity = 0.006f; // counts/(μW/cm²)
// 转换为勒克斯
float lux = (als_raw / (gain * integration_time)) * sensitivity;
return lux;
}
提示:实际应用中,可能需要根据具体环境对转换公式进行校准。我发现在强光环境下,这个公式的结果偏小,因此在我的项目中增加了一个1.2倍的校正系数。
LTR-390UV-01的中断功能非常实用,可以设置当光照强度超过或低于某个阈值时触发中断,避免MCU频繁轮询:
c复制void LTR390_Configure_Interrupt(uint32_t threshold_low, uint32_t threshold_high)
{
uint8_t data[4];
// 设置上限阈值(24位)
data[0] = (threshold_high >> 16) & 0xFF;
data[1] = (threshold_high >> 8) & 0xFF;
data[2] = threshold_high & 0xFF;
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, 0x21, I2C_MEMADD_SIZE_8BIT, data, 3, 100);
// 设置下限阈值(24位)
data[0] = (threshold_low >> 16) & 0xFF;
data[1] = (threshold_low >> 8) & 0xFF;
data[2] = threshold_low & 0xFF;
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, 0x24, I2C_MEMADD_SIZE_8BIT, data, 3, 100);
// 配置中断(超出阈值范围时触发)
data[0] = 0x34; // UVS/ALS超出阈值范围时触发
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_INT_CFG, I2C_MEMADD_SIZE_8BIT, data, 1, 100);
// 设置中断持久化(连续2次超出阈值才触发)
data[0] = 0x01;
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_INT_PST, I2C_MEMADD_SIZE_8BIT, data, 1, 100);
}
在STM32端,需要配置外部中断来响应传感器的中断信号:
c复制void EXTI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOC_CLK_ENABLE();
// 配置PC13为输入,连接到LTR390的INT引脚
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// 配置EXTI中断
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
// 中断服务程序
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}
// 中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_13)
{
// 处理LTR390中断
uint8_t int_status;
HAL_I2C_Mem_Read(&hi2c1, LTR390_ADDR, 0x1F, I2C_MEMADD_SIZE_8BIT, &int_status, 1, 100);
if(int_status & 0x08) // ALS中断标志
{
uint32_t als_value = LTR390_Read_ALS();
// 处理光照变化...
}
}
}
在电池供电的应用中,低功耗设计至关重要。LTR-390UV-01支持睡眠模式,可以将功耗降至0.5μA:
c复制void LTR390_Enter_Sleep(void)
{
uint8_t reg_data = LTR390_CTRL_MODE_STANDBY;
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_MAIN_CTRL, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100);
}
void LTR390_Wake_Up(void)
{
uint8_t reg_data = LTR390_CTRL_ALS_EN | LTR390_CTRL_MODE_ACTIVE;
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_MAIN_CTRL, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100);
HAL_Delay(10); // 等待传感器稳定
}
在我的项目中,采用了以下策略来优化功耗:
实现代码框架如下:
c复制void System_Low_Power_Mode(void)
{
// 配置RTC唤醒定时器(5分钟)
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0x7530, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
// 进入停止模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后重新配置系统时钟
SystemClock_Config();
}
void Main_Loop(void)
{
while(1)
{
// 读取光照值
uint32_t als_value = LTR390_Read_ALS();
float lux = LTR390_Convert_To_Lux(als_value);
// 根据光照值调整系统行为...
// 根据当前光照变化率决定下次唤醒时间
uint32_t sleep_time = Calculate_Sleep_Time(lux);
// 配置唤醒定时器
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, sleep_time, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
// 进入低功耗模式
System_Low_Power_Mode();
}
}
在实际部署中,我发现出厂校准在极端环境下(如强直射阳光或极暗环境)仍有偏差。我的校准方法是:
示例校准代码:
c复制void LTR390_Calibrate(float reference_lux[], uint32_t raw_values[], int num_samples)
{
float sum_x = 0, sum_y = 0, sum_xy = 0, sum_x2 = 0;
// 计算各项和
for(int i = 0; i < num_samples; i++)
{
sum_x += raw_values[i];
sum_y += reference_lux[i];
sum_xy += raw_values[i] * reference_lux[i];
sum_x2 += raw_values[i] * raw_values[i];
}
// 计算线性回归系数 y = a*x + b
float a = (num_samples * sum_xy - sum_x * sum_y) / (num_samples * sum_x2 - sum_x * sum_x);
float b = (sum_y - a * sum_x) / num_samples;
// 保存校准系数
Save_Calibration_Params(a, b);
}
在开发过程中,我遇到并解决了以下典型问题:
I2C通信失败
数据不准确
中断不触发
高功耗
经过多次迭代,我总结出以下优化建议:
除了基本的光照检测,LTR-390UV-01还可以用于以下场景:
紫外线检测的实现与ALS类似,只需切换模式:
c复制float LTR390_Read_UV_Index(void)
{
uint8_t reg_data;
// 切换到UVS模式
reg_data = LTR390_CTRL_UVS_EN | LTR390_CTRL_MODE_ACTIVE;
HAL_I2C_Mem_Write(&hi2c1, LTR390_ADDR, LTR390_REG_MAIN_CTRL, I2C_MEMADD_SIZE_8BIT, ®_data, 1, 100);
HAL_Delay(100); // 等待模式切换
// 读取UV数据
uint8_t data[3];
HAL_I2C_Mem_Read(&hi2c1, LTR390_ADDR, LTR390_REG_UVS_DATA_0, I2C_MEMADD_SIZE_8BIT, data, 3, 100);
uint32_t uvs_value = ((uint32_t)data[2] << 16) | ((uint32_t)data[1] << 8) | data[0];
// 转换为UV指数(根据数据手册公式)
float uv_index = (float)uvs_value * 0.01f;
return uv_index;
}
在实际项目中,我发现这款传感器虽然小巧,但功能强大且稳定。经过三个月的连续运行测试,没有出现数据漂移或通信故障。特别是在低功耗模式下,整个系统(STM32L4 + LTR390)的平均电流可以控制在15μA以下,非常适合电池供电的IoT设备。