光感传感器移植指南¶
本章节将实现光感传感器在Solution Sensor框架下的移植,以LTR303为参考。
1. 原生驱动移植¶
本节实现传感器芯片最基础的硬件通信功能,包括I2C总线操作、中断处理和电源管理。
1.1 配置与宏定义¶
在 sdk\customer\peripherals\sensor 目录下创建以LTR303命名的目录,添加原生驱动LTR303.c。
修改sdk\customer\peripherals\sensor对应LTR303传感器类型的目录下kconfig文件,新建宏来实现驱动的控制
宏名称 |
类型 |
说明 |
|---|---|---|
|
必需 |
启用LTR303光感传感器 |
|
必需 |
I2C总线设备名称(如"i2c2") |
|
必需 |
传感器设备模型名称(如"ltr303") |
|
可选 |
电源控制GPIO引脚号 |
1.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;
}
1.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;
}
1.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 接口修改该寄存器,可软件方式开启或关闭传感器供电,从而实现低功耗管理。
2. RT-Thread 传感器设备移植¶
本节将原生驱动封装为RT-Thread标准传感器设备,实现与系统框架的无缝对接。
2.1 配置与宏定义¶
在目录solution\components\sensor\sensor_algo\asl添加对应的器件目录“ltr303”,在“ltr303"目录下创建ltr303_asl_service.c文件,用于实现ltr303注册为Sensor设备;
宏名称 |
类型 |
功能说明 |
取值/注意事项 |
|---|---|---|---|
|
宏定义 |
数据读取周期,决定应用层数据刷新频率 |
默认200ms,单位:毫秒 |
|
条件编译宏 |
控制整个驱动模块的编译开关 |
需在menuconfig选项中定义以启用驱动 |
2.2 关键数据结构¶
定义设备私有数据结构体和全局变量。
int rt_hw_ltr303_regis
ter(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()完成设备的注册。
2.4 传感器接口实现¶
注册为Sensor设备必须完善接口,实现rt_sensor_ops结构体。
static struct rt_sensor_ops sensor_ops =
{
ltr303_fetch_data,
ltr303_control
};
2.4.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;
}
2.4.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;
}
2.4.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;
}
2.5 设备初始化函数¶
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;
}
3.光感传感器调度模块¶
调度模块是 Solution 传感器系统的核心,负责将器件采集的原始数据(如 PPG 信号、加速度值)转换为业务可用数据(如心率、计步数)并通过IPC框架上报给应用层。
3.1 器件与算法关联¶
3.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;
3.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;
}
3.2 模块调度¶
3.2.1 基础概念¶
Solution 基于 RT-Thread 软件定时器 实现传感器的自动化调度,替代原生轮询机制,可灵活配置采样周期,同时适配低功耗场景(支持动态调整周期)。该调度器是 “器件采集→算法处理→数据上报” 全流程的触发核心。
3.2.3 示例代码¶
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定时器定时调用,实现了数据的采集、处理、上报全流程