Skip to content

Commit ff3ba21

Browse files
committed
Add support for the acknowledge pin
The acknowledge pulse can be very short: 4.5 us on my 32u4 board, 3 us on my PAL PSX. Therefore polling the pin risks missing the pulse. However, by using a masked external interrupt we can monitor the pulse reliably.
1 parent 8a87eb2 commit ff3ba21

File tree

2 files changed

+65
-6
lines changed

2 files changed

+65
-6
lines changed

src/PsxControllerHwSpi.h

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class PsxControllerHwSpi: public PsxController {
4646
}
4747

4848
public:
49+
PsxControllerHwSpi (uint8_t ackPin = NOT_A_PIN) : PsxController(ackPin) {}
50+
4951
virtual boolean begin () override {
5052
att.config (OUTPUT, HIGH); // HIGH -> Controller not selected
5153

src/PsxNewLib.h

+63-6
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ enum GunconStatus {
293293
GUNCON_OTHER_ERROR
294294
};
295295

296+
EMPTY_INTERRUPT (psxAcknowledgeEmptyISR);
297+
296298
/** \brief PSX Controller Interface
297299
*
298300
* This is the base class implementing interactions with PSX controllers. It is
@@ -307,6 +309,13 @@ class PsxController {
307309
*/
308310
static const byte BUFFER_SIZE = 32;
309311

312+
/** \brief Acknowledge interrupt bitmask
313+
*
314+
* Bitmask for the acknowledge pin's interrupt in EIFR.
315+
* If we didn't acquire an interrupt, the mask is zero.
316+
*/
317+
uint8_t ackBitmask;
318+
310319
/** \brief Internal communication buffer
311320
*
312321
* This is used to hold replies received from the controller.
@@ -413,22 +422,44 @@ class PsxController {
413422
* \param[out] in The data bytes returned by the controller, must be sized
414423
* to hold at least \a len bytes
415424
* \param[in] len The amount of bytes to be exchanged
425+
* \param[in] waitForAck Whether we should wait for an acknowledgement before
426+
* transferring the first byte
416427
*/
417-
void shiftInOut (const byte *out, byte *in, const byte len) {
428+
void shiftInOut (const byte *out, byte *in, const byte len, bool waitForAck) {
418429
#ifdef DUMP_COMMS
419430
byte inbuf[len];
420431
#endif
421432

422433
for (byte i = 0; i < len; ++i) {
434+
if (ackBitmask) {
435+
if (waitForAck) {
436+
// Wait for acknowledge interrupt flag to be set.
437+
unsigned long start = micros ();
438+
while (!(EIFR & ackBitmask)) {
439+
if (micros () - start >= INTER_CMD_BYTE_DELAY * 2) {
440+
// Timeout: probably no controller present.
441+
break;
442+
}
443+
}
444+
}
445+
// Reset acknowledge flag.
446+
EIFR = ackBitmask;
447+
} else {
448+
if (waitForAck) {
449+
// Wait for a fixed amount of time such that the controller is
450+
// ready for the next byte.
451+
delayMicroseconds (INTER_CMD_BYTE_DELAY);
452+
}
453+
}
454+
waitForAck = true;
455+
423456
byte tmp = shiftInOut (out != NULL ? out[i] : 0x5A);
424457
#ifdef DUMP_COMMS
425458
inbuf[i] = tmp;
426459
#endif
427460
if (in != NULL) {
428461
in[i] = tmp;
429462
}
430-
431-
delayMicroseconds (INTER_CMD_BYTE_DELAY); // Very important!
432463
}
433464

434465
#ifdef DUMP_COMMS
@@ -473,14 +504,14 @@ class PsxController {
473504

474505
if (len >= 3 && len <= BUFFER_SIZE) {
475506
// All commands have at least 3 bytes, so shift out those first
476-
shiftInOut (out, inputBuffer, 3);
507+
shiftInOut (out, inputBuffer, 3, false);
477508
if (isValidReply (inputBuffer)) {
478509
// Reply is good, get full length
479510
byte replyLen = getReplyLength (inputBuffer);
480511

481512
// Shift out rest of command
482513
if (len > 3) {
483-
shiftInOut (out + 3, inputBuffer + 3, len - 3);
514+
shiftInOut (out + 3, inputBuffer + 3, len - 3, true);
484515
}
485516

486517
byte left = replyLen - len + 3;
@@ -493,7 +524,7 @@ class PsxController {
493524
ret = inputBuffer;
494525
} else if (len + left <= BUFFER_SIZE) {
495526
// Part of reply is still missing and we have space for it
496-
shiftInOut (NULL, inputBuffer + len, left);
527+
shiftInOut (NULL, inputBuffer + len, left, true);
497528
ret = inputBuffer;
498529
} else {
499530
// Reply incomplete but not enough space provided
@@ -557,6 +588,32 @@ class PsxController {
557588

558589

559590
public:
591+
/**
592+
* \param[in] ackPin Pin number of the acknowledge pin (optional).
593+
* If this pin can generate a hardware interrupt, its interrupt
594+
* flag is used to listen for the controller acknowledging that
595+
* it is ready to receive the next byte.
596+
* If such a pin is not available, we wait a fixed amount of time
597+
* and assume the controller is ready by then.
598+
*/
599+
PsxController (uint8_t ackPin = NOT_A_PIN) {
600+
ackBitmask = 0x00;
601+
if (ackPin != NOT_A_PIN) {
602+
fastPinConfig (ackPin, INPUT, HIGH);
603+
uint8_t interruptNum = digitalPinToInterrupt (ackPin);
604+
if (interruptNum != NOT_AN_INTERRUPT) {
605+
// We only care about the interrupt flag in EIFR, not about actually
606+
// handling the interrupt. Therefore we attach an empty ISR and revert
607+
// the interrupt mask after attaching it.
608+
uint8_t orgMask = EIMSK;
609+
attachInterrupt (interruptNum, psxAcknowledgeEmptyISR, RISING);
610+
// Assume EIMSK and EIFR use the same bit fields.
611+
ackBitmask = EIMSK & ~orgMask;
612+
EIMSK = orgMask;
613+
}
614+
}
615+
}
616+
560617
/** \brief Initialize library
561618
*
562619
* This function shall be called before any others, it will initialize the

0 commit comments

Comments
 (0)