RK3588 Android12平台sensor调试总结

一、 硬件烧录环境搭建

      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  驱动可以正常上报数据。

<think>好的,用户想了解如何为RK3588芯片编写驱动程序,需要教程或指南。首先,我需要从提供的引用中提取相关信息。引用[1]和[2]提到了开发环境搭建和工具准备,引用[4]提到有3500页以上的文档,包括驱动开发手册,这应该包含开发指南。用户可能需要具体的步骤,比如环境配置、源码获取、驱动编写流程和示例。此外,可能需要推荐迅为电子的资料,因为引用[3]和[4]强调了他们的开发板文档丰富。还要注意用户可能使用Linux或Android系统,所以需要分开说明两者的不同。需要确保回答结构清晰,分步骤讲解,并附上示例代码。同时,根据系统级指令,生成相关问题,比如环境搭建问题、调试方法等。最后,检查LaTeX格式是否正确,行内公式用$...$,独立公式用$$...$$,但驱动开发可能涉及较少数学公式,需视情况添加。确保引用标识正确添加,比如提到文档时引用[^4]。</think>[逐步解答] 为RK3588芯片编写驱动程序需要遵循以下关键步骤: 1. **开发环境搭建** - 安装Ubuntu 20.04/22.04 LTS系统(建议分配至少200GB磁盘空间) - 配置交叉编译工具链: ```bash sudo apt install gcc-aarch64-linux-gnu ``` - 获取SDK源码包(需联系原厂或开发板供应商获取完整Linux/Android BSP)[^2][^4] 2. **驱动开发基础架构** - 内核源码路径:`kernel/drivers/` 目录下按子系统分类 - 典型驱动文件结构示例: ```c #include <linux/module.h> static int __init mydriver_init(void) { printk(KERN_INFO "RK3588 driver loaded\n"); return 0; } static void __exit mydriver_exit(void) { printk(KERN_INFO "RK3588 driver unloaded\n"); } module_init(mydriver_init); module_exit(mydriver_exit); MODULE_LICENSE("GPL"); ``` 3. **硬件接口开发要点** - GPIO控制: ```c #include <linux/gpio.h> // 获取GPIO编号 int gpio = of_get_named_gpio(dev->of_node, "led-gpios", 0); // 配置为输出模式 gpio_direction_output(gpio, 1); ``` - I2C设备注册: ```c static struct i2c_board_info __initdata rk3588_i2c_devices[] = { { I2C_BOARD_INFO("sensor", 0x50), } }; i2c_register_board_info(0, rk3588_i2c_devices, 1); ``` 4. **调试与验证** - 使用dmesg实时查看内核日志 - 通过sysfs接口调试驱动: ```bash echo 1 > /sys/class/misc/mydriver/debug_level ``` - 使用示波器验证硬件信号时序 [推荐学习路径] 1. 先掌握《Linux设备驱动程序》第三版基础理论 2. 研究RK3588技术参考手册(TRM)的寄存器描述 3. 参考迅为提供的3500+页开发文档(含驱动开发专题) 4. 从简单字符设备驱动开始,逐步过渡到复杂子系统(如V4L2、DRM)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值