Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

MTK平台Android Gsensor数据校准与数据获取

$
0
0

http://blog.csdn.net/morixinguan/article/details/76850600

上节,写WIFI MAC地址的时候我们已经知道,MTKAndroid系统的Gsensor校准的数据其实也是存储在NVRAM中的,Gsensor隶属于传感器系统架构。

 


接下来我们来看下Gsensor校准的基准图像:




那么如何来校准Gsensor的X,Y,Z三个方向呢?我们可以参考MTK提供的工厂测试factroymode的代码:

位置在:vendor\mediatek\proprietary\factory\src\test\ftm_gs_cali.c  ftm_gsensor.c

ftm_gs_cali.c就是校准的源码,我们可以打开来看看它具体的实现原理:

在ftm_gs_cali.c的static void*gs_cali_update_iv_thread(void *priv)这个函数中,我们可以看到如何校准Gsensor数值的过程:

static void *gs_cali_update_iv_thread(void *priv)
{
	struct gsc_data *dat = (struct gsc_data *)priv; 
	struct gsc_priv *gsc = &dat->gsc;
	struct itemview *iv = dat->iv;
	int err = 0, len = 0;
	char *status;
	HwmData cali;
	static int op = -1;
	int  max_retry = 3, retry_period = 100, retry=0;
    unsigned int flags = 1;

	LOGD(TAG "%s: Start\n", __FUNCTION__);
	//打开gsensor
	err = gsensor_open(&(gsc->fd));
	if(err)
	{
		memset(dat->info, 0x00, sizeof(dat->info));
		sprintf(dat->info, uistr_info_sensor_init_fail);
		iv->redraw(iv);
		GSCLOGE("gs_cali_open() err = %d(%s)\n", err, dat->info);
		pthread_exit(NULL);
		return NULL;
	}
/** Enable G-sensor **/
//使能Gsensor,让它开始工作
    while ((err = ioctl(gsc->fd, GSENSOR_IOCTL_INIT, &flags)) && (retry ++ < max_retry))
    usleep(retry_period*1000);
    if (err) {
        LOGD("enable g-sensor fail: %s", strerror(errno));
        return -1;
	}

	while(1)
	{

		if(dat->exit_thd)
		{
			break;
		}

		pthread_mutex_lock(&dat->gsc.evtmutex);
		if(op != dat->gsc.pending_op)
		{
			op = dat->gsc.pending_op;
			GSCLOGD("op: %d\n", dat->gsc.pending_op);
		}
		pthread_mutex_unlock(&dat->gsc.evtmutex);
		err = 0;

		if(op == GS_OP_CLEAR)
		{
			memset(&dat->gsc.cali_nvram, 0x00, sizeof(dat->gsc.cali_nvram));
			memset(&dat->gsc.cali_drv, 0x00, sizeof(dat->gsc.cali_drv));
			err = gsensor_rst_cali(gsc->fd);
			if(err)
			{
				GSCLOGE("rst calibration: %d\n", err);                
			}
			else if((err = gsensor_write_nvram(&dat->gsc.cali_nvram)) != 0)
			{
				GSCLOGE("write nvram: %d\n", err);                
			}

			if(err)
			{
				snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_fail);
				//dat->mod->test_result = FTM_TEST_FAIL;
			}
			else
			{
				snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_ok);
				//dat->mod->test_result = FTM_TEST_PASS;
			}

			gsc->bUpToDate = false;    
			pthread_mutex_lock(&dat->gsc.evtmutex);
			dat->gsc.pending_op = GS_OP_NONE;
			pthread_mutex_unlock(&dat->gsc.evtmutex);
		}
		else if(op == GS_OP_CALI_PRE)
		{
			err = 0;
			/*by-pass*/
			snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_ongoing);            
			pthread_mutex_lock(&dat->gsc.evtmutex);
			dat->gsc.pending_op = GS_OP_CALI;
			pthread_mutex_unlock(&dat->gsc.evtmutex);
		}
		else if(op == GS_OP_CALI)
		{
			if(!dat->gsc.cali_delay || !dat->gsc.cali_num || !dat->gsc.cali_tolerance)
			{
				GSCLOGE("ignore calibration: %d %d %d\n", dat->gsc.cali_delay, dat->gsc.cali_num, dat->gsc.cali_tolerance);                
			}
			//执行校准的动作
			else if((err = gsensor_calibration(gsc->fd, dat->gsc.cali_delay, dat->gsc.cali_num, 
			                          dat->gsc.cali_tolerance, 0, &cali)) != 0)
			{
				GSCLOGE("calibrate acc: %d\n", err);                
			}
			//设置校准cali,让校准的数据开始生效
			else if((err = gsensor_set_cali(gsc->fd, &cali)) != 0)
			{    
				GSCLOGE("set calibration fail: (%s) %d\n", strerror(errno), err);
			}
			else if((err = gsensor_get_cali(gsc->fd, &cali)) != 0)
			{    
				GSCLOGE("get calibration fail: (%s) %d\n", strerror(errno), err);
			}
			//将校准的数据写入到nvram中去
			else if((err = gsensor_write_nvram(&cali)) != 0)
			{
				GSCLOGE("write nvram fail: (%s) %d\n", strerror(errno), err);
			}
			else
			{
				dat->gsc.cali_delay = dat->gsc.cali_num = dat->gsc.cali_tolerance = 0;
				dat->gsc.bUpToDate = false;
			}
			
			if(err)
			{
				len = snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_fail);  
				dat->mod->test_result = FTM_TEST_FAIL;
			}
			else
			{
				len = snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_ok);
				dat->mod->test_result = FTM_TEST_PASS;
			}

			pthread_mutex_lock(&dat->gsc.evtmutex);
			dat->gsc.pending_op = GS_OP_NONE;
			pthread_mutex_unlock(&dat->gsc.evtmutex);
		}

		err = gs_cali_update_info(gsc);
		if(err)
		{
			GSCLOGE("gs_cali_update_info() = (%s), %d\n", strerror(errno), err);
			break;
		} 


		len = 0;
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "R: %+7.4f %+7.4f %+7.4f\n", gsc->dat.x, gsc->dat.y, gsc->dat.z);
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "D: %+7.4f %+7.4f %+7.4f\n", gsc->cali_drv.x, gsc->cali_drv.y, gsc->cali_drv.z);
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "N: %+7.4f %+7.4f %+7.4f\n", gsc->cali_nvram.x, gsc->cali_nvram.y, gsc->cali_nvram.z);
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "%s\n", gsc->status);
		        
		iv->set_text(iv, &dat->text);
		iv->redraw(iv);
	}
	//关闭gsensor
	gs_cali_close(gsc);
	LOGD(TAG "%s: Exit\n", __FUNCTION__);    
	pthread_exit(NULL);

	return NULL;
}

