1 P_Sensor功能简述
接近传感器(proximity sensor)可以不断向外发射红外光,当有遮挡物遮挡住其所发射的红外光时,会有部分甚至全部的红外光反射至接近传感器。由此,可以根据遮挡物反射的红外光的能量信号检测用户的移动信息。接近传感器可以将光强度转换为数字信号,且可以通过I2C进行数据的读取。psensor的红外线会周期性的计算反光量,并将光强度转化为数字值,存储于psensor的DATA寄存器中。当遮挡效果最好时,反光量最大,DATA寄存器中的测量数值最大为2047,当无遮挡时,则无反光量,此时,DATA寄存器中的测量值最小为0。MCU周期性读取proximity sensor中寄存器中的测量数据,并与设定的阈值相比较,从而检测头戴的佩戴状态。
2 设计方案
1).通过IIC配置P_Sensor的参数(以LTR-559ALS-01为例)
Setting | Value | Register |
PS Mode | Enabled | 0x81 |
Pulse frequency | 60Hz | 0x82 |
Duty cycle | 100% | 0x82 |
Pulse current | 100mA | 0x82 |
Pulse count | 1 | 0x83 |
PS measurement rate | 100ms | 0x84 |
2). 循环读取P_Sensor测量数据
3). 将测量数据与设置的阈值相比较,以检测佩戴情况
3 P_Sensor 主要寄存器介绍
1)PS_COUNER Rigister (0x81)
用来控制psensor工作模式,在配置psensor时要将PS Mode配置为Active 模式,因为默认为standby 模式
2)PS_LED Register(0x82)
主要用来控制LED的脉冲频率、占空比、峰值电流。这三个参数,会影响发射能量,从而影响测量距离
脉冲频率越低,导通时间越长,则在相同的距离下,测量的Psensor的DATA值越大,也就是要想得到同样的DATA值
的话,需要的测量距离要越远
占空比越大,在相同距离下,测量的Psensor的DATA值越大
电流越大,在相同距离下,测量的Psensor的DATA值越大
3)PS_N_Pulse Register (0x83)用来控制发射脉冲个数,影响发射能量
发射的脉冲个数越多,在相同距离下,测量的Psensor的DATA值越大
4)PS_MEAS_RATE Register(0x84)
控制psensor 在active mode下的周期性测量时间(psensor会周期性的将测量值写入DATA寄存器中)
这个时间设置的越长的话,psensor测量数据(DATA值)就会更新的越慢,实时性较差
脉冲频率、占空比、峰值电流、脉冲个数、周期性测量时间关系如图:
1 脉冲频率:为采样周期的倒数,即为1/T2
2 占空比:T1/T2
3 峰值电流:I1
4 测量时间:T3(psensor来将测量值写入寄存器中的周期性时间)
5 脉冲个数:在T3时间内发送的脉冲数,脉冲如图① ② ③所示,均为脉冲
一般来讲采样周期会远小于psensor的周期测量时间,而在psensor去存一次测量数据的时间(T3)内,脉冲
发送个数是可调的,发送个数越多,则能量越大
5)PS_DATA Registers(0x8D/0x8E) (Read Only)
psensor测量值存储在这两个寄存器中,有效数据11位(bit)
4 代码实现
1)先对寄存器进行配置
UHAL_ASSERT(ltr559als01_ps_configure(&m_regmap, &m_config),
"Failed to configure proximity sensor");
sensor");
/**
* @brief Sensor configuration
*/
static ltr559als01_ps_configuration_t m_config = {
.current = LTR559ALS01_PS_LED_CURRENT_100mA,
.duty_cycle = LTR559ALS01_PS_LED_DUTY_CYCLE_100PCT,
.pulse_modulation_frequency = LTR559ALS01_PS_LED_PULSE_MODULATION_FREQUENCY_60kHz,
.pulse_count = 1,
.repeat_rate = LTR559ALS01_PS_MEASUREMENT_RATE_100ms
};
// Global register map handle
static fwregmap_handle_t m_regmap = {
.context = NULL,
.reg_reader_fn = reg_reader,
.reg_writer_fn = reg_writer
};
bool ltr559als01_ps_configure(
fwregmap_handle_t* device,
const ltr559als01_ps_configuration_t* config)
{
UHAL_ASSERT((config->pulse_count > 0) &&
(config->pulse_count < 16),
"Pulse count should be 1-15 inclusive");
int status = 0;
ltr559als01_PS_LED_t led = {
.current = config->current,
.duty_cycle = config->duty_cycle,
.pulse_modulation_frequency = config->pulse_modulation_frequency
};
ltr559als01_PS_N_PULSES_t n_pulses = {};
n_pulses.pulse_count = config->pulse_count;
ltr559als01_PS_MEAS_RATE_t meas_rate = {};
meas_rate.repeat_rate = config->repeat_rate;
if ((status = ltr559als01_write_PS_LED(device, led)) != 0) {
LOG_ERROR("Failed to write PS_LED: %i", status);
return false;
}
if ((status = ltr559als01_write_PS_N_PULSES(device, n_pulses)) != 0) {
LOG_ERROR("Failed to write PS_N_PULSES: %i", status);
return false;
}
if ((status = ltr559als01_write_PS_MEAS_RATE(device, meas_rate)) != 0) {
LOG_ERROR("Failed to write PS_MEAS_RATE: %i", status);
return false;
}
return true;
}
其中:
// PS_LED : (what this does is a mystery)
typedef struct
{
uint8_t current : 3;
uint8_t duty_cycle : 2;
uint8_t pulse_modulation_frequency : 3;
} ltr559als01_PS_LED_t;
// PS_N_PULSES : (what this does is a mystery)
typedef struct
{
uint8_t pulse_count : 4;
} ltr559als01_PS_N_PULSES_t;
// PS_MEAS_RATE : (what this does is a mystery)
typedef struct
{
uint8_t repeat_rate : 4;
} ltr559als01_PS_MEAS_RATE_t;
2)psensor线程周期性的读取寄存器中测量数据的值
void proximity_sensor_read(void)
{
ltr559als01_ps_measurement_t measurement = {};
if (ltr559als01_ps_read(&m_regmap, &measurement)) {
__atomic_store(&m_value, &measurement.value, __ATOMIC_RELAXED);
} else {
LOG_ERROR("Failed to poll proximity sensor");
}
}
bool ltr559als01_ps_read(
fwregmap_handle_t* device,
ltr559als01_ps_measurement_t* measurement)
{
int status = 0;
uint8_t data_0 = 0;
ltr559als01_PS_DATA_1_t data_1 = {};
if ((status = ltr559als01_read_PS_DATA_0(device, &data_0)) != 0) {
LOG_ERROR("Failed to read PS_DATA_0: %i", status);
return false;
}
if ((status = ltr559als01_read_PS_DATA_1(device, &data_1)) != 0) {
LOG_ERROR("Failed to read PS_DATA_1: %i", status);
return false;
}
measurement->value = (uint16_t)data_0 | ((uint16_t)data_1.high << 8);
measurement->sensor_saturated = data_1.sensor_saturated;
return true;
}
// PS_DATA_0 : (what this does is a mystery)
static inline int ltr559als01_read_PS_DATA_0(fwregmap_handle_t* handle, uint8_t* val)
{
int status = 0;
FWREGMAP_LOG("Reading PS_DATA_0 (0x8d) ");
status = handle->reg_reader_fn(handle->context, 0x8d, 1, (uint8_t*)val);
if (status == 0) {
FWREGMAP_LOG("--> ");
FWREGMAP_LOG("0x%02x", (unsigned int)*val);
} else {
FWREGMAP_LOG("FAIL (error %i)", status);
}
FWREGMAP_LOG("\n");
return status;
}
// PS_DATA_1 : (what this does is a mystery)
typedef struct
{
uint8_t high : 3;
uint8_t rsvd0 : 4;
uint8_t sensor_saturated : 1;
} ltr559als01_PS_DATA_1_t;
static inline int ltr559als01_read_PS_DATA_1(fwregmap_handle_t* handle, ltr559als01_PS_DATA_1_t* val)
{
int status = 0;
FWREGMAP_LOG("Reading PS_DATA_1 (0x8e) ");
status = handle->reg_reader_fn(handle->context, 0x8e, 1, (uint8_t*)val);
if (status == 0) {
FWREGMAP_LOG("--> ");
FWREGMAP_LOG("0x%02x {", *((uint8_t*)&*val)); FWREGMAP_LOG("high = "); FWREGMAP_LOG("%i", (*val).high); FWREGMAP_LOG(", ");FWREGMAP_LOG("sensor_saturated = "); FWREGMAP_LOG("%i", (*val).sensor_saturated); FWREGMAP_LOG(", ");FWREGMAP_LOG("}");
} else {
FWREGMAP_LOG("FAIL (error %i)", status);
}
FWREGMAP_LOG("\n");
return status;
}
3)读取的测量数据与设定的阈值进行比较
typedef uint8_t proximity_sensor_digest_t;
static int m_threshold = 512;
proximity_sensor_digest_t proximity_sensor_digest(void)
{
return (proximity_sensor_digest_t)(proximity_sensor_value() > m_threshold);
}
遮挡为1 不遮挡为0