Issues:Frequency out of range: (expecting between 112.50Hz and 275.00Hz, measured 99.85Hz)

1. 分析 CTS log

   junit.framework.AssertionFailedError: VerifySensorOperation | sensor='SL SC7A20 3-axis Accelerometer', samplingPeriod=0us, maxReportLatency=10000000us | Frequency out of range: Requested "SL SC7A20 3-axis Accelerometer" at fastest (expecting between 112.50Hz and 275.00Hz, measured 99.85Hz), Magnitude mean out of range: mean=7.7100506 (expected 9.80665+/-1.5)

第二个问题:mean=7.7100506 (expected 9.80665+/-1.5),表示X/Y/Z 三轴数据不达标,原因是加速度sensor没有校准或者校准失败,请客户做校准再测试或者校准有问题联系sensor FAE修正校准失败问题,最终复测X/Y/Z 三轴数据达标。
第一个问题:Frequency out of range: Requested "SL SC7A20 3-axis Accelerometer" at fastest (expecting between 112.50Hz and 275.00Hz, measured 99.85Hz),表示CTS在测试最快频率时不达标,期望在112.5HZ~275.0HZ之间,换算成毫秒即3.64ms~8.9ms;可是实测是99.85HZ,10ms。

下面继续分析第一个采样频率不达标问题。

2. 分析 Acc sensor HAL+Driver 代码,排查APP 通过HAL setDelay设置的采样频率是否有真实正确的传递到Driver轮询采样的代码处

  • HAL
Acc_Sc7a20.cpp
    AccSensor::setDelay
{
    ...
	sprintf(sysfsEnable, "%s/%s",
				input_sysfs_path, SYSFS_NODE_BMA_DELAY);
	err = write_sys_attribute(sysfsEnable, writeBuffer, writeBytes);
    ...
}
  • Driver
sc7a20_delay_store()-->sc7a20_acc_update_odr(sc7a20_acc, sc7a20_acc->pdata->poll_interval);

    int sc7a20_acc_update_odr(struct sc7a20_acc_data *acc, int poll_interval_ms)
{
    ...
    err = sc7a20_acc_i2c_write(acc, buf, 1);
    return 0;//david
...
}

==> 从驱动可以看出,客户这份驱动居然直接 APP 通过HAL传递来的delay根本没有用到,请客户联系 sensor FAE确认此问题。

3. sensor FAE 需改驱动后继续测试fail

新测试的log,四个问题testAccelerometer_fastest_batching,testAccelerometer_fastest_flush,testAccelerometer_200hz和testAccelerometer_fastest的fail原因都是一样的(expecting between 112.50Hz and 275.00Hz, measured 95.69Hz)

==》
检查驱动代码发现:

