rk3399_android7.1平臺調試sensor流程記錄

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數據,也就是讀取輸入子系統上報到上層的數據
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章