Library for PAT9125 on L476RG platform

Fork of pat9125_mbed by PixArt Imaging

pat9125_mbed.cpp

Committer:
pixus_mbed
Date:
2018-02-01
Revision:
1:4b7c15d9c864
Parent:
0:3459848530ee

File content as of revision 1:4b7c15d9c864:


#include "pat9125_mbed.h"
#define delay(ms) wait_ms(ms)

//define OTS state - X
#define OTS_ROT_NO_CHANGE   0x00
#define OTS_ROT_UP          0x01
#define OTS_ROT_DOWN        0x02

//define downscale factor for rotation of shaft  
#define EXPECTED_COUNT_PER_ROUND    360
#define REAL_AVG_COUNT_PER_ROUND    446 //base on: sensor Reg0x0d=0x65, shaft diameter=2mm, sensor-to-shaft distance=2mm

//define OTS state - Y
#define OTS_BTN_NO_CHANGE   0x00
#define OTS_BTN_RELEASE     0x01
#define OTS_BTN_PRESS       0x02

#define LOW 0
#define HIGH 1
#define digitalRead(pin) *pin

static pat9125_mbed_state_s *gp_state ;

#define PIN_BTN_L       gp_state->pBTN_L
#define PIN_BTN_R       gp_state->pBTN_R
#define PIN_SEN_MOTION  gp_state->pINT

#define PIN_RLED        gp_state->pRLED
#define PIN_GLED        gp_state->pGLED
#define digitalWrite(pin,level) *pin =  level
#define LED_RED_ON      digitalWrite(PIN_RLED,LOW)
#define LED_RED_OFF     digitalWrite(PIN_RLED,HIGH)
#define LED_GREEN_ON    digitalWrite(PIN_GLED,LOW)
#define LED_GREEN_OFF   digitalWrite(PIN_GLED,HIGH)
#define attachInterrupt(pin,b,c)  //pin->enable_irq()
#define digitalPinToInterrupt(pin) pin
#define detachInterrupt(pin)  //pin->disable_irq()
//#define LCM_DisplayString_Reset gp_state->pLCM->LCM_DisplayString_Reset
//#define LCM_DisplayDecimal(a,b,c,d) gp_state->pLCM->LCM_DisplayDecimal(a,b,c,d)
//#define LCM_DisplayString(a,b,c) gp_state->pLCM->LCM_DisplayString(a,b,c)

#define I2C_RESET gp_state->p_i2c = gp_state->p_i2c->reset(); //workaround for nRF51 mbed


static void LCM_DisplayString(unsigned char line, unsigned char position, const char *ptr)
{
    if(gp_state->pLCM == NULL) return ;
    gp_state->pLCM->LCM_DisplayString(line, position, ptr);
}
static void LCM_DisplayDecimal(unsigned char line, unsigned char position, unsigned int hex_word, unsigned char digits)
{
    if(gp_state->pLCM == NULL) return ;
    gp_state->pLCM->LCM_DisplayDecimal(line, position, hex_word, digits) ;
}
static void LCM_DisplayString_Reset(void)
{
    if(gp_state->pLCM == NULL) return ;
    gp_state->pLCM->LCM_DisplayString_Reset();
}

unsigned char  xy2uart_enh=0;

//for OTS
signed int deltaX16;
signed int deltaY16;
unsigned char OTS_ROT_Status;
unsigned char OTS_BTN_Status;

signed long x_sum=0;
signed long ds_x_sum=0;
signed long pre_dsCountX=0;
unsigned int OTS_BTN_Press_Cnt=0;

volatile unsigned char MotionPinEventTriggered=0;

// Register write function
void OTS_Write_Reg(unsigned char address, unsigned char value) 
{
    int ret ;
    char data_write[2];
    
    data_write[0] = address;
    data_write[1] = value;
    ret = gp_state->p_i2c->write(gp_state->slave_id, data_write, 2, 0);
}

