1. 程式人生 > >G-Sensor 8452驅動及相關

G-Sensor 8452驅動及相關

      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則是第二次敲擊的識別時間區間,不在這個時間區間內的敲擊不會被識別,以免造成誤操作。

       說到中斷產生,一般gsensor有兩個種類,一種如上的方向喚醒和tap/shake喚醒,是gsensor的閥值達到一定程度後才發中斷;另一種是sample event,設定取樣速率後,每次取樣都會有中斷訊號產生。可用在不同的場合。

 (3)關於layout的說明

                                  

使用圖示如下:

參照上圖的layout位置圖,可以設定具體使用時的方向引數,最終只有一個值是正確的。另一方面,使用時發現gsensor相容時,不同的gsensor的方向值也不同,之前以為相同

        舉一個例項,一個四方向旋轉的平板整機,當前方向值是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。

同理,如果方向值為0時,Z軸反向,XY對調,推理應改成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上報的資料是一定範圍跳動的。

(8)關於gsensor的raw值

       使用sensorhub時,它會把掛接的gsensor的raw值通過Input往上報,這點可以通過getevent -lt來檢視,比如平方時:

/dev/input/event4: EV_ABS       ABS_Z                000001df 

這個值跟重力加速度g是什麼關係呢?首先對一顆gsensor,要確定它的量程(-2g-+2g/-4g-+4g/-8g-+8g);第二是它的精度位數,比如8bit/12bit/16bit。上述的例子是一顆12位+-4g的gsensor的平放時的z軸值,那麼它的每一位表示的g值是8g/2的12次方,0.001953125g。所以0x1df轉成十進位制是479,兩者相乘就是0.9355g,接近於Z軸平方時的g。

       對於有的文件寫的“G-sensor 在靜止時,±2G 下最大值約±16384,±4G 下最大值約±8192,±8G 下最大值約±4096”,是如何得到的?12位的479左移4位變成16位,即是7664,接近於8192。