这段代码虽然很多,但我们可以找出校准Gsensor的核心流程主要如下:

(1)    打开Gsensor

(2)    使能Gsensor

(3)    执行校准的动作

(4)    设置校准的Cail,让校准的数据生效

(5)    将校准得到的数据写入到nvram中

(6)    关闭Gsensor

核心流程我们已经清楚了,那么接下来如何来写这个程序呢?我们要弄明白,这些函数上哪个文件里去找这是第一步:

通过grep命令搜索相关函数,最终确定,这些函数的头文件在:

./pskyed/libs/em_emmc_comm/libhwm/include/libhwm.h这里,在这个头文件中有相关的函数可以给我们使用:

extern int gsensor_calibration(int fd, int period, int count, int tolerance, int trace, HwmData *cali);
extern int gsensor_write_nvram(HwmData *dat);
extern int gsensor_read_nvram(HwmData *dat);
extern int gsensor_rst_cali(int fd);
extern int gsensor_set_cali(int fd, HwmData *dat);
extern int gsensor_get_cali(int fd, HwmData *dat);
extern int gsensor_read(int fd, HwmData *dat);
extern int gsensor_init(int fd);
extern int gsensor_close(int fd);
extern int gsensor_open(int *fd);
extern int gyroscope_calibration(int fd, int period, int count, int tolerance, int trace, HwmData *cali);
extern int gyroscope_write_nvram(HwmData *dat);
extern int gyroscope_read_nvram(HwmData *dat);
extern int gyroscope_rst_cali(int fd);
extern int gyroscope_set_cali(int fd, HwmData *dat);
extern int gyroscope_get_cali(int fd, HwmData *dat);
extern int gyroscope_read(int fd, HwmData *dat);
extern int gyroscope_close(int fd);
extern int gyroscope_open(int *fd);
extern int gyroscope_init(int fd);

