From b53a03049f4f3fff2ad10742573fe0862ceaeec5 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Fri, 7 Jun 2024 16:11:58 +0200 Subject: [PATCH 01/12] Direct port access for streaming mode and general speed ups. Implmented wheel mouse mode. Deprecated streaming mode toggle via jumper (wheel mode replaces this) --- firmware/ps2adapter/.gitignore | 2 + firmware/ps2adapter/ProMini.h | 70 +++++++++++++++ firmware/ps2adapter/Ps2Mouse.cpp | 137 +++++++++++++++++++++-------- firmware/ps2adapter/Ps2Mouse.h | 19 ++-- firmware/ps2adapter/platformio.ini | 18 ++++ firmware/ps2adapter/ps2adapter.ino | 68 ++++++++------ 6 files changed, 246 insertions(+), 68 deletions(-) create mode 100644 firmware/ps2adapter/.gitignore create mode 100644 firmware/ps2adapter/ProMini.h create mode 100644 firmware/ps2adapter/platformio.ini diff --git a/firmware/ps2adapter/.gitignore b/firmware/ps2adapter/.gitignore new file mode 100644 index 0000000..b9f3806 --- /dev/null +++ b/firmware/ps2adapter/.gitignore @@ -0,0 +1,2 @@ +.pio +.vscode diff --git a/firmware/ps2adapter/ProMini.h b/firmware/ps2adapter/ProMini.h new file mode 100644 index 0000000..ad0dda9 --- /dev/null +++ b/firmware/ps2adapter/ProMini.h @@ -0,0 +1,70 @@ +#pragma once +/* + * Created by Jason Hill + * 2023/06/18 + * + * Updated by Peter Edwards + * 2024/01/02 + * + */ + +/**************************************************** + * The following defines assume default pinout + * of Arduino Pro Mini or similiar: + * + * PS2_CLOCK = 2; //PD2 - Port D, bit 2 + * PS2_DATA = 17;//PC3 - Port C, bit 3 + * + * RS232_RTS = 3; //PD3 - Port D, bit 3 + * RS232_TX = 4; //PD4 - Port D, bit 4 + * + * JP12 = 11; //PB3 - Port B, bit 3 + * JP24 = 12; //PB4 - Port B, bit 4 + * LED = 13; //PB5 - Port B, bit 5 + * + * The following defines Pin and Port addressing to + * more quickly set individual pins to their desired + * states. + *PS/2 data pin operations***Bit:76543210************/ +#define PS2_SETDATAHIGH (PORTC |=0b00001000) +#define PS2_SETDATALOW (PORTC &=0b11110111) +#define PS2_SETDATA(val) val ? PS2_SETDATAHIGH : PS2_SETDATALOW +#define PS2_DIRDATAIN (DDRC &=0b11110111) +#define PS2_DIRDATAIN_UP (DDRC &=0b11110111); PS2_SETDATAHIGH +#define PS2_DIRDATAOUT (DDRC |=0b00001000) +#define PS2_READDATA ((PINC &=0b00001000)>>3) + +/*PS/2 clock pin operations**Bit:76543210************/ +#define PS2_SETCLOCKHIGH (PORTD |=0b00000100) +#define PS2_SETCLOCKLOW (PORTD &=0b11111011) +#define PS2_SETCLOCK(val) val ? PS2_SETCLOCKHIGH : PS2_SETCLOCKLOW +#define PS2_DIRCLOCKIN (DDRD &=0b11111011) +#define PS2_DIRCLOCKIN_UP (DDRD &=0b11111011); PS2_SETCLOCKHIGH +#define PS2_DIRCLOCKOUT (DDRD |=0b00000100) +#define PS2_READCLOCK ((PIND &=0b00000100)>>2) + +/*RS-232 pin operations******Bit:76543210************/ +#define RS_SETTXHIGH (PORTD |=0b00010000) +#define RS_SETTXLOW (PORTD &=0b11101111) +#define RS_SETTX(val) val ? RS_SETTXHIGH : RS_SETTXLOW +#define RS_DIRTXOUT (DDRD |=0b00010000) +/****************************************************/ + +/*JUMPER pin operations******Bit:76543210************/ +#define JP12_SETHIGH (PORTB |=0b00001000) +#define JP12_DIRIN (DDRB &=0b11110111) +#define JP12_DIRIN_UP (DDRB &=0b11110111); JP12_SETHIGH +#define JP12_READ ((PINB &=0b00001000)>>3) + +#define JP34_SETHIGH (PORTB |=0b00010000) +#define JP34_DIRIN (DDRB &=0b11101111) +#define JP34_DIRIN_UP (DDRB &=0b11101111); JP34_SETHIGH +#define JP34_READ ((PINB &=0b00010000)>>4) +/****************************************************/ + +/*LED pin operations*********Bit:76543210************/ +#define LED_SETHIGH (PORTB |=0b00100000) +#define LED_SETLOW (PORTB &=0b11011111) +#define LED_SET(val) val ? LED_SETHIGH : LED_SETLOW +#define LED_DIROUT (DDRB |=0b00100000) +/****************************************************/ \ No newline at end of file diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index a5b97ac..0c23278 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -1,3 +1,4 @@ +#include "ProMini.h" #include "Ps2Mouse.h" namespace { @@ -30,6 +31,7 @@ struct Packet { byte xMovement; byte yMovement; + byte wheelData; }; enum class Command { @@ -52,6 +54,7 @@ enum class Command { enum class Response { isMouse = 0x00, + isWheelMouse = 0x03, selfTestPassed = 0xAA, ack = 0xFA, error = 0xFC, @@ -64,29 +67,29 @@ struct Ps2Mouse::Impl { const Ps2Mouse& m_ref; void sendBit(int value) const { - while (digitalRead(m_ref.m_clockPin) != LOW) {} - digitalWrite(m_ref.m_dataPin, value); - while (digitalRead(m_ref.m_clockPin) != HIGH) {} + while (PS2_READCLOCK != LOW) {} + PS2_SETDATA(value); + while (PS2_READCLOCK != HIGH) {} } int recvBit() const { - while (digitalRead(m_ref.m_clockPin) != LOW) {} - auto result = digitalRead(m_ref.m_dataPin); - while (digitalRead(m_ref.m_clockPin) != HIGH) {} + while (PS2_READCLOCK != LOW) {} + auto result = PS2_READDATA; + while (PS2_READCLOCK != HIGH) {} return result; } bool sendByte(byte value) const { // Inhibit communication - pinMode(m_ref.m_clockPin, OUTPUT); - digitalWrite(m_ref.m_clockPin, LOW); + PS2_DIRCLOCKOUT; + PS2_SETCLOCKLOW; delayMicroseconds(10); // Set start bit and release the clock - pinMode(m_ref.m_dataPin, OUTPUT); - digitalWrite(m_ref.m_dataPin, LOW); - pinMode(m_ref.m_clockPin, INPUT_PULLUP); + PS2_DIRDATAOUT; + PS2_SETDATALOW; + PS2_DIRCLOCKIN_UP; // Send data bits byte parity = 1; @@ -103,15 +106,15 @@ struct Ps2Mouse::Impl { sendBit(1); // Enter receive mode and wait for ACK bit - pinMode(m_ref.m_dataPin, INPUT); + PS2_DIRDATAIN; return recvBit() == 0; } bool recvByte(byte& value) const { // Enter receive mode - pinMode(m_ref.m_clockPin, INPUT); - pinMode(m_ref.m_dataPin, INPUT); + PS2_DIRCLOCKIN; + PS2_DIRDATAIN; // Receive start bit if (recvBit() != 0) { @@ -147,11 +150,9 @@ struct Ps2Mouse::Impl { return true; } - template - bool recvData(T& data) const { - auto ptr = reinterpret_cast(&data); - for (auto i = 0u; i < sizeof(data); i++) { - if (!recvByte(ptr[i])) { + bool recvData(byte* data, size_t size) const { + for (size_t i = 0u; i < size; i++) { + if (!recvByte(data[i])) { return false; } } @@ -182,15 +183,15 @@ struct Ps2Mouse::Impl { } bool getStatus(Status& status) const { - return sendCommand(Command::statusRequest) && recvData(status); + return sendCommand(Command::statusRequest) && recvData((byte*) &status, sizeof(Status)); } }; -Ps2Mouse::Ps2Mouse(int clockPin, int dataPin) - : m_clockPin(clockPin), m_dataPin(dataPin), m_stream(false) +Ps2Mouse::Ps2Mouse() + : m_stream(false), m_wheelMouse(false) {} -bool Ps2Mouse::reset() const { +bool Ps2Mouse::reset(bool streaming) { Impl impl{*this}; if (!impl.sendCommand(Command::reset)) { return false; @@ -205,32 +206,91 @@ bool Ps2Mouse::reset() const { return false; } + // Determine if this is a wheel mouse + if (!impl.sendCommand(Command::setSampleRate, 200) || + !impl.sendCommand(Command::setSampleRate, 100) || + !impl.sendCommand(Command::setSampleRate, 80) || + !impl.sendCommand(Command::getDeviceId) || + !impl.recvByte(reply)) { + return false; + } + + m_wheelMouse = (reply == byte(Response::isWheelMouse)); + + if (streaming) { + // Streaming mode is the default after a reset. + m_stream = true; + // switch on data reporting + return impl.sendCommand(Command::enableDataReporting); + } + return disableStreaming() && impl.sendCommand(Command::enableDataReporting); } -bool Ps2Mouse::enableStreaming() const { - return Impl{*this}.sendCommand(Command::setStreamMode); +bool Ps2Mouse::enableStreaming() { + if (Impl{*this}.sendCommand(Command::setStreamMode)) { + m_stream = true; + return true; + } + return false; } -bool Ps2Mouse::disableStreaming() const { - return Impl{*this}.sendCommand(Command::setRemoteMode); +bool Ps2Mouse::disableStreaming() { + if (Impl{*this}.sendCommand(Command::setRemoteMode)) { + m_stream = false; + return true; + } + return false; } bool Ps2Mouse::setScaling(bool flag) const { - return Impl{*this}.sendCommand(flag ? Command::enableScaling : Command::disableScaling); + if (m_stream) { + Impl{*this}.sendCommand(Command::disableDataReporting); + } + bool res = Impl{*this}.sendCommand(flag ? Command::enableScaling : Command::disableScaling); + if (m_stream) { + Impl{*this}.sendCommand(Command::enableDataReporting); + } + return res; } bool Ps2Mouse::setResolution(byte resolution) const { - return Impl{*this}.sendCommand(Command::setResolution, resolution); + if (m_stream) { + Impl{*this}.sendCommand(Command::disableDataReporting); + } + bool res = Impl{*this}.sendCommand(Command::setResolution, resolution); + if (m_stream) { + Impl{*this}.sendCommand(Command::enableDataReporting); + } + return res; } bool Ps2Mouse::setSampleRate(byte sampleRate) const { - return Impl{*this}.sendCommand(Command::setSampleRate, sampleRate); + if (m_stream) { + Impl{*this}.sendCommand(Command::disableDataReporting); + } + bool res = Impl{*this}.sendCommand(Command::setSampleRate, sampleRate); + if (m_stream) { + Impl{*this}.sendCommand(Command::enableDataReporting); + } + return res; } bool Ps2Mouse::getSettings(Settings& settings) const { Status status; - if (Impl{*this}.getStatus(status)) { + if (m_stream) { + Impl{*this}.sendCommand(Command::disableDataReporting); + } + bool res = Impl{*this}.getStatus(status); + if (m_stream) { + Impl{*this}.sendCommand(Command::enableDataReporting); + } + if (res) { + settings.rightBtn = status.rightButton; + settings.middleBtn = status.middleButton; + settings.leftBtn = status.leftButton; + settings.remoteMode = status.remoteMode; + settings.enable = status.dataReporting; settings.scaling = status.scaling; settings.resolution = status.resolution; settings.sampleRate = status.sampleRate; @@ -244,7 +304,7 @@ bool Ps2Mouse::readData(Data& data) const { Impl impl{*this}; if (m_stream) { - if (digitalRead(m_clockPin) != LOW) { + if (PS2_READCLOCK != LOW) { return false; } } @@ -252,9 +312,15 @@ bool Ps2Mouse::readData(Data& data) const { return false; } - Packet packet; - if (!impl.recvData(packet)) { - return false; + Packet packet = {0}; + if (m_wheelMouse) { + if (!impl.recvData((byte*)&packet, sizeof(packet))) { + return false; + } + } else { + if (!impl.recvData((byte*)&packet, sizeof(packet) - 1)) { + return false; + } } data.leftButton = packet.leftButton; @@ -262,5 +328,6 @@ bool Ps2Mouse::readData(Data& data) const { data.rightButton = packet.rightButton; data.xMovement = (packet.xSign ? -0x100 : 0) | packet.xMovement; data.yMovement = (packet.ySign ? -0x100 : 0) | packet.yMovement; + data.wheelMovement = m_wheelMouse ? packet.wheelData : 0; return true; } diff --git a/firmware/ps2adapter/Ps2Mouse.h b/firmware/ps2adapter/Ps2Mouse.h index 2bd55d2..2634173 100644 --- a/firmware/ps2adapter/Ps2Mouse.h +++ b/firmware/ps2adapter/Ps2Mouse.h @@ -11,17 +11,23 @@ class Ps2Mouse { bool rightButton; int xMovement; int yMovement; + int wheelMovement; }; struct Settings { + bool rightBtn; + bool middleBtn; + bool leftBtn; bool scaling; + bool enable; + bool remoteMode; byte resolution; byte sampleRate; }; - - Ps2Mouse(int clockPin, int dataPin); - bool reset() const; + Ps2Mouse(); + + bool reset(bool streaming); bool setScaling(bool flag) const; bool setResolution(byte resolution) const; @@ -29,14 +35,13 @@ class Ps2Mouse { bool getSettings(Settings& settings) const; - bool enableStreaming() const; - bool disableStreaming() const; + bool enableStreaming(); + bool disableStreaming(); bool readData(Data& data) const; private: struct Impl; - int m_clockPin; - int m_dataPin; bool m_stream; + bool m_wheelMouse; }; diff --git a/firmware/ps2adapter/platformio.ini b/firmware/ps2adapter/platformio.ini new file mode 100644 index 0000000..5228a98 --- /dev/null +++ b/firmware/ps2adapter/platformio.ini @@ -0,0 +1,18 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html +[platformio] +src_dir = . + +[env:pro16MHzatmega328] +platform = atmelavr +board = pro16MHzatmega328 +framework = arduino +monitor_speed = 115200 + diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index 79e0a41..19b84d3 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -1,20 +1,16 @@ +#include "ProMini.h" #include "Ps2Mouse.h" -static const int PS2_CLOCK = 2; -static const int PS2_DATA = 17; static const int RS232_RTS = 3; -static const int RS232_TX = 4; -static const int JP12 = 11; -static const int JP34 = 12; -static const int LED = 13; -static Ps2Mouse mouse(PS2_CLOCK, PS2_DATA); +static Ps2Mouse mouse; static bool threeButtons = false; +static bool wheelMouse = false; static void sendSerialBit(int data) { // Delay between the signals to match 1200 baud static const auto usDelay = 1000000 / 1200; - digitalWrite(RS232_TX, data); + RS_SETTX(data); delayMicroseconds(usDelay); } @@ -46,19 +42,29 @@ static void sendToSerial(const Ps2Mouse::Data& data) { sendSerialByte(dx & 0x3F); sendSerialByte(dy & 0x3F); if (threeButtons) { - byte mb = data.middleButton ? 0x20 : 0; + byte mb; + if (wheelMouse) { + mb = data.middleButton ? 0x10 : 0; + mb |= (data.wheelMovement & 0x0F); + } else { + mb = data.middleButton ? 0x20 : 0; + } sendSerialByte(mb); } } static void initSerialPort() { Serial.println("Starting serial port"); - digitalWrite(RS232_TX, HIGH); + RS_SETTXHIGH; delayMicroseconds(10000); sendSerialByte('M'); if(threeButtons) { - sendSerialByte('3'); - Serial.println("Init 3-buttons mode"); + if(wheelMouse) { + sendSerialByte('Z'); + } else { + sendSerialByte('3'); + } + Serial.println(wheelMouse ? "Init Wheel mode" : "Init 3-button mode"); } delayMicroseconds(10000); @@ -69,8 +75,18 @@ static void initSerialPort() { static void initPs2Port() { Serial.println("Reseting PS/2 mouse"); - mouse.reset(); - mouse.setSampleRate(20); + + if (mouse.reset(true)) { + Serial.println("PS/2 mouse reset OK"); + } else { + Serial.println("Failed to reset PS/2 mouse"); + } + + if (mouse.setSampleRate(20)) { + Serial.println("Sample rate set to 20"); + } else { + Serial.println("Failed to set sample rate"); + } Ps2Mouse::Settings settings; if (mouse.getSettings(settings)) { @@ -80,30 +96,30 @@ static void initPs2Port() { Serial.println(settings.resolution); Serial.print("samplingRate = "); Serial.println(settings.sampleRate); + } else { + Serial.println("Failed to get settings"); } } void setup() { // PS/2 Data input must be initialized shortly after power on, // or the mouse will not initialize - pinMode(PS2_DATA, INPUT_PULLUP); - pinMode(RS232_TX, OUTPUT); - pinMode(JP12, INPUT_PULLUP); - pinMode(JP34, INPUT_PULLUP); - pinMode(LED, OUTPUT); - digitalWrite(LED, HIGH); - threeButtons = digitalRead(JP12); + PS2_DIRDATAIN_UP; + RS_DIRTXOUT; + JP12_DIRIN_UP; + JP34_DIRIN_UP; + LED_DIROUT; + LED_SET(HIGH); + threeButtons = JP12_READ; + wheelMouse = (JP34_READ == LOW); Serial.begin(115200); initSerialPort(); initPs2Port(); Serial.println("Setup done!"); - digitalWrite(LED, LOW); - if (digitalRead(JP34) == LOW) { - Serial.println("Enabling streaming mode"); - mouse.enableStreaming(); - } + LED_SETLOW; } + void loop() { Ps2Mouse::Data data; if (mouse.readData(data)) { From acd29c54b65806b5d9471b1ca02d09d581374ccb Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Fri, 7 Jun 2024 18:14:48 +0200 Subject: [PATCH 02/12] added kvm hack --- README.md | 22 ++++++++-------------- firmware/ps2adapter/Ps2Mouse.cpp | 24 ++++++++++++++---------- firmware/ps2adapter/Ps2Mouse.h | 2 +- firmware/ps2adapter/ps2adapter.ino | 21 +++++++++++++++------ 4 files changed, 38 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 4058951..7f169e8 100644 --- a/README.md +++ b/README.md @@ -52,20 +52,14 @@ Cute Mouse Driver is part of FreeDos project and is free and open source. ## Jumper Settings There are two jumpers, which can be used to set different modes. Currently you -can choose between 2- and 3-button mouse modes and select so called remote and -streaming mode. Remote mode sends mouse data all the time and has a better -response times in current implementation. Streaming mode sends only data, when -mouse position or button state changes. Unfortunately, this mode is currently -not in a good shape. It has a higher delay and can result in some timing -issues. However, it is interesting to have it for experimental purpose. Default -values are when the jumpers are unset. - -JP1 | Setting |Description -----|---------|------------------------------ -1-2 | unset | 3-button Logitech mouse -1-2 | set | 2-button Microsoft mouse -3-4 | unset | remote mode -3-4 | set | streaming mode (experimental) +can choose between 2- and 3-button and wheel mouse modes. In the table below O means open or unset and X means closed or set. + +JP1 |JP2 |Description +-----|-----|------------------------------ + O | O | 3-button Logitech mouse + X | O | 2-button Microsoft mouse + O | X | Microsoft Wheel Mouse + X | X | 3-button Logitech mouse with KVM hack ## Bill of materials diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index 0c23278..3b1d6f9 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -191,19 +191,23 @@ Ps2Mouse::Ps2Mouse() : m_stream(false), m_wheelMouse(false) {} -bool Ps2Mouse::reset(bool streaming) { +bool Ps2Mouse::reset(bool streaming, bool kvmHack) { Impl impl{*this}; - if (!impl.sendCommand(Command::reset)) { - return false; - } - byte reply; - if (!impl.recvByte(reply) || reply != byte(Response::selfTestPassed)) { - return false; - } - if (!impl.recvByte(reply) || reply != byte(Response::isMouse)) { - return false; + // don't send reset command if we are in kvmHack mode + if (!kvmHack) { + if (!impl.sendCommand(Command::reset)) { + return false; + } + + if (!impl.recvByte(reply) || reply != byte(Response::selfTestPassed)) { + return false; + } + + if (!impl.recvByte(reply) || reply != byte(Response::isMouse)) { + return false; + } } // Determine if this is a wheel mouse diff --git a/firmware/ps2adapter/Ps2Mouse.h b/firmware/ps2adapter/Ps2Mouse.h index 2634173..c4d034c 100644 --- a/firmware/ps2adapter/Ps2Mouse.h +++ b/firmware/ps2adapter/Ps2Mouse.h @@ -27,7 +27,7 @@ class Ps2Mouse { Ps2Mouse(); - bool reset(bool streaming); + bool reset(bool streaming, bool kvmHack); bool setScaling(bool flag) const; bool setResolution(byte resolution) const; diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index 19b84d3..d845816 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -4,8 +4,9 @@ static const int RS232_RTS = 3; static Ps2Mouse mouse; -static bool threeButtons = false; +static bool twoButtons = false; static bool wheelMouse = false; +static bool kvmHack = false; static void sendSerialBit(int data) { // Delay between the signals to match 1200 baud @@ -41,7 +42,7 @@ static void sendToSerial(const Ps2Mouse::Data& data) { sendSerialByte(0x40 | lb | rb | ((dy >> 4) & 0xC) | ((dx >> 6) & 0x3)); sendSerialByte(dx & 0x3F); sendSerialByte(dy & 0x3F); - if (threeButtons) { + if (!twoButtons) { byte mb; if (wheelMouse) { mb = data.middleButton ? 0x10 : 0; @@ -58,13 +59,15 @@ static void initSerialPort() { RS_SETTXHIGH; delayMicroseconds(10000); sendSerialByte('M'); - if(threeButtons) { + if(!twoButtons) { if(wheelMouse) { sendSerialByte('Z'); } else { sendSerialByte('3'); } Serial.println(wheelMouse ? "Init Wheel mode" : "Init 3-button mode"); + } else { + Serial.println("Init 2-button mode"); } delayMicroseconds(10000); @@ -76,7 +79,7 @@ static void initSerialPort() { static void initPs2Port() { Serial.println("Reseting PS/2 mouse"); - if (mouse.reset(true)) { + if (mouse.reset(true, kvmHack)) { Serial.println("PS/2 mouse reset OK"); } else { Serial.println("Failed to reset PS/2 mouse"); @@ -110,9 +113,15 @@ void setup() { JP34_DIRIN_UP; LED_DIROUT; LED_SET(HIGH); - threeButtons = JP12_READ; - wheelMouse = (JP34_READ == LOW); Serial.begin(115200); + twoButtons = (JP12_READ == LOW); + wheelMouse = (JP34_READ == LOW); + if (wheelMouse && twoButtons) { + twoButtons = false; + wheelMouse = false; + kvmHack = true; + Serial.println("KVM hack enabled"); + } initSerialPort(); initPs2Port(); Serial.println("Setup done!"); From 8b944fcee6832ff9c4a8aa56e54f7fbfef3ff5f1 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Fri, 7 Jun 2024 18:18:54 +0200 Subject: [PATCH 03/12] updated README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7f169e8..4129fbc 100644 --- a/README.md +++ b/README.md @@ -52,14 +52,14 @@ Cute Mouse Driver is part of FreeDos project and is free and open source. ## Jumper Settings There are two jumpers, which can be used to set different modes. Currently you -can choose between 2- and 3-button and wheel mouse modes. In the table below O means open or unset and X means closed or set. +can choose between 2- and 3-button and wheel mouse modes. In the table below O means open or unset and X means closed or set. The KVM Hack mode is an experimental mode where the PS2 mouse is not reset, but assumed to be initialized and in stream mode. JP1 |JP2 |Description -----|-----|------------------------------ O | O | 3-button Logitech mouse X | O | 2-button Microsoft mouse O | X | Microsoft Wheel Mouse - X | X | 3-button Logitech mouse with KVM hack + X | X | KVM Hack wirh 3-button Logitech mouse ## Bill of materials From 57f8ab04ae889d21a7ae1a002852cd9518e30d78 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Fri, 7 Jun 2024 18:20:12 +0200 Subject: [PATCH 04/12] updated README.md again --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4129fbc..5a47e28 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Cute Mouse Driver is part of FreeDos project and is free and open source. ## Jumper Settings There are two jumpers, which can be used to set different modes. Currently you -can choose between 2- and 3-button and wheel mouse modes. In the table below O means open or unset and X means closed or set. The KVM Hack mode is an experimental mode where the PS2 mouse is not reset, but assumed to be initialized and in stream mode. +can choose between 2- and 3-button and wheel mouse modes. In the table below O means open or unset and X means closed or set. The KVM Hack mode is an experimental mode where the PS2 mouse is not reset, but assumed to be initialized and in stream mode. This is done to work around incompatibilities with some KVM switches. JP1 |JP2 |Description -----|-----|------------------------------ From d396d0f534b984c540f16a6a1e792415f419ced1 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Fri, 7 Jun 2024 18:53:16 +0200 Subject: [PATCH 05/12] typos in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a47e28..c5c555c 100644 --- a/README.md +++ b/README.md @@ -52,14 +52,14 @@ Cute Mouse Driver is part of FreeDos project and is free and open source. ## Jumper Settings There are two jumpers, which can be used to set different modes. Currently you -can choose between 2- and 3-button and wheel mouse modes. In the table below O means open or unset and X means closed or set. The KVM Hack mode is an experimental mode where the PS2 mouse is not reset, but assumed to be initialized and in stream mode. This is done to work around incompatibilities with some KVM switches. +can choose between 2- and 3-button and wheel mouse modes. In the table below, O means open or unset and X means closed or set. The KVM Hack mode is an experimental mode where the PS2 mouse is not reset, but assumed to be initialized and in stream mode. This is done to work around incompatibilities with some KVM switches. JP1 |JP2 |Description -----|-----|------------------------------ O | O | 3-button Logitech mouse X | O | 2-button Microsoft mouse O | X | Microsoft Wheel Mouse - X | X | KVM Hack wirh 3-button Logitech mouse + X | X | KVM Hack with 3-button Logitech mouse ## Bill of materials From b5bcc6679d83f555c26c3d529c3631720f769342 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Mon, 17 Jun 2024 15:43:38 +0200 Subject: [PATCH 06/12] Refactor serial init (#2) * Refactored init sequence (PS2, then serial on RTS signal) Refactored Ps2Mouse to bring all Impl related functions, structs and enums into the class definition. Upated README.md to indicate jumper change. * updated comment about wheel mouse support in ctmouse driver * fixit serial message logic * updated README.md with better wording * added parity check to Ps2 code --- README.md | 6 +- firmware/ps2adapter/Ps2Mouse.cpp | 405 ++++++++++++----------------- firmware/ps2adapter/Ps2Mouse.h | 64 ++++- firmware/ps2adapter/ps2adapter.ino | 57 ++-- 4 files changed, 273 insertions(+), 259 deletions(-) diff --git a/README.md b/README.md index c5c555c..24c09d0 100644 --- a/README.md +++ b/README.md @@ -52,14 +52,14 @@ Cute Mouse Driver is part of FreeDos project and is free and open source. ## Jumper Settings There are two jumpers, which can be used to set different modes. Currently you -can choose between 2- and 3-button and wheel mouse modes. In the table below, O means open or unset and X means closed or set. The KVM Hack mode is an experimental mode where the PS2 mouse is not reset, but assumed to be initialized and in stream mode. This is done to work around incompatibilities with some KVM switches. +can choose between 2-button, 3-button, and wheel mouse modes. In the table below, O means open or unset, and X means closed or set. JP1 |JP2 |Description -----|-----|------------------------------ O | O | 3-button Logitech mouse X | O | 2-button Microsoft mouse - O | X | Microsoft Wheel Mouse - X | X | KVM Hack with 3-button Logitech mouse + O | X | Wheel mouse (cutemouse driver required) + X | X | Undefined (2-button mode will override) ## Bill of materials diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index 3b1d6f9..ed92d3b 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -3,21 +3,6 @@ namespace { -struct Status { - - byte rightButton : 1; - byte middleButton : 1; - byte leftButton : 1; - byte na2 : 1; - byte scaling : 1; - byte dataReporting : 1; - byte remoteMode : 1; - byte na1 : 1; - - byte resolution; - byte sampleRate; -}; - struct Packet { byte leftButton : 1; @@ -34,25 +19,9 @@ struct Packet { byte wheelData; }; -enum class Command { - disableScaling = 0xE6, - enableScaling = 0xE7, - setResolution = 0xE8, - statusRequest = 0xE9, - setStreamMode = 0xEA, - readData = 0xEB, - resetWrapMode = 0xEC, // Not implemented - setWrapMode = 0xEE, // Not implemented - reset = 0xFF, - setRemoteMode = 0xF0, - getDeviceId = 0xF2, // Not implemented - setSampleRate = 0xF3, - enableDataReporting = 0xF4, - disableDataReporting = 0xF5, - setDefaults = 0xF6, // Not implemented -}; enum class Response { + isMouse = 0x00, isWheelMouse = 0x03, selfTestPassed = 0xAA, @@ -63,232 +32,89 @@ enum class Response { } // namespace -struct Ps2Mouse::Impl { - const Ps2Mouse& m_ref; - - void sendBit(int value) const { - while (PS2_READCLOCK != LOW) {} - PS2_SETDATA(value); - while (PS2_READCLOCK != HIGH) {} - } - - int recvBit() const { - while (PS2_READCLOCK != LOW) {} - auto result = PS2_READDATA; - while (PS2_READCLOCK != HIGH) {} - return result; - } - - bool sendByte(byte value) const { - - // Inhibit communication - PS2_DIRCLOCKOUT; - PS2_SETCLOCKLOW; - delayMicroseconds(10); - - // Set start bit and release the clock - PS2_DIRDATAOUT; - PS2_SETDATALOW; - PS2_DIRCLOCKIN_UP; - - // Send data bits - byte parity = 1; - for (auto i = 0; i < 8; i++) { - byte nextBit = (value >> i) & 0x01; - parity ^= nextBit; - sendBit(nextBit); - } - // Send parity bit - sendBit(parity); - - // Send stop bit - sendBit(1); - - // Enter receive mode and wait for ACK bit - PS2_DIRDATAIN; - return recvBit() == 0; - } - - bool recvByte(byte& value) const { +Ps2Mouse::Ps2Mouse() + : m_type(MouseType::threeButton) +{} - // Enter receive mode - PS2_DIRCLOCKIN; - PS2_DIRDATAIN; +bool Ps2Mouse::reset() { + + byte reply; - // Receive start bit - if (recvBit() != 0) { + if (!sendCommand(Command::reset)) { return false; - } - - // Receive data bits - value = 0; - byte parity = 1; - for (int i = 0; i < 8; i++) { - byte nextBit = recvBit(); - value |= nextBit << i; - parity ^= nextBit; - } - - // Receive and check parity bit - recvBit(); // TODO check parity - - // Receive stop bit - recvBit(); - - return true; } - template - bool sendData(const T& data) const { - auto ptr = reinterpret_cast(&data); - for (auto i = 0; i < sizeof(data); i++) { - if (!sendByte(ptr[i])) { - return false; - } - } - return true; - } - - bool recvData(byte* data, size_t size) const { - for (size_t i = 0u; i < size; i++) { - if (!recvByte(data[i])) { - return false; - } - } - return true; - } - - bool sendByteWithAck(byte value) const { - while (true) { - if (sendByte(value)) { - byte response; - if (recvByte(response)) { - if (response == static_cast(Response::resend)) { - continue; - } - return response == static_cast(Response::ack); - } - } + if (!recvByte(reply) || reply != byte(Response::selfTestPassed)) { return false; - } - } - - bool sendCommand(Command command) const { - return sendByteWithAck(static_cast(command)); - } - - bool sendCommand(Command command, byte setting) const { - return sendCommand(command) && sendByteWithAck(setting); - } - - bool getStatus(Status& status) const { - return sendCommand(Command::statusRequest) && recvData((byte*) &status, sizeof(Status)); } -}; - -Ps2Mouse::Ps2Mouse() - : m_stream(false), m_wheelMouse(false) -{} - -bool Ps2Mouse::reset(bool streaming, bool kvmHack) { - Impl impl{*this}; - byte reply; - - // don't send reset command if we are in kvmHack mode - if (!kvmHack) { - if (!impl.sendCommand(Command::reset)) { - return false; - } - - if (!impl.recvByte(reply) || reply != byte(Response::selfTestPassed)) { - return false; - } - if (!impl.recvByte(reply) || reply != byte(Response::isMouse)) { - return false; - } + if (!recvByte(reply) || reply != byte(Response::isMouse)) { + return false; } // Determine if this is a wheel mouse - if (!impl.sendCommand(Command::setSampleRate, 200) || - !impl.sendCommand(Command::setSampleRate, 100) || - !impl.sendCommand(Command::setSampleRate, 80) || - !impl.sendCommand(Command::getDeviceId) || - !impl.recvByte(reply)) { + if (!sendCommand(Command::setSampleRate, 200) || + !sendCommand(Command::setSampleRate, 100) || + !sendCommand(Command::setSampleRate, 80) || + !sendCommand(Command::getDeviceId) || + !recvByte(reply)) { return false; } - m_wheelMouse = (reply == byte(Response::isWheelMouse)); - - if (streaming) { - // Streaming mode is the default after a reset. - m_stream = true; - // switch on data reporting - return impl.sendCommand(Command::enableDataReporting); + if (reply == byte(Response::isWheelMouse)) { + m_type = MouseType::wheelMouse; } - return disableStreaming() && impl.sendCommand(Command::enableDataReporting); + // switch on data reporting + return setReporting(true); } -bool Ps2Mouse::enableStreaming() { - if (Impl{*this}.sendCommand(Command::setStreamMode)) { - m_stream = true; - return true; - } - return false; +bool Ps2Mouse::setReporting(bool enable) const { + + return sendCommand(enable ? Command::enableDataReporting : Command::disableDataReporting); } -bool Ps2Mouse::disableStreaming() { - if (Impl{*this}.sendCommand(Command::setRemoteMode)) { - m_stream = false; - return true; - } - return false; +bool Ps2Mouse::setStreamMode() { + + return (sendCommand(Command::setStreamMode)); +} + +bool Ps2Mouse::setRemoteMode() { + + return (sendCommand(Command::setRemoteMode)); } bool Ps2Mouse::setScaling(bool flag) const { - if (m_stream) { - Impl{*this}.sendCommand(Command::disableDataReporting); - } - bool res = Impl{*this}.sendCommand(flag ? Command::enableScaling : Command::disableScaling); - if (m_stream) { - Impl{*this}.sendCommand(Command::enableDataReporting); - } + + setReporting(false); + bool res = sendCommand(flag ? Command::enableScaling : Command::disableScaling); + setReporting(true); return res; } bool Ps2Mouse::setResolution(byte resolution) const { - if (m_stream) { - Impl{*this}.sendCommand(Command::disableDataReporting); - } - bool res = Impl{*this}.sendCommand(Command::setResolution, resolution); - if (m_stream) { - Impl{*this}.sendCommand(Command::enableDataReporting); - } + + setReporting(false); + bool res = sendCommand(Command::setResolution, resolution); + setReporting(true); return res; } bool Ps2Mouse::setSampleRate(byte sampleRate) const { - if (m_stream) { - Impl{*this}.sendCommand(Command::disableDataReporting); - } - bool res = Impl{*this}.sendCommand(Command::setSampleRate, sampleRate); - if (m_stream) { - Impl{*this}.sendCommand(Command::enableDataReporting); - } + + setReporting(false); + bool res = sendCommand(Command::setSampleRate, sampleRate); + setReporting(true); return res; } bool Ps2Mouse::getSettings(Settings& settings) const { + Status status; - if (m_stream) { - Impl{*this}.sendCommand(Command::disableDataReporting); - } - bool res = Impl{*this}.getStatus(status); - if (m_stream) { - Impl{*this}.sendCommand(Command::enableDataReporting); - } + setReporting(false); + bool res = getStatus(status); + setReporting(true); if (res) { settings.rightBtn = status.rightButton; settings.middleBtn = status.middleButton; @@ -303,26 +129,23 @@ bool Ps2Mouse::getSettings(Settings& settings) const { return false; } -bool Ps2Mouse::readData(Data& data) const { +Ps2Mouse::MouseType Ps2Mouse::getType() const { + return m_type; +} - Impl impl{*this}; +bool Ps2Mouse::readData(Data& data) const { - if (m_stream) { - if (PS2_READCLOCK != LOW) { - return false; - } - } - else if (!impl.sendCommand(Command::readData)) { - return false; + if (PS2_READCLOCK != LOW) { + return false; } Packet packet = {0}; - if (m_wheelMouse) { - if (!impl.recvData((byte*)&packet, sizeof(packet))) { + if (m_type == MouseType::wheelMouse) { + if (!recvData((byte*)&packet, sizeof(packet))) { return false; } } else { - if (!impl.recvData((byte*)&packet, sizeof(packet) - 1)) { + if (!recvData((byte*)&packet, sizeof(packet) - 1)) { return false; } } @@ -332,6 +155,120 @@ bool Ps2Mouse::readData(Data& data) const { data.rightButton = packet.rightButton; data.xMovement = (packet.xSign ? -0x100 : 0) | packet.xMovement; data.yMovement = (packet.ySign ? -0x100 : 0) | packet.yMovement; - data.wheelMovement = m_wheelMouse ? packet.wheelData : 0; + data.wheelMovement = (m_type == MouseType::wheelMouse) ? packet.wheelData : 0; + return true; +} + +void Ps2Mouse::sendBit(int value) const { + + while (PS2_READCLOCK != LOW) {} + PS2_SETDATA(value); + while (PS2_READCLOCK != HIGH) {} +} + +bool Ps2Mouse::sendByte(byte value) const { + + // Inhibit communication + PS2_DIRCLOCKOUT; + PS2_SETCLOCKLOW; + delayMicroseconds(10); + + // Set start bit and release the clock + PS2_DIRDATAOUT; + PS2_SETDATALOW; + PS2_DIRCLOCKIN_UP; + + // Send data bits + byte parity = 1; + for (auto i = 0; i < 8; i++) { + byte nextBit = (value >> i) & 0x01; + parity ^= nextBit; + sendBit(nextBit); + } + + // Send parity bit + sendBit(parity); + + // Send stop bit + sendBit(1); + + // Enter receive mode and wait for ACK bit + PS2_DIRDATAIN; + return recvBit() == 0; +} + +int Ps2Mouse::recvBit() const { + + while (PS2_READCLOCK != LOW) {} + auto result = PS2_READDATA; + while (PS2_READCLOCK != HIGH) {} + return result; +} + +bool Ps2Mouse::recvByte(byte& value) const { + + // Enter receive mode + PS2_DIRCLOCKIN; + PS2_DIRDATAIN; + + // Receive start bit + if (recvBit() != 0) { + return false; + } + + // Receive data bits + value = 0; + byte parity = 1; + for (int i = 0; i < 8; i++) { + byte nextBit = recvBit(); + value |= nextBit << i; + parity ^= nextBit; + } + + // Receive parity bit + byte actualParity = recvBit(); + if (parity != actualParity) { + Serial.println("P!"); + } + + // Receive stop bit + recvBit(); + + return parity == actualParity; +} + +bool Ps2Mouse::recvData(byte* data, size_t size) const { + for (size_t i = 0u; i < size; i++) { + if (!recvByte(data[i])) { + return false; + } + } return true; } + +bool Ps2Mouse::sendByteWithAck(byte value) const { + while (true) { + if (sendByte(value)) { + byte response; + if (recvByte(response)) { + if (response == static_cast(Response::resend)) { + continue; + } + return response == static_cast(Response::ack); + } + } + return false; + } +} + +bool Ps2Mouse::sendCommand(Command command) const { + return sendByteWithAck(static_cast(command)); +} + +bool Ps2Mouse::sendCommand(Command command, byte setting) const { + return sendCommand(command) && sendByteWithAck(setting); +} + +bool Ps2Mouse::getStatus(Status& status) const { + return sendCommand(Command::statusRequest) && recvData((byte*) &status, sizeof(Status)); +} diff --git a/firmware/ps2adapter/Ps2Mouse.h b/firmware/ps2adapter/Ps2Mouse.h index c4d034c..4b0a79b 100644 --- a/firmware/ps2adapter/Ps2Mouse.h +++ b/firmware/ps2adapter/Ps2Mouse.h @@ -5,7 +5,14 @@ class Ps2Mouse { public: + enum class MouseType { + + threeButton, + wheelMouse, + }; + struct Data { + bool leftButton; bool middleButton; bool rightButton; @@ -15,6 +22,7 @@ class Ps2Mouse { }; struct Settings { + bool rightBtn; bool middleBtn; bool leftBtn; @@ -27,21 +35,65 @@ class Ps2Mouse { Ps2Mouse(); - bool reset(bool streaming, bool kvmHack); + bool reset(); + bool setReporting(bool enable) const; + + bool setStreamMode(); + bool setRemoteMode(); bool setScaling(bool flag) const; bool setResolution(byte resolution) const; bool setSampleRate(byte sampleRate) const; bool getSettings(Settings& settings) const; + Ps2Mouse::MouseType getType() const; - bool enableStreaming(); - bool disableStreaming(); bool readData(Data& data) const; private: - struct Impl; + enum class Command { + + disableScaling = 0xE6, + enableScaling = 0xE7, + setResolution = 0xE8, + statusRequest = 0xE9, + setStreamMode = 0xEA, + readData = 0xEB, + resetWrapMode = 0xEC, // Not implemented + setWrapMode = 0xEE, // Not implemented + reset = 0xFF, + setRemoteMode = 0xF0, + getDeviceId = 0xF2, // Not implemented + setSampleRate = 0xF3, + enableDataReporting = 0xF4, + disableDataReporting = 0xF5, + setDefaults = 0xF6, // Not implemented + }; + + struct Status { + + byte rightButton : 1; + byte middleButton : 1; + byte leftButton : 1; + byte na2 : 1; + byte scaling : 1; + byte dataReporting : 1; + byte remoteMode : 1; + byte na1 : 1; + + byte resolution; + byte sampleRate; + }; + + void sendBit(int value) const; + int recvBit() const; + bool sendByte(byte value) const; + bool recvByte(byte& value) const; + bool recvData(byte* data, size_t size) const; + bool sendByteWithAck(byte value) const; + bool sendCommand(Command command) const; + bool sendCommand(Command command, byte setting) const; + bool getStatus(Status& status) const; - bool m_stream; - bool m_wheelMouse; + MouseType m_type; }; diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index d845816..071c64e 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -6,7 +6,8 @@ static const int RS232_RTS = 3; static Ps2Mouse mouse; static bool twoButtons = false; static bool wheelMouse = false; -static bool kvmHack = false; +static volatile bool doSerialInit = false; +static volatile bool serialInitDone = false; static void sendSerialBit(int data) { // Delay between the signals to match 1200 baud @@ -35,6 +36,7 @@ static void sendSerialByte(byte data) { } static void sendToSerial(const Ps2Mouse::Data& data) { + auto dx = constrain(data.xMovement, -127, 127); auto dy = constrain(-data.yMovement, -127, 127); byte lb = data.leftButton ? 0x20 : 0; @@ -45,9 +47,12 @@ static void sendToSerial(const Ps2Mouse::Data& data) { if (!twoButtons) { byte mb; if (wheelMouse) { + // according to: https://sourceforge.net/p/cutemouse/trunk/ci/master/tree/cutemouse/PROTOCOL.TXT + // for a wheel mouse, the middle button should be reported in bit 0x10 mb = data.middleButton ? 0x10 : 0; mb |= (data.wheelMovement & 0x0F); } else { + // for a 3-button mouse, the middle button should be reported in bit 0x20 mb = data.middleButton ? 0x20 : 0; } sendSerialByte(mb); @@ -55,31 +60,37 @@ static void sendToSerial(const Ps2Mouse::Data& data) { } static void initSerialPort() { + Serial.println("Starting serial port"); RS_SETTXHIGH; delayMicroseconds(10000); sendSerialByte('M'); if(!twoButtons) { - if(wheelMouse) { + // if wheelMouse mode has been set by jumper, confirm that the mouse has the capability + if(wheelMouse && (mouse.getType() == Ps2Mouse::MouseType::wheelMouse)) { + // set wheelMouse mode sendSerialByte('Z'); + Serial.println("Init wheel mode"); } else { + // set 3-button (Logitech) mode sendSerialByte('3'); + Serial.println("Init 3-button mode"); } - Serial.println(wheelMouse ? "Init Wheel mode" : "Init 3-button mode"); } else { Serial.println("Init 2-button mode"); } delayMicroseconds(10000); - - Serial.println("Listening on RTS"); - void (*resetHack)() = 0; - attachInterrupt(digitalPinToInterrupt(RS232_RTS), resetHack, FALLING); + // Renable PS/2 mouse data reporting + mouse.setReporting(true); + // Signal the main loop that the serial port is ready + serialInitDone = true; } static void initPs2Port() { + Serial.println("Reseting PS/2 mouse"); - if (mouse.reset(true, kvmHack)) { + if (mouse.reset()) { Serial.println("PS/2 mouse reset OK"); } else { Serial.println("Failed to reset PS/2 mouse"); @@ -104,6 +115,15 @@ static void initPs2Port() { } } +void resetSerialSignal() { + // signal the main loop to ignore the PS/2 mouse data until the serial port is ready + serialInitDone = false; + // disable the PS/2 mouse data reporting while the serial port is being initialized + mouse.setReporting(false); + // signal the main loop to initialize the serial port + doSerialInit = true; +} + void setup() { // PS/2 Data input must be initialized shortly after power on, // or the mouse will not initialize @@ -113,25 +133,30 @@ void setup() { JP34_DIRIN_UP; LED_DIROUT; LED_SET(HIGH); + Serial.begin(115200); twoButtons = (JP12_READ == LOW); wheelMouse = (JP34_READ == LOW); - if (wheelMouse && twoButtons) { - twoButtons = false; - wheelMouse = false; - kvmHack = true; - Serial.println("KVM hack enabled"); - } - initSerialPort(); + + // PS/2 initialization can take > 400ms for reset to complete. + // Init the mouse here, determine it's characteristics and then + // wait for the RTS signal to initialize the serial port comms initPs2Port(); + Serial.println("Listening on RTS"); + attachInterrupt(digitalPinToInterrupt(RS232_RTS), resetSerialSignal, FALLING); + Serial.println("Setup done!"); LED_SETLOW; } void loop() { + if (doSerialInit) { + initSerialPort(); + doSerialInit = false; + } Ps2Mouse::Data data; - if (mouse.readData(data)) { + if (serialInitDone && mouse.readData(data)) { sendToSerial(data); } } From d0e008a54ae5ccb81852803b21182263774598df Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Wed, 19 Jun 2024 08:44:52 +0200 Subject: [PATCH 07/12] initial attempt to handle Ps2 data via interrupt --- firmware/ps2adapter/Blinker.cpp | 30 +++++++ firmware/ps2adapter/Blinker.h | 14 +++ firmware/ps2adapter/ProMini.h | 47 ++++++---- firmware/ps2adapter/Ps2Mouse.cpp | 137 +++++++++++++++++++++++++++-- firmware/ps2adapter/Ps2Mouse.h | 17 +++- firmware/ps2adapter/ps2adapter.ino | 100 +++++++++++++++++++-- 6 files changed, 308 insertions(+), 37 deletions(-) create mode 100644 firmware/ps2adapter/Blinker.cpp create mode 100644 firmware/ps2adapter/Blinker.h diff --git a/firmware/ps2adapter/Blinker.cpp b/firmware/ps2adapter/Blinker.cpp new file mode 100644 index 0000000..008a80b --- /dev/null +++ b/firmware/ps2adapter/Blinker.cpp @@ -0,0 +1,30 @@ +#include +#include "Blinker.h" +#include "ProMini.h" + +Blinker::Blinker() : _ms(500), _lastMark(0), _state(false), _enabled(false) {} + +void Blinker::setTiming(long ms) { + _ms = ms; +} + +void Blinker::enable() { + _enabled = true; + _lastMark = millis(); +} + +void Blinker::disable() { + _enabled = false; +} + +void Blinker::update() { + if (!_enabled) { + return; + } + + if (millis() - _lastMark > _ms) { + _lastMark = millis(); + _state = !_state; + LED_SET(_state ? HIGH : LOW); + } +} diff --git a/firmware/ps2adapter/Blinker.h b/firmware/ps2adapter/Blinker.h new file mode 100644 index 0000000..787893d --- /dev/null +++ b/firmware/ps2adapter/Blinker.h @@ -0,0 +1,14 @@ +#pragma once + +class Blinker { + public: + Blinker(); + void setTiming(long ms); + void enable(); + void disable(); + void update(); + private: + unsigned long _ms, _lastMark; + bool _state; + bool _enabled; +}; \ No newline at end of file diff --git a/firmware/ps2adapter/ProMini.h b/firmware/ps2adapter/ProMini.h index ad0dda9..588d195 100644 --- a/firmware/ps2adapter/ProMini.h +++ b/firmware/ps2adapter/ProMini.h @@ -26,27 +26,36 @@ * more quickly set individual pins to their desired * states. *PS/2 data pin operations***Bit:76543210************/ -#define PS2_SETDATAHIGH (PORTC |=0b00001000) -#define PS2_SETDATALOW (PORTC &=0b11110111) -#define PS2_SETDATA(val) val ? PS2_SETDATAHIGH : PS2_SETDATALOW -#define PS2_DIRDATAIN (DDRC &=0b11110111) -#define PS2_DIRDATAIN_UP (DDRC &=0b11110111); PS2_SETDATAHIGH -#define PS2_DIRDATAOUT (DDRC |=0b00001000) -#define PS2_READDATA ((PINC &=0b00001000)>>3) + +#define PS2_DATA_BIT (_BV(3)) +#define PS2_SETDATAHIGH() ( PORTC |= PS2_DATA_BIT ) +#define PS2_SETDATALOW() ( PORTC &= ~PS2_DATA_BIT ) +#define PS2_SETDATA(val) (val) ? PS2_SETDATAHIGH() : PS2_SETDATALOW() +#define PS2_DIRDATAIN() ( DDRC &= ~PS2_DATA_BIT ) +#define PS2_DIRDATAIN_UP() ( DDRC &= ~PS2_DATA_BIT ) +#define PS2_DIRDATAOUT() ( DDRC |= PS2_DATA_BIT ) +#define PS2_READDATA() ( (PINC &= PS2_DATA_BIT)>>3 ) /*PS/2 clock pin operations**Bit:76543210************/ -#define PS2_SETCLOCKHIGH (PORTD |=0b00000100) -#define PS2_SETCLOCKLOW (PORTD &=0b11111011) -#define PS2_SETCLOCK(val) val ? PS2_SETCLOCKHIGH : PS2_SETCLOCKLOW -#define PS2_DIRCLOCKIN (DDRD &=0b11111011) -#define PS2_DIRCLOCKIN_UP (DDRD &=0b11111011); PS2_SETCLOCKHIGH -#define PS2_DIRCLOCKOUT (DDRD |=0b00000100) -#define PS2_READCLOCK ((PIND &=0b00000100)>>2) +#define PS2_CLOCK_BIT (_BV(2)) +#define PS2_SETCLOCKHIGH ( PORTD |= PS2_CLOCK_BIT ) +#define PS2_SETCLOCKLOW ( PORTD &= ~PS2_CLOCK_BIT) +#define PS2_SETCLOCK(val) (val) ? PS2_SETCLOCKHIGH : PS2_SETCLOCKLOW +#define PS2_DIRCLOCKIN ( DDRD &= ~PS2_CLOCK_BIT ) +#define PS2_DIRCLOCKIN_UP ( DDRD &= ~PS2_CLOCK_BIT ); PS2_SETCLOCKHIGH +#define PS2_DIRCLOCKOUT ( DDRD |= PS2_CLOCK_BIT ) +#define PS2_READCLOCK ( (PIND &= PS2_CLOCK_BIT)>>2 ) + +#define PS2_CLOCK_INT_CLEAR() ( EIFR = PS2_CLOCK_INT ) +#define PS2_CLOCK_INT_ENABLE() ( EIMSK |= _BV(INT0) ) +#define PS2_CLOCK_INT_DISABLE() ( EIMSK &= ~_BV(INT0) ) +#define PS2_CLOCK_INT_FALLING() ( EICRA = (EICRA & ~_BV(ISC00)) | _BV(ISC01) ) +#define PS2_CLOCK_INT_CHANGE() ( EICRA = (EICRA & ~_BV(ISC01)) | _BV(ISC00) ) /*RS-232 pin operations******Bit:76543210************/ #define RS_SETTXHIGH (PORTD |=0b00010000) #define RS_SETTXLOW (PORTD &=0b11101111) -#define RS_SETTX(val) val ? RS_SETTXHIGH : RS_SETTXLOW +#define RS_SETTX(val) (val) ? RS_SETTXHIGH : RS_SETTXLOW #define RS_DIRTXOUT (DDRD |=0b00010000) /****************************************************/ @@ -63,8 +72,8 @@ /****************************************************/ /*LED pin operations*********Bit:76543210************/ -#define LED_SETHIGH (PORTB |=0b00100000) -#define LED_SETLOW (PORTB &=0b11011111) -#define LED_SET(val) val ? LED_SETHIGH : LED_SETLOW -#define LED_DIROUT (DDRB |=0b00100000) +#define LED_SETHIGH() (PORTB |=0b00100000) +#define LED_SETLOW() (PORTB &=0b11011111) +#define LED_SET(val) (val) ? LED_SETHIGH() : LED_SETLOW() +#define LED_DIROUT() (DDRB |=0b00100000) /****************************************************/ \ No newline at end of file diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index ed92d3b..33f9e30 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -32,10 +32,13 @@ enum class Response { } // namespace +static Ps2Mouse* classPtr = NULL; Ps2Mouse::Ps2Mouse() : m_type(MouseType::threeButton) -{} +{ + classPtr = this; +} bool Ps2Mouse::reset() { @@ -159,6 +162,121 @@ bool Ps2Mouse::readData(Data& data) const { return true; } +void Ps2Mouse::clockInterruptStatic() { + + if (classPtr != NULL) { + classPtr->interruptHandler(); + } +} + +void Ps2Mouse::startInterrupt() { + + // Enter receive mode + PS2_DIRCLOCKIN; + PS2_DIRDATAIN(); + m_mouseBits = 0; + m_bitCount = 0; + m_bufferHead = m_bufferTail = m_bufferCount = 0; + memset(m_buffer, 0, 256); + attachInterrupt(0, clockInterruptStatic, FALLING); +} + +void Ps2Mouse::stopInterrupt() { + + detachInterrupt(0); +} + +bool Ps2Mouse::getByte(uint8_t* data) { + + if (m_bufferHead == m_bufferTail) { + return false; + } + m_bufferCount--; + *data = m_buffer[m_bufferTail++]; + return true; +} + +bool Ps2Mouse::getBytes(uint8_t* data, int length) { + + while (length) { + if (getByte(data)) { + data++; + length--; + } + } + return true; +} + +bool Ps2Mouse::readData2(Data& data) { + + Packet packet = {0}; + if (m_type == MouseType::wheelMouse) { + if (!getBytes((byte*)&packet, sizeof(packet))) { + return false; + } + } else { + if (!getBytes((byte*)&packet, sizeof(packet) - 1)) { + return false; + } + } + + data.leftButton = packet.leftButton; + data.middleButton = packet.middleButton; + data.rightButton = packet.rightButton; + data.xMovement = (packet.xSign ? -0x100 : 0) | packet.xMovement; + data.yMovement = (packet.ySign ? -0x100 : 0) | packet.yMovement; + data.wheelMovement = (m_type == MouseType::wheelMouse) ? packet.wheelData : 0; + return true; +} + + +void Ps2Mouse::interruptHandler() { + + uint8_t bit = PS2_READDATA(); + + if (m_bitCount == 0) { + // start bit should ALWAYS be 0 + if (bit) { + LED_SETHIGH(); + // error on start bit + return; + } + // start bit: bit 1 + m_parityBit = 1; + m_mouseBits = 0; + m_bitCount++; + } else if ((m_bitCount > 0) && (m_bitCount < 9)) { + // data bits: bits 2 - 9 + m_mouseBits >>= 1; + m_mouseBits |= (bit << 7); + m_parityBit ^= bit; + m_bitCount++; + } else if (m_bitCount == 9) { + // parity bit: bit 10 + // parity bit should match the calculated parity + // so m_parityBit xor bit will be 0 if parity matches: + // 1 ^ 1 = 0 + // 0 ^ 0 = 0 + // 1 ^ 0 = 1 + // 0 ^ 1 = 1 + m_parityBit ^= bit; + m_bitCount++; + } else { + // stop bit: bit 11 + // if stop bit is 1 and parity 0, then add this byte to the buffer + if (bit && !m_parityBit) { + m_buffer[m_bufferHead] = m_mouseBits; + m_bufferCount++; + m_bufferHead++; + m_bitCount++; + LED_SETLOW(); + } else { + LED_SETHIGH(); + } + m_bitCount = 0; + } +} + void Ps2Mouse::sendBit(int value) const { while (PS2_READCLOCK != LOW) {} @@ -174,8 +292,8 @@ bool Ps2Mouse::sendByte(byte value) const { delayMicroseconds(10); // Set start bit and release the clock - PS2_DIRDATAOUT; - PS2_SETDATALOW; + PS2_DIRDATAOUT(); + PS2_SETDATALOW(); PS2_DIRCLOCKIN_UP; // Send data bits @@ -193,23 +311,24 @@ bool Ps2Mouse::sendByte(byte value) const { sendBit(1); // Enter receive mode and wait for ACK bit - PS2_DIRDATAIN; + PS2_DIRDATAIN(); return recvBit() == 0; } int Ps2Mouse::recvBit() const { while (PS2_READCLOCK != LOW) {} - auto result = PS2_READDATA; + auto result = PS2_READDATA(); while (PS2_READCLOCK != HIGH) {} return result; } + bool Ps2Mouse::recvByte(byte& value) const { // Enter receive mode PS2_DIRCLOCKIN; - PS2_DIRDATAIN; + PS2_DIRDATAIN(); // Receive start bit if (recvBit() != 0) { @@ -227,13 +346,13 @@ bool Ps2Mouse::recvByte(byte& value) const { // Receive parity bit byte actualParity = recvBit(); - if (parity != actualParity) { - Serial.println("P!"); - } // Receive stop bit recvBit(); + if (parity != actualParity) { + Serial.println("P!"); + } return parity == actualParity; } diff --git a/firmware/ps2adapter/Ps2Mouse.h b/firmware/ps2adapter/Ps2Mouse.h index 4b0a79b..1433c4f 100644 --- a/firmware/ps2adapter/Ps2Mouse.h +++ b/firmware/ps2adapter/Ps2Mouse.h @@ -49,7 +49,14 @@ class Ps2Mouse { Ps2Mouse::MouseType getType() const; bool readData(Data& data) const; - + void startInterrupt(); + void stopInterrupt(); + bool getByte(uint8_t* data); + bool getBytes(uint8_t* data, int length); + bool readData2(Data& data); + uint8_t getBufferCount() { return m_bufferCount; }; + uint8_t getBufferHead() { return m_bufferHead; }; + uint8_t getBufferTail() { return m_bufferTail; }; private: enum class Command { @@ -94,6 +101,14 @@ class Ps2Mouse { bool sendCommand(Command command) const; bool sendCommand(Command command, byte setting) const; bool getStatus(Status& status) const; + + static void clockInterruptStatic(); + void interruptHandler(); MouseType m_type; + uint8_t m_mouseBits; + uint8_t m_bitCount; + uint8_t m_parityBit; + volatile uint8_t m_bufferTail, m_bufferHead, m_bufferCount; + uint8_t m_buffer[256]; }; diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index 071c64e..d63e43b 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -1,12 +1,13 @@ #include "ProMini.h" #include "Ps2Mouse.h" +#include "Blinker.h" static const int RS232_RTS = 3; static Ps2Mouse mouse; static bool twoButtons = false; static bool wheelMouse = false; -static volatile bool doSerialInit = false; +static volatile bool doSerialInit = true; static volatile bool serialInitDone = false; static void sendSerialBit(int data) { @@ -127,12 +128,12 @@ void resetSerialSignal() { void setup() { // PS/2 Data input must be initialized shortly after power on, // or the mouse will not initialize - PS2_DIRDATAIN_UP; + PS2_DIRDATAIN_UP(); RS_DIRTXOUT; JP12_DIRIN_UP; JP34_DIRIN_UP; - LED_DIROUT; - LED_SET(HIGH); + LED_DIROUT(); + LED_SETHIGH(); Serial.begin(115200); twoButtons = (JP12_READ == LOW); @@ -146,17 +147,100 @@ void setup() { attachInterrupt(digitalPinToInterrupt(RS232_RTS), resetSerialSignal, FALLING); Serial.println("Setup done!"); - LED_SETLOW; + mouse.startInterrupt(); + LED_SETLOW(); } +enum class SettingState { + ProcessMouse, + SettingEnterDetected, + SettingStateConfirmed, + + +}; + +void blinkNtimes(int n, unsigned long ms = 100) { + for (int i = 0; i < n; i++) { + LED_SET(HIGH); + delay(ms); + LED_SET(LOW); + delay(ms); + } +} + +Blinker blinker; + +static SettingState settingState = SettingState::ProcessMouse; +static unsigned long lastMillis = 0; + +void processStateMachine() { + + Ps2Mouse::Data data; + bool validData = mouse.readData2(data); + blinker.update(); + + switch (settingState) { + case SettingState::ProcessMouse: + if (!validData) { + return; + } + Serial.println("PM"); + if (data.leftButton && data.rightButton && data.middleButton) { + settingState = SettingState::SettingEnterDetected; + lastMillis = millis(); + } else { + sendToSerial(data); + } + break; + case SettingState::SettingEnterDetected: + Serial.println("SED"); + if (validData && !(data.leftButton && data.rightButton && data.middleButton)) { + settingState = SettingState::ProcessMouse; + return; + } + if (millis() - lastMillis > 3000) { + settingState = SettingState::SettingStateConfirmed; + lastMillis = millis(); + blinker.enable(); + } + break; + case SettingState::SettingStateConfirmed: + Serial.println("SSC"); + if (!validData) { + return; + } + + if (millis() - lastMillis > 1000) { + // left handed mode selected + if (data.rightButton && !data.leftButton && !data.middleButton) { + settingState = SettingState::ProcessMouse; + blinker.disable(); + } + } + break; + } +} void loop() { if (doSerialInit) { initSerialPort(); doSerialInit = false; } - Ps2Mouse::Data data; - if (serialInitDone && mouse.readData(data)) { - sendToSerial(data); + if (serialInitDone) { + //processStateMachine(); + //return; + Ps2Mouse::Data data; + mouse.readData2(data); + Serial.println("count = " + String(mouse.getBufferCount())); + Serial.println("head = " + String(mouse.getBufferHead())); + Serial.println("tail = " + String(mouse.getBufferTail())); + + Serial.print("LB = "); Serial.println(data.leftButton); + Serial.print("MB = "); Serial.println(data.middleButton); + Serial.print("RB = "); Serial.println(data.rightButton); + Serial.print("X = "); Serial.println(data.xMovement); + Serial.print("Y = "); Serial.println(data.yMovement); + Serial.print("W = "); Serial.println(data.wheelMovement); + } } From f64b8328d9d8787daabbe58e860287adc64289c2 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Wed, 19 Jun 2024 10:16:41 +0200 Subject: [PATCH 08/12] cleanup on detatch --- firmware/ps2adapter/Ps2Mouse.cpp | 81 +++++++++++++++++--------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index 33f9e30..df2bf52 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -182,8 +182,11 @@ void Ps2Mouse::startInterrupt() { } void Ps2Mouse::stopInterrupt() { - detachInterrupt(0); + m_mouseBits = 0; + m_bitCount = 0; + m_bufferHead = m_bufferTail = m_bufferCount = 0; + memset(m_buffer, 0, 256); } bool Ps2Mouse::getByte(uint8_t* data) { @@ -277,44 +280,6 @@ void Ps2Mouse::interruptHandler() { } } -void Ps2Mouse::sendBit(int value) const { - - while (PS2_READCLOCK != LOW) {} - PS2_SETDATA(value); - while (PS2_READCLOCK != HIGH) {} -} - -bool Ps2Mouse::sendByte(byte value) const { - - // Inhibit communication - PS2_DIRCLOCKOUT; - PS2_SETCLOCKLOW; - delayMicroseconds(10); - - // Set start bit and release the clock - PS2_DIRDATAOUT(); - PS2_SETDATALOW(); - PS2_DIRCLOCKIN_UP; - - // Send data bits - byte parity = 1; - for (auto i = 0; i < 8; i++) { - byte nextBit = (value >> i) & 0x01; - parity ^= nextBit; - sendBit(nextBit); - } - - // Send parity bit - sendBit(parity); - - // Send stop bit - sendBit(1); - - // Enter receive mode and wait for ACK bit - PS2_DIRDATAIN(); - return recvBit() == 0; -} - int Ps2Mouse::recvBit() const { while (PS2_READCLOCK != LOW) {} @@ -365,6 +330,44 @@ bool Ps2Mouse::recvData(byte* data, size_t size) const { return true; } +void Ps2Mouse::sendBit(int value) const { + + while (PS2_READCLOCK != LOW) {} + PS2_SETDATA(value); + while (PS2_READCLOCK != HIGH) {} +} + +bool Ps2Mouse::sendByte(byte value) const { + + // Inhibit communication + PS2_DIRCLOCKOUT; + PS2_SETCLOCKLOW; + delayMicroseconds(10); + + // Set start bit and release the clock + PS2_DIRDATAOUT(); + PS2_SETDATALOW(); + PS2_DIRCLOCKIN_UP; + + // Send data bits + byte parity = 1; + for (auto i = 0; i < 8; i++) { + byte nextBit = (value >> i) & 0x01; + parity ^= nextBit; + sendBit(nextBit); + } + + // Send parity bit + sendBit(parity); + + // Send stop bit + sendBit(1); + + // Enter receive mode and wait for ACK bit + PS2_DIRDATAIN(); + return recvBit() == 0; +} + bool Ps2Mouse::sendByteWithAck(byte value) const { while (true) { if (sendByte(value)) { From 04f1525780d8ec2ddb2af42c43c455f67a137aff Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Wed, 19 Jun 2024 20:04:17 +0200 Subject: [PATCH 09/12] Ps2 clock interrupt working. Added setting state machine - in progress --- firmware/ps2adapter/Blinker.cpp | 22 +++- firmware/ps2adapter/Blinker.h | 5 +- firmware/ps2adapter/ProMini.h | 85 ++++++++------- firmware/ps2adapter/Ps2Mouse.cpp | 160 +++++++++-------------------- firmware/ps2adapter/Ps2Mouse.h | 40 ++++---- firmware/ps2adapter/ps2adapter.ino | 48 ++++----- 6 files changed, 146 insertions(+), 214 deletions(-) diff --git a/firmware/ps2adapter/Blinker.cpp b/firmware/ps2adapter/Blinker.cpp index 008a80b..deb77ac 100644 --- a/firmware/ps2adapter/Blinker.cpp +++ b/firmware/ps2adapter/Blinker.cpp @@ -2,29 +2,41 @@ #include "Blinker.h" #include "ProMini.h" -Blinker::Blinker() : _ms(500), _lastMark(0), _state(false), _enabled(false) {} +Blinker::Blinker() : _ms(500), _lastMark(0), _state(false), _enabled(false), _count(0) {} -void Blinker::setTiming(long ms) { - _ms = ms; +void Blinker::message(unsigned long ms, int count) { + _count = count << 1; + enable(ms); } -void Blinker::enable() { +void Blinker::enable(unsigned long ms) { + _state = true; _enabled = true; _lastMark = millis(); + _ms = ms; + LED_SETHIGH(); } void Blinker::disable() { + _state = false; _enabled = false; + LED_SETLOW(); } void Blinker::update() { if (!_enabled) { return; } - + if (millis() - _lastMark > _ms) { _lastMark = millis(); _state = !_state; LED_SET(_state ? HIGH : LOW); + if (_count) { + _count--; + if (!_count) { + disable(); + } + } } } diff --git a/firmware/ps2adapter/Blinker.h b/firmware/ps2adapter/Blinker.h index 787893d..ac4e17c 100644 --- a/firmware/ps2adapter/Blinker.h +++ b/firmware/ps2adapter/Blinker.h @@ -3,12 +3,13 @@ class Blinker { public: Blinker(); - void setTiming(long ms); - void enable(); + void message(unsigned long ms, int count); + void enable(unsigned long ms); void disable(); void update(); private: unsigned long _ms, _lastMark; bool _state; bool _enabled; + int _count; }; \ No newline at end of file diff --git a/firmware/ps2adapter/ProMini.h b/firmware/ps2adapter/ProMini.h index 588d195..d8dabb5 100644 --- a/firmware/ps2adapter/ProMini.h +++ b/firmware/ps2adapter/ProMini.h @@ -25,55 +25,50 @@ * The following defines Pin and Port addressing to * more quickly set individual pins to their desired * states. - *PS/2 data pin operations***Bit:76543210************/ - -#define PS2_DATA_BIT (_BV(3)) -#define PS2_SETDATAHIGH() ( PORTC |= PS2_DATA_BIT ) -#define PS2_SETDATALOW() ( PORTC &= ~PS2_DATA_BIT ) -#define PS2_SETDATA(val) (val) ? PS2_SETDATAHIGH() : PS2_SETDATALOW() -#define PS2_DIRDATAIN() ( DDRC &= ~PS2_DATA_BIT ) -#define PS2_DIRDATAIN_UP() ( DDRC &= ~PS2_DATA_BIT ) -#define PS2_DIRDATAOUT() ( DDRC |= PS2_DATA_BIT ) -#define PS2_READDATA() ( (PINC &= PS2_DATA_BIT)>>3 ) +*/ -/*PS/2 clock pin operations**Bit:76543210************/ -#define PS2_CLOCK_BIT (_BV(2)) -#define PS2_SETCLOCKHIGH ( PORTD |= PS2_CLOCK_BIT ) -#define PS2_SETCLOCKLOW ( PORTD &= ~PS2_CLOCK_BIT) -#define PS2_SETCLOCK(val) (val) ? PS2_SETCLOCKHIGH : PS2_SETCLOCKLOW -#define PS2_DIRCLOCKIN ( DDRD &= ~PS2_CLOCK_BIT ) -#define PS2_DIRCLOCKIN_UP ( DDRD &= ~PS2_CLOCK_BIT ); PS2_SETCLOCKHIGH -#define PS2_DIRCLOCKOUT ( DDRD |= PS2_CLOCK_BIT ) -#define PS2_READCLOCK ( (PIND &= PS2_CLOCK_BIT)>>2 ) +/****************************************************/ +/* PS/2 data pin operations */ +#define PS2_DATA_BIT (_BV(3)) +#define PS2_SETDATAHIGH() ( PORTC |= PS2_DATA_BIT ) +#define PS2_SETDATALOW() ( PORTC &= ~PS2_DATA_BIT ) +#define PS2_SETDATA(val) (val) ? PS2_SETDATAHIGH() : PS2_SETDATALOW() +#define PS2_DIRDATAIN() ( DDRC &= ~PS2_DATA_BIT ) +#define PS2_DIRDATAIN_UP() ( DDRC &= ~PS2_DATA_BIT ) +#define PS2_DIRDATAOUT() ( DDRC |= PS2_DATA_BIT ) +#define PS2_READDATA() ( (PINC &= PS2_DATA_BIT)>>3 ) -#define PS2_CLOCK_INT_CLEAR() ( EIFR = PS2_CLOCK_INT ) -#define PS2_CLOCK_INT_ENABLE() ( EIMSK |= _BV(INT0) ) -#define PS2_CLOCK_INT_DISABLE() ( EIMSK &= ~_BV(INT0) ) -#define PS2_CLOCK_INT_FALLING() ( EICRA = (EICRA & ~_BV(ISC00)) | _BV(ISC01) ) -#define PS2_CLOCK_INT_CHANGE() ( EICRA = (EICRA & ~_BV(ISC01)) | _BV(ISC00) ) +#define PS2_CLOCK_BIT (_BV(2)) +#define PS2_SETCLOCKHIGH() ( PORTD |= PS2_CLOCK_BIT ) +#define PS2_SETCLOCKLOW() ( PORTD &= ~PS2_CLOCK_BIT) +#define PS2_SETCLOCK(val) (val) ? PS2_SETCLOCKHIGH() : PS2_SETCLOCKLOW() +#define PS2_DIRCLOCKIN() ( DDRD &= ~PS2_CLOCK_BIT ) +#define PS2_DIRCLOCKIN_UP() ( DDRD &= ~PS2_CLOCK_BIT ); PS2_SETCLOCKHIGH() +#define PS2_DIRCLOCKOUT() ( DDRD |= PS2_CLOCK_BIT ) +#define PS2_READCLOCK() ( (PIND &= PS2_CLOCK_BIT)>>2 ) -/*RS-232 pin operations******Bit:76543210************/ -#define RS_SETTXHIGH (PORTD |=0b00010000) -#define RS_SETTXLOW (PORTD &=0b11101111) -#define RS_SETTX(val) (val) ? RS_SETTXHIGH : RS_SETTXLOW -#define RS_DIRTXOUT (DDRD |=0b00010000) /****************************************************/ +/* RS-232 pin operations */ +#define RS_SETTXHIGH() (PORTD |=0b00010000) +#define RS_SETTXLOW() (PORTD &=0b11101111) +#define RS_SETTX(val) (val) ? RS_SETTXHIGH() : RS_SETTXLOW() +#define RS_DIRTXOUT() (DDRD |=0b00010000) -/*JUMPER pin operations******Bit:76543210************/ -#define JP12_SETHIGH (PORTB |=0b00001000) -#define JP12_DIRIN (DDRB &=0b11110111) -#define JP12_DIRIN_UP (DDRB &=0b11110111); JP12_SETHIGH -#define JP12_READ ((PINB &=0b00001000)>>3) - -#define JP34_SETHIGH (PORTB |=0b00010000) -#define JP34_DIRIN (DDRB &=0b11101111) -#define JP34_DIRIN_UP (DDRB &=0b11101111); JP34_SETHIGH -#define JP34_READ ((PINB &=0b00010000)>>4) /****************************************************/ +/* JUMPER pin operations */ +#define JP12_SETHIGH() (PORTB |=0b00001000) +#define JP12_DIRIN() (DDRB &=0b11110111) +#define JP12_DIRIN_UP() (DDRB &=0b11110111); JP12_SETHIGH() +#define JP12_READ() ((PINB &=0b00001000)>>3) -/*LED pin operations*********Bit:76543210************/ -#define LED_SETHIGH() (PORTB |=0b00100000) -#define LED_SETLOW() (PORTB &=0b11011111) -#define LED_SET(val) (val) ? LED_SETHIGH() : LED_SETLOW() -#define LED_DIROUT() (DDRB |=0b00100000) -/****************************************************/ \ No newline at end of file +#define JP34_SETHIGH() (PORTB |=0b00010000) +#define JP34_DIRIN() (DDRB &=0b11101111) +#define JP34_DIRIN_UP() (DDRB &=0b11101111); JP34_SETHIGH() +#define JP34_READ() ((PINB &=0b00010000)>>4) + +/****************************************************/ +/* LED pin operations */ +#define LED_SETHIGH() (PORTB |=0b00100000) +#define LED_SETLOW() (PORTB &=0b11011111) +#define LED_SET(val) (val) ? LED_SETHIGH() : LED_SETLOW() +#define LED_DIROUT() (DDRB |=0b00100000) diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index df2bf52..b63e270 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -19,7 +19,6 @@ struct Packet { byte wheelData; }; - enum class Response { isMouse = 0x00, @@ -48,11 +47,11 @@ bool Ps2Mouse::reset() { return false; } - if (!recvByte(reply) || reply != byte(Response::selfTestPassed)) { + if (!getByte(reply) || reply != byte(Response::selfTestPassed)) { return false; } - if (!recvByte(reply) || reply != byte(Response::isMouse)) { + if (!getByte(reply) || reply != byte(Response::isMouse)) { return false; } @@ -61,7 +60,7 @@ bool Ps2Mouse::reset() { !sendCommand(Command::setSampleRate, 100) || !sendCommand(Command::setSampleRate, 80) || !sendCommand(Command::getDeviceId) || - !recvByte(reply)) { + !getByte(reply)) { return false; } @@ -73,7 +72,7 @@ bool Ps2Mouse::reset() { return setReporting(true); } -bool Ps2Mouse::setReporting(bool enable) const { +bool Ps2Mouse::setReporting(bool enable) { return sendCommand(enable ? Command::enableDataReporting : Command::disableDataReporting); } @@ -88,7 +87,7 @@ bool Ps2Mouse::setRemoteMode() { return (sendCommand(Command::setRemoteMode)); } -bool Ps2Mouse::setScaling(bool flag) const { +bool Ps2Mouse::setScaling(bool flag) { setReporting(false); bool res = sendCommand(flag ? Command::enableScaling : Command::disableScaling); @@ -96,7 +95,7 @@ bool Ps2Mouse::setScaling(bool flag) const { return res; } -bool Ps2Mouse::setResolution(byte resolution) const { +bool Ps2Mouse::setResolution(byte resolution) { setReporting(false); bool res = sendCommand(Command::setResolution, resolution); @@ -104,7 +103,7 @@ bool Ps2Mouse::setResolution(byte resolution) const { return res; } -bool Ps2Mouse::setSampleRate(byte sampleRate) const { +bool Ps2Mouse::setSampleRate(byte sampleRate) { setReporting(false); bool res = sendCommand(Command::setSampleRate, sampleRate); @@ -112,7 +111,7 @@ bool Ps2Mouse::setSampleRate(byte sampleRate) const { return res; } -bool Ps2Mouse::getSettings(Settings& settings) const { +bool Ps2Mouse::getSettings(Settings& settings) { Status status; setReporting(false); @@ -136,32 +135,6 @@ Ps2Mouse::MouseType Ps2Mouse::getType() const { return m_type; } -bool Ps2Mouse::readData(Data& data) const { - - if (PS2_READCLOCK != LOW) { - return false; - } - - Packet packet = {0}; - if (m_type == MouseType::wheelMouse) { - if (!recvData((byte*)&packet, sizeof(packet))) { - return false; - } - } else { - if (!recvData((byte*)&packet, sizeof(packet) - 1)) { - return false; - } - } - - data.leftButton = packet.leftButton; - data.middleButton = packet.middleButton; - data.rightButton = packet.rightButton; - data.xMovement = (packet.xSign ? -0x100 : 0) | packet.xMovement; - data.yMovement = (packet.ySign ? -0x100 : 0) | packet.yMovement; - data.wheelMovement = (m_type == MouseType::wheelMouse) ? packet.wheelData : 0; - return true; -} - void Ps2Mouse::clockInterruptStatic() { if (classPtr != NULL) { @@ -172,7 +145,7 @@ void Ps2Mouse::clockInterruptStatic() { void Ps2Mouse::startInterrupt() { // Enter receive mode - PS2_DIRCLOCKIN; + PS2_DIRCLOCKIN(); PS2_DIRDATAIN(); m_mouseBits = 0; m_bitCount = 0; @@ -189,28 +162,17 @@ void Ps2Mouse::stopInterrupt() { memset(m_buffer, 0, 256); } -bool Ps2Mouse::getByte(uint8_t* data) { - - if (m_bufferHead == m_bufferTail) { - return false; - } - m_bufferCount--; - *data = m_buffer[m_bufferTail++]; - return true; -} -bool Ps2Mouse::getBytes(uint8_t* data, int length) { +bool Ps2Mouse::readData(Data& data) { - while (length) { - if (getByte(data)) { - data++; - length--; - } + size_t pktSize = sizeof(Packet); + if (m_type != MouseType::wheelMouse) { + pktSize--; } - return true; -} -bool Ps2Mouse::readData2(Data& data) { + if (m_bufferCount < pktSize) { + return false; + } Packet packet = {0}; if (m_type == MouseType::wheelMouse) { @@ -240,7 +202,7 @@ void Ps2Mouse::interruptHandler() { if (m_bitCount == 0) { // start bit should ALWAYS be 0 if (bit) { - LED_SETHIGH(); + // LED_SETHIGH(); // error on start bit return; } @@ -272,58 +234,30 @@ void Ps2Mouse::interruptHandler() { m_bufferCount++; m_bufferHead++; m_bitCount++; - LED_SETLOW(); + //LED_SETLOW(); } else { - LED_SETHIGH(); + // LED_SETHIGH(); } m_bitCount = 0; } } -int Ps2Mouse::recvBit() const { - - while (PS2_READCLOCK != LOW) {} - auto result = PS2_READDATA(); - while (PS2_READCLOCK != HIGH) {} - return result; -} - - -bool Ps2Mouse::recvByte(byte& value) const { - - // Enter receive mode - PS2_DIRCLOCKIN; - PS2_DIRDATAIN(); - - // Receive start bit - if (recvBit() != 0) { - return false; - } - - // Receive data bits - value = 0; - byte parity = 1; - for (int i = 0; i < 8; i++) { - byte nextBit = recvBit(); - value |= nextBit << i; - parity ^= nextBit; - } - - // Receive parity bit - byte actualParity = recvBit(); - - // Receive stop bit - recvBit(); - - if (parity != actualParity) { - Serial.println("P!"); +bool Ps2Mouse::getByte(uint8_t& data) { + unsigned long count = 0; + while (m_bufferHead == m_bufferTail) { + if (count++ > 5000000) { + return false; + } } - return parity == actualParity; + m_bufferCount--; + data = m_buffer[m_bufferTail++]; + return true; } -bool Ps2Mouse::recvData(byte* data, size_t size) const { +bool Ps2Mouse::getBytes(uint8_t* data, size_t size) { + for (size_t i = 0u; i < size; i++) { - if (!recvByte(data[i])) { + if (!getByte(data[i])) { return false; } } @@ -332,22 +266,23 @@ bool Ps2Mouse::recvData(byte* data, size_t size) const { void Ps2Mouse::sendBit(int value) const { - while (PS2_READCLOCK != LOW) {} + while (PS2_READCLOCK() != LOW) {} PS2_SETDATA(value); - while (PS2_READCLOCK != HIGH) {} + while (PS2_READCLOCK() != HIGH) {} } -bool Ps2Mouse::sendByte(byte value) const { +bool Ps2Mouse::sendByte(byte value) { // Inhibit communication - PS2_DIRCLOCKOUT; - PS2_SETCLOCKLOW; + stopInterrupt(); + PS2_DIRCLOCKOUT(); + PS2_SETCLOCKLOW(); delayMicroseconds(10); // Set start bit and release the clock PS2_DIRDATAOUT(); PS2_SETDATALOW(); - PS2_DIRCLOCKIN_UP; + PS2_DIRCLOCKIN_UP(); // Send data bits byte parity = 1; @@ -365,14 +300,19 @@ bool Ps2Mouse::sendByte(byte value) const { // Enter receive mode and wait for ACK bit PS2_DIRDATAIN(); - return recvBit() == 0; + while (PS2_READCLOCK() != LOW) {} + byte ack = PS2_READDATA(); + while (PS2_READCLOCK() != HIGH) {} + + startInterrupt(); + return ack == 0; } -bool Ps2Mouse::sendByteWithAck(byte value) const { +bool Ps2Mouse::sendByteWithAck(byte value) { while (true) { if (sendByte(value)) { byte response; - if (recvByte(response)) { + if (getByte(response)) { if (response == static_cast(Response::resend)) { continue; } @@ -383,14 +323,14 @@ bool Ps2Mouse::sendByteWithAck(byte value) const { } } -bool Ps2Mouse::sendCommand(Command command) const { +bool Ps2Mouse::sendCommand(Command command) { return sendByteWithAck(static_cast(command)); } -bool Ps2Mouse::sendCommand(Command command, byte setting) const { +bool Ps2Mouse::sendCommand(Command command, byte setting) { return sendCommand(command) && sendByteWithAck(setting); } -bool Ps2Mouse::getStatus(Status& status) const { - return sendCommand(Command::statusRequest) && recvData((byte*) &status, sizeof(Status)); +bool Ps2Mouse::getStatus(Status& status) { + return sendCommand(Command::statusRequest) && getBytes((byte*) &status, sizeof(Status)); } diff --git a/firmware/ps2adapter/Ps2Mouse.h b/firmware/ps2adapter/Ps2Mouse.h index 1433c4f..7fb45e2 100644 --- a/firmware/ps2adapter/Ps2Mouse.h +++ b/firmware/ps2adapter/Ps2Mouse.h @@ -36,27 +36,21 @@ class Ps2Mouse { Ps2Mouse(); bool reset(); - bool setReporting(bool enable) const; + bool setReporting(bool enable); bool setStreamMode(); bool setRemoteMode(); - bool setScaling(bool flag) const; - bool setResolution(byte resolution) const; - bool setSampleRate(byte sampleRate) const; + bool setScaling(bool flag); + bool setResolution(byte resolution); + bool setSampleRate(byte sampleRate); - bool getSettings(Settings& settings) const; + bool getSettings(Settings& settings); Ps2Mouse::MouseType getType() const; - bool readData(Data& data) const; - void startInterrupt(); - void stopInterrupt(); - bool getByte(uint8_t* data); - bool getBytes(uint8_t* data, int length); - bool readData2(Data& data); - uint8_t getBufferCount() { return m_bufferCount; }; - uint8_t getBufferHead() { return m_bufferHead; }; - uint8_t getBufferTail() { return m_bufferTail; }; + bool readData(Data& data); + uint8_t getBufferCount() const { return m_bufferCount; }; + private: enum class Command { @@ -92,16 +86,18 @@ class Ps2Mouse { byte sampleRate; }; + bool getByte(uint8_t& data); + bool getBytes(uint8_t* data, size_t size); + void sendBit(int value) const; - int recvBit() const; - bool sendByte(byte value) const; - bool recvByte(byte& value) const; - bool recvData(byte* data, size_t size) const; - bool sendByteWithAck(byte value) const; - bool sendCommand(Command command) const; - bool sendCommand(Command command, byte setting) const; - bool getStatus(Status& status) const; + bool sendByte(byte value); + bool sendByteWithAck(byte value); + bool sendCommand(Command command); + bool sendCommand(Command command, byte setting); + bool getStatus(Status& status); + void startInterrupt(); + void stopInterrupt(); static void clockInterruptStatic(); void interruptHandler(); diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index d63e43b..0224f49 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -62,8 +62,11 @@ static void sendToSerial(const Ps2Mouse::Data& data) { static void initSerialPort() { + // disable the PS/2 mouse data reporting while the serial port is being initialized + mouse.setReporting(false); + Serial.println("Starting serial port"); - RS_SETTXHIGH; + RS_SETTXHIGH(); delayMicroseconds(10000); sendSerialByte('M'); if(!twoButtons) { @@ -89,12 +92,12 @@ static void initSerialPort() { static void initPs2Port() { - Serial.println("Reseting PS/2 mouse"); + Serial.print("Reseting PS/2 mouse... "); if (mouse.reset()) { - Serial.println("PS/2 mouse reset OK"); + Serial.println("OK"); } else { - Serial.println("Failed to reset PS/2 mouse"); + Serial.println("Failed!"); } if (mouse.setSampleRate(20)) { @@ -119,8 +122,7 @@ static void initPs2Port() { void resetSerialSignal() { // signal the main loop to ignore the PS/2 mouse data until the serial port is ready serialInitDone = false; - // disable the PS/2 mouse data reporting while the serial port is being initialized - mouse.setReporting(false); + // signal the main loop to initialize the serial port doSerialInit = true; } @@ -129,15 +131,15 @@ void setup() { // PS/2 Data input must be initialized shortly after power on, // or the mouse will not initialize PS2_DIRDATAIN_UP(); - RS_DIRTXOUT; - JP12_DIRIN_UP; - JP34_DIRIN_UP; + RS_DIRTXOUT(); + JP12_DIRIN_UP(); + JP34_DIRIN_UP(); LED_DIROUT(); LED_SETHIGH(); Serial.begin(115200); - twoButtons = (JP12_READ == LOW); - wheelMouse = (JP34_READ == LOW); + twoButtons = (JP12_READ() == LOW); + wheelMouse = (JP34_READ() == LOW); // PS/2 initialization can take > 400ms for reset to complete. // Init the mouse here, determine it's characteristics and then @@ -147,7 +149,6 @@ void setup() { attachInterrupt(digitalPinToInterrupt(RS232_RTS), resetSerialSignal, FALLING); Serial.println("Setup done!"); - mouse.startInterrupt(); LED_SETLOW(); } @@ -176,7 +177,7 @@ static unsigned long lastMillis = 0; void processStateMachine() { Ps2Mouse::Data data; - bool validData = mouse.readData2(data); + bool validData = mouse.readData(data); blinker.update(); switch (settingState) { @@ -201,7 +202,7 @@ void processStateMachine() { if (millis() - lastMillis > 3000) { settingState = SettingState::SettingStateConfirmed; lastMillis = millis(); - blinker.enable(); + blinker.enable(500); } break; case SettingState::SettingStateConfirmed: @@ -214,7 +215,7 @@ void processStateMachine() { // left handed mode selected if (data.rightButton && !data.leftButton && !data.middleButton) { settingState = SettingState::ProcessMouse; - blinker.disable(); + blinker.message(200, 3); } } break; @@ -226,21 +227,8 @@ void loop() { initSerialPort(); doSerialInit = false; } - if (serialInitDone) { - //processStateMachine(); - //return; - Ps2Mouse::Data data; - mouse.readData2(data); - Serial.println("count = " + String(mouse.getBufferCount())); - Serial.println("head = " + String(mouse.getBufferHead())); - Serial.println("tail = " + String(mouse.getBufferTail())); - - Serial.print("LB = "); Serial.println(data.leftButton); - Serial.print("MB = "); Serial.println(data.middleButton); - Serial.print("RB = "); Serial.println(data.rightButton); - Serial.print("X = "); Serial.println(data.xMovement); - Serial.print("Y = "); Serial.println(data.yMovement); - Serial.print("W = "); Serial.println(data.wheelMovement); + if (serialInitDone) { + processStateMachine(); } } From 840f3f2c0b0b9ca6db5674fde9409f0815bab87f Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Thu, 20 Jun 2024 19:21:02 +0200 Subject: [PATCH 10/12] made Ps2Mouse a singleton --- firmware/ps2adapter/Blinker.cpp | 8 +++++++- firmware/ps2adapter/Ps2Mouse.cpp | 19 +++++++++++++++++-- firmware/ps2adapter/Ps2Mouse.h | 4 +++- firmware/ps2adapter/ps2adapter.ino | 29 ++++++++++------------------- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/firmware/ps2adapter/Blinker.cpp b/firmware/ps2adapter/Blinker.cpp index deb77ac..97676f5 100644 --- a/firmware/ps2adapter/Blinker.cpp +++ b/firmware/ps2adapter/Blinker.cpp @@ -2,7 +2,13 @@ #include "Blinker.h" #include "ProMini.h" -Blinker::Blinker() : _ms(500), _lastMark(0), _state(false), _enabled(false), _count(0) {} +Blinker::Blinker() { + _ms = 500; + _lastMark =0; + _state = false; + _enabled = false; + _count = 0; +} void Blinker::message(unsigned long ms, int count) { _count = count << 1; diff --git a/firmware/ps2adapter/Ps2Mouse.cpp b/firmware/ps2adapter/Ps2Mouse.cpp index b63e270..7d87751 100644 --- a/firmware/ps2adapter/Ps2Mouse.cpp +++ b/firmware/ps2adapter/Ps2Mouse.cpp @@ -33,10 +33,25 @@ enum class Response { static Ps2Mouse* classPtr = NULL; +Ps2Mouse* Ps2Mouse::instance() { + + if (classPtr == NULL) { + classPtr = new Ps2Mouse(); + } + return classPtr; +} + Ps2Mouse::Ps2Mouse() - : m_type(MouseType::threeButton) { - classPtr = this; + + m_type = MouseType::threeButton; + m_mouseBits = 0; + m_bitCount = 0; + m_parityBit = 0; + m_bufferTail = 0; + m_bufferHead = 0; + m_bufferCount = 0; + memset(m_buffer, 0, 256); } bool Ps2Mouse::reset() { diff --git a/firmware/ps2adapter/Ps2Mouse.h b/firmware/ps2adapter/Ps2Mouse.h index 7fb45e2..24a5f52 100644 --- a/firmware/ps2adapter/Ps2Mouse.h +++ b/firmware/ps2adapter/Ps2Mouse.h @@ -33,7 +33,7 @@ class Ps2Mouse { byte sampleRate; }; - Ps2Mouse(); + static Ps2Mouse* instance(); bool reset(); bool setReporting(bool enable); @@ -86,6 +86,8 @@ class Ps2Mouse { byte sampleRate; }; + Ps2Mouse(); + bool getByte(uint8_t& data); bool getBytes(uint8_t* data, size_t size); diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index 0224f49..b2ac783 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -4,7 +4,7 @@ static const int RS232_RTS = 3; -static Ps2Mouse mouse; +static Ps2Mouse* pMouse; static bool twoButtons = false; static bool wheelMouse = false; static volatile bool doSerialInit = true; @@ -63,7 +63,7 @@ static void sendToSerial(const Ps2Mouse::Data& data) { static void initSerialPort() { // disable the PS/2 mouse data reporting while the serial port is being initialized - mouse.setReporting(false); + pMouse->setReporting(false); Serial.println("Starting serial port"); RS_SETTXHIGH(); @@ -71,7 +71,7 @@ static void initSerialPort() { sendSerialByte('M'); if(!twoButtons) { // if wheelMouse mode has been set by jumper, confirm that the mouse has the capability - if(wheelMouse && (mouse.getType() == Ps2Mouse::MouseType::wheelMouse)) { + if(wheelMouse && (pMouse->getType() == Ps2Mouse::MouseType::wheelMouse)) { // set wheelMouse mode sendSerialByte('Z'); Serial.println("Init wheel mode"); @@ -85,7 +85,7 @@ static void initSerialPort() { } delayMicroseconds(10000); // Renable PS/2 mouse data reporting - mouse.setReporting(true); + pMouse->setReporting(true); // Signal the main loop that the serial port is ready serialInitDone = true; } @@ -94,20 +94,20 @@ static void initPs2Port() { Serial.print("Reseting PS/2 mouse... "); - if (mouse.reset()) { + if (pMouse->reset()) { Serial.println("OK"); } else { Serial.println("Failed!"); } - if (mouse.setSampleRate(20)) { + if (pMouse->setSampleRate(20)) { Serial.println("Sample rate set to 20"); } else { Serial.println("Failed to set sample rate"); } Ps2Mouse::Settings settings; - if (mouse.getSettings(settings)) { + if (pMouse->getSettings(settings)) { Serial.print("scaling = "); Serial.println(settings.scaling); Serial.print("resolution = "); @@ -120,9 +120,9 @@ static void initPs2Port() { } void resetSerialSignal() { + // signal the main loop to ignore the PS/2 mouse data until the serial port is ready serialInitDone = false; - // signal the main loop to initialize the serial port doSerialInit = true; } @@ -140,6 +140,7 @@ void setup() { Serial.begin(115200); twoButtons = (JP12_READ() == LOW); wheelMouse = (JP34_READ() == LOW); + pMouse = Ps2Mouse::instance(); // PS/2 initialization can take > 400ms for reset to complete. // Init the mouse here, determine it's characteristics and then @@ -157,18 +158,8 @@ enum class SettingState { SettingEnterDetected, SettingStateConfirmed, - }; -void blinkNtimes(int n, unsigned long ms = 100) { - for (int i = 0; i < n; i++) { - LED_SET(HIGH); - delay(ms); - LED_SET(LOW); - delay(ms); - } -} - Blinker blinker; static SettingState settingState = SettingState::ProcessMouse; @@ -177,7 +168,7 @@ static unsigned long lastMillis = 0; void processStateMachine() { Ps2Mouse::Data data; - bool validData = mouse.readData(data); + bool validData = pMouse->readData(data); blinker.update(); switch (settingState) { From 6e1a0f2cd64294055c279e5dea8b0a30d4424b05 Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Sat, 22 Jun 2024 10:01:17 +0200 Subject: [PATCH 11/12] timer based LED messenger class for settings feedback --- firmware/ps2adapter/Blinker.cpp | 48 ----------- firmware/ps2adapter/Blinker.h | 15 ---- firmware/ps2adapter/LEDMessenger.cpp | 122 +++++++++++++++++++++++++++ firmware/ps2adapter/LEDMessenger.h | 38 +++++++++ firmware/ps2adapter/ps2adapter.ino | 29 ++++--- 5 files changed, 179 insertions(+), 73 deletions(-) delete mode 100644 firmware/ps2adapter/Blinker.cpp delete mode 100644 firmware/ps2adapter/Blinker.h create mode 100644 firmware/ps2adapter/LEDMessenger.cpp create mode 100644 firmware/ps2adapter/LEDMessenger.h diff --git a/firmware/ps2adapter/Blinker.cpp b/firmware/ps2adapter/Blinker.cpp deleted file mode 100644 index 97676f5..0000000 --- a/firmware/ps2adapter/Blinker.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include "Blinker.h" -#include "ProMini.h" - -Blinker::Blinker() { - _ms = 500; - _lastMark =0; - _state = false; - _enabled = false; - _count = 0; -} - -void Blinker::message(unsigned long ms, int count) { - _count = count << 1; - enable(ms); -} - -void Blinker::enable(unsigned long ms) { - _state = true; - _enabled = true; - _lastMark = millis(); - _ms = ms; - LED_SETHIGH(); -} - -void Blinker::disable() { - _state = false; - _enabled = false; - LED_SETLOW(); -} - -void Blinker::update() { - if (!_enabled) { - return; - } - - if (millis() - _lastMark > _ms) { - _lastMark = millis(); - _state = !_state; - LED_SET(_state ? HIGH : LOW); - if (_count) { - _count--; - if (!_count) { - disable(); - } - } - } -} diff --git a/firmware/ps2adapter/Blinker.h b/firmware/ps2adapter/Blinker.h deleted file mode 100644 index ac4e17c..0000000 --- a/firmware/ps2adapter/Blinker.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -class Blinker { - public: - Blinker(); - void message(unsigned long ms, int count); - void enable(unsigned long ms); - void disable(); - void update(); - private: - unsigned long _ms, _lastMark; - bool _state; - bool _enabled; - int _count; -}; \ No newline at end of file diff --git a/firmware/ps2adapter/LEDMessenger.cpp b/firmware/ps2adapter/LEDMessenger.cpp new file mode 100644 index 0000000..76f0c97 --- /dev/null +++ b/firmware/ps2adapter/LEDMessenger.cpp @@ -0,0 +1,122 @@ +#include +#include "LEDMessenger.h" +#include "ProMini.h" + + +LEDMessenger::LEDMessenger() { + list = NULL; + length = 0; + m_timerResolution = 10; + m_nextMessageDelay = 0; +}; + +LEDMessenger::~LEDMessenger() { + while (list) { + node *tmp = list; + list = list->next; + delete tmp; + } +}; + +LEDMessenger* LEDMessenger::instance = NULL; + +void LEDMessenger::TimerStatic() { + LEDMessenger::instance->update(); +}; + +typedef void (*voidFuncPtr)(void); +static volatile voidFuncPtr intFunc = NULL; + +ISR(TIMER1_OVF_vect) +{ + TCNT1 = 45535; // Timer Preloading + if (intFunc != NULL) + intFunc(); +} + +void LEDMessenger::start() { + instance = this; + intFunc = TimerStatic; + LED_SETLOW(); + TCCR1A = 0; // Init Timer1A + TCCR1B = 0; // Init Timer1B + TCCR1B |= B00000010; // Prescaler = 64 + TCNT1 = 45535; // Timer Preloading + TIMSK1 |= B00000001; // Enable Timer Overflow Interrupt +}; + +void LEDMessenger::push(int delay, int repeat, int nextMessageDelay) { + node *newNode = new node; + newNode->item.state = HIGH; + newNode->item.stepDelay = delay; + newNode->item.curStepDelay = delay; + newNode->item.repeat = repeat; + newNode->item.nextMessageDelay = nextMessageDelay; + newNode->next = NULL; + if (list) { + node *tmp = list; + while (tmp->next) { + tmp = tmp->next; + } + tmp->next = newNode; + } else { + list = newNode; + } + length++; +}; + +void LEDMessenger::pop() { + if (list) { + node *tmp = list; + list = list->next; + delete tmp; + length--; + } +}; + +LEDMessenger::LEDMessage* LEDMessenger::get() { + if (list) { + return &(list->item); + } else { + return NULL; + }; +}; + +int LEDMessenger::size() { + return length; +}; + +void LEDMessenger::update() { + if (m_nextMessageDelay > 0) { + m_nextMessageDelay -= m_nextMessageDelay; + return; + } + + LEDMessenger::LEDMessage* pItem = get(); + if (pItem) { + LED_SET(pItem->state); + if (pItem->curStepDelay > 0) { + pItem->curStepDelay -= m_timerResolution; + } else { + pItem->curStepDelay = pItem->stepDelay; + pItem->state = HIGH - pItem->state; + // one full HIGH -> LOW -> HIGH transistion + if (pItem->state == HIGH) { + // continous blink + if (pItem->repeat < 0) { + // waiting message + if (size() > 1) { + m_nextMessageDelay = pItem->nextMessageDelay; + pop(); + } + return; + } + pItem->repeat--; + if (pItem->repeat <= 0) { + m_nextMessageDelay = pItem->nextMessageDelay; + pop(); + } + } + } + } +} diff --git a/firmware/ps2adapter/LEDMessenger.h b/firmware/ps2adapter/LEDMessenger.h new file mode 100644 index 0000000..bc8b61d --- /dev/null +++ b/firmware/ps2adapter/LEDMessenger.h @@ -0,0 +1,38 @@ +#pragma once + +class LEDMessenger +{ +public: + LEDMessenger(); + ~LEDMessenger(); + void start(); + void push(int delay, int repeat, int nextMessageDelay); + +private: + int m_timerResolution; + int m_nextMessageDelay; + static LEDMessenger *instance; + + struct LEDMessage { + byte state; + int repeat; + int stepDelay; + int curStepDelay; + int nextMessageDelay; + }; + + typedef struct node + { + LEDMessage item; + node *next; + } node; + + node *list; + int length; + + void update(); + static void TimerStatic(); + void pop(); + LEDMessage* get(); + int size(); +}; diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index b2ac783..f15abcf 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -1,6 +1,6 @@ #include "ProMini.h" #include "Ps2Mouse.h" -#include "Blinker.h" +#include "LEDMessenger.h" static const int RS232_RTS = 3; @@ -127,6 +127,8 @@ void resetSerialSignal() { doSerialInit = true; } +LEDMessenger blinker; + void setup() { // PS/2 Data input must be initialized shortly after power on, // or the mouse will not initialize @@ -138,6 +140,8 @@ void setup() { LED_SETHIGH(); Serial.begin(115200); + blinker.start(); + twoButtons = (JP12_READ() == LOW); wheelMouse = (JP34_READ() == LOW); pMouse = Ps2Mouse::instance(); @@ -157,56 +161,61 @@ enum class SettingState { ProcessMouse, SettingEnterDetected, SettingStateConfirmed, - }; -Blinker blinker; static SettingState settingState = SettingState::ProcessMouse; static unsigned long lastMillis = 0; +static bool swapButtons = false; void processStateMachine() { Ps2Mouse::Data data; bool validData = pMouse->readData(data); - blinker.update(); switch (settingState) { case SettingState::ProcessMouse: if (!validData) { return; } - Serial.println("PM"); if (data.leftButton && data.rightButton && data.middleButton) { settingState = SettingState::SettingEnterDetected; lastMillis = millis(); + blinker.push(500, -1, 500); } else { + if (swapButtons) { + bool tmp = data.leftButton; + data.leftButton = data.rightButton; + data.rightButton = tmp; + } + Serial.println("L: " + String(data.leftButton) + " M: " + String(data.middleButton) + " R: " + String(data.rightButton) + " X: " + String(data.xMovement) + " Y: " + String(data.yMovement) + " W: " + String(data.wheelMovement)); sendToSerial(data); } break; case SettingState::SettingEnterDetected: - Serial.println("SED"); if (validData && !(data.leftButton && data.rightButton && data.middleButton)) { settingState = SettingState::ProcessMouse; + blinker.push(0, 0, 200); return; } if (millis() - lastMillis > 3000) { settingState = SettingState::SettingStateConfirmed; lastMillis = millis(); - blinker.enable(500); + blinker.push(300, -1, 500); } break; case SettingState::SettingStateConfirmed: - Serial.println("SSC"); if (!validData) { return; } if (millis() - lastMillis > 1000) { - // left handed mode selected + // button swap mode selected if (data.rightButton && !data.leftButton && !data.middleButton) { + swapButtons = !swapButtons; + Serial.println("Button Swap: " + String(swapButtons)); settingState = SettingState::ProcessMouse; - blinker.message(200, 3); + blinker.push(200, 3, 1000); } } break; From 520e598792d31684e4f0202a5ccecd94f6f12b3c Mon Sep 17 00:00:00 2001 From: Peter Edwards Date: Thu, 27 Jun 2024 13:23:50 +0200 Subject: [PATCH 12/12] grammar --- firmware/ps2adapter/ps2adapter.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/ps2adapter/ps2adapter.ino b/firmware/ps2adapter/ps2adapter.ino index f15abcf..4a6b26c 100644 --- a/firmware/ps2adapter/ps2adapter.ino +++ b/firmware/ps2adapter/ps2adapter.ino @@ -147,7 +147,7 @@ void setup() { pMouse = Ps2Mouse::instance(); // PS/2 initialization can take > 400ms for reset to complete. - // Init the mouse here, determine it's characteristics and then + // Init the mouse here, determine its characteristics and then // wait for the RTS signal to initialize the serial port comms initPs2Port(); Serial.println("Listening on RTS");