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方案,最終問題解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章