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。
下面继续分析第一个采样频率不达标问题。
Acc_Sc7a20.cpp
AccSensor::setDelay
{
...
sprintf(sysfsEnable, "%s/%s",
input_sysfs_path, SYSFS_NODE_BMA_DELAY);
err = write_sys_attribute(sysfsEnable, writeBuffer, writeBytes);
...
}
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确认此问题。
新测试的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(在该物料能力范围内),并将轮询读数据的地方打印出来,看实际跑起来的采样间隔是多少?
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),这恰好是写死固定5ms
poll_interval的2倍
,这不符合逻辑,此处必有妖!
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_interval
ms 换算成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方案,最终问题解决。