Library for PAT9125 on L476RG platform
Fork of pat9125_mbed by
Diff: pat9125_mbed.cpp
- 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