光感传感器移植指南¶
本文说明如何将光感传感器接入 Solution Sensor 框架。接入后,应用可订阅环境光强数据,用于自动亮度调节等场景。
本文以 LTR303 为例,说明驱动适配、RT-Thread Sensor 注册、调度上报和应用订阅流程。
1. 参考代码¶
文件 |
作用 |
|---|---|
|
接收传感器数据并分发给订阅者 |
|
向 UI 提供传感器数据订阅和取消订阅接口 |
|
为传感器算法提供周期调度定时器 |
|
绑定传感器设备和算法实例 |
|
完成光感算法初始化和周期数据调度 |
|
注册 LTR303 光感设备并读取原始光强数据 |
2. 应用例程¶
场景 |
文件 |
调用接口 |
|---|---|---|
Grid View 自动亮度 / 光感订阅 |
|
|
Watch 自动亮度 / 光感订阅 |
|
|
3. 说明¶
以下以 LTR303 为参考完成光感传感器在 Solution Sensor 框架下的移植。
4. 原生驱动移植¶
本节实现传感器芯片最基础的硬件通信功能,包括I2C总线操作、中断处理和电源管理。
4.1 配置与宏定义¶
路径:SiFli SDK configuration → Board Config → Board Peripherals → Sensor config → ASL Sensor → LTR303
Board Peripherals在 PC Simulator 工程中不显示,因此该路径仅适用于真实硬件工程。
配置项 |
说明 |
建议 |
|---|---|---|
|
启用光感传感器框架 |
必选 |
|
启用 LTR303 驱动 |
使用 LTR303 时必选 |
|
I2C 总线名 |
按板级实际连接填写,如 |
|
设备名 |
默认 |
|
I2C DMA 方式 |
需要 DMA 时打开 |
|
电源控制 GPIO |
无控制脚时填 |
如果启用 LTR303_USE_DMA,还需要同时打开对应 I2C 实例的 DMA 配置。例如 LTR303_BUS_NAME="i2c2" 时,需要确认 BSP_USING_I2C2 和 BSP_I2C2_USING_DMA 已打开。
新增器件时,应在 sdk/customer/peripherals/sensor/asl/Kconfig 中增加对应配置项,并在 sdk/customer/peripherals/sensor/asl 下加入驱动实现。
4.2 I2C总线初始化¶
初始化需要通过宏定义找到对应的总线设备打开并对总线进行对应的参数设置。
static rt_err_t LTR303_I2C_Init(const char *name)
{
/* get i2c bus device */
LTR303_bus = rt_i2c_bus_device_find(name);
if (LTR303_bus)
{
LOG_D("Find i2c bus device %s\n", name);
}
else
{
LOG_E("Can not found i2c bus %s, init fail\n", name);
return -RT_ERROR;
}
return RT_EOK;
}
4.3 I2C读写接口¶
总线读写函数供原生驱动调用,实现寄存器的数据交互。
rt_err_t LTR303_SetGain(ltr303_gain_t Gain)
{
uint8_t Reg[1];
if (rt_i2c_mem_read(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1) == 0)
return -RT_ERROR;
// Gain(4:2)
Reg[0] &= 0xe3;
Reg[0] |= (Gain << 2);
if (rt_i2c_mem_write(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1) == 0)
return -RT_ERROR;
return RT_EOK;
}
ltr303_gain_t LTR303_GetGain(void)
{
uint8_t data[1];
rt_i2c_mem_read(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, data, 1);
uint8_t LTR303_Gain = (data[0] & 0x1c) >> 2;
return LTR303_Gain;
}
4.4 电源管理¶
void LTR303_PowerOff(void)
{
uint8_t Reg[1];
rt_i2c_mem_read(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1);
Reg[0] &= 0xfe;
rt_i2c_mem_write(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1);
}
void LTR303_PowerOn(void)
{
uint8_t Reg[1];
rt_i2c_mem_read(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1);
Reg[0] |= 0x01;
rt_i2c_mem_write(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1);
rt_i2c_mem_read(LTR303_bus, LTR303_I2CADDR_DEFAULT, LTR303_ALS_CTRL, 8, Reg, 1);
}
注:LTR303 环境光传感器的电源状态由其控制寄存器LTR303_ALS_CTRL控制。通过 I²C 接口修改该寄存器,可软件方式开启或关闭传感器供电,从而实现低功耗管理。
5. RT-Thread 传感器设备移植¶
本节将原生驱动封装为RT-Thread标准传感器设备,实现与系统框架的无缝对接。
5.1 配置与宏定义¶
在目录solution\components\sensor\sensor_algo\asl添加对应的器件目录“ltr303”,在“ltr303"目录下创建ltr303_asl_service.c文件,用于实现ltr303注册为Sensor设备;
宏名称 |
类型 |
功能说明 |
取值/注意事项 |
|---|---|---|---|
|
宏定义 |
数据读取周期,决定应用层数据刷新频率 |
默认200ms,单位:毫秒 |
|
Kconfig 配置符号 |
控制整个驱动模块的编译开关 |
在 menuconfig 选择该项以启用驱动;构建系统生成对应条件编译宏,不应手工定义 |
5.2 关键数据结构¶
定义设备私有数据结构体和全局变量。
int rt_hw_ltr303_register(const char *name)
{
int result;
static char bus_name[] = LTR303_BUS_NAME;
{
sensor_asl = rt_calloc(1, sizeof(struct rt_sensor_device));
if (sensor_asl == RT_NULL)
goto __exit;
sensor_asl->info.type = RT_SENSOR_CLASS_LIGHT;
sensor_asl->info.vendor = RT_SENSOR_VENDOR_UNKNOWN;
sensor_asl->info.model = NULL;
sensor_asl->info.unit = RT_SENSOR_UNIT_LUX;
sensor_asl->info.intf_type = RT_SENSOR_INTF_I2C;
sensor_asl->info.range_max = 65535;
sensor_asl->info.range_min = 1;
sensor_asl->info.period_min = 120;
sensor_asl->data_len = 0;
sensor_asl->data_buf = NULL;
sensor_asl->config.mode = RT_SENSOR_MODE_POLLING;
sensor_asl->config.intf.dev_name = bus_name;
sensor_asl->config.irq_pin.pin = RT_PIN_NONE;
sensor_asl->ops = &sensor_ops;
result = rt_hw_sensor_register(sensor_asl, name, RT_DEVICE_FLAG_RDWR, RT_NULL);
if (result != RT_EOK)
{
rt_kprintf("ASL [%s] register err code: %d", name, result);
goto __exit;
}
}
rt_kprintf("ASL [%s] init success!\n", name);
return RT_EOK;
__exit:
if (sensor_asl != RT_NULL)
{
rt_free(sensor_asl);
sensor_asl = RT_NULL;
}
return -RT_ERROR;
}
int ltr303_register(void)
{
/*if menuconfig config name, use menuconfig name*/
#ifdef LTR303_MODEL_NAME
static char name[] = LTR303_MODEL_NAME;
#else
static char name[] = "ltr303";
#endif
int ret = rt_hw_ltr303_register(name);
return ret;
}
INIT_COMPONENT_EXPORT(ltr303_register);
注:INIT_COMPONENT_EXPORT(ltr303_register); 是 RT-Thread 系统组件初始化命令,用于在系统启动时调用 ltr303_register() 完成设备注册。
5.3 传感器接口实现¶
注册为Sensor设备必须完善接口,实现rt_sensor_ops结构体。
static struct rt_sensor_ops sensor_ops =
{
ltr303_fetch_data,
ltr303_control
};
5.3.1 数据获取接口¶
ltr303_fetch_data()用于服务层向驱动层获取数据。
static rt_size_t ltr303_fetch_data(struct rt_sensor_device *sensor, void *buf, rt_size_t len)
{
RT_ASSERT(buf);
return ltr303_polling_get_data(sensor, buf);
}
ltr303_polling_get_data()实现具体的数据读取逻辑:
static rt_size_t ltr303_polling_get_data(rt_sensor_t sensor, void *data)
{
asl_info_t *asl_info = (asl_info_t *)data;
asl_info->light = (uint32_t)LTR303_ReadVisible();
#ifdef LTR303_DEBUG
rt_kprintf("ltr303 value = %d;\n", asl_info->light);
#endif
return 1;
}
5.3.2 设备控制接口¶
ltr303_control()用于服务层对驱动层的控制。
static rt_err_t ltr303_control(struct rt_sensor_device *sensor, int cmd, void *args)
{
rt_err_t result = RT_EOK;
switch (cmd)
{
case RT_SENSOR_CTRL_GET_ID:
{
sensor_reg_info_t *info = (sensor_reg_info_t *)args;
if (!ltr303_dev)
{
info->sensor_id = 0;
return RT_ERROR;
}
info->sensor_id = ltr303_dev->id;
info->dev_period = LTR303_PERIOD_TIMER;
*(uint8_t *)args = ltr303_dev->id;
}
break;
case RT_SENSOR_CTRL_SET_RANGE:
result = ltr303_set_range(sensor, (rt_int32_t)args);
break;
case RT_SENSOR_CTRL_SET_ODR:
result = -RT_EINVAL;
break;
case RT_SENSOR_CTRL_SET_MODE:
result = ltr303_set_mode(sensor, (rt_uint32_t)args & 0xff);
break;
case RT_SENSOR_CTRL_SET_POWER:
result = ltr303_set_power(sensor, (rt_uint32_t)args & 0xff);
break;
case RT_SENSOR_CTRL_SELF_TEST:
result = ltr303_self_test(sensor, *((rt_uint8_t *)args));
break;
default:
return -RT_ERROR;
}
return result;
}
5.3.3 电源控制实现¶
ltr303_set_power() 实现对传感器电源状态的控制。
static rt_err_t ltr303_set_power(rt_sensor_t sensor, rt_uint8_t power)
{
switch (power)
{
case RT_SENSOR_POWER_DOWN:
ltr303_close();
break;
case RT_SENSOR_POWER_NORMAL:
ltr303_open();
break;
case RT_SENSOR_POWER_LOW:
ltr303_lowpowermode();
break;
case RT_SENSOR_POWER_HIGH:
break;
default:
break;
}
return RT_EOK;
}
5.4 设备初始化函数¶
ltr303_open()作为ltr303光电传感器设备的初始化入口,完成硬件唤醒、资源分配和参数配置,使传感器进入可工作状态。
static rt_err_t ltr303_open(void)
{
rt_err_t ret = RT_EOK;
#ifdef LTR303_DEBUG
rt_kprintf("ltr303 open start ! \n");
#endif
if (!ltr303_dev)
{
if (ltr303_init() == RT_EOK)
{
ltr303_dev = rt_calloc(1, sizeof(struct ltr303_device));
if (ltr303_dev == RT_NULL)
{
ret = RT_ENOMEM;
goto err;
}
ltr303_dev->bus = (rt_device_t)ltr303_get_i2c_handle();
ltr303_dev->i2c_addr = ltr303_get_dev_addr();
ltr303_dev->id = ltr303_get_dev_id();
rt_kprintf("(%s) open ok!\n", sensor_asl->parent.parent.name);
return RT_EOK;
}
ret = RT_ERROR;
err:
rt_kprintf("ltr303 init err! code: %d\n", ret);
}
return ret;
}
6. 光感传感器调度模块¶
调度模块是 Solution 传感器系统的核心,负责周期性读取环境光强数据,并通过 IPC 框架上报给应用层,供自动亮度调节等场景使用。
6.1 器件与算法关联¶
6.1.1 核心接口说明¶
sensor_reg_open 是 Solution 基于 RT-Thread 扩展的核心接口,负责在系统初始化阶段“连接”已注册的传感器设备与算法设备,构建“硬件采集→算法处理”的数据链路,是传感器从“注册态”进入“可用态”的关键步骤。 主注册接口
rt_err_t sensor_reg_open(
const char *sensor_name, // 传感器设备名(如"li_ltr303")
const char *algo_name, // 算法设备名
sensor_reg_info_t *sensor_info, // 输出:传感器+算法信息
rt_device_t *sensor_device // 输出:传感器设备句柄
);
功能:完成传感器设备与算法模块的查找、验证与绑定。
数据结构定义
// 算法信息载体(在sensor_reg.h中定义)
typedef struct {
sensor_type_t type; // 传感器类型
uint32_t sensor_id; // 传感器芯片ID
uint32_t dev_period; // 采样周期(ms)
uint8_t fifo_len; // FIFO深度
} sensor_reg_info_t;
6.1.2 应用示例¶
asl_algo.c 中的宏定义,修改ASL_NAME为指定的"ltr303".
初始化绑定设备,通过
sensor_reg_open()查找并验证传感器设备(ltr303).
static int asl_init(void)
{
char *asl_name = "li_"ASL_NAME;
char *asl_algo_name = NULL;
sensor_info.type = SENSOR_ASL;
if (RT_EOK != sensor_reg_open(asl_name, asl_algo_name, &sensor_info, &asl_device))
{
return RT_ERROR;
}
asl_id = sensor_info.sensor_id;
asl_period = sensor_info.dev_period;;
LOG_I("%s: type = %d, period = %d!", __func__, SENSOR_ASL, asl_period);
return RT_EOK;
}
传感器设备成功绑定,此时可以通过rt_device_read()直接读取传感器数据。
static int32_t asl_data_fetch(uint32_t data_size, void *data)
{
if (!asl_device || !data) return RT_ERROR;
rt_size_t size = rt_device_read(asl_device, 0, data, 1);
if (0 == size) LOG_W("asl_data_fetch: err!!!\n");
return size;
}
6.2 模块调度¶
6.2.1 基础概念¶
Solution 基于 RT-Thread 软件定时器 实现传感器的自动化调度,替代原生轮询机制,可灵活配置采样周期,同时适配低功耗场景(支持动态调整周期)。该调度器是 “器件采集→算法处理→数据上报” 全流程的触发核心。
6.2.2 示例代码¶
void asl_algo_scheduler(uint32_t period)
{
static uint16_t asl_peroid_time = 0;
static uint16_t asl_result_peroid_time = 0;
int32_t ret = 0;
if (!asl_is_opened())
return;
asl_peroid_time += period;
asl_result_peroid_time += period;
if (asl_peroid_time < asl_period)
return;
asl_peroid_time = 0;
#ifndef SENSOR_IN_HCPU
ret = asl_data_fetch(0, &asl_data);
#endif
if (asl_result_peroid_time >= asl_result_period)
{
asl_result_peroid_time = 0;
if (asl_data.light > 0)
{
#if defined (BF0_HCPU) && !defined(SENSOR_IN_HCPU)
/*if mag in hcpu and ohter sensor in lcpu*/
sensors_msg_process_in_hcpu_cb(SENSOR_APP_RT_ASL_INFO_ID, &asl_data, sizeof(asl_info_t));
#else
/*if mag and ohter sensor all in lcpu, or all in lcpu*/
ipc_send_lcpu_msg_to_hcpu(SENSOR_APP_RT_ASL_INFO_ID, &asl_data, sizeof(asl_info_t));
#endif
}
}
}
注:该函数由sensor_timer定时器定时调用,实现了数据的采集、处理、上报全流程