一、 硬件烧录环境搭建
1.1、电脑安装开发板驱动,解压DriverAssitant_v5.1.1 双击exe 文件安装,因为电脑系统是win11这里一般会遇到一些报错:
按照手册上说是不支持win11 安装,这里尝试过多种方法最后解决方案是:重启电脑然后必须使用管理员权限运行exe ,先卸载驱动之后,然后点击驱动安装,这一步会等待大概20秒后弹出一个提示框,问是否安装点击“是”后等待安装完成。
1.2、驱动安装完成后,连上开发板先cmd 执行adb devices命令看是否可以找到设备,如果能够识别到设备说明驱动安装ok.
接下来打开RKDevTool ,recovery+reset 键同时按,2秒钟释放reset 后进入LOADER模式,选择固件然后点击升级等待固件烧录
这里如果遇到通信异常问题,需要重复第(1)步操作,如果正常进入LOADER 模式设备管理器中能看到一个Class for rockusb devices的设备。
二、 软件调试
2.1、Android 编译报错
dex_preopt_config.mk 会用out/路径下的文件,该文件需要root 权限,解决办法:
chmod -R 777 ./out
2.2、 磁传感器移植
2.2.1、 驱动层移植
RK3588 Android sensor 架构类似MTK1.0 ap sensor架构,驱动是放在kernel 端的。具体驱动路径如下:
/kernel-5.10/drivers/input/sensors/compass/
<1>, Kconfig 编写
config COMPASS_AK09918
tristate "Asahi Kasei AK09918 3-Axis Magnetometer"
depends on I2C
help
Say yes here to build support for Asahi Kasei AK09918 3-Axis Magnetometer.
To compile this driver as a module, choose M here: the module
will be called AK09911.
endif
<2>, Makefile 编写
obj-$(CONFIG_COMPASS_AKM09918) += akm09918.o
<3>, 编译相关宏配置
kernel-5.10/arch/arm64/configs/OK3588-Android_defconfig/ 添加
CONFIG_COMPASS_AK09918=y
CONFIG_GS_DA223=y
CONFIG_SENSOR_DEVICE=y
CONFIG_GSENSOR_DEVICE=y
CONFIG_COMPASS_DEVICE=y
<4>, DTS 添加设备信息
kernel-5.10\arch\arm64\boot\dts\rockchip\OK3588-C-common.dtsi 中添加
mag
&i2c3 { //硬件ic挂在i2c3 上
status = "okay";
ak09918_compass@0c {
compatible = "ak09918"; //必须和ic驱动struct i2c_driver xxx_driver结构体中
的.id_table 中某一属性匹配
reg = <0x0c>; //硬件ic slave地址
type = <SENSOR_TYPE_COMPASS>; //ic驱动中struct sensor_operate xxx_ops结构体中的.type一致
irq_enable = <0>; //ic 上报数据的模式,0表示polling模式,1 表示中断模式
poll_delay_ms = <30>; //ic 轮询上报数据的周期
layout = <4>; //ic direciton配置
status = "okay";
};
};
acc
&i2c4 {
status = "okay";
pinctrl-0 = <&i2c4m1_xfer>;
da223_acc@27{
compatible = "gs_da223";
reg = <0x27>;
type = <SENSOR_TYPE_ACCEL>;
//irq-gpio = <&gpio2 GPIO_B2 IRQ_TYPE_EDGE_FALLING>;
irq_enable = <0>;
poll_delay_ms = <30>;
//layout = <6>;
layout = <3>;
};
};
<5>, 增加 sensor id
sensor-dev.h 中添加 sensor_id,且驱动中的id_table ,其中”ak09918”字段与 dts 中的 compatible字段对应
include/linux/sensor-dev.h 定义COMPASS_ID_AK09918
enum sensor_id {
ID_INVALID = 0,
...
COMPASS_ID_AK09918,
ACCEL_ID_BMA2XX,
}
/kernel-5.10/drivers/input/sensors/compass/akm09918中
static const struct i2c_device_id compass_akm09918_id[] = {
{"ak09918", COMPASS_ID_AK09918},
{}
};
2.2.2、 HAL 层移植
(1)、设置设备节点selinux 权限
device/rockchip/common/sepolicy/vendor/file_contexts
/dev/mir3da 0660 system system
/dev/gsensor 0660 system system
/dev/compass 0660 system system
/dev/akm09918_dev 0660 system system
device/rockchip/common/rootdir/ueventd.rockchip.rc
/dev/mir3da 0660 system system
/dev/akm09918_dev 0660 system system
/dev/gsensor 0660 system system
/dev/compass 0660 system system
(2)、comapss校准算法移植到下面目录
hardware/rockchip/sensor/st/akm09918-64
2.2.3、 根目录device路径下编译配置
device/rockchip/rk3588/BoardConfig.mk
配置编译的平台算法库宏定义
# Sensors
BOARD_SENSOR_ST := true
device/rockchip/rk3588/ok3588_c/BoardConfig.mk
配置平台支持的Sensor type宏
BOARD_GRAVITY_SENSOR_SUPPORT := true
BOARD_COMPASS_SENSOR_SUPPORT := true
BOARD_SENSOR_COMPASS_AK8963-64 := true
BOARD_GYROSCOPE_SENSOR_SUPPORT := true
BOARD_PROXIMITY_SENSOR_SUPPORT := true
BOARD_LIGHT_SENSOR_SUPPORT := true
device/rockchip/rk3588/AndroidProducts.mk
配置平台产品选择类型(一般不需要配置)
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/ok3588_c/ok3588_c.mk
三、Debug 总结
3.1、确认驱动是否注册HAL层成功
确认方法:通过getevent 查看是否有名为compass 和 gsensor 的input 设备
如果没有注册成功,需要排查的思路如下:
<1>, 硬件i2c 是否上拉和接线是否正确
<2>, dts 配置是否正确,通过执行
adb shell
dmesg -w > kernel.log //kernel log搜索关键字确定驱动问题
<3>, 确认 hal 层代码是否正常运行,执行如下命令
adb shell
logcat >hal.log //log 中搜索sensor关键字定位问题
3.2、确认APP 数据是否正确
<1>, 上层apk 确认
<2>, 通过打印hal log 确认
logcat –s SensorsHal //打开hal 数据log 可以查看到 hal 上报
的 sensor 数值;
四、软件调试问题总结
4.1、地磁apk 没有数据
问题分析:驱动注册成功,dts 解析成功,可以正常进入probe 驱动上报数据失败。
1)、sensor-dev.c 中上层通过ioctrl发送enable cmd 没有传递到驱动端
compass_dev_ioctl(ECS_IOCTL_APP_SET_MVFLAG)
{
switch(cmd) {
case ECS_IOCTL_APP_SET_MVFLAG:
atomic_set(&sensor->flags.mv_flag, flag);
sensor_enable(sensor,flag); //增加enable调用
break;
}
}
2)、驱动上报数据流程修改
/kernel-5.10/drivers/input/sensors/compass/akm09918.c 中sensor_report_value()流程:
4.2、指南针角度无值
4.2.1、HAL 层部分移植
1)、sensor_list 中增加一个orientation sensor
hardware/rockchip/sensor/st/sensors.c sSensorList[]中增加一个虚拟sensor
#ifdef COMPASS_SENSOR_SUPPORT
{ .name = "Orientation sensor",
.vendor = "The Android Open Source Project",
.version = 1,
.handle = SENSORS_HANDLE_BASE+ID_O,
.type = SENSOR_TYPE_ORIENTATION,
.maxRange = 20000.0f,
.resolution = 0.495f,
.power = 6.8f,
.minDelay = 7000,
.fifoReservedEventCount = 0,
.fifoMaxEventCount = 0,
.stringType = SENSOR_STRING_TYPE_ORIENTATION,
.requiredPermission = 0,
.maxDelay = 20000,
.flags = SENSOR_FLAG_CONTINUOUS_MODE,
.reserved = {}
},
#endif
2)、hardware\rockchip\sensor\st\xxxSensor.cpp 接口中定义 orientation的 handle_id 和接口处理
xxxSensor::xxxSensor()
{
/* init prara */
mPendingEvents[Orientation ].version = sizeof(sensors_event_t);
mPendingEvents[Orientation ].sensor = ID_O;
mPendingEvents[Orientation ].type = SENSOR_TYPE_ORIENTATION;
mPendingEvents[Orientation ].orientation.status = SENSOR_STATUS_ACCURACY_HIGH;
if ((dev_fd > 0) && (!ioctl(dev_fd, ECS_IOCTL_APP_GET_MVFLAG, &flags))) {
if (flags) {
mEnabled |= 1<<MagneticField;
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_MAGV_X), &absinfo)) {
mPendingEvents[MagneticField].magnetic.x = absinfo.value * CONVERT_M_X;
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_MAGV_Y), &absinfo)) {
mPendingEvents[MagneticField].magnetic.y = absinfo.value * CONVERT_M_Y;
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_MAGV_Z), &absinfo)) {
mPendingEvents[MagneticField].magnetic.z = absinfo.value * CONVERT_M_Z;
}
}
}
if (!ioctl(dev_fd, ECS_IOCTL_APP_GET_MFLAG, &flags)) {
if (flags) {
mEnabled |= 1<<Orientation;
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_YAW), &absinfo)) {
mPendingEvents[Orientation].orientation.azimuth = absinfo.value;
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_PITCH), &absinfo)) {
mPendingEvents[Orientation].orientation.pitch = absinfo.value;
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ROLL), &absinfo)) {
mPendingEvents[Orientation].orientation.roll = -absinfo.value;
}
if (!ioctl(data_fd, EVIOCGABS(EVENT_TYPE_ORIENT_STATUS), &absinfo)) {
mPendingEvents[Orientation].orientation.status = uint8_t(absinfo.value & SENSOR_STATE_MASK);
}
}
}
// disable temperature sensor, since it is not reported
flags = 0;
if (dev_fd > 0)
ioctl(dev_fd, ECS_IOCTL_APP_SET_TFLAG, &flags);
if ( !mEnabled ) {
LOGI("hal AkmSensor 000: close_device = %d\n", mEnabled); //by li debug
close_device();
}
}
enable()
setDelay()
isActivated()
proccessEvent()
hardware\rockchip\sensor\st\nusensors.cpp sensors_poll_context_t增加
\hardware\rockchip\sensor\st\xxxSensor.h public类 SensorBase()
4.2.2、 驱动层修改
1)、4.1.1 移植完成后,指南针app 没有角度,
根据hal 层log分析,一直打印“ AKMD: Suspended”,原因是AKD_GetOpenStatus(&st) 获取到的*st值一直是0,进一步分析原因发现驱动层收到OPEN_STATUS 命令后并没有传给HAL 层,所以导致status 值一直是0。驱动收到指令后调用了copy_to_user() 传参到 HAL 层问题仍然存在分析hal enable() 下发给驱动的命令是ECS_IOCTL_APP_SET_MFLAG 传到驱动是调用sensor-dev.c 中的compass_dev_ioctl(file,ECS_IOCTL_APP_SET_MFLAG ,arg)调用atomic_set(&sensor->flags.m_flag, flag)上层flag 的值是传给sensor->flags.m_flag而compass_akm_get_openstatus()返回值atomic_read(&sensor->flags.open_flag)是sensor->flags.open_flag所以根因是返回的值不对compass_akm_get_closestatus()同样的问题。(备注:一直红线上半部分是HAL)
具体代码修改:
驱动中接口修改:
static int compass_akm_get_openstatus(void)
{
...
return atomic_read(&sensor->flags.m_flag);
}
static int compass_akm_get_closestatus(void)
{
...
return atomic_read(&sensor->flags.m_flag);
}
static long compass_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
...
switch(cmd)
{
case ECS_IOCTL_GET_OPEN_STATUS:
status = compass_akm_get_openstatus();
if (copy_to_user(argp, &status, sizeof(status)))
return -EFAULT;
break;
case ECS_IOCTL_GET_CLOSE_STATUS:
status = compass_akm_get_closestatus();
if (copy_to_user(argp, &status, sizeof(status)))
return -EFAULT;
break;
}
}
2)、 1)部分修改完发现指南针apk 值还是没有,此刻非常郁闷,于是进一步分析hal 层计算角度部分代码流程
问题解决部分代码
驱动修改hal 层传角度到驱动的api
static void compass_report_angle(int *angle)
{
struct sensor_private_data *sensor =
(struct sensor_private_data *) i2c_get_clientdata(this_client);
static int flag;
if (atomic_read(&sensor->flags.m_flag))
{
dev_err(&this_client->dev, "[akm] %s: pitch %d, yaw= %d, row = %d\n",__func__, angle[1], angle[0], angle[2]);
input_report_abs(sensor->input_dev, ABS_RX, angle[0]);
input_report_abs(sensor->input_dev, ABS_RY, angle[1]);
input_report_abs(sensor->input_dev, ABS_RZ, angle[2]);
}
input_sync(sensor->input_dev);
}
static void compass_set_YPR(int *rbuf)
{
...
int temp[3]=0;
memcpy(temp, rbuf+9, 3 * sizeof(int));
compass_report_angle(temp); //新增上报apk 接口
}
4.3、指南针apk 和 地磁apk 同时打开数据会有问题
问题原因: 解 4.1、地磁apk 没有数据问题引入,上层发送ECS_IOCTL_APP_SET_MVFLAG 这个命令的目的是在上报数据的时候会用到flags_mv_flag ,换句话说ECS_IOCTL_APP_SET_MVFLAG这个命令并不是上层用来enable sensor 的命令的,当时为了解决地磁app 无数据问题强行调用sensor_enable()接口导致。
正常流程:上层使能sensor 的是通过回调的sensor_dev.c 中的compass_dev_open()接口(前提是驱动注册一个misc device)通过automic_set(&sensor->flags.open_flag,1)把sensor->flags.open_flag设置为1。同时打开apk hal层先获取sensor 状态发送ECS_IOCTL_GET_OPEN_STATUS命令调用compass_akm_get_oepndatatus()返回sensor->flags.open_flag的值给hal 层,sensor->flags.open_flag=1 表示sensor enable然后hal层发送ECS_IOCTL_RESET命令复位sensor ,然后发送ECS_IOCTL_GET_DELAY命令获取delay 值,这个delay 值是上层app 设置不同odr(normal(100hz), ui(20hz),game(50hz),fast(100hz))调用set_delay()->update_delay()发送命令ECS_IOCTL_APP_SET_DELAY给驱动层然后返回值赋值给delay变量的,这个delay 实质是上层请求驱动数据的时间间隔,然后驱动配置工作模式是一个固定值。接下来是设置sensor 工作模式,上层调用AKD_SetMode() 发送ECS_IOCTL_SET_MODE 给驱动设置为SING_MEASURE 方式,此模式是上报数据不是周期性上报的,而是设置一次上报一笔数据然后芯片进去power down 模式。最后就是上层发送ECS_IOCTL_GETDATA 命令获取数据,通过copy_to_user(compass_data) 上报给hal 层。compass_data 数据从哪里来的,这里涉及到内核的延时工作队列的函数INIT_DELAYED_WORK(&sensor->delaywork,sensor_delaywork_func)和schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)) 个人理解如下:
//个人理解:delayed work 机制是针对设置polling 模式的sensor上报数据的一种方式
具体配置
<1>、先注册一个delaywork func
static int sensor_irq_Init(struct i2c_client *client)
{
if (sensor->pdata->irq_enable)
{...}
else if (!sensor->pdata->irq_enable) {
INIT_DELAYED_WORK(&sensor->delaywork, sensor_delaywork_func); //注册一个delayed worrk func
sensor->stop_work = 1;
if (sensor->pdata->poll_delay_ms <= 0)
sensor->pdata->poll_delay_ms = 30;
dev_info(&client->dev, "%s:use polling,delay=%d ms\n", __func__, sensor->pdata->poll_delay_ms);
}
<2>、然后调用schedule_delayed_work设置调用delaywork func 的执行周期
static void sensor_delaywork_func(struct work_struct *work)
{
struct delayed_work *delaywork = container_of(work, struct delayed_work, work);
struct sensor_private_data *sensor = container_of(delaywork, struct sensor_private_data, delaywork);
struct i2c_client *client = sensor->client;
int result;
mutex_lock(&sensor->sensor_mutex);
result = sensor->ops->report(client);
if (result < 0)
dev_err(&client->dev, "%s: Get data failed\n", __func__);
mutex_unlock(&sensor->sensor_mutex);
if ((!sensor->pdata->irq_enable) && (sensor->stop_work == 0))
schedule_delayed_work(&sensor->delaywork, msecs_to_jiffies(sensor->pdata->poll_delay_ms)); //根据上层的请求的polling_delay 时间启动注册的sensor->delaywork
}
static int sensor_misc_device_register(struct sensor_private_data *sensor, int type)
{
swtich(type)
{
......
case SENSOR_TYPE_COMPASS:
if (!sensor->ops->misc_dev) {
sensor->fops.owner = THIS_MODULE;
sensor->fops.unlocked_ioctl = compass_dev_ioctl;
#ifdef CONFIG_COMPAT
sensor->fops.compat_ioctl = compass_dev_compat_ioctl;
#endif
sensor->fops.open = compass_dev_open; // 打开apk调用
sensor->fops.release = compass_dev_release; //关闭apk调用
sensor->miscdev.minor = MISC_DYNAMIC_MINOR;
sensor->miscdev.name = "compass";
sensor->miscdev.fops = &sensor->fops;
} else {
memcpy(&sensor->miscdev, sensor->ops->misc_dev, sizeof(*sensor->ops->misc_dev));
}
break;
}
}
4.4、地磁上报数据odr 错误
当初的解决思路:驱动enable 的时候根据上层传过来的rate 做做区间判断最终选择一个合适的值来配置sensor 的ord, 但是发现当上层配置不同mode(10,20,50,100hz)驱动数据一直是不变的100hz 上报一笔数据,最后发现是100hz 一直有问题,而且会出现数据异常的情况。
最后采用解决思路:
驱动采用single 模式上报数据,具体读取周期根据上层请求的delay然后上报,上报一笔后下一笔在设置single 再上报这样的方式。发现app 设值mode 驱动可以正常上报数据。