那么这些函数的源码在哪里呢?源码是没有的,因为MTK厂商将这部分代码给封装成了so动态库文件,所以,我们需要找到这个头文件对应的so文件,这样我们才能使用这个头文件,调用到so动态库中的函数。

通过搜索得知,这个so动态库文件在以下路径:

./pskyed/libs/em_emmc_comm/libhwm/libhwm.so

知道这些以后,下面我们就可以写一个简单的程序来验证这个过程了,这个留给读者自己去测试,接口我已经写好了,我的项目源码不便于公开,请读者自己拿去修改验证,流程是一样的,接口没有改过,至于想实现什么样的效果请读者自己去尝试添加,移植我的程序进行修改。

下面实现这个校准程序:

gs_cali.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/mount.h>
#include <sys/statfs.h>
#include <sys/reboot.h>
#include <dirent.h>
#include <linux/input.h>
#include <math.h>
#include <dirent.h>
#include <ctype.h>
#include <errno.h>
#include <linux/hwmsensor.h>
#include <linux/sensors_io.h>
#include "Keypad.h"
#include "libhwm.h"

#define GSENSOR_NAME  "/dev/gsensor"
#define OPEN_FILE_FAIR -1 
#define OPEN_SUCCESS 0
#define RETURE_SUCCESS 0
#define ENABLED_SUCCESS 0
#define GSENSOR_CALIBRATION_SUCCESS 0
#define ENABLED_FAIR -2 
#define CALIBRATION_FAIR -3 
#define SET_CALIBRATION_FAIR -4
#define WRITE_NVRAM_FAIR  -5 

int gs_fd ; 

//打开gsensor
int gs_open(char *gs_name) ;
//关闭gsensor
int gs_close(int fd) ;
//gsensor开始工作
int gs_enable(unsigned int command) ;
//校准gsensor
int gs_calibration(unsigned int cali_delay ,unsigned int cali_num ,unsigned int cali_tolerance) ;

int main(void)
{
	int gs_ret = 0 ;
	int  cali_delay = 50;
int  cali_num = 20;
//这里的20表示把校准的数值做20次平均
//如果想要更精确,也可以做40次平均计算
    int  cali_tolerance = 20 ; //40
	//打开gsensor
	gs_ret = gs_open(GSENSOR_NAME);
	if(gs_ret != 0){
		printf("gs open fair!\n") ;
		return -1 ;
	}
	//使能gsensor
	gs_ret = gs_enable(GSENSOR_IOCTL_INIT);
	if(gs_ret != 0){
		printf("gs enable fair!\n") ;
		return -2 ;
	}
	//校准---->包括:执行校准的动作、设置校准数值、将校准数值写入nvram
	gs_ret = gs_calibration(cali_delay,cali_num,cali_tolerance);
	if(gs_ret != 0){
		printf("gs_calibration fair!\n") ;
		return -3 ;
	}
	//关闭gsensor
	gs_ret = gs_close(gs_fd);
	if(gs_ret != 0){
		printf("gs_close fair!\n");
		return -4 ;
	}
	printf("runexec call gsensorCalibrate end\n");
    return 0 ;
}

//1、open
int gs_open(char *gs_name)
{
	gs_fd = open(gs_name,O_RDONLY) ;
	if(gs_fd < 0)
	{
		printf("open gsensor dev fair!\n") ;
		return OPEN_FILE_FAIR ;
	}
	printf("gsensor open success!\n");
	return OPEN_SUCCESS ;
}