static long sc7a20_acc_misc_ioctl(struct file *file, unsigned int cmd,
				unsigned long arg)
{
...
	case SC7A20_ACC_IOCTL_SET_DELAY:
		if (copy_from_user(&interval, argp, sizeof(interval)))
			return -EFAULT;
		interval=interval/1000000;
		if (interval < 0 || interval > 1000)
			return -EINVAL;

		if(interval > acc->pdata->min_interval)	
			acc->pdata->poll_interval = interval;
		else
			acc->pdata->poll_interval = acc->pdata->min_interval;  //关键!
			
		err = sc7a20_acc_update_odr(acc, acc->pdata->poll_interval);
		/* TODO: if update fails poll is still set */
		if (err < 0)
			return err;
		break;
...

检查客户代码发现,代码中有从dts解析驱动最小轮询(采样)频率min_interval,怀疑此值设置的较大,客户确认是2ms,此路不通。

另外,请客户与sensor FAE确认这颗acc sensor 物料上报数据的最快频率是400HZ(2.5ms)(后面会用到)也同步佐证了上面那点。

没招了,请客户临时验证尝试将驱动采样轮询频率poll_interval写死设置为5ms(在该物料能力范围内),并将轮询读数据的地方打印出来,看实际跑起来的采样间隔是多少?

4. 进一步分析驱动log中实际的轮询采样时间间隔

 kernel log:
Line 14765: 748[02-21 10:47:30.270] <6>[ 1510.554556] c0 sc7a20_acc read x=1014064, y=-25376, z=-116144
	Line 14767: 750[02-21 10:47:30.271] <6>[ 1510.564421] c0 sc7a20_acc read x=1014064, y=-25376, z=-115168
	Line 14769: 752[02-21 10:47:30.290] <6>[ 1510.574402] c0 sc7a20_acc read x=1014064, y=-28304, z=-120048
	Line 14771: 754[02-21 10:47:30.291] <6>[ 1510.584388] c0 sc7a20_acc read x=1013088, y=-30256, z=-125904
	Line 14773: 756[02-21 10:47:30.310] <6>[ 1510.594335] c0 sc7a20_acc read x=1014064, y=-28304, z=-125904
	Line 14775: 758[02-21 10:47:30.311] <6>[ 1510.604359] c0 sc7a20_acc read x=1012112, y=-29280, z=-122000
	Line 14777: 760[02-21 10:47:30.332] <6>[ 1510.614335] c0 sc7a20_acc read x=1011136, y=-32208, z=-126880
	Line 14779: 762[02-21 10:47:30.332] <6>[ 1510.624352] c0 sc7a20_acc read x=1013088, y=-27328, z=-124928
	Line 14781: 764[02-21 10:47:30.350] <6>[ 1510.634793] c0 sc7a20_acc read x=1010160, y=-31232, z=-126880

==》 从kernel时间戳[ 1510.xxxxxx](以秒为单位)可以看到,驱动实际数据的轮询采样频率居然是10ms(0.01s),这恰好是写死固定5mspoll_interval的2倍,这不符合逻辑,此处必有妖!

5. 问题根本原因与解决方案

sc7a20_acc_probe()-->sc7a20_acc_input_init()

static int sc7a20_acc_input_init(struct sc7a20_acc_data *acc)
{
...
	INIT_DELAYED_WORK(&acc->input_work, sc7a20_acc_input_work_func);
...
}

static void sc7a20_acc_input_work_func(struct work_struct *work)
{
	struct sc7a20_acc_data *acc;

	int xyz[3] = { 0 };
	int err;

	acc = container_of((struct delayed_work *)work,
			   struct sc7a20_acc_data, input_work);

	mutex_lock(&acc->lock);
	err = sc7a20_acc_get_acceleration_data(acc, xyz); // 上面读数据的log是这里打印的
	if (err < 0)
		dev_err(&acc->client->dev, "get_acceleration_data failed\n");
	else
		sc7a20_acc_report_values(acc, xyz);

	schedule_delayed_work(&acc->input_work,
			      msecs_to_jiffies(acc->pdata->poll_interval)); //使用的是delayed work的方式周期性工作的
	mutex_unlock(&acc->lock);
}

可以看到使用的是delayed work的方式周期性采样。轮询周期为将以ms为单位的 poll_intervalms 换算成jiffies为单位的时间。

其中MSEC_PER_SEC是1000(1s有多少ms),那么唯一的变量就是HZ了.

关于 HZ、tick和jiffies三者间的关系:

- HZ

Linux核心每隔固定周期会发出timer interrupt (IRQ 0),HZ是用来定义每一秒有几次timer interrupts。举例来

说,HZ为1000,代表每秒有1000次timer interrupts,比较常见的设置是HZ=100。

可以通过 cat /proc/interrupt 查看timer中断次数,并于一秒后再次观察其值,通过前后差值可以估算HZ的值。

要检查内核源码中HZ的值是什么,可以执行命令:

#cat kernel/.config | grep 'CONFIG_HZ='

还可以直接更改文件param.h

- tick

tick是HZ的倒数,意即timer interrupt每发生一次中断的时间。如HZ为250时,tick为4毫秒(millisecond)。

- jiffies

jiffies为Linux核心变数(32位元变数,unsigned long),它被用来纪录系统自开机以来,已经过多少的tick。每发生一次timer interrupt,Jiffies变数会被加一。

HZ CPU架构相关的,查看 /proc/config.gz 发现当前平台HZ配置的为100。

sp7731e_1h10:/ $ zcat /proc/config.gz | grep HZ
zcat /proc/config.gz | grep HZ
CONFIG_NO_HZ_COMMON=y
# CONFIG_HZ_PERIODIC is not set
CONFIG_NO_HZ_IDLE=y
# CONFIG_NO_HZ_FULL is not set
CONFIG_NO_HZ=y
CONFIG_HZ_FIXED=0
CONFIG_HZ_100=y
# CONFIG_HZ_200 is not set
# CONFIG_HZ_250 is not set
# CONFIG_HZ_300 is not set
# CONFIG_HZ_500 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=100

由此,问题的根本原因知道了:

当前sc7a20的驱动轮询读数据的方案使用的是delay work, 而delay work采用的是低精度定时器,其时钟单位精度jiffies等同于HZ,HZ是平台架构相关的,在该平台上HZ设定为100,也就是说采用delay work的轮询方案,采样频率最高只能是100HZ(10ms),这是无法满足google cts的标准–expecting between 112.50Hz and 275.00Hz, measured 95.69Hz。

解决方案:轮询采样使用的delay work方案改成高精度定时器hrtimer方案,最终问题解决。

你可能感兴趣的:(Sensor,Linux,Android)