Library for PAT9125 on L476RG platform

Fork of pat9125_mbed by PixArt Imaging

Revision:
0:3459848530ee
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pat9125_mbed.cpp	Fri Oct 20 11:07:23 2017 +0000
@@ -0,0 +1,615 @@
+
+#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();
+}
\ No newline at end of file