Android系统sensor大致框架流程:
App中传感器注册,获取系统服务,数据上报给app(onSensorChange)都是通过SystemSensorManager.java完成的,
SensorManager.java仅仅是类似于接口类,功能是由SystemSensorManager.java实现
SystemSensorManager.java 中函数会调用 jni 的android_hardware_SensorManager.cpp实现
android_hardware_SensorManager.cpp调用SensorManager.cpp实现
SensorManager.cpp 与sensorService.cpp 间进行通信,其中命令传输通过binder实现,数据传输通过管道实现;其中binder做主
要贡献的是ISensorEventConnection.cpp,完成binder的服务端和客户端
sensorService.cpp通过中通过 threadloop 不断的从hardware 层获取sensor数据,其中对sensor 的操作主要有sensordevice 类完成
hardware (sensor hal)层则通过read / write ,ioctl操作 sensor驱动设备节点去操作sensor 设备,读取sensor上报的数据(一般是input子系统)
kenel中sensor驱动从IC中通过i2c或者其他接口读取到数据,然后将数据放入到input子系统中上报给上层。
Rk平台sensor调试说明
Rk平台sensor驱动代码路径:kernel/drivers/input/sensors,其中 sensor-dev.c 是核心代码,整合了不同类型的sensor,包括 accel, gyro, lsensor, psensor, compass等。
下面是实际工作中的调试流程:
这里只记录调试gsensor部分,其他gryo+compass同理,他们在rk平台分开三个不同.c文件实现驱动加载和id匹配和数据上报等:
1、硬件板子确认
拿到硬件板子之后,首先确认上电ok[供电电压对,如3.3v],万用表量到供电(3.3v)供电正常,i2c空闲上拉状态正常等。
2、确认资料和硬件连接IO
对照供应商那边提供的资料,规格书(datasheet)、驱动或者demo代码资料等。首先从资料中确定模块的I2C从设备地址,确定模块的id寄存器和寄存器值;
驱动代码方面,不一定拿到的就是直接可以用的驱动,除非在相同平台上调试过,不然的话,别想着驱动一拿过来就直接可以编译进去跑起来,要么是编译问题,总之各种问题,反正这也是你们驱动工程师搞的活,比如我这次拿到的资料是这样的:
这样的:
但是在rk平台调试的话,如果客户没有提供直接可以使用的驱动代码,我们可以根据它的sensor驱动框架,穿插进去你的实现代码就可以了,其实主要代码部分就是读取id,设置使能寄存器,还有读取数据寄存器,以及上报数据等操作而已,可以近似的拷贝一个现有的gsesnor的.c文件来修改即可。这里调试的lsm9ds1九轴sensor的gsensor功能, 首先第一步,配置你的dts资源描述,例如我这里,配置dts信息如下:
&i2c1 {
status = "okay";
lsm9ds1_accel@6b { /* SDO_AG: if 0 ==> 0x6a; if 1 ==> 0x6b */
status = "okay";
compatible = "lsm9ds1_acc";
pinctrl-names = "default";
pinctrl-0 = <&lsm9ds1a_irq_gpio>, <&lsm9ds1a_power_gpio>;
reg = <0x6b>; //i2c地址
irq-gpio = <&gpio1 13 IRQ_TYPE_EDGE_RISING>; //GPIO1_B5,中断脚
power-gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>; //GPIO1_C6,供电脚
type = <SENSOR_TYPE_ACCEL>;
irq_enable = <0>; //poll mode
poll_delay_ms = <30>;
//power-off-in-suspend = <1>; //resume init sensor
layout = <4>;
reprobe_en = <1>;
};
};
硬件原理方面需要确认你的i2c连接总线是哪一组,还有就是从规格书或者资料确定lsm9ds1模组的i2c从设备地址,确认供电脚的gpio[如果长供电不用理会],如果使用中断模式需要确认你中断脚连接的GPIO口是哪个[我这里是GPIO1_B5]等。
3、添加sensor的id
在sensor-dev.c中添加sensor_id,其中“lsm9ds1_acc”字段与dts中的compatible字段对应。
ACCEL_ID_LSM9DS1定义在 include/linux/sensor-dev.h 中
4、增加gsensor驱动内容
driver/input/sensors/accel/目录下增加相应驱动:
1)实现 gsensor_lsm9ds1_ops实例:
struct sensor_operate gsensor_lsm9ds1_ops = {
.name = "lsm9ds1_acc", //与 sensor-dev.c 中定义的名称一致
.type = SENSOR_TYPE_ACCEL, //对应sensor类型是gsensor
.id_i2c = ACCEL_ID_LSM9DS1, //定义在include/linux/sensor-dev.h 中
.read_reg = OUT_X_L_A, //0x28,读取 gsensor 数据的起始寄存器地址
.read_len = 6, //需要读取的 gsensor 数据的字节数
.id_reg = WHO_AM_I_A, //芯片唯一ID寄存器,从datasheet获知是0x0f
.id_data = LSM9DS1_DEVICE_ID_A, //芯片的ID值 ,从datasheet获知是0x68
.precision = 16, //采样gsensor数据的adc位数
.ctrl_reg = CTRL_REG5_A, //0x1F,用于使能 gsensor 的寄存器地址
.int_status_reg = STATUS_REG_A, //中断状态寄存器地址
.range = {-32768, 32768}, //量程,这里表示上报的量程为+-2G
.trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT, //中断类型
.active = sensor_active, //用于开关 gsensor
.init = sensor_init, //用于初始化 gsensor
.report = sensor_report_value, //用于上报gsensor数据
};
确认i2c通信上之后第一步就是读取模块的id是否跟datasheet上的一致,这个是i2c通讯识别到你的模块的方法,例如这里的id寄存器是0x0f,id值是0x68。也就是当i2c通讯上,读取到的id寄存器的id值是0x68,那说明识别到设备了,可以去设置它的控制器,然后读取它的数据了
2)注册sensor驱动到sensor-dev.c
static struct sensor_operate *gsensor_get_ops(void)
{
return &gsensor_lsm9ds1_ops;
}
static int __init gsensor_lsm9ds1_init(void)
{
struct sensor_operate *ops = gsensor_get_ops();
int result = 0;
int type = ops->type;
printk("\n# -czd-: __%s__, _line[%d]_ #\n", __func__, __LINE__);
result = sensor_register_slave(type, NULL, NULL, gsensor_get_ops);
return result;
}
static void __exit gsensor_lsm9ds1_exit(void)
{
struct sensor_operate *ops = gsensor_get_ops();
int type = ops->type;
sensor_unregister_slave(type, NULL, NULL, gsensor_get_ops);
}
初始化sensor,主要是对sensor的控制寄存器部分做初始化设置,说白了就是写寄存器设置寄存器值:
static int sensor_init(struct i2c_client *client) //初始化sensor控制寄存器
{
struct sensor_private_data *sensor =
(struct sensor_private_data *)i2c_get_clientdata(client);
int result = 0;
result = sensor->ops->active(client, 0, 0);
if (result) {
dev_err(&client->dev, "%s:line=%d,error\n",
__func__, __LINE__);
return result;
}
sensor->status_cur = SENSOR_OFF;
result = sensor_write_reg(client, CTRL_REG4_A, 0x38);
if (result) {
dev_err(&client->dev, "%s:fail to set CTRL_REG4_A.\n",
__func__);
return result;
}
/* Normal / Low power mode (1600 Hz) */
result = sensor_write_reg(client, CTRL_REG5_A, 0x78);
if (result) {
dev_err(&client->dev, "%s:fail to set CTRL_REG5_A.\n",
__func__);
return result;
}
result = sensor_write_reg(client, CTRL_REG6_A, 0x40);
if (result) {
dev_err(&client->dev, "%s:fail to set CTRL_REG6_A.\n",
__func__);
return result;
}
result = sensor_write_reg(client, CTRL_REG7_A, 0x00);
if (result) {
dev_err(&client->dev, "%s:fail to set CTRL_REG7_A.\n",
__func__);
return result;
}
return result;
}
当我们上层apk打开gsensor读取数据时,会通过ioctl调用drivers/input/sensors/sensor-dev.c文件中的gsensor_dev_ioctl这个接口函数实现对gsensor的打开操作,然后通过判断宏case GSENSOR_IOCTL_START 调用sensor_enable(sensor, SENSOR_ON);打开gsensor功能,然后再调用result = sensor->ops->active进入到具体sensor的驱动里面去设置gsensor active;
106 static int sensor_active(struct i2c_client *client, int enable, int rate)
107 {
108 struct sensor_private_data *sensor =
109 (struct sensor_private_data *)i2c_get_clientdata(client);
110 int result = 0;
111
112
114 sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg);
115 if (enable)
116 result = sensor_write_reg(client,
117 sensor->ops->ctrl_reg,
118 sensor->ops->ctrl_data | 0x78); //0x07
119 else
120 result = sensor_write_reg(client, sensor->ops->ctrl_reg, 0x00);
121
122 if (result)
123 dev_err(&client->dev, "%s:fail to active sensor\n", __func__);
124
125 DBG("%s:reg=0x%x,reg_ctrl=0x%x,enable=%d\n",
126 __func__,
127 sensor->ops->ctrl_reg,
128 sensor->ops->ctrl_data, enable);
129 return result;
130 }
打开gsensor功能之后,然后去读取gsensor的三轴原始数据,这里连续读取6个字节的寄存器值,分别对应三轴数据:
reg_buf = sensor->ops->read_reg; //这里从read_reg读取连续6个寄存器的值(0x28,0x29,0x2A...),他们是gsensor的三轴数据寄存器
for (i = 0; i < sensor->ops->read_len; i++) {
buffer[i] = sensor_read_reg(client, reg_buf);
reg_buf++;
printk("\n# lsm9ds1: __%s__, _line[%d]_ (buf[%d]= %x)\n", __func__, __LINE__, i, buffer[i]);
}
input子系统上报数据,上报之后就可以通过getevent获取到数值:
static int gsensor_report_value(struct i2c_client *client,
struct sensor_axis *axis)
{
struct sensor_private_data *sensor =
(struct sensor_private_data *)i2c_get_clientdata(client);
if (sensor->status_cur == SENSOR_ON) {
input_report_abs(sensor->input_dev, ABS_X, axis->x);
input_report_abs(sensor->input_dev, ABS_Y, axis->y);
input_report_abs(sensor->input_dev, ABS_Z, axis->z);
input_sync(sensor->input_dev);
}
return 0;
}
内核驱动加载和卸载函数:【kernel跑起来会自动执行module_init函数】
module_init(gsensor_lsm9ds1_init);
module_exit(gsensor_lsm9ds1_exit);
5、Android层中的sensor相关宏配置
BoardConfig.mk 中:
# Sensors
BOARD_SENSOR_ST := true //采用 RK 的 sensors Hal,也就是本文介绍的
BOARD_SENSOR_MPU_PAD := false //仅适用MPU6500、mpu6050 等芯片
支持哪些类型的sensor,如果没有,要配置成false,否则vts和cts测试会失败:
BOARD_GRAVITY_SENSOR_SUPPORT := true
BOARD_COMPASS_SENSOR_SUPPORT := false
BOARD_GYROSCOPE_SENSOR_SUPPORT := false
BOARD_PROXIMITY_SENSOR_SUPPORT := false
BOARD_LIGHT_SENSOR_SUPPORT := true
BOARD_PRESSURE_SENSOR_SUPPORT := false
BOARD_TEMPERATURE_SENSOR_SUPPORT := false
6、Gsensor和gyro的校准
Gsensor和gyro的校准可通过命令行方式,pcba测试的时候也可以做校准,具体查
看pcba的配置。
命令行校准方法:保持机器水平静止放置,输入以下命令校准:
Gsensor: echo 1 > /sys/class/sensor_class/accel_calibration
GYRO : echo 1 > /sys/class/sensor_class/gyro_calibration
查看校准值:
cat /sys/class/sensor_class/accel_calibration
cat /sys/class/sensor_class/gyro_calibration
如果无法查看校准值,则说明校准失败,可以打印 kernel log 确定失败原因。校准成功后,校准的值会保存到nand或emmc的vendor storage里面,不会被擦除,开机自动生效。
7、调试可能遇到问题点
7.1 getevent查看gsensor无数据上报
排查思路:
1) 确认驱动是否注册上,可以通过getevent 查看是否有名为gsensor的input设备;没有的话就要看为何没注册上,有可能是 dts 配置问题,驱动问题,i2c通讯失败,chip id 不匹配等等可能的原因,查看kernel log是否,具体问题具体分析;如果有注册上,往下看。
2) 确认android 层相关宏是否打开;
3) 确认hal层代码是否正常运行;开机打印logcat,搜索sensor字段查看相关log。
4 ) 确认上报的事件类型是否对应(kernel上报的和HAL读取的是否一致(ABS_X还是ABS_RX,或者ABS_MISC等))
7.2 Getevent有数据上报,屏幕不旋转或者旋转无规律;
除可通过sensor的apk查看数值是否正确外,还可以通过命令的方式查看android
上报的gsensor数据是否正确:
setprop sensor.debug.level 2
手动灭屏休眠,再唤醒后命令生效,通过 logcat –s SensorsHal 可以查看到 hal 上报
的gsensor数值;
静止水平放置时,理论正确的数值应该是有个轴的值为+9.8 或者-9.8,另外两个轴的
值为 0;
如果数值不对,请确认上面章节介绍的数据上报转换是否正确。
7.2 gsensor的方向不对
Sensor-dev.c定义了layout 值,对应不同的矩阵,具体可以查看 sensor-dev.c 代码查询,所以,适配方向可以通过修改dts里面的layout值来选择对应的转换矩阵,达到方向的变化。
layout = <4>;
8、rk3399 android7.1 sensor代码框架简单梳理:
HAL代码目录:
hardware/rockchip/sensor/st
MmaSensor.cpp //gsensor
GyroSensor.cpp //gyro
AkmSensor.cpp //compass
kernel代码目录:
kernel/drivers/input/sensors/
accel //gsensor
compass //compass
gyro //gyro
首先开机起来,设备和driver的compatible属性匹配,然后跑sensor_probe探测函数,在里面进行一些初始化还有sensor 输入设备的注册;
初始化部分主要调用sensor_chip_init进行具体sensor的初始化调用,跑到对应sensor驱动里面去执行,通过sensor->ops = ops;这个结构体指针调用;
调用sensor_get_id获取sensor id值是否跟你具体sensor驱动的id值(sensor->ops->id_data)一致;
调用sensor_initial -->调用 sensor->ops->init(client)初始化sensor,主要进行一些寄存器值的设置;
调用devm_input_allocate_device分配输入设备,然后调用input_register_device注册输入子设备;
调用sensor_irq_init初始化中断:包括申请中断号,注册中断子程序sensor_interrupt等的动作;
当打开sensor 测试apk时,hal层到底层驱动的调用流程如下[HAL]表示在hal层执行,[kernel表示在kernel层的driver实现]:
这里以gsensor的HAL到driver的调用为例说明:
[HAL]==>init_nusensors
[HAL]==>poll__activate
[HAL]==>调用int MmaSensor::enable,
[HAL]==>然后调用ioctl(dev_fd, GSENSOR_IOCTL_START)打开gsensor,
[kernel]==>调用到驱动的unlocked_ioctl = gsensor_dev_ioctl
[kernel]==>sensor_enable(sensor, SENSOR_ON); //通过cmd(GSENSOR_IOCTL_START)调用到sensor_enable
[kernel]==>然后调用到具体sensor驱动的sensor->ops->active(client, 1, sensor->pdata->poll_delay_ms); //使能sensor
[kernel]==>然后打开中断或者打开循环工作队列
使能中断:enable_irq(client->irq);
加载工作队列:schedule_delayed_work
[kernel]==>调用input_report_abs/input_sync上报读取到的sensor数据,如果调用成功可以在adb中执行getevent有数据上报
[HAL]==>然后调用 MmaSensor::readEvents来读取gsensor数据,也就是读取输入子系统上报到上层的数据