From f6400b3bff0be2e2c91505dbd888a3c674a120b0 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 16 Nov 2020 10:25:44 -0300 Subject: [PATCH 1/9] Adding frame count to full example --- examples/full/src/scenes/TestScene.cpp | 10 ++++++++-- examples/full/src/scenes/TestScene.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/full/src/scenes/TestScene.cpp b/examples/full/src/scenes/TestScene.cpp index aa6eec8..9ac2501 100644 --- a/examples/full/src/scenes/TestScene.cpp +++ b/examples/full/src/scenes/TestScene.cpp @@ -42,6 +42,8 @@ void TestScene::tick(u16 keys) { if (engine->isTransitioning()) return; + frameCounter++; + // check keys aHandler->setIsPressed(keys & KEY_A); bHandler->setIsPressed(keys & KEY_B); @@ -78,6 +80,9 @@ void TestScene::tick(u16 keys) { value = counter; } + if (linkState->isConnected() && linkState->currentPlayerId == 0) + linkConnection->send(10000); + // send data if (lHandler->hasBeenPressedNow()) { send(1); @@ -93,7 +98,8 @@ void TestScene::tick(u16 keys) { for (u32 i = 0; i < linkState->playerCount; i++) while (linkState->hasMessage(i)) { u16 message = linkState->readMessage(i); - if (i != linkState->currentPlayerId) - DEBULOG("<-p" + asStr(i) + ": " + asStr(message)); + if (i != linkState->currentPlayerId && message != 10000) + DEBULOG("<-p" + asStr(i) + ": " + asStr(message) + " (frame " + + asStr(frameCounter) + ")"); } } diff --git a/examples/full/src/scenes/TestScene.h b/examples/full/src/scenes/TestScene.h index 90b4067..a2c5a5f 100644 --- a/examples/full/src/scenes/TestScene.h +++ b/examples/full/src/scenes/TestScene.h @@ -23,6 +23,7 @@ class TestScene : public Scene { u32 counter = 0; bool isConnected = false; bool initialized = false; + u32 frameCounter = 0; void log(std::string text); }; From efccd8f5a3b6dadadccba38b9c62e8e9416c90ae Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 16 Nov 2020 10:43:23 -0300 Subject: [PATCH 2/9] Adding manual sync --- examples/full/src/scenes/TestScene.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/full/src/scenes/TestScene.cpp b/examples/full/src/scenes/TestScene.cpp index 9ac2501..998e8c4 100644 --- a/examples/full/src/scenes/TestScene.cpp +++ b/examples/full/src/scenes/TestScene.cpp @@ -83,11 +83,14 @@ void TestScene::tick(u16 keys) { if (linkState->isConnected() && linkState->currentPlayerId == 0) linkConnection->send(10000); + while (linkState->isConnected() && !linkState->_IRQFlag) + ; + // send data if (lHandler->hasBeenPressedNow()) { send(1); send(2); - } else if (rHandler->hasBeenPressedNow()) { + } else if (rHandler->getIsPressed()) { send(43981); send(257); } else if (value != LINK_NO_DATA) From 75c08976ab996cdfec1ffbd898951727075ae90f Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 16 Nov 2020 16:15:43 -0300 Subject: [PATCH 3/9] Updating test --- examples/full/src/main.cpp | 8 ++++---- examples/full/src/scenes/TestScene.cpp | 17 +++-------------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/examples/full/src/main.cpp b/examples/full/src/main.cpp index 5d6ccac..31b33fa 100644 --- a/examples/full/src/main.cpp +++ b/examples/full/src/main.cpp @@ -74,10 +74,10 @@ void printTutorial() { DEBULOG("START: turn on connection"); DEBULOG("(on connection, p1 sends 999)"); DEBULOG(""); - DEBULOG("A: send 555 once per frame"); - DEBULOG("B: send counter once"); - DEBULOG("L: send 1, then 2"); - DEBULOG("R: send 43981, then 257"); + DEBULOG("A: send counter (once)"); + DEBULOG("B: send counter (cont)"); + DEBULOG("L: send 257, then 43981 (once)"); + DEBULOG("R: send 257, then 43981 (cont)"); DEBULOG("SELECT: force lag (9k lines)"); DEBULOG("DOWN: turn off connection"); DEBULOG(""); diff --git a/examples/full/src/scenes/TestScene.cpp b/examples/full/src/scenes/TestScene.cpp index 998e8c4..b60f850 100644 --- a/examples/full/src/scenes/TestScene.cpp +++ b/examples/full/src/scenes/TestScene.cpp @@ -73,26 +73,15 @@ void TestScene::tick(u16 keys) { initialized = true; value = 999; } - if (aHandler->getIsPressed()) - value = 555; - if (bHandler->hasBeenPressedNow()) { + if (aHandler->getIsPressed() || bHandler->hasBeenPressedNow()) { counter++; value = counter; } - if (linkState->isConnected() && linkState->currentPlayerId == 0) - linkConnection->send(10000); - - while (linkState->isConnected() && !linkState->_IRQFlag) - ; - // send data - if (lHandler->hasBeenPressedNow()) { - send(1); - send(2); - } else if (rHandler->getIsPressed()) { - send(43981); + if (rHandler->getIsPressed() || lHandler->hasBeenPressedNow()) { send(257); + send(43981); } else if (value != LINK_NO_DATA) send(value); From 935cae5eb71ab813685a2f28a5f346243443f397 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Mon, 16 Nov 2020 19:33:42 -0300 Subject: [PATCH 4/9] Improving robustness and speed using timer 3 --- README.md | 6 ++- examples/basic/src/main.cpp | 1 + examples/full/src/main.cpp | 13 +++-- examples/full/src/scenes/TestScene.cpp | 6 ++- lib/LinkConnection.h | 69 +++++++++++++------------- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index a2a5e20..23ed3e1 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,9 @@ Name | Type | Default | Description `startNow` | **bool** | `true` | Automatically starts serial communication. Otherwise, you'll need to call `linkConnection->activate()`. `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate. `timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection. -`bufferSize` | **u32** | `60` | Number of messages that the queues will be able to store. +`bufferSize` | **u32** | `10` | Number of messages that the queues will be able to store. +`timerId` | **u8** (0~3) | `3` | GBA Timer to use. +`frequency` | **u16** | `25` | Number of 1024cycles/61.04μs ticks between messages *(25 = 1,526ms)*. ## Makefile actions @@ -140,4 +142,4 @@ Above is not counting the additional CPU time that must be spent on initiating a > Fast One-Way Transmission Beside for the actual SIO Multiplayer mode, you can also use SIO Normal mode for fast one-way data transfer from Master unit to all Child unit(s). See chapter about SIO Normal mode for details. -``` \ No newline at end of file +``` diff --git a/examples/basic/src/main.cpp b/examples/basic/src/main.cpp index c158aca..03de0cb 100644 --- a/examples/basic/src/main.cpp +++ b/examples/basic/src/main.cpp @@ -17,6 +17,7 @@ void init() { irq_init(NULL); // (2) Add the interrupt service routines + irq_add(II_TIMER3, LINK_ISR_TIMER); irq_add(II_VBLANK, LINK_ISR_VBLANK); irq_add(II_SERIAL, LINK_ISR_SERIAL); } diff --git a/examples/full/src/main.cpp b/examples/full/src/main.cpp index 31b33fa..2b1c90f 100644 --- a/examples/full/src/main.cpp +++ b/examples/full/src/main.cpp @@ -57,10 +57,9 @@ inline void ISR_reset() { inline void setUpInterrupts() { irq_init(NULL); - // VBlank + // LinkConnection irq_add(II_VBLANK, LINK_ISR_VBLANK); - - // Link connection + irq_add(II_TIMER3, LINK_ISR_TIMER); irq_add(II_SERIAL, LINK_ISR_SERIAL); // A+B+START+SELECT @@ -74,10 +73,10 @@ void printTutorial() { DEBULOG("START: turn on connection"); DEBULOG("(on connection, p1 sends 999)"); DEBULOG(""); - DEBULOG("A: send counter (once)"); - DEBULOG("B: send counter (cont)"); - DEBULOG("L: send 257, then 43981 (once)"); - DEBULOG("R: send 257, then 43981 (cont)"); + DEBULOG("A: send counter++ (once)"); + DEBULOG("B: send counter++ (cont)"); + DEBULOG("L: send counter++ twice (once)"); + DEBULOG("R: send counter++ twice (cont)"); DEBULOG("SELECT: force lag (9k lines)"); DEBULOG("DOWN: turn off connection"); DEBULOG(""); diff --git a/examples/full/src/scenes/TestScene.cpp b/examples/full/src/scenes/TestScene.cpp index b60f850..1eccfe9 100644 --- a/examples/full/src/scenes/TestScene.cpp +++ b/examples/full/src/scenes/TestScene.cpp @@ -80,8 +80,10 @@ void TestScene::tick(u16 keys) { // send data if (rHandler->getIsPressed() || lHandler->hasBeenPressedNow()) { - send(257); - send(43981); + counter++; + send(counter); + counter++; + send(counter); } else if (value != LINK_NO_DATA) send(value); diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h index d1dd372..6176652 100644 --- a/lib/LinkConnection.h +++ b/lib/LinkConnection.h @@ -11,9 +11,11 @@ #define LINK_MAX_PLAYERS 4 #define LINK_DISCONNECTED 0xFFFF #define LINK_NO_DATA 0x0 -#define LINK_TRANSFER_VCOUNT_WAIT 2 #define LINK_DEFAULT_TIMEOUT 3 -#define LINK_DEFAULT_BUFFER_SIZE 60 +#define LINK_DEFAULT_BUFFER_SIZE 10 +#define LINK_DEFAULT_TIMER_ID 3 +#define LINK_DEFAULT_FREQUENCY 25 +#define LINK_BASE_FREQUENCY TM_FREQ_1024 #define LINK_BIT_SLAVE 2 #define LINK_BIT_READY 3 #define LINK_BITS_PLAYER_ID 4 @@ -33,6 +35,7 @@ // LinkConnection* linkConnection = new LinkConnection(); // - 2) Add the interrupt service routines: // irq_add(II_VBLANK, LINK_ISR_VBLANK); +// irq_add(II_TIMER3, LINK_ISR_TIMER); // irq_add(II_SERIAL, LINK_ISR_SERIAL); // - 3) Send/read messages by using: // linkConnection->send(...); @@ -43,6 +46,7 @@ // (they mean 'disconnected' and 'no data' respectively) void LINK_ISR_VBLANK(); +void LINK_ISR_TIMER(); void LINK_ISR_SERIAL(); u16 LINK_QUEUE_POP(std::queue& q); void LINK_QUEUE_CLEAR(std::queue& q); @@ -84,10 +88,14 @@ class LinkConnection { explicit LinkConnection(bool startNow = true, BaudRate baudRate = BAUD_RATE_3, u32 timeout = LINK_DEFAULT_TIMEOUT, - u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE) { + u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE, + u8 timerId = LINK_DEFAULT_TIMER_ID, + u32 frequency = LINK_DEFAULT_FREQUENCY) { this->baudRate = baudRate; this->timeout = timeout; this->bufferSize = bufferSize; + this->timerId = timerId; + this->frequency = frequency; if (startNow) activate(); @@ -115,9 +123,7 @@ class LinkConnection { push(linkState->_outgoingMessages, data); } - bool isReady() { - return isBitHigh(LINK_BIT_READY) && !isBitHigh(LINK_BIT_ERROR); - } + bool hasError() { return isBitHigh(LINK_BIT_ERROR); } void _onVBlank() { if (!isEnabled || resetIfNeeded()) @@ -128,17 +134,22 @@ class LinkConnection { if (linkState->_IRQTimeout >= timeout) reset(); - else if (isMaster()) - transfer(LINK_NO_DATA, true); } linkState->_IRQFlag = false; } - void _onSerial() { + void _onTimer() { if (!isEnabled || resetIfNeeded()) return; + sendPendingData(); + } + + void _onSerial() { + if (!isEnabled || resetIfNeeded(false)) + return; + linkState->_IRQFlag = true; linkState->_IRQTimeout = 0; @@ -156,36 +167,29 @@ class LinkConnection { } else LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); } - - if (linkState->isConnected()) - sendPendingData(); } private: BaudRate baudRate; u32 timeout; u32 bufferSize; + u8 timerId; + u32 frequency; bool isEnabled = false; void sendPendingData() { transfer(LINK_QUEUE_POP(linkState->_outgoingMessages)); } - void transfer(u16 data, bool force = false) { - bool shouldNotify = isMaster() && (data != LINK_NO_DATA || force); - - if (shouldNotify) - setBitLow(LINK_BIT_START); - - wait(LINK_TRANSFER_VCOUNT_WAIT); + void transfer(u16 data) { REG_SIOMLT_SEND = data; - if (shouldNotify) + if (isMaster()) setBitHigh(LINK_BIT_START); } - bool resetIfNeeded() { - if (!isReady()) { + bool resetIfNeeded(bool checkErrors = false) { + if (checkErrors && hasError()) { reset(); return true; } @@ -210,11 +214,16 @@ class LinkConnection { } void stop() { + REG_TM[timerId].cnt = REG_TM[timerId].cnt & (~TM_ENABLE); + LINK_SET_LOW(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_LOW); LINK_SET_HIGH(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_HIGH); } void start() { + REG_TM[timerId].start = -frequency; + REG_TM[timerId].cnt = TM_ENABLE | TM_IRQ | LINK_BASE_FREQUENCY; + LINK_SET_LOW(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_HIGH); REG_SIOCNT = baudRate; REG_SIOMLT_SEND = 0; @@ -229,18 +238,6 @@ class LinkConnection { q.push(value); } - void wait(u32 verticalLines) { - u32 lines = 0; - u32 vCount = REG_VCOUNT; - - while (lines < verticalLines) { - if (REG_VCOUNT != vCount) { - lines++; - vCount = REG_VCOUNT; - } - }; - } - bool isMaster() { return !isBitHigh(LINK_BIT_SLAVE); } bool isBitHigh(u8 bit) { return (REG_SIOCNT >> bit) & 1; } void setBitHigh(u8 bit) { LINK_SET_HIGH(REG_SIOCNT, bit); } @@ -253,6 +250,10 @@ inline void LINK_ISR_VBLANK() { linkConnection->_onVBlank(); } +inline void LINK_ISR_TIMER() { + linkConnection->_onTimer(); +} + inline void LINK_ISR_SERIAL() { linkConnection->_onSerial(); } From ce2edf56db8287caffe1f3f1d95e074e496a6fa5 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 17 Nov 2020 11:45:22 -0300 Subject: [PATCH 5/9] Adding waitCycles between transfers --- examples/full/src/main.cpp | 1 + examples/full/src/scenes/TestScene.cpp | 2 +- lib/LinkConnection.h | 72 +++++++++++++++++++------- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/examples/full/src/main.cpp b/examples/full/src/main.cpp index 2b1c90f..d4f1054 100644 --- a/examples/full/src/main.cpp +++ b/examples/full/src/main.cpp @@ -60,6 +60,7 @@ inline void setUpInterrupts() { // LinkConnection irq_add(II_VBLANK, LINK_ISR_VBLANK); irq_add(II_TIMER3, LINK_ISR_TIMER); + irq_add(II_TIMER2, NULL); irq_add(II_SERIAL, LINK_ISR_SERIAL); // A+B+START+SELECT diff --git a/examples/full/src/scenes/TestScene.cpp b/examples/full/src/scenes/TestScene.cpp index 1eccfe9..8029943 100644 --- a/examples/full/src/scenes/TestScene.cpp +++ b/examples/full/src/scenes/TestScene.cpp @@ -69,7 +69,7 @@ void TestScene::tick(u16 keys) { // determine which value should be sent u16 value = LINK_NO_DATA; - if (!initialized && linkConnection->linkState->currentPlayerId == 1) { + if (!initialized && linkState->currentPlayerId == 1) { initialized = true; value = 999; } diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h index 6176652..70b9f19 100644 --- a/lib/LinkConnection.h +++ b/lib/LinkConnection.h @@ -1,6 +1,7 @@ #ifndef LINK_CONNECTION_H #define LINK_CONNECTION_H +#include #include #include #include @@ -8,6 +9,8 @@ #include #include +#include "utils/SceneUtils.h" // TODO: REMOVE + #define LINK_MAX_PLAYERS 4 #define LINK_DISCONNECTED 0xFFFF #define LINK_NO_DATA 0x0 @@ -50,6 +53,8 @@ void LINK_ISR_TIMER(); void LINK_ISR_SERIAL(); u16 LINK_QUEUE_POP(std::queue& q); void LINK_QUEUE_CLEAR(std::queue& q); +const u16 LINK_TIMER_IRQ_IDS[] = {IRQ_TIMER0, IRQ_TIMER1, IRQ_TIMER2, + IRQ_TIMER3}; struct LinkState { u8 playerCount; @@ -123,50 +128,63 @@ class LinkConnection { push(linkState->_outgoingMessages, data); } + bool isReady() { return isBitHigh(LINK_BIT_READY); } bool hasError() { return isBitHigh(LINK_BIT_ERROR); } void _onVBlank() { - if (!isEnabled || resetIfNeeded()) + if (!isEnabled) return; - if (!linkState->_IRQFlag) { + if (!linkState->_IRQFlag) linkState->_IRQTimeout++; - if (linkState->_IRQTimeout >= timeout) - reset(); - } - linkState->_IRQFlag = false; } void _onTimer() { - if (!isEnabled || resetIfNeeded()) + if (!isEnabled || !isReady()) return; + if (linkState->_IRQTimeout > timeout) { + reset(); + return; + } + + stopTimer(); sendPendingData(); + startTimer(); } void _onSerial() { - if (!isEnabled || resetIfNeeded(false)) + if (!isEnabled) + return; + + waitCycles(1000); // TODO: PARAMETERIZE + if (resetIfNeeded()) return; linkState->_IRQFlag = true; linkState->_IRQTimeout = 0; - linkState->playerCount = 0; - linkState->currentPlayerId = - (REG_SIOCNT & (0b11 << LINK_BITS_PLAYER_ID)) >> LINK_BITS_PLAYER_ID; - + u8 newPlayerCount = 0; for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) { u16 data = REG_SIOMULTI[i]; if (data != LINK_DISCONNECTED) { if (data != LINK_NO_DATA) push(linkState->_incomingMessages[i], data); - linkState->playerCount++; + newPlayerCount++; } else LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); } + + if (linkState->playerCount == 2 && newPlayerCount == 1) { + DEBULOG("hey, rompió: " + asStr(REG_SIOMULTI[0]) + "-" + + asStr(REG_SIOMULTI[1])); + } + linkState->playerCount = newPlayerCount; + linkState->currentPlayerId = + (REG_SIOCNT & (0b11 << LINK_BITS_PLAYER_ID)) >> LINK_BITS_PLAYER_ID; } private: @@ -184,12 +202,16 @@ class LinkConnection { void transfer(u16 data) { REG_SIOMLT_SEND = data; + waitCycles(1000); // PARAMETERIZE + if (isMaster()) setBitHigh(LINK_BIT_START); } - bool resetIfNeeded(bool checkErrors = false) { - if (checkErrors && hasError()) { + bool resetIfNeeded() { + if (hasError()) { + DEBULOG("resetting: " + asStr(isReady()) + "-" + asStr(hasError()) + "-" + + asStr(linkState->_IRQTimeout)); reset(); return true; } @@ -214,15 +236,14 @@ class LinkConnection { } void stop() { - REG_TM[timerId].cnt = REG_TM[timerId].cnt & (~TM_ENABLE); + stopTimer(); LINK_SET_LOW(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_LOW); LINK_SET_HIGH(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_HIGH); } void start() { - REG_TM[timerId].start = -frequency; - REG_TM[timerId].cnt = TM_ENABLE | TM_IRQ | LINK_BASE_FREQUENCY; + startTimer(); LINK_SET_LOW(REG_RCNT, LINK_BIT_GENERAL_PURPOSE_HIGH); REG_SIOCNT = baudRate; @@ -231,6 +252,13 @@ class LinkConnection { setBitHigh(LINK_BIT_IRQ); } + void stopTimer() { REG_TM[timerId].cnt = REG_TM[timerId].cnt & (~TM_ENABLE); } + + void startTimer() { + REG_TM[timerId].start = -frequency; + REG_TM[timerId].cnt = TM_ENABLE | TM_IRQ | LINK_BASE_FREQUENCY; + } + void push(std::queue& q, u16 value) { if (q.size() >= bufferSize) LINK_QUEUE_POP(q); @@ -238,6 +266,14 @@ class LinkConnection { q.push(value); } + void waitCycles(u16 amount) { + // TODO: Parameterize + REG_TM[2].start = -amount; + REG_TM[2].cnt = TM_ENABLE | TM_IRQ | TM_FREQ_1; + IntrWait(1, LINK_TIMER_IRQ_IDS[2]); + REG_TM[2].cnt = 0; + } + bool isMaster() { return !isBitHigh(LINK_BIT_SLAVE); } bool isBitHigh(u8 bit) { return (REG_SIOCNT >> bit) & 1; } void setBitHigh(u8 bit) { LINK_SET_HIGH(REG_SIOCNT, bit); } From 821ffcd189c94f37bd7744b92a2e4b58749014d7 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 17 Nov 2020 12:26:27 -0300 Subject: [PATCH 6/9] Adding logs and waitCycles. The START bit check fixed a random bug on hardware --- README.md | 5 ++- examples/full/src/main.cpp | 2 +- lib/LinkConnection.h | 91 ++++++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 23ed3e1..4f12796 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,9 @@ Name | Type | Default | Description `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate. `timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection. `bufferSize` | **u32** | `10` | Number of messages that the queues will be able to store. -`timerId` | **u8** (0~3) | `3` | GBA Timer to use. -`frequency` | **u16** | `25` | Number of 1024cycles/61.04μs ticks between messages *(25 = 1,526ms)*. +`sendTimerId` | **u8** *(0~3)* | `3` | GBA Timer to use for sending. +`waitTimerId` | **u8** *(0~3)* | `2` | GBA Timer to use for waiting. +`frequency` | **u16** | `25` | Number of 1024cycles/61.04μs ticks between messages *(25 = 1,526ms)*. It's the interval of Timer #`sendTimerId`. ## Makefile actions diff --git a/examples/full/src/main.cpp b/examples/full/src/main.cpp index d4f1054..dbbd9aa 100644 --- a/examples/full/src/main.cpp +++ b/examples/full/src/main.cpp @@ -59,9 +59,9 @@ inline void setUpInterrupts() { // LinkConnection irq_add(II_VBLANK, LINK_ISR_VBLANK); + irq_add(II_SERIAL, LINK_ISR_SERIAL); irq_add(II_TIMER3, LINK_ISR_TIMER); irq_add(II_TIMER2, NULL); - irq_add(II_SERIAL, LINK_ISR_SERIAL); // A+B+START+SELECT REG_KEYCNT = 0b1100000000001111; diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h index 70b9f19..d57f0c2 100644 --- a/lib/LinkConnection.h +++ b/lib/LinkConnection.h @@ -9,15 +9,17 @@ #include #include -#include "utils/SceneUtils.h" // TODO: REMOVE +#include "utils/SceneUtils.h" // TODO: REMOVE and remove DEBULOGs #define LINK_MAX_PLAYERS 4 #define LINK_DISCONNECTED 0xFFFF #define LINK_NO_DATA 0x0 #define LINK_DEFAULT_TIMEOUT 3 #define LINK_DEFAULT_BUFFER_SIZE 10 -#define LINK_DEFAULT_TIMER_ID 3 -#define LINK_DEFAULT_FREQUENCY 25 +#define LINK_DEFAULT_SEND_TIMER_ID 3 +#define LINK_DEFAULT_WAIT_TIMER_ID 2 +#define LINK_DEFAULT_FREQUENCY 100 +#define LINK_TRANSFER_WAIT_CYCLES 1000 #define LINK_BASE_FREQUENCY TM_FREQ_1024 #define LINK_BIT_SLAVE 2 #define LINK_BIT_READY 3 @@ -36,10 +38,11 @@ // Usage: // - 1) Include this header in your main.cpp file and add: // LinkConnection* linkConnection = new LinkConnection(); -// - 2) Add the interrupt service routines: +// - 2) Add the required interrupt service routines: // irq_add(II_VBLANK, LINK_ISR_VBLANK); -// irq_add(II_TIMER3, LINK_ISR_TIMER); // irq_add(II_SERIAL, LINK_ISR_SERIAL); +// irq_add(II_TIMER3, LINK_ISR_TIMER); +// irq_add(II_TIMER2, NULL); // - 3) Send/read messages by using: // linkConnection->send(...); // linkConnection->linkState @@ -94,12 +97,14 @@ class LinkConnection { BaudRate baudRate = BAUD_RATE_3, u32 timeout = LINK_DEFAULT_TIMEOUT, u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE, - u8 timerId = LINK_DEFAULT_TIMER_ID, + u8 sendTimerId = LINK_DEFAULT_SEND_TIMER_ID, + u8 waitTimerId = LINK_DEFAULT_WAIT_TIMER_ID, u32 frequency = LINK_DEFAULT_FREQUENCY) { this->baudRate = baudRate; this->timeout = timeout; this->bufferSize = bufferSize; - this->timerId = timerId; + this->sendTimerId = sendTimerId; + this->waitTimerId = waitTimerId; this->frequency = frequency; if (startNow) @@ -128,9 +133,6 @@ class LinkConnection { push(linkState->_outgoingMessages, data); } - bool isReady() { return isBitHigh(LINK_BIT_READY); } - bool hasError() { return isBitHigh(LINK_BIT_ERROR); } - void _onVBlank() { if (!isEnabled) return; @@ -142,24 +144,27 @@ class LinkConnection { } void _onTimer() { - if (!isEnabled || !isReady()) - return; - - if (linkState->_IRQTimeout > timeout) { - reset(); + if (!isEnabled || !isReady()) { + if (!isReady()) { + DEBULOG("not ready"); + // reset(); // TODO: MOVE TO resetIfNeeded() + } return; } - stopTimer(); - sendPendingData(); - startTimer(); + if (didTimeout()) { + DEBULOG("timeout!"); + reset(); + return; + } + if (isMaster() && !isBitHigh(LINK_BIT_START)) + sendPendingData(); } void _onSerial() { if (!isEnabled) return; - - waitCycles(1000); // TODO: PARAMETERIZE + wait(); if (resetIfNeeded()) return; @@ -179,22 +184,31 @@ class LinkConnection { } if (linkState->playerCount == 2 && newPlayerCount == 1) { - DEBULOG("hey, rompió: " + asStr(REG_SIOMULTI[0]) + "-" + - asStr(REG_SIOMULTI[1])); + DEBULOG("broken: " + asStr(REG_SIOMULTI[0]) + "-" + + asStr(REG_SIOMULTI[1])); // TODO: REMOVE } linkState->playerCount = newPlayerCount; linkState->currentPlayerId = (REG_SIOCNT & (0b11 << LINK_BITS_PLAYER_ID)) >> LINK_BITS_PLAYER_ID; + + if (!isMaster()) + sendPendingData(); } private: BaudRate baudRate; u32 timeout; u32 bufferSize; - u8 timerId; + u8 sendTimerId; + u8 waitTimerId; u32 frequency; bool isEnabled = false; + bool isReady() { return isBitHigh(LINK_BIT_READY); } + bool hasError() { return isBitHigh(LINK_BIT_ERROR); } + bool isMaster() { return !isBitHigh(LINK_BIT_SLAVE); } + bool didTimeout() { return linkState->_IRQTimeout >= timeout; } + void sendPendingData() { transfer(LINK_QUEUE_POP(linkState->_outgoingMessages)); } @@ -202,16 +216,15 @@ class LinkConnection { void transfer(u16 data) { REG_SIOMLT_SEND = data; - waitCycles(1000); // PARAMETERIZE - - if (isMaster()) + if (isMaster()) { + wait(); setBitHigh(LINK_BIT_START); + } } bool resetIfNeeded() { - if (hasError()) { - DEBULOG("resetting: " + asStr(isReady()) + "-" + asStr(hasError()) + "-" + - asStr(linkState->_IRQTimeout)); + if (!isReady() || hasError()) { + DEBULOG("resetting: " + asStr(isReady()) + "-" + asStr(hasError())); reset(); return true; } @@ -252,11 +265,13 @@ class LinkConnection { setBitHigh(LINK_BIT_IRQ); } - void stopTimer() { REG_TM[timerId].cnt = REG_TM[timerId].cnt & (~TM_ENABLE); } + void stopTimer() { + REG_TM[sendTimerId].cnt = REG_TM[sendTimerId].cnt & (~TM_ENABLE); + } void startTimer() { - REG_TM[timerId].start = -frequency; - REG_TM[timerId].cnt = TM_ENABLE | TM_IRQ | LINK_BASE_FREQUENCY; + REG_TM[sendTimerId].start = -frequency; + REG_TM[sendTimerId].cnt = TM_ENABLE | TM_IRQ | LINK_BASE_FREQUENCY; } void push(std::queue& q, u16 value) { @@ -266,15 +281,13 @@ class LinkConnection { q.push(value); } - void waitCycles(u16 amount) { - // TODO: Parameterize - REG_TM[2].start = -amount; - REG_TM[2].cnt = TM_ENABLE | TM_IRQ | TM_FREQ_1; - IntrWait(1, LINK_TIMER_IRQ_IDS[2]); - REG_TM[2].cnt = 0; + void wait() { + REG_TM[waitTimerId].start = -LINK_TRANSFER_WAIT_CYCLES; + REG_TM[waitTimerId].cnt = TM_ENABLE | TM_IRQ | TM_FREQ_1; + IntrWait(1, LINK_TIMER_IRQ_IDS[waitTimerId]); + REG_TM[waitTimerId].cnt = 0; } - bool isMaster() { return !isBitHigh(LINK_BIT_SLAVE); } bool isBitHigh(u8 bit) { return (REG_SIOCNT >> bit) & 1; } void setBitHigh(u8 bit) { LINK_SET_HIGH(REG_SIOCNT, bit); } void setBitLow(u8 bit) { LINK_SET_LOW(REG_SIOCNT, bit); } From 0d63ae0248e4f9e46953ba003436f0977ddcc194 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 17 Nov 2020 16:04:01 -0300 Subject: [PATCH 7/9] Cleaning up code --- README.md | 4 ++-- examples/basic/src/main.cpp | 7 ++++--- lib/LinkConnection.h | 33 ++++++++++++--------------------- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 4f12796..0f0b962 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ All the complexity is abstracted in a single header file that exposes an easy-to - Include [LinkConnection.h](lib/LinkConnection.h) in your game code, and read its comment with instructions. - Check out the [examples](examples) folder * Builds are available in *Releases*. - * They can be tested on real GBAs or with emulators (*NO$GBA*, *mGBA*, or *VBA-M*). + * They can be tested on real GBAs or with emulators (*NO$GBA* or *VBA-M*). ## Constructor options @@ -25,9 +25,9 @@ Name | Type | Default | Description `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate. `timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection. `bufferSize` | **u32** | `10` | Number of messages that the queues will be able to store. +`speed` | **u16** | `100` | Number of 1024cycles (61.04μs) ticks between messages *(100 = 6,104ms)*. It's the interval of Timer #`sendTimerId`. `sendTimerId` | **u8** *(0~3)* | `3` | GBA Timer to use for sending. `waitTimerId` | **u8** *(0~3)* | `2` | GBA Timer to use for waiting. -`frequency` | **u16** | `25` | Number of 1024cycles/61.04μs ticks between messages *(25 = 1,526ms)*. It's the interval of Timer #`sendTimerId`. ## Makefile actions diff --git a/examples/basic/src/main.cpp b/examples/basic/src/main.cpp index 03de0cb..f6bbb59 100644 --- a/examples/basic/src/main.cpp +++ b/examples/basic/src/main.cpp @@ -17,9 +17,10 @@ void init() { irq_init(NULL); // (2) Add the interrupt service routines - irq_add(II_TIMER3, LINK_ISR_TIMER); irq_add(II_VBLANK, LINK_ISR_VBLANK); irq_add(II_SERIAL, LINK_ISR_SERIAL); + irq_add(II_TIMER3, LINK_ISR_TIMER); + irq_add(II_TIMER2, NULL); } int main() { @@ -30,7 +31,7 @@ int main() { while (1) { // (3) Send/read messages messages u16 keys = ~REG_KEYS & KEY_ANY; - u16 message = (keys << 1) | 1; + u16 message = keys + 1; linkConnection->send(message); auto linkState = linkConnection->linkState.get(); @@ -40,7 +41,7 @@ int main() { for (u32 i = 0; i < linkState->playerCount; i++) { while (linkState->hasMessage(i)) - data[i] = linkState->readMessage(i) >> 1; + data[i] = linkState->readMessage(i) - 1; output += "Player " + std::to_string(i) + ": " + std::to_string(data[i]) + "\n"; diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h index d57f0c2..fbc5008 100644 --- a/lib/LinkConnection.h +++ b/lib/LinkConnection.h @@ -9,16 +9,14 @@ #include #include -#include "utils/SceneUtils.h" // TODO: REMOVE and remove DEBULOGs - #define LINK_MAX_PLAYERS 4 #define LINK_DISCONNECTED 0xFFFF #define LINK_NO_DATA 0x0 #define LINK_DEFAULT_TIMEOUT 3 #define LINK_DEFAULT_BUFFER_SIZE 10 +#define LINK_DEFAULT_SPEED 100 #define LINK_DEFAULT_SEND_TIMER_ID 3 #define LINK_DEFAULT_WAIT_TIMER_ID 2 -#define LINK_DEFAULT_FREQUENCY 100 #define LINK_TRANSFER_WAIT_CYCLES 1000 #define LINK_BASE_FREQUENCY TM_FREQ_1024 #define LINK_BIT_SLAVE 2 @@ -97,15 +95,15 @@ class LinkConnection { BaudRate baudRate = BAUD_RATE_3, u32 timeout = LINK_DEFAULT_TIMEOUT, u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE, + u16 speed = LINK_DEFAULT_SPEED, u8 sendTimerId = LINK_DEFAULT_SEND_TIMER_ID, - u8 waitTimerId = LINK_DEFAULT_WAIT_TIMER_ID, - u32 frequency = LINK_DEFAULT_FREQUENCY) { + u8 waitTimerId = LINK_DEFAULT_WAIT_TIMER_ID) { this->baudRate = baudRate; this->timeout = timeout; this->bufferSize = bufferSize; this->sendTimerId = sendTimerId; this->waitTimerId = waitTimerId; - this->frequency = frequency; + this->speed = speed; if (startNow) activate(); @@ -144,27 +142,24 @@ class LinkConnection { } void _onTimer() { - if (!isEnabled || !isReady()) { - if (!isReady()) { - DEBULOG("not ready"); - // reset(); // TODO: MOVE TO resetIfNeeded() - } + if (!isEnabled || !isReady()) return; - } if (didTimeout()) { - DEBULOG("timeout!"); reset(); return; } - if (isMaster() && !isBitHigh(LINK_BIT_START)) + + if (isMaster() && !isSending()) sendPendingData(); } void _onSerial() { if (!isEnabled) return; + wait(); + if (resetIfNeeded()) return; @@ -183,10 +178,6 @@ class LinkConnection { LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); } - if (linkState->playerCount == 2 && newPlayerCount == 1) { - DEBULOG("broken: " + asStr(REG_SIOMULTI[0]) + "-" + - asStr(REG_SIOMULTI[1])); // TODO: REMOVE - } linkState->playerCount = newPlayerCount; linkState->currentPlayerId = (REG_SIOCNT & (0b11 << LINK_BITS_PLAYER_ID)) >> LINK_BITS_PLAYER_ID; @@ -201,12 +192,13 @@ class LinkConnection { u32 bufferSize; u8 sendTimerId; u8 waitTimerId; - u32 frequency; + u32 speed; bool isEnabled = false; bool isReady() { return isBitHigh(LINK_BIT_READY); } bool hasError() { return isBitHigh(LINK_BIT_ERROR); } bool isMaster() { return !isBitHigh(LINK_BIT_SLAVE); } + bool isSending() { return isBitHigh(LINK_BIT_START); } bool didTimeout() { return linkState->_IRQTimeout >= timeout; } void sendPendingData() { @@ -224,7 +216,6 @@ class LinkConnection { bool resetIfNeeded() { if (!isReady() || hasError()) { - DEBULOG("resetting: " + asStr(isReady()) + "-" + asStr(hasError())); reset(); return true; } @@ -270,7 +261,7 @@ class LinkConnection { } void startTimer() { - REG_TM[sendTimerId].start = -frequency; + REG_TM[sendTimerId].start = -speed; REG_TM[sendTimerId].cnt = TM_ENABLE | TM_IRQ | LINK_BASE_FREQUENCY; } From 87808853f5e38bb06f27509bc2e817e3b4166964 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 17 Nov 2020 16:23:14 -0300 Subject: [PATCH 8/9] Removing startNow parameter --- README.md | 1 - examples/basic/src/main.cpp | 5 ++++- examples/full/src/main.cpp | 2 +- lib/LinkConnection.h | 13 ++++++------- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0f0b962..71295d4 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,6 @@ All the complexity is abstracted in a single header file that exposes an easy-to Name | Type | Default | Description --- | --- | --- | --- -`startNow` | **bool** | `true` | Automatically starts serial communication. Otherwise, you'll need to call `linkConnection->activate()`. `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate. `timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection. `bufferSize` | **u32** | `10` | Number of messages that the queues will be able to store. diff --git a/examples/basic/src/main.cpp b/examples/basic/src/main.cpp index f6bbb59..2e99a7e 100644 --- a/examples/basic/src/main.cpp +++ b/examples/basic/src/main.cpp @@ -21,6 +21,9 @@ void init() { irq_add(II_SERIAL, LINK_ISR_SERIAL); irq_add(II_TIMER3, LINK_ISR_TIMER); irq_add(II_TIMER2, NULL); + + // (3) Initialize the library + linkConnection->activate(); } int main() { @@ -29,7 +32,7 @@ int main() { u16 data[LINK_MAX_PLAYERS]; while (1) { - // (3) Send/read messages messages + // (4) Send/read messages messages u16 keys = ~REG_KEYS & KEY_ANY; u16 message = keys + 1; linkConnection->send(message); diff --git a/examples/full/src/main.cpp b/examples/full/src/main.cpp index dbbd9aa..d651433 100644 --- a/examples/full/src/main.cpp +++ b/examples/full/src/main.cpp @@ -9,7 +9,7 @@ void setUpInterrupts(); void printTutorial(); static std::shared_ptr engine{new GBAEngine()}; static std::unique_ptr testScene{new TestScene(engine)}; -LinkConnection* linkConnection = new LinkConnection(false); +LinkConnection* linkConnection = new LinkConnection(); int main() { setUpInterrupts(); diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h index fbc5008..7d8e26c 100644 --- a/lib/LinkConnection.h +++ b/lib/LinkConnection.h @@ -37,11 +37,14 @@ // - 1) Include this header in your main.cpp file and add: // LinkConnection* linkConnection = new LinkConnection(); // - 2) Add the required interrupt service routines: +// irq_init(NULL); // irq_add(II_VBLANK, LINK_ISR_VBLANK); // irq_add(II_SERIAL, LINK_ISR_SERIAL); // irq_add(II_TIMER3, LINK_ISR_TIMER); // irq_add(II_TIMER2, NULL); -// - 3) Send/read messages by using: +// - 3) Initialize the library with: +// linkConnection->activate(); +// - 4) Send/read messages by using: // linkConnection->send(...); // linkConnection->linkState @@ -91,8 +94,7 @@ class LinkConnection { }; std::unique_ptr linkState{new LinkState()}; - explicit LinkConnection(bool startNow = true, - BaudRate baudRate = BAUD_RATE_3, + explicit LinkConnection(BaudRate baudRate = BAUD_RATE_3, u32 timeout = LINK_DEFAULT_TIMEOUT, u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE, u16 speed = LINK_DEFAULT_SPEED, @@ -105,10 +107,7 @@ class LinkConnection { this->waitTimerId = waitTimerId; this->speed = speed; - if (startNow) - activate(); - else - stop(); + stop(); } bool isActive() { return isEnabled; } From 0c7bb5d334c9437647406e51be2d482d464cae98 Mon Sep 17 00:00:00 2001 From: Rodrigo Alfonso Date: Tue, 17 Nov 2020 16:33:38 -0300 Subject: [PATCH 9/9] Adding remote timeouts and fixing slave timeout when !isReady --- README.md | 7 ++++--- lib/LinkConnection.h | 29 ++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 71295d4..36f0dbd 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,10 @@ All the complexity is abstracted in a single header file that exposes an easy-to Name | Type | Default | Description --- | --- | --- | --- `baudRate` | **BaudRate** | `BaudRate::BAUD_RATE_3` | Sets a specific baud rate. -`timeout` | **u32** | `3` | Number of frames without an `II_SERIAL` IRQ to reset the connection. -`bufferSize` | **u32** | `10` | Number of messages that the queues will be able to store. -`speed` | **u16** | `100` | Number of 1024cycles (61.04μs) ticks between messages *(100 = 6,104ms)*. It's the interval of Timer #`sendTimerId`. +`timeout` | **u32** | `3` | Number of *frames* without an `II_SERIAL` IRQ to reset the connection. +`remoteTimeout` | **u32** | `5` | Number of *messages* with `0xFFFF` to mark a player as disconnected. +`bufferSize` | **u32** | `10` | Number of *messages* that the queues will be able to store. +`speed` | **u16** | `100` | Number of *1024cycles* (61.04μs) ticks between messages *(100 = 6,104ms)*. It's the interval of Timer #`sendTimerId`. `sendTimerId` | **u8** *(0~3)* | `3` | GBA Timer to use for sending. `waitTimerId` | **u8** *(0~3)* | `2` | GBA Timer to use for waiting. diff --git a/lib/LinkConnection.h b/lib/LinkConnection.h index 7d8e26c..3f0c6cf 100644 --- a/lib/LinkConnection.h +++ b/lib/LinkConnection.h @@ -13,6 +13,7 @@ #define LINK_DISCONNECTED 0xFFFF #define LINK_NO_DATA 0x0 #define LINK_DEFAULT_TIMEOUT 3 +#define LINK_DEFAULT_REMOTE_TIMEOUT 5 #define LINK_DEFAULT_BUFFER_SIZE 10 #define LINK_DEFAULT_SPEED 100 #define LINK_DEFAULT_SEND_TIMER_ID 3 @@ -65,6 +66,7 @@ struct LinkState { u8 currentPlayerId; std::queue _incomingMessages[LINK_MAX_PLAYERS]; std::queue _outgoingMessages; + int _timeouts[LINK_MAX_PLAYERS]; bool _IRQFlag; u32 _IRQTimeout; @@ -96,16 +98,18 @@ class LinkConnection { explicit LinkConnection(BaudRate baudRate = BAUD_RATE_3, u32 timeout = LINK_DEFAULT_TIMEOUT, + u32 remoteTimeout = LINK_DEFAULT_REMOTE_TIMEOUT, u32 bufferSize = LINK_DEFAULT_BUFFER_SIZE, u16 speed = LINK_DEFAULT_SPEED, u8 sendTimerId = LINK_DEFAULT_SEND_TIMER_ID, u8 waitTimerId = LINK_DEFAULT_WAIT_TIMER_ID) { this->baudRate = baudRate; this->timeout = timeout; + this->remoteTimeout = remoteTimeout; this->bufferSize = bufferSize; + this->speed = speed; this->sendTimerId = sendTimerId; this->waitTimerId = waitTimerId; - this->speed = speed; stop(); } @@ -141,7 +145,7 @@ class LinkConnection { } void _onTimer() { - if (!isEnabled || !isReady()) + if (!isEnabled) return; if (didTimeout()) { @@ -149,7 +153,7 @@ class LinkConnection { return; } - if (isMaster() && !isSending()) + if (isMaster() && isReady() && !isSending()) sendPendingData(); } @@ -173,8 +177,16 @@ class LinkConnection { if (data != LINK_NO_DATA) push(linkState->_incomingMessages[i], data); newPlayerCount++; - } else - LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); + linkState->_timeouts[i] = 0; + } else if (linkState->_timeouts[i] > 0) { + linkState->_timeouts[i]++; + + if (linkState->_timeouts[i] >= (int)remoteTimeout) { + LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); + linkState->_timeouts[i] = -1; + } else + newPlayerCount++; + } } linkState->playerCount = newPlayerCount; @@ -188,10 +200,11 @@ class LinkConnection { private: BaudRate baudRate; u32 timeout; + u32 remoteTimeout; u32 bufferSize; + u32 speed; u8 sendTimerId; u8 waitTimerId; - u32 speed; bool isEnabled = false; bool isReady() { return isBitHigh(LINK_BIT_READY); } @@ -231,8 +244,10 @@ class LinkConnection { void resetState() { linkState->playerCount = 0; linkState->currentPlayerId = 0; - for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) + for (u32 i = 0; i < LINK_MAX_PLAYERS; i++) { LINK_QUEUE_CLEAR(linkState->_incomingMessages[i]); + linkState->_timeouts[i] = -1; + } LINK_QUEUE_CLEAR(linkState->_outgoingMessages); linkState->_IRQFlag = false; linkState->_IRQTimeout = 0;