mirror of
https://github.com/afska/gba-link-connection.git
synced 2026-09-07 16:36:58 -05:00
Moving cpp files to iwram_code directory
This commit is contained in:
144
lib/iwram_code/LinkIR.cpp
Normal file
144
lib/iwram_code/LinkIR.cpp
Normal file
@@ -0,0 +1,144 @@
|
||||
#include "../LinkIR.hpp"
|
||||
|
||||
// To modulate a signal at 38kHz, we need to stay 13.15µs LOW and 13.15µs HIGH.
|
||||
// 38kHz signal => 38000/second =>
|
||||
// period = 1000000µs / 38000 = 26.31µs
|
||||
// halfPeriod = 13.15µs
|
||||
// LED ON => RCNT = 0x80BA (GPIO mode, SC, SD, SO as OUTPUT, SD=HIGH, SO=HIGH)
|
||||
// LED OFF => RCNT = 0x80B2 (GPIO mode, SC, SD, SO as OUTPUT, SD=HIGH, SO=LOW)
|
||||
|
||||
LINK_CODE_IWRAM void LinkIR::generate38kHzSignal(u32 microseconds) {
|
||||
// halfPeriods = ceil(microseconds / 13.15 µs) (in fixed-point math)
|
||||
u32 halfPeriods = Link::_max((microseconds * 100 + 1315) / 1316, 1);
|
||||
|
||||
// the GBA is 16.776MHz => 13.15 µs ~= 220 cycles per half-period
|
||||
|
||||
asm volatile(
|
||||
"mov r0, %0 \n" // r0 = address of REG_RCNT
|
||||
"ldr r1, =0x80BA \n" // r1 = initial value 0x80BA (LED ON)
|
||||
"mov r2, %1 \n" // r2 = main loop count (halfPeriods)
|
||||
"1: \n" // --- main loop ---
|
||||
"strh r1, [r0] \n" // write current value to REG_RCNT
|
||||
"mov r3, #54 \n" // r3 = inner loop count (54) (*)
|
||||
"2: \n" // --- inner loop ---
|
||||
"subs r3, r3, #1 \n" // decrement inner loop count
|
||||
// [1 cycle]
|
||||
"bne 2b \n" // repeat inner loop if needed
|
||||
// [taken: ~3 cycles, final: ~1 cycles]
|
||||
// (*) the we need to wait ~220 cycles between <main loop> iterations:
|
||||
// [first 53 iterations (branch taken): 53 * ~4 cycles = ~212 cycles]
|
||||
// [final iteration (branch not taken): ~2 cycles]
|
||||
// [overhead: ~6 cycles]
|
||||
"eor r1, r1, #8 \n" // toggle r1: 0x80BA^8 = 0x80B2 (& viceversa)
|
||||
"subs r2, r2, #1 \n" // decrement main loop count
|
||||
"bne 1b \n" // repeat main loop if needed
|
||||
"ldr r1, =0x80B2 \n" // ensure we end with 0x80B2
|
||||
"strh r1, [r0] \n" // write REG_RCNT = 0x80B2 (LED OFF)
|
||||
:
|
||||
: "r"(&Link::_REG_RCNT), "r"(halfPeriods)
|
||||
: "r0", "r1", "r2", "r3");
|
||||
}
|
||||
|
||||
LINK_CODE_IWRAM void LinkIR::waitMicroseconds(u32 microseconds) {
|
||||
if (!microseconds)
|
||||
return;
|
||||
|
||||
asm volatile(
|
||||
"mov r1, %0 \n" // r1 = main loop count (microseconds)
|
||||
"1: \n" // --- main loop ---
|
||||
"mov r2, #3 \n" // r2 = inner loop count (3)
|
||||
"nop \n" // extra cycle
|
||||
"nop \n" // extra cycle
|
||||
"2: \n" // --- inner loop ---
|
||||
"subs r2, r2, #1 \n" // decrement inner loop count
|
||||
"bne 2b \n" // repeat inner loop if needed
|
||||
"subs r1, r1, #1 \n" // decrement main loop count
|
||||
"bne 1b \n" // repeat main loop if needed
|
||||
:
|
||||
: "r"(microseconds)
|
||||
: "r1", "r2");
|
||||
}
|
||||
|
||||
LINK_CODE_IWRAM void LinkIR::send(u16 pulses[]) {
|
||||
setLight(false);
|
||||
|
||||
for (u32 i = 0; pulses[i] != 0; i++) {
|
||||
u32 microseconds = pulses[i];
|
||||
bool isMark = i % 2 == 0;
|
||||
|
||||
if (isMark) {
|
||||
generate38kHzSignal(microseconds);
|
||||
} else {
|
||||
setLight(false);
|
||||
waitMicroseconds(microseconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LINK_CODE_IWRAM bool LinkIR::receive(u16 pulses[],
|
||||
u32 maxEntries,
|
||||
u32 timeout,
|
||||
u32 startTimeout) {
|
||||
bool hasStarted = false;
|
||||
bool isMark = false;
|
||||
|
||||
u32 pulseIndex = 0;
|
||||
u32 lastTransitionTime = 0;
|
||||
u32 initialTime = 0;
|
||||
|
||||
startCount();
|
||||
initialTime = getCount();
|
||||
|
||||
while (true) {
|
||||
// begin a fixed demodulation window
|
||||
u32 windowStart = getCount();
|
||||
u32 transitionsCount = 0;
|
||||
bool previousRaw = isDetectingLight();
|
||||
|
||||
// sample for a fixed window duration
|
||||
while (getCount() - windowStart < DEMODULATION_SAMPLE_WINDOW_CYCLES) {
|
||||
bool currentRaw = isDetectingLight();
|
||||
if (currentRaw != previousRaw) {
|
||||
transitionsCount++;
|
||||
previousRaw = currentRaw;
|
||||
}
|
||||
}
|
||||
|
||||
bool isCarrierPresent = transitionsCount >= DEMODULATION_MIN_TRANSITIONS;
|
||||
|
||||
// new transition?
|
||||
if (isCarrierPresent != isMark) {
|
||||
// estimate transition time as the middle of the current window
|
||||
u32 estimatedNow = windowStart + DEMODULATION_SAMPLE_WINDOW_CYCLES / 2;
|
||||
if (!hasStarted && isCarrierPresent) {
|
||||
// first mark initializes the capture
|
||||
hasStarted = true;
|
||||
lastTransitionTime = estimatedNow;
|
||||
} else if (hasStarted) {
|
||||
// record the pulse duration in microseconds
|
||||
if (pulseIndex >= maxEntries - 1)
|
||||
break;
|
||||
u32 pulseDuration =
|
||||
(estimatedNow - lastTransitionTime) / CYCLES_PER_MICROSECOND;
|
||||
pulses[pulseIndex++] = pulseDuration;
|
||||
lastTransitionTime = estimatedNow;
|
||||
}
|
||||
isMark = isCarrierPresent;
|
||||
}
|
||||
|
||||
// if we've started and we're in a space, check for overall timeout
|
||||
if (hasStarted && !isMark &&
|
||||
(getCount() - lastTransitionTime) / CYCLES_PER_MICROSECOND >= timeout)
|
||||
break;
|
||||
|
||||
// if we haven't started and we've waited longer than startTimeout, then
|
||||
// timeout too
|
||||
if (!hasStarted &&
|
||||
(getCount() - initialTime) / CYCLES_PER_MICROSECOND >= startTimeout)
|
||||
break;
|
||||
}
|
||||
|
||||
pulses[pulseIndex] = LINK_IR_SIGNAL_END;
|
||||
stopCount();
|
||||
return pulseIndex > 0;
|
||||
}
|
||||
53
lib/iwram_code/LinkWireless.cpp
Normal file
53
lib/iwram_code/LinkWireless.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "../LinkWireless.hpp"
|
||||
|
||||
#ifdef LINK_WIRELESS_PUT_ISR_IN_IWRAM
|
||||
|
||||
#if LINK_WIRELESS_PUT_ISR_IN_IWRAM_SERIAL == 1
|
||||
#define _LINK_SERIAL_ISR \
|
||||
LINK_CODE_IWRAM \
|
||||
__attribute__((optimize(LINK_WIRELESS_PUT_ISR_IN_IWRAM_SERIAL_LEVEL)))
|
||||
#else
|
||||
#define _LINK_SERIAL_ISR
|
||||
#endif
|
||||
|
||||
#if LINK_WIRELESS_PUT_ISR_IN_IWRAM_TIMER == 1
|
||||
#define _LINK_TIMER_ISR \
|
||||
LINK_CODE_IWRAM \
|
||||
__attribute__((optimize(LINK_WIRELESS_PUT_ISR_IN_IWRAM_TIMER_LEVEL)))
|
||||
#else
|
||||
#define _LINK_TIMER_ISR
|
||||
#endif
|
||||
|
||||
_LINK_SERIAL_ISR void LinkWireless::_onSerial() {
|
||||
__onSerial();
|
||||
}
|
||||
|
||||
_LINK_TIMER_ISR void LinkWireless::_onTimer() {
|
||||
__onTimer();
|
||||
}
|
||||
|
||||
_LINK_SERIAL_ISR void LinkWireless::processMessage(u32 playerId,
|
||||
u32 data,
|
||||
u32& currentPacketId,
|
||||
u32& playerBitMap,
|
||||
int& playerBitMapCount) {
|
||||
_processMessage(playerId, data, currentPacketId, playerBitMap,
|
||||
playerBitMapCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTES:
|
||||
* When using `LINK_WIRELESS_ENABLE_NESTED_IRQ`:
|
||||
* - Any user ISR can interrupt the library ISRs.
|
||||
* - SERIAL ISR can interrupt TIMER ISR.
|
||||
* -> This doesn't cause data races since TIMER ISR only works when
|
||||
* there is no active async task.
|
||||
* -> When TIMER ISR starts an async task (`transferAsync(...)`),
|
||||
* nested interrupts are disabled (`REG_IME = 0`) and SERIAL cannot
|
||||
* interrupt anymore.
|
||||
* - TIMER interrupts are skipped if SERIAL ISR is running.
|
||||
* - VBLANK interrupts are postponed if SERIAL or TIMER ISRs are running.
|
||||
* - Nobody can interrupt VBLANK ISR.
|
||||
*/
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user