//2、enable gsensor
int gs_enable(unsigned int command)
{
	int err = 0; 
	unsigned int flags = 1;
	int  max_retry = 3, retry_period = 100, retry=0;
	while ((err = ioctl(gs_fd, command, &flags)) && (retry ++ < max_retry)) ;
	usleep(retry_period*1000);
    if (err) {
        printf("enable g-sensor fail: %s", strerror(errno));
        return ENABLED_FAIR;
	}
	printf("enable gsensor success!\n");
	return ENABLED_SUCCESS ;
}
//3、校准
int gs_calibration(unsigned int cali_delay ,unsigned int cali_num ,unsigned cali_tolerance)
{
	int  err ;
	int flag = 0;
	HwmData dat; //dat.x  dat.y  dat.z 
	HwmData cali;
	HwmData cali_nvram;
	while(1)
	{
		//执行校准的动作
		err = gsensor_calibration(gs_fd , cali_delay , cali_num , cali_tolerance , 0 , &cali);
		if(err != 0)
		{
			printf("calibrate acc: %d\n", err); 
			return CALIBRATION_FAIR ;
		}
		//设置校准cali,让校准数据生效
		err = gsensor_set_cali(gs_fd,&cali) ;
		if(err != 0)
		{
			printf("set calibration fail: (%s) %d\n", strerror(errno), err);
			return SET_CALIBRATION_FAIR ;
		}
		//将校准数据写入nvram中
		err = gsensor_write_nvram(&cali) ;
		if(err != 0)
		{
			printf ("write nvram fail: (%s) %d\n", strerror(errno), err);
			return WRITE_NVRAM_FAIR ;
		}
		flag = 1 ;
		if(flag == 1)
		{
			printf("Gsensor calibrate success!\n") ;
			break ;
		}
	}
	return GSENSOR_CALIBRATION_SUCCESS ;
}

//关闭
int gs_close(int fd)
{
	close(fd) ;
	return 0 ;
}

然后写一个简单的Android.mk

LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES += libcutils libutils   libhwm
LOCAL_STATIC_LIBRARIES += libz libstdc++ 
LOCAL_SRC_FILES:= \
	gs_cali.c 
	
LOCAL_MODULE:= gsensor_calibrate
include $(BUILD_EXECUTABLE)

将.c和Android.mk一并放在external下的一个自己定义的文件夹中,比如gs_cali文件夹,然后最后编译会在out目录下生成gsensor_calibrate这个二进制文件。

特别需要注意一点:使用这种方法进行校准后,需要将gsensor驱动的自动校准功能屏蔽,具体屏蔽方法还需要分析驱动源代码。

但这仅仅只是校准数值而已,如何把校准完的数值读出来呢?参考ftm_gsensor.c这个文件源代码,最终得知是通过打开/dev/gsensor这个节点,然后通过总线访问机制获取:

/sys/bus/platform/drivers/gsensor/sensordata这个文件存放的数据。

读取Gsensor的x,y,z的数据的方法可以参考static int gsensor_read(struct acc_priv *acc)这个函数的实现方法,但它的方法和我的是有区别的,MTK中实现的gsensor_read方法是读取从驱动中读取已经转化成十进制的数据,而我读取的是原始数据十六进制,所以必须要做进制转换,我们可以来看下这个函数:

static int gsensor_read(struct acc_priv *acc)
{
	static char buf[128];    
	int x, y, z, err;

	if(acc->fd == -1)
	{
		FTGLOGE("invalid file descriptor\n");
		err = -EINVAL;
	}
	else if((acc->support_selftest == 1) && (!acc->selftest) && (err = gsensor_selftest(acc, 10, 20)))
	{    
		FTGLOGE("selftest fail: %s(%d)\n", strerror(errno), errno);
	}
	else
	{
		//使用GSENSOR_IOCTL_READ_SENSORDATA命令获取sensor的数据,并存储在buf里
		err = ioctl(acc->fd, GSENSOR_IOCTL_READ_SENSORDATA, buf);
		if(err)
		{
			FTGLOGE("read data fail: %s(%d)\n", strerror(errno), errno);
		}
		//从buf中将x,y,z三个值取出来
		else if(3 != sscanf(buf, "%x %x %x", &x, &y, &z))
		{
			FTGLOGE("read format fail: %s(%d)\n", strerror(errno), errno);
		}
		else
		{
			//除以1000,并转成浮点数
			acc->evt.x = (float)(x)/1000;
			acc->evt.y = (float)(y)/1000;
			acc->evt.z = (float)(z)/1000;
			err = 0;
			gsensor_statistic(acc);
			//返回gsensor数据
			//add sensor data to struct sp_ata_data for PC side
			return_data.gsensor.g_sensor_x = acc->evt.x;
			return_data.gsensor.g_sensor_y = acc->evt.y;
			return_data.gsensor.g_sensor_z = acc->evt.z;
			return_data.gsensor.accuracy = 3;
			
		}
	}
	return err;    
}

预知详情,可以去分析factory工厂测试的源码,看看具体实现,这里就简单的一笔带过了。

当然我们也可以采用:./pskyed/libs/em_emmc_comm/libhwm/include/libhwm.h这里面的读取gsensor数据的接口,如果要用就需要在Android.mk中包含相关的动态库。

使用adb进入Android根文件系统,此时,通过cat命令可以得知/sys/bus/platform/drivers/gsensor/sensordata这个文件存放的数据并不是字符串,而是十六进制数据,以下是机器平放在桌子上读取的x,y,z的数值:

Sensordata文件的数值:    算法转换:

-------------------------------------------------------------------------------------------------------------------------------

X:0000                                    转十进制(float)0/1000    =  0

Y:0072                                    转十进制(float)114/ 1000  = 0.114

Z:264f                                    转十进制(float)9807/ 1000 = 9.807

-------------------------------------------------------------------------------------------------------------------------------

x,y,z数值的校准范围是(0,0,9.8),以上数值说明机器校准后的数值是正确的,误差比较小。

在Window上用Notepad++打开这个文件看到里面的数据如下。

使用Winhex查看sensordata这个文件

以下是我实现的方法:

//Gsensor读取

//这个文件存放的数据不是字符串,而是十六进制数据,所以必须做进制转换,然后才能转成浮点数

#define	GSENSOR_NAME "/sys/bus/platform/drivers/gsensor/sensordata"
//存储gsensor节点数据的结构体
struct gsensor_info
{
	//x,y,z坐标值
	char x[10] ;
	char y[10] ;
	char z[10] ;
};
int Hex_to_dec(const char* str)  ;
static int check_flag ;
int Gsensor_Test()
{
	struct gsensor_info g ;
	char ln[80];//用于存取读出数据的数组
	FILE *f;
	int i,n;
	char *p;
	int x,y,z ;
	float x1,y1,z1 ;
	int xv = 0 , yv = 0 , zv = 0 ;
	while(1)
	{
		f=fopen(GSENSOR_NAME,"r");//打开txt文件
		if(NULL==f){
			printf("Can not open file sensordata!\n");//判断是否可以打开文件
			return 1;
		}
		i=0;
		while (1)
		{
		   //从文件中读取一行数据
		   if (NULL==fgets(ln,80,f)) 
				break;
		   p = ln ; 
		   sscanf(p,"%s%s%s",g.x,g.y,g.z);
		   i++;
		}
		//存储的数据是十六进制,需要做数据转换
		x = Hex_to_dec(g.x);
		y = Hex_to_dec(g.y);
		z = Hex_to_dec(g.z);
		x1 = (float)x / 1000 ;
		y1 = (float)y / 1000 ;
		z1 = (float)z / 1000 ;
		printf("x: %4.2f   y:%4.2f  z:%4.2f\n",x1,y1,z1);
		break ;
	}
	return 0 ;
	while_r:
	while(1);
}
//16进制转10进制算法实现
int Hex_to_dec(const char* str)  
{  
    int value;  
  
    if (! str)  
    {  
        return 0;  
    }  
    value = 0;  
    while (1)  
    {  
        if ((*str >= '0') && (*str <= '9'))  
        {  
            value = value*16 + (*str - '0');  
        }  
        else if ((*str >= 'A') && (*str <= 'F'))  
        {  
            value = value*16 + (*str - 'A') + 10;  
        }  
        else if ((*str >= 'a') && (*str <= 'f'))  
        {  
            value = value*16 + (*str - 'a') + 10;  
        }  
        else  
        {  
            break;  
        }  
        str++;  
    }  
    return value;  
} 








作者:morixinguan 发表于2017/8/10 20:42:38 原文链接
阅读:65 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>