/*********************************************************************************************************************** MMBasic mouse.c Handles the a few miscellaneous functions for the MX470 version. Copyright 2016 - 2021 Peter Mather. All Rights Reserved. This file and modified versions of this file are supplied to specific individuals or organisations under the following provisions: - This file, or any files that comprise the MMBasic source (modified or not), may not be distributed or copied to any other person or organisation without written permission. - Object files (.o and .hex files) generated using this file (modified or not) may not be distributed or copied to any other person or organisation without written permission. - This file is provided in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ************************************************************************************************************************/ #include "MMBasic_Includes.h" #include "Hardware_Includes.h" char *mouse0Interruptc=NULL; volatile int mouse0foundc=0; bool mouse0=false; int mouseID=0; volatile int readreturn=-1; static volatile int PS2State, KCount, KParity, runmode=0; bool mouseupdated=false; volatile unsigned char Code = 0; int MOUSE_CLOCK,MOUSE_DATA; volatile short mouse[4]; volatile unsigned int bno=0; volatile unsigned char LastCode = 0; void setstream(void); void mouse_init(); static bool sendCommand(int cmd); int ReadReturn(int timeout); // definition of the mouse PS/2 state machine #define PS2START 0 #define PS2BIT 1 #define PS2PARITY 2 #define PS2STOP 3 #define PS2ERROR 9 #define MDATA 1 #define MCLK 0 #define HIGH 1 #define LOW 0 #define MouseTimeout 500 void MouseKBDIntEnable(int status){ if (status) { PinSetBit(MOUSE_CLOCK, TRISSET); // same for data PinSetBit(MOUSE_DATA, TRISSET); // data low if (!CallBackEnabled) { CallBackEnabled = 64; gpio_set_irq_enabled_with_callback(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, true, &gpio_callback); } else { CallBackEnabled |= 64; gpio_set_irq_enabled(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, true); } } else { PinSetBit(MOUSE_CLOCK, TRISSET); // same for data PinSetBit(MOUSE_DATA, TRISSET); // data low if (CallBackEnabled == 64){ CallBackEnabled=0; gpio_set_irq_enabled_with_callback(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, false, &gpio_callback); } else { gpio_set_irq_enabled(PinDef[MOUSE_CLOCK].GPno, GPIO_IRQ_EDGE_FALL, false); CallBackEnabled &= (~64); } PS2State = PS2START; } } void mousecheck(int n){ if(MouseTimer < MouseTimeout){ routinechecks(); return; } MouseKBDIntEnable(0); // disable interrupt in case called from within CNInterrupt() runmode=0; mouse0=false; ExtCfg(MOUSE_CLOCK, EXT_NOT_CONFIG, 0); ExtCfg(MOUSE_DATA, EXT_NOT_CONFIG, 0); PS2State = PS2START; error("Mouse timeout % ",n); } /*************************************************************************************************** initMouse Initialise the mouse routine. ****************************************************************************************************/ void initMouse0(int sensitivity) { if(!(MOUSE_CLOCK || Option.MOUSE_CLOCK))return; if(!Option.MOUSE_CLOCK){ ExtCfg(MOUSE_CLOCK, EXT_COM_RESERVED, 0); ExtCfg(MOUSE_DATA, EXT_COM_RESERVED, 0); } else { MOUSE_CLOCK=Option.MOUSE_CLOCK; MOUSE_DATA=Option.MOUSE_DATA; } gpio_init(PinDef[MOUSE_CLOCK].GPno); gpio_set_pulls(PinDef[MOUSE_CLOCK].GPno,true,false); gpio_set_dir(PinDef[MOUSE_CLOCK].GPno, GPIO_IN); gpio_set_input_hysteresis_enabled(PinDef[MOUSE_CLOCK].GPno,true); gpio_init(PinDef[MOUSE_DATA].GPno); gpio_set_pulls(PinDef[MOUSE_DATA].GPno,true,false); gpio_set_dir(PinDef[MOUSE_DATA].GPno, GPIO_IN); gpio_set_drive_strength(PinDef[MOUSE_DATA].GPno,GPIO_DRIVE_STRENGTH_8MA); gpio_set_drive_strength(PinDef[MOUSE_CLOCK].GPno,GPIO_DRIVE_STRENGTH_8MA); int maxW=HRes; int maxH=VRes; mouseID=0; runmode=0; MouseKBDIntEnable(0); // disable interrupt in case called from within CNInterrupt() // enable pullups on the clock and data lines. // This stops them from floating and generating random chars when no mouse is attached // reserve the mouse pins if(!sendCommand(0xFF))return; // Reset ReadReturn(500); sendCommand(0xF5); // Turn off streaming ReadReturn(5); if(sensitivity){ int scaling; if(sensitivity>4){ scaling=1; sensitivity-=4; } sensitivity--; if(scaling){ sendCommand(0xE7); // ReadReturn(5); } sendCommand(0xE8); ReadReturn(5); sendCommand(sensitivity); ReadReturn(5); } sendCommand(0xF3); // ReadReturn(5); sendCommand(200); // ReadReturn(5); sendCommand(0xF3); // ReadReturn(5); sendCommand(100); // ReadReturn(5); sendCommand(0xF3); // ReadReturn(5); sendCommand(80); // ReadReturn(5); sendCommand(0xF2); // mouseID=ReadReturn(10); sendCommand(0xF3); // ReadReturn(5); sendCommand(200); // ReadReturn(5); sendCommand(0xF4); // Turn on streaming ReadReturn(5); // setup Change Notification interrupt PS2State = PS2START; memset((struct s_nunstruct *)&nunstruct[2],0,sizeof(struct s_nunstruct)); nunstruct[2].classic[0]=mouseID; nunstruct[2].type=0; //used for the double click timer nunstruct[2].ax=maxW/2; nunstruct[2].ay=maxH/2; runmode=1; Code = 0; bno=0; LastCode = 0; // __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_3); MouseKBDIntEnable(1); // enable interrupt mouse0=true; } void mouse0close(void){ if(!mouse0)return; mouseID=0; runmode=0; sendCommand(0xFF); // Turn off streaming ReadReturn(5); MouseKBDIntEnable(0); // disable interrupt in case called from within CNInterrupt() ExtCfg(MOUSE_CLOCK, EXT_NOT_CONFIG, 0); ExtCfg(MOUSE_DATA, EXT_NOT_CONFIG, 0); mouse0=false; runmode=0; mouse0Interruptc=NULL; memset((struct s_nunstruct *)&nunstruct[2],0,sizeof(struct s_nunstruct)); } static bool sendCommand(int cmd) { int i, j; // calculate the parity and add to the command as the 9th bit for (j = i = 0; i < 8; i++) j += ((cmd >> i) & 1); cmd = (cmd & 0xff) | (((j + 1) & 1) << 8); PinSetBit(MOUSE_CLOCK, TRISCLR); PinSetBit(MOUSE_CLOCK, LATCLR); uSec(250); PinSetBit(MOUSE_DATA, TRISCLR); PinSetBit(MOUSE_DATA, LATCLR); PinSetBit(MOUSE_CLOCK, TRISSET); InkeyTimer = 0; uSec(2); while (PinRead(MOUSE_CLOCK)) if (InkeyTimer >= 500) { // wait for the keyboard to pull the clock low return false; // wait for the keyboard to pull the clock low } // send each bit including parity for (i = 0; i < 9; i++) { if (cmd & 1) { PinSetBit(MOUSE_DATA, LATSET); } else { PinSetBit(MOUSE_DATA, LATCLR); } while (!PinRead(MOUSE_CLOCK)) if (InkeyTimer >= 500) { // wait for the keyboard to pull the clock low return false; // wait for the keyboard to pull the clock low } while (PinRead(MOUSE_CLOCK)) if (InkeyTimer >= 500) { // wait for the keyboard to pull the clock low return false; // wait for the keyboard to pull the clock low } cmd >>= 1; } // PinSetBit(MOUSE_CLOCK, TRISSET); PinSetBit(MOUSE_DATA, TRISSET); while (PinRead(MOUSE_DATA)) if (InkeyTimer >= 500) { // wait for the keyboard to pull the clock low return false; // wait for the keyboard to pull the clock low } // wait for the keyboard to pull data low (ACK) while (PinRead(MOUSE_CLOCK)) if (InkeyTimer >= 500) { // wait for the keyboard to pull the clock low return false; // wait for the keyboard to pull the clock low } // wait for the clock to go low while (!(PinRead(MOUSE_CLOCK)) || !(PinRead(MOUSE_DATA))) if (InkeyTimer >= 500) { // wait for the keyboard to pull the clock low return false; // wait for the keyboard to pull the clock low } return true; } /*************************************************************************************************** sendCommand - Send a command to to mouse. ****************************************************************************************************/ int ReadReturn(int timeout){ int i; Code = 0; bno=0; LastCode = 0; readreturn=-1; MouseKBDIntEnable(1); i=100; MouseTimer = 0; while(MouseTimer>= 1; // shift in data bit if(d) Code |= 0x80; // PS2DAT == 1 KParity ^= Code; // calculate parity if (--KCount <= 0) PS2State = PS2PARITY; // all bit read break; case PS2PARITY: if(d) KParity ^= 0x80; // PS2DAT == 1 if (KParity & 0x80) { // parity odd, continue PS2State = PS2STOP; } else { PS2State = PS2ERROR; } break; case PS2STOP: if(d) { // PS2DAT == 1 readreturn=Code; if(runmode){ mouse[bno++]=Code; if(!(mouse[0] & 0x08))bno=0;//bit 3 must be set in first byte if(bno==(mouseID==3 ? 4: 3)){ bno=0; if(mouse[0] & 0b10000)mouse[1] |=0xFF00; if(mouse[0] & 0b100000)mouse[2] |=0xFF00; nunstruct[2].ax+=mouse[1]; if(nunstruct[2].ax<0)nunstruct[2].ax=0; if(nunstruct[2].ax>=maxW)nunstruct[2].ax=maxW-1; nunstruct[2].ay-=mouse[2]; mouseupdated=1; if(nunstruct[2].ay<0)nunstruct[2].ay=0; if(nunstruct[2].ay>=maxH)nunstruct[2].ay=maxH-1; nunstruct[2].L = mouse[0] & 0b1; nunstruct[2].R=(mouse[0] & 0b10)>>1; nunstruct[2].C=(mouse[0] & 0b100)>>2; if(nunstruct[2].type>1000) nunstruct[2].Z=0; if((mouse[0] & 3) != (LastCode & 3)){ if((mouse[0] & 1) && !(LastCode & 1) && (mSecTimer-lefttimer>16)){ //left button press nunfoundc[2]=1; if(nunstruct[2].type>=500 || nunstruct[2].type<100) nunstruct[2].type=0; else { nunstruct[2].Z=1; nunstruct[2].type=500 ; } lefttimer=mSecTimer; } if(!(mouse[0] & 1) && (LastCode & 1)){ //left button release } if((mouse[0] & 2) && !(LastCode & 2) && (mSecTimer-righttimer>16)){ righttimer=mSecTimer; } } LastCode=mouse[0]; if(mouseID==3){ if(mouse[3] & 0x80)mouse[3]|=0xFF00; nunstruct[2].az=(volatile int)nunstruct[2].az+mouse[3]; } nunstruct[2].x1=nunstruct[2].ax/(FontTable[gui_font >> 4][0] * (gui_font & 0b1111)); nunstruct[2].y1=nunstruct[2].ay/(FontTable[gui_font >> 4][1] * (gui_font & 0b1111)); } } } PS2State = PS2START; break; } } } void cmd_mouse(void){ unsigned char *tp=NULL; int n; if((tp=checkstring(cmdline, (unsigned char *)"OPEN"))){ getargs(&tp,7,(unsigned char *)","); // if(Option.MOUSE_CLOCK)error("Already open"); if(!(argc==5))error("Syntax"); getint(argv[0],2,2); char code; if(!(code=codecheck(argv[2])))argv[2]+=2; int pin1 = getinteger(argv[2]); if(!code)pin1=codemap(pin1); if(!(code=codecheck(argv[4])))argv[4]+=2; int pin2 = getinteger(argv[4]); if(!code)pin2=codemap(pin2); if(IsInvalidPin(pin1)) error("Invalid pin %/|",pin1,pin1); if(IsInvalidPin(pin2)) error("Invalid pin %/|",pin2,pin2); if(!(pin1==Option.MOUSE_CLOCK && pin2==Option.MOUSE_DATA)){ if(ExtCurrentConfig[pin1] >= EXT_COM_RESERVED ) error("Pin %/| is in use",pin1,pin1); if(ExtCurrentConfig[pin2] >= EXT_COM_RESERVED ) error("Pin %/| is in use",pin2,pin2); } if(Option.MOUSE_CLOCK && !(pin1==Option.MOUSE_CLOCK && pin2==Option.MOUSE_DATA))error("OPTION MOUSE declared with different pins"); MOUSE_CLOCK=pin1; MOUSE_DATA=pin2; if(!mouse0)initMouse0(0); if(!mouse0){ if(!Option.MOUSE_CLOCK){ ExtCfg(MOUSE_CLOCK, EXT_NOT_CONFIG, 0); ExtCfg(MOUSE_DATA, EXT_NOT_CONFIG, 0); } MOUSE_CLOCK=0; MOUSE_DATA=0; error("Open failed"); } } else if((tp=checkstring(cmdline, (unsigned char *)"CLOSE"))){ getargs(&tp,1,(unsigned char *)","); if(!mouse0)error("Not open"); if(Option.MOUSE_CLOCK)error("Option MOUSE set - close invalid"); n=getint(argv[0],2,2); mouse0close(); } else if((tp=checkstring(cmdline,(unsigned char *)"INTERRUPT ENABLE"))){ getargs(&tp,3,(unsigned char *)","); if(!mouse0)error("Not open"); if(!(argc==3))error("Syntax"); n=getint(argv[0],2,2); nunInterruptc[n] = (char *)GetIntAddress(argv[2]); // get the interrupt location InterruptUsed = true; return; } else if((tp = checkstring(cmdline, (unsigned char *)"SET"))){ getargs(&tp,7,(unsigned char *)","); if(!mouse0)error("Not open"); if(!(argc==7))error("Syntax"); n=getint(argv[0],2,2); nunstruct[n].ax=getint(argv[2],-HRes,HRes); nunstruct[n].ay=getint(argv[4],-VRes,VRes); nunstruct[n].az=getint(argv[6],-1000000,1000000); } else if((tp = checkstring(cmdline, (unsigned char *)"INTERRUPT DISABLE"))){ getargs(&tp,1,(unsigned char *)","); if(!mouse0)error("Not open"); n=getint(argv[0],2,2); nunInterruptc[n]=NULL; } else error("Syntax"); }