// Register Read function
unsigned char OTS_Read_Reg(unsigned char address)
{
    unsigned char rdata = 0;   
    gp_state->p_i2c->write(gp_state->slave_id, (char *)&address, 1, 0);
    gp_state->p_i2c->read(gp_state->slave_id, (char *)&rdata, 1, 0);

    return(rdata);  
}

// Register write & read back check function
void OTS_WriteRead_Reg(unsigned char address, unsigned char wdata) 
{
    unsigned char rdata;
    do
    {
      OTS_Write_Reg(address, wdata);    // Write data to specified address
      rdata = OTS_Read_Reg(address);    // Read back previous written data
    } while(rdata != wdata);            // Check if the data is correctly written
   
}

boolean OTS_Sensor_Init(void)
{
    unsigned char sensor_pid=0;
    boolean read_id_ok=false;
    
    // Read sensor_pid in address 0x00 to check if the serial link is valid, PID should be 0x31
    sensor_pid = OTS_Read_Reg(0x00);
    if(sensor_pid == 0x31)
    {   
        read_id_ok = true;

    //PAT9125 sensor recommended settings as below:     
        OTS_Write_Reg(0x7F, 0x00);  // switch to bank0, not allowed to perform OTS_WriteRead_Reg
        OTS_Write_Reg(0x06, 0x97); // Software Reset (i.e. set bit7 to 1), then it will reset to 0 automatically

        I2C_RESET;

        delay(1);    // delay 1ms
        OTS_Write_Reg(0x06, 0x17); // ensure the sensor has left the reset state.
    
        OTS_WriteRead_Reg(0x09, 0x5A);  // disable write protect
        OTS_WriteRead_Reg(0x0D, 0x65);  // set X-axis resolution (depends on application)
        OTS_WriteRead_Reg(0x0E, 0xFF);  // set Y-axis resolution (depends on application)
        OTS_WriteRead_Reg(0x19, 0x04);  // set 12-bit X/Y data format (depends on application)
        //OTS_WriteRead_Reg(0x4B, 0x04); // ONLY for VDD=VDDA=1.7~1.9V: for power saving    
    
        OTS_WriteRead_Reg(0x09, 0x00);  // enable write protect 
    }
    
    return read_id_ok;
} 

// Read motion
void OTS_Read_Motion(signed int *dx16, signed int *dy16) 
{
    int shift = (sizeof(signed int) << 3) - 12 ;
    signed int deltaX_l=0, deltaY_l=0, deltaXY_h=0; 
    signed int deltaX_h=0, deltaY_h=0;    
    char motion = OTS_Read_Reg(0x02) ;
    if(motion & 0x80)   //check motion bit in bit7
    {        
        deltaX_l = OTS_Read_Reg(0x03);
        deltaY_l = OTS_Read_Reg(0x04);                
        deltaXY_h = OTS_Read_Reg(0x12);                                     

        deltaX_h = (deltaXY_h<<4) & 0xF00;
        deltaX_h = (deltaX_h << shift) >> shift ;
        //if(deltaX_h & 0x800)    deltaX_h |= 0xfffff000; // 12-bit data convert to 16-bit 
        
        deltaY_h = (deltaXY_h<<8) & 0xF00;
        //if(deltaY_h & 0x800)    deltaY_h |= 0xfffff000; // 12-bit data convert to 16-bit
        deltaY_h = (deltaY_h << shift) >> shift ;
        
    }
    *dx16 = -(deltaX_h | deltaX_l);                 //inverse the data (depends on sensor's orientation and application)
    *dy16 = -(deltaY_h | deltaY_l);                 //inverse the data (depends on sensor's orientation and application) 
}

void OTS_Reset_Variables(void)
{
    //reset variables
    x_sum=0;
    ds_x_sum=0; 
    pre_dsCountX=0;
    
    OTS_BTN_Press_Cnt=0;
    LCM_DisplayString_Reset();
}

unsigned char Detect_Rotation(signed long dsCountX) 
{
    #define EVENT_NUM_PER_ROUND     360//10
    #define EVENT_COUNT_TH          (EXPECTED_COUNT_PER_ROUND / EVENT_NUM_PER_ROUND)    //360/10=36 //360/360=1

    signed long diff_count = 0;
    unsigned char OutRotState = OTS_ROT_NO_CHANGE;
    
    diff_count = dsCountX - pre_dsCountX;
    if( diff_count >= EVENT_COUNT_TH )
    {
        pre_dsCountX = dsCountX;
        OutRotState = OTS_ROT_UP;
    }
    else if( diff_count <= (-EVENT_COUNT_TH) )
    {
        pre_dsCountX = dsCountX;
        OutRotState = OTS_ROT_DOWN;
    }   
    
    return OutRotState;
}

signed long OTS_Resolution_Downscale(signed int delta_count) 
{       
    x_sum += delta_count;
    return (x_sum * EXPECTED_COUNT_PER_ROUND / REAL_AVG_COUNT_PER_ROUND);
}

unsigned char OTS_Detect_Rotation(signed int dx16, signed int dy16)
{   
    ds_x_sum = OTS_Resolution_Downscale(dx16);
    LCM_DisplayDecimal(1,12,ds_x_sum,4);//show downscale value
    
    return Detect_Rotation(ds_x_sum);
}

unsigned char OTS_Detect_Pressing(signed int dx16, signed int dy16)
{
    #define PRESS           1
    #define RELEASE         0
        
    #define DX_ROTATE_TH    2   
    #define DY_VALID_TH     1
    #define ACCY_PRESS_TH   5   
    #define DY_RELEASE_TH   (-2)        
    
    unsigned char OutBtnState = OTS_BTN_NO_CHANGE;
    static signed long AccY = 0;    
    static unsigned char State = RELEASE; //0:release, 1:press
    
    if((dx16 >= DX_ROTATE_TH)||(dx16 <= (-DX_ROTATE_TH)))
    {
        AccY = 0;   
    }
    else
    {                   
        if(State == PRESS) 
        {
            if(dy16 <= DY_RELEASE_TH)
            {       
                State = RELEASE;    
                OutBtnState = OTS_BTN_RELEASE;  
            }   
        }
        else 
        {
            if(dy16 < DY_VALID_TH)
            {
                AccY = 0;
            }
            else 
            {
                AccY += dy16;           
                if(AccY >= ACCY_PRESS_TH)
                {
                    AccY = 0;
                    State = PRESS;          
                    OutBtnState = OTS_BTN_PRESS;
                }               
            }
        }       
    }   
    
    return OutBtnState;
}

//-----------------------------------------------------------------------
void OTS_MotionPin_ISR(void) 
{
    detachInterrupt(digitalPinToInterrupt(PIN_SEN_MOTION));
    MotionPinEventTriggered=1;  
}

//-----------------------------------------------------------------------

/*
   SerialEvent occurs whenever a new data comes in the hardware serial RX.  This routine is run between each time loop() runs, so using delay inside loop can delay response.  
   Multiple bytes of data may be available.
  
   Host Command Supported:   
   Read and write sensor register
   Sensor read = "r aa", aa must be hex value, example "r 0f" means read register 0x0f.
   Sensor write = "w aa bb", aa and bb must be hex value, example "w 02 00" means write register 0x02 with value 0x00.
*/
#include <string>
#include <algorithm> 
#include <cctype>
#include <locale>

#define String string
#define VERSRION "2.40" //PAT9125 FW version


void serialProcess(String input);
bool IsParamValid(char *param, String str_pre, bool hex =false);
void PrintHex8(uint8_t data);
bool IsDigit(char *p, bool hex);
typedef unsigned char byte ;
String strRead;
char hexChar[3];  
  
void println()
{
    if(gp_state->p_pc == NULL) return ;
    gp_state->p_pc->printf("\n");
}

void println(String str)
{
    if(gp_state->p_pc == NULL) return ;
    const char * c = str.c_str();
    gp_state->p_pc->printf(c);
    gp_state->p_pc->printf("\n");
}
void print(String str)
{
    if(gp_state->p_pc == NULL) return ;
    const char * c = str.c_str();
    gp_state->p_pc->printf(c);
}
void print(signed int value)
{
    if(gp_state->p_pc == NULL) return ;
    gp_state->p_pc->printf("%d",value);
}


string trim(const string& src, const string& c)
{
    int p2 = src.find_last_not_of(c);
    if (p2 == string::npos) {
        return string();
    }
    int p1 = src.find_first_not_of(c);
    if (p1 == string::npos) {
        p1 = 0;
    }
 
    return src.substr(p1, (p2-p1)+1);
}

void toCharArray(char *buffer, string s)
{
    strcpy(buffer, s.c_str());
}

void serialEvent() 
{
  if(gp_state->p_pc == NULL) return ;
  while (gp_state->p_pc->readable())
  {
    // get the new byte:
    char inChar = (char)gp_state->p_pc->getc() ; //Serial.read();
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if ((inChar == '\n') || (inChar == '\r'))
    {
      //strRead.trim();
      strRead = trim(strRead, " ");
      strRead += " ";  // for tokenizing
      serialProcess(strRead);
      strRead = "";
    }
    else if (inChar != 0xf0)
    {  
    // add it to the inputString:
      strRead += inChar;
    }
  }
}

void serialProcess(String input)
{
  int val;
  int i, j;
  String output;  
  unsigned char Address,Value;
    
  // ====================================================
  //  Single command goes here
  //  NOTE: All command compare must have " " at the back
  // ====================================================
  if (input == "pxi ")
  {
    println("PixArt-PAT9125");
  }
  else if (input == "ver ")
  {
    println(VERSRION);
  }
  else if (input == "xy2uart_on ")
  {
    xy2uart_enh=1;
    println("Sensor XY Output Enable");
  } 
  else if (input == "xy2uart_off ")
  {
    xy2uart_enh=0;
    println("Sensor XY Output Disable");
  }   
  // ====================================================
  //  Command with parameters goes here, like r 00, w 48 00
  // ====================================================
  else if (input != " ")    // not empty string, then proceed to decode the command
  {
    char buff[50];
    //input.toCharArray(buff, input.length());
    toCharArray(buff, input);
    
    // Get the command
    char *tok = strtok(buff, " ");
    if (!tok)
    {
      println("empty command");
      return;
    }
    
    // Get parameters, up to 4 parameters, if more than that, individual command will need to extract it on their own
    char *param[4];
    for (val = 0; val < 4; val++)
    {
      param[val] = strtok(NULL, " ");
      if (!param[val])  // break if we reach the end
        break;
    }

    // switch for command
    String str = String(tok);
    if ((str == "r") || (str == "w"))
    {
      // Address is the first byte
      if (!IsParamValid(param[0], "Error (r/w): address"))
      {
        return;
      }
      // convert to integer
      Address = strtol(param[0], NULL, 16);
      if (Address > 0x7F)
      {
        println("Error (r/w): address > 0x7F");
        return;
      }
      
      if (str == "w")
      {
        // Value is second byte
        if (!IsParamValid(param[1], "Error (w): value"))
        {
          return;
        }
        // convert to integer
        Value = strtol(param[1], NULL, 16);
        if (Value > 0xFF)
        {
          println("Error (w): value > 0xFF");
          return;
        }
        
        OTS_Write_Reg(Address, Value);
      }
      
      print("ADDR ");
      PrintHex8(Address);
      print(" ");
      PrintHex8(OTS_Read_Reg(Address));
      println();
    }
    else
    {
      println("Bad command");
    }
  }
}

// Check if param is not given or not hexa, print the error as well
bool IsParamValid(char *param, String str_pre, bool hex)
{
  if (!param)
  {
    print(str_pre);
    println(" not given");
    return false;
  }
  if (!IsDigit(param, hex))
  {
    print(str_pre);
    if (hex)
      println(" not hex");
    else
      println(" not digit");
    return false;
  }
  return true;
}

// Check whether is hexa for given char
bool IsDigit(char *p, bool hex)
{
  for (int i; i < strlen(p); i++)
  {
    if (hex)
    {
      if (!isxdigit(p[i]))
        return false;
    }
    else if (!isdigit(p[i]))
        return false;
  }
  return true;
}

void Hex8(uint8_t data)
{
  byte first ;
  first = (data >> 4) | 48;
  if (first > 57) hexChar[0] = first + (byte)39;
  else hexChar[0] = first ;
  
  first = (data & 0x0F) | 48;
  if (first > 57) hexChar[1] = first + (byte)39;
  else hexChar[1] = first;
}

// Print in hex with leading zero
void PrintHex8(uint8_t data)
{
  Hex8(data);
  
  print(hexChar);
}


//-----------------------------------------------------------------------
void loop()
{
    
    if(digitalRead(PIN_BTN_L) == LOW)//or reset whenever idle_timer timeout
    {
        OTS_Reset_Variables();
    }
    
    if(MotionPinEventTriggered==1)      
    {
        MotionPinEventTriggered=0;//clear flag after read 'Motion Status and Data'
        OTS_Read_Motion(&deltaX16,&deltaY16);       
        
        if(deltaX16 || deltaY16)
        {
            OTS_ROT_Status = OTS_Detect_Rotation(deltaX16,deltaY16);
            OTS_BTN_Status = OTS_Detect_Pressing(deltaX16,deltaY16);
            
            if(OTS_ROT_Status == OTS_ROT_UP)        
            {
                LED_RED_ON; 
                LCM_DisplayString(1,8,"Up ");
            }
            else if(OTS_ROT_Status == OTS_ROT_DOWN)
            {
                LED_GREEN_ON;   
                LCM_DisplayString(1,8,"Dn ");
            }       
            
            if(OTS_BTN_Status == OTS_BTN_PRESS)
            {
                OTS_BTN_Press_Cnt++;
                if(OTS_BTN_Press_Cnt > 999) 
                {   
                    OTS_BTN_Press_Cnt=0;
                }
                LCM_DisplayString(2,8,"Pre");   
                LCM_DisplayDecimal(2,12,OTS_BTN_Press_Cnt,4);       
            }
            else if(OTS_BTN_Status == OTS_BTN_RELEASE)
            {
                LCM_DisplayString(2,8,"Rel");   
            }
        }
        
        if(xy2uart_enh == 1)
        {   
            //output to UART
            print("DX:\t");print(deltaX16);print("\t");
            print("DY:\t");print(deltaY16);print("\t");
            print("\n");
        }        

        //re-enable interrupt for MOTION pin
        attachInterrupt(digitalPinToInterrupt(PIN_SEN_MOTION), OTS_MotionPin_ISR, LOW);
        
    }

    if(digitalRead(PIN_SEN_MOTION) == HIGH) 
    {
        LED_GREEN_OFF;
        LED_RED_OFF;
    }   
      
}
pat9125_mbed::pat9125_mbed(pat9125_mbed_state_s *state)
{
    gp_state = state ;    
    gp_state->sen_status = OTS_Sensor_Init(); 
    print("OTS_Sensor_Init\n"); 
    gp_state->pINT->fall(&OTS_MotionPin_ISR); //interrupt at low state
}

void pat9125_mbed::task()
{
   if(digitalRead(PIN_SEN_MOTION) == LOW)
   {
        //gp_state->p_pc->printf("Motion Low\n"); 
        MotionPinEventTriggered = 1 ;
    }
    loop();
    serialEvent();
}