8452是一款G-Sensor芯片,采用I2C跟主芯片通讯,采用中断方式跟操作系统协作。通过内部检测XYZ三个方向的加速度,实现各种应用。
(1)原理框图如下:
现在来实现在WINCE中的I2C驱动,读写的时序波形图分别如下:
基础写函数实现如下:
static P_XLLP_OST_T ost_reg = 0;
static XLLP_I2C_T *i2c_reg = NULL;
static XLLP_CLKMGR_T *clk_reg = NULL; //在初始化中要映射
static int OS_I2CMasterWriteData(XLLP_UINT8_T slaveAddr, const XLLP_UINT8_T * bytesBuf, int bytesCount)
{
volatile int status;
XLLP_BOOL_T bSENDSTOP = XLLP_TRUE; //写完之后发停止位
status = XllpCustomI2CWrite((P_XLLP_I2C_T)(i2c_reg), (P_XLLP_OST_T)(ost_reg), slaveAddr, bytesBuf, bytesCount, bSENDSTOP,25);
return status;
}
static int MMA8452_WriteSensorReg( const XLLP_UINT8_T subAddress, XLLP_UINT8_T *bufP )
{
XLLP_UINT8_T buffer[2];
int status;
int lock;
buffer[0] = subAddress;
buffer[1] = *bufP;
gSensorSlaveAddr = 0x1c; //I2C地址
lock = __i2c_acquire_lock();
status = OS_I2CMasterWriteData( gSensorSlaveAddr, buffer, 2);
if (XLLP_STATUS_SUCCESS != status) {
RETAILMSG(1, (TEXT("Failed to write MMA8452_WriteSensorReg./r/n")));
}
__i2c_release_lock(lock);
return status;
}
基础读函数实现如下:
static int OS_I2CMasterWriteData_Read(XLLP_UINT8_T slaveAddr, const XLLP_UINT8_T * bytesBuf, int bytesCount)
{
volatile int status;
XLLP_BOOL_T bSENDSTOP = XLLP_FALSE; //写完后不发停止位
status = XllpCustomI2CWrite((P_XLLP_I2C_T)(i2c_reg), (P_XLLP_OST_T)(ost_reg), slaveAddr, bytesBuf, bytesCount, bSENDSTOP,25);
return status;
}
static int OS_I2CMasterReadData(XLLP_UINT8_T slaveAddr, XLLP_UINT8_T * bytesBuf, int bufLen)
{
volatile int status;
XLLP_BOOL_T bSENDSTOP = XLLP_TRUE; //读完后发停止位
status = XllpCustomI2CRead((P_XLLP_I2C_T)(i2c_reg), (P_XLLP_OST_T)(ost_reg), slaveAddr, bytesBuf, bufLen, bSENDSTOP,25);
return status;
}
static int MMA8452_ReadSensorReg( const XLLP_UINT8_T subAddress, XLLP_UINT8_T *bufP )
{
XLLP_UINT8_T buffer[1];
int status;
int lock;
buffer[0] = subAddress;
*bufP = 0x00;
gSensorSlaveAddr = 0x1c;
lock = __i2c_acquire_lock();
status = OS_I2CMasterWriteData_For_Read( gSensorSlaveAddr, buffer, 1); //写要读的子地址,注意没有停止位
if (XLLP_STATUS_SUCCESS == status)
{
status = OS_I2CMasterReadData( gSensorSlaveAddr, buffer, 1); //重写器件地址并读
*bufP = buffer[0]; //回传数据
}
else
{
RETAILMSG(1, (TEXT("Failed to MMA8452_ReadSensorReg./r/n")));
}
if (XLLP_STATUS_SUCCESS != status) {
RETAILMSG(1, (TEXT("Failed to MMA8452_ReadSensorReg./r/n")));
}
__i2c_release_lock(lock);
return status;
}
(2)唤醒功能的解析
在实际使用中,会用到g-sensor唤醒系统。一般有方向唤醒和点击唤醒两种。两者都是利用XYZ方向轴上的加速度变化,来中断操作系统。在配置睡眠唤醒的时候,一般有若干参数寄存器需要设置合适值。其中,双击唤醒的图示如下(从图中可以看出是默认低电平有效时是高电平):
对于8452,MMA8452_PULSE_THSX、MMA8452_PULSE_THSY、MMA8452_PULSE_THSZ这三个寄存器是用来设置加速度门限,值越大,需要敲击的力度也越大,对唤醒反应越迟钝。MMA8452_PULSE_TMLT是对第一次敲击的响应时间;MMA8452_PULSE_LTCY是第一次敲击后滤波去噪的延迟时间,该参数太小,会造成有可能把第一次敲击的杂波当作第二次敲击,该参数太大,会造成相隔很短的第二次敲击不会被识别;MMA8452_PULSE_WIND则是第二次敲击的识别时间区间,不在这个时间区间内的敲击不会被识别,以免造成误操作。
(3)关于layout的说明
使用图示如下:
参照上图的layout位置图,可以设置具体使用时的方向参数,最终只有一个值是正确的。注意:始终以正常使用手机的方位来看图,0-7的参数由于不同平台的软件不同,也可能意味着是从1-8。
举一个实例,一个四方向旋转的平板整机,当前方向值是1,平放时Z轴为-9.XX,说明Z轴反了,那么决定正确值的范围只能在(4、5、6、7)之间;以屏幕旋转的正确视图为准(X Y轴的指向,跟手机一样类似竖屏。但不以这个为准),发现右旋X是9.XX左旋X是-9.XX,是正确的;但是Y轴的视图上下反了,且从Y的读值看出来也是反的。综合以上,X轴不变Y轴反一下的图示只有5符合要求。从上上图的座标变换表格也可以看出:1对应的是(-y,x,z),把Y轴Z轴都倒的就是(y,x,-z),对应的方向值就是5。
(4)gsensor返回值的说明及gsensor校准
值域范围有正负数之分,正负是方向,以跟重力加速度对比来确定下来;值则以是否动态来说明。静止误差范围在300mg内算正常,也就是说<9.8-0.3,9.8+0.3>,超出该范围内说明GSENSOR的内部出厂校准参数出了偏差,可能原因是温度、运输、贴片所导致,该错误是不可逆的。
出现以上值超限的话,则需要对GSENSOR的工作过程进行校准,这个过程仅仅是对后期上报的数据进行修正,不可能再去纠正GSENSOR的内部属性。一般的过程是,平台放在一个平面上,分别得到GSENSOR的三个方向的校准offset,将其存入NVRAM中,以后再上报数据时读GSENSOR的读出值跟offset进行运算后再上报。由于GSENSOR的内部偏差是固定的,所以该补偿可用于任何工作状态的GSENSOR应用,此过程可采用若干次采样的平均值上报以减少误差。
需要注意的是,该校准仅仅是对出现偏差的现象进行校准,要么偏大要么偏小;如果某个时候GSENSOR读出的值的上限和下限均超出范围,应该考虑是否是其他原因(电压纹波,高采样率)导致的,此时使用offset偏差是解决不了问题的。
(5)Z轴补丁
8452的某些批次芯片本身存在质量问题。Z轴受敲击一旦出现超出范围问题之后达到20或者-20(超出-2g/2g),除非受其他敲击可能恢复的话,绝大部分时候是不会自动恢复的,这是芯片自身的问题,内部物理结构发生变化了。所以,可以采用在SENSOR HAL补丁方式解决这个问题,方法是Z轴出问题之后用XY模拟出Z轴的值,以让上层软件可以使用。以下的PATCH目前是可以保证平放时是9.8。同时芯片厂工程师说明该补丁的缺陷有两个:一是无法判断出手机是正放还是反放,提供的值只能是9.8没有-9.8;二是在手机动态时,模拟出的Z值是有偏差的。
补丁CODE如下:
#define ZCORRECTACTIVE 1 /* switch on /off the z stuck correction code */
#define ONEGCOUNTS 1024 /* 1024 1g counts for MMA8452 */
#define ZLOCKTHRESHOLD 2*ONEGCOUNTS*0.9 /* 10% below 2g stuck counts */
#define ZNORMALDIRECTION 1 /* define the sign of Z axis for normal screen face up operation, supposing the Z sign is positive here */
#define ZTIMEOUTCOUNTS 5 /* Z lock timeout counts, suppose sampling interval is 25Hz,40ms, 5 x 40ms=200ms for timeout delay*/
int zneg_out_counts = 0;
int zpos_out_counts = 0;
在SENSOR HAL的POLL函数内添加
if(sensors_data.data[i].sensor==0) //只针对gsensor处理
{
LOGD("%s:get sensor value,type: %d, value0 %d, value1 %d,value2 %d,updata %d!zhangcheng\r\n", __func__,
sensors_data.data[i].sensor, sensors_data.data[i].values[0], sensors_data.data[i].values[1],
sensors_data.data[i].values[2],sensors_data.data[i].update); //打印出当前读出的gsensor的原始值
xacc = sensors_data.data[i].values[0]*ONEGCOUNTS/9806;
yacc = sensors_data.data[i].values[1]*ONEGCOUNTS/9806;
zacc = sensors_data.data[i].values[2]*ONEGCOUNTS/9806; //转换,将原始重力加速度转换成g系数
if ((ZCORRECTACTIVE == 1))
{
if(zacc >= ZLOCKTHRESHOLD) //正向超限
{
if(zneg_out_counts == 0)
{
zpos_out_counts++;
if (zpos_out_counts >= ZTIMEOUTCOUNTS)
{
zpos_out_counts = ZTIMEOUTCOUNTS;
zacc = ZNORMALDIRECTION *sqrt(abs(ONEGCOUNTS*ONEGCOUNTS-xacc*xacc-yacc*yacc)); //用XY轴模拟Z轴
sensors_data.data[i].values[2] = zacc*9806/ONEGCOUNTS; //反转换后传给上层应用
}
}
else if(zneg_out_counts > 0)
{
zneg_out_counts = 0;
}
}
else if(zacc<= (-1)*ZLOCKTHRESHOLD) //反向超限
{
if ((zpos_out_counts == 0))
{
zneg_out_counts++;
if (zneg_out_counts >= ZTIMEOUTCOUNTS)
{
zneg_out_counts = ZTIMEOUTCOUNTS;
zacc = ZNORMALDIRECTION * sqrt(abs(ONEGCOUNTS*ONEGCOUNTS-xacc*xacc-yacc*yacc)); //用XY轴模拟Z轴
sensors_data.data[i].values[2] = zacc*9806/ONEGCOUNTS; //反转换后传给上层应用
}
}
else if(zpos_out_counts > 0)
{
zpos_out_counts = 0;
}
}
else
{
zpos_out_counts = 0;
zneg_out_counts = 0;
}
}
}
(6)GSENSOR跟陀螺仪的差别
陀螺仪能够测量沿一个轴或几个轴运动的角速度,是补充加速计功能的理想技术。如果组合使用加速计和陀螺仪这两种传感器,系统设计人员可以跟踪并捕捉三维空间的完整运动,为最终用户提供现场感更强的用户使用体验、精确的导航系统以及其它功能。
(7)GSENSOR游戏反应迟钝的分析
很多重力游戏比如摩托车/飞行器/枪击等游戏,依赖于GSENSOR的即时响应来操作,如果GSENSOR的响应不够及时,那么这游戏基本上是很难玩,极大影响用户体验。出现该问题的原因有两种:(1)如果GSENSOR是轮询工作的,轮询的频率很重要;(2)GSENSOR的采样频率,影响到即时响应。
曾经碰到过这样一个现象:手机断电后开机重力游戏正常,但是假关机再开机后重力游戏就响应非常慢。从上面两个可能点入手,通过TRACE可以确定上层对底层轮询的IOCTL的频率是正常的,这个可以通过内核TRACE的时间看出来,那么问题就出现在GSENSOR本身。后来分析出确实是采样频率被从60HZ设定成1HZ了,难怪上层响应这么慢,这个最直接的体现就是GSENSOR上报的是一大串相同的数据,而正常的时候GSENSOR上报的数据是一定范围跳动的。
(转载)