mirror of
https://github.com/clockworkpi/PicoCalc.git
synced 2025-12-12 18:28:53 +01:00
448 lines
17 KiB
C
448 lines
17 KiB
C
/***********************************************************************************************************************
|
|
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<timeout){
|
|
if(readreturn!=-1){
|
|
i=readreturn;
|
|
readreturn=-1;
|
|
routinechecks();
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
/***************************************************************************************************
|
|
change notification interrupt service routine
|
|
****************************************************************************************************/
|
|
void __not_in_flash_func(MNInterrupt)(uint64_t dd) {
|
|
static unsigned long long int lefttimer=0, righttimer=0;
|
|
int maxW=HRes;
|
|
int maxH=VRes;
|
|
int d = dd & (1<<PinDef[MOUSE_DATA].GPno);
|
|
|
|
// Make sure it was a falling edge
|
|
if (!(dd & (1<<PinDef[MOUSE_CLOCK].GPno)))
|
|
{
|
|
if(!Timer5)PS2State=PS2START;
|
|
// Sample the data
|
|
switch(PS2State){
|
|
default:
|
|
case PS2ERROR: // this can happen if a timing or parity error occurs
|
|
// fall through to PS2START
|
|
|
|
case PS2START:
|
|
if(!d) { // PS2DAT == 0
|
|
KCount = 8; // init bit counter
|
|
KParity = 0; // init parity check
|
|
Code = 0;
|
|
PS2State = PS2BIT;
|
|
Timer5=5;
|
|
}
|
|
break;
|
|
|
|
case PS2BIT:
|
|
Code >>= 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");
|
|
}
|
|
|