@@ -293,6 +293,8 @@ enum GunconStatus {
293
293
GUNCON_OTHER_ERROR
294
294
};
295
295
296
+ EMPTY_INTERRUPT (psxAcknowledgeEmptyISR);
297
+
296
298
/* * \brief PSX Controller Interface
297
299
*
298
300
* This is the base class implementing interactions with PSX controllers. It is
@@ -307,6 +309,13 @@ class PsxController {
307
309
*/
308
310
static const byte BUFFER_SIZE = 32 ;
309
311
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
+
310
319
/* * \brief Internal communication buffer
311
320
*
312
321
* This is used to hold replies received from the controller.
@@ -413,22 +422,44 @@ class PsxController {
413
422
* \param[out] in The data bytes returned by the controller, must be sized
414
423
* to hold at least \a len bytes
415
424
* \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
416
427
*/
417
- void shiftInOut (const byte *out, byte *in, const byte len) {
428
+ void shiftInOut (const byte *out, byte *in, const byte len, bool waitForAck ) {
418
429
#ifdef DUMP_COMMS
419
430
byte inbuf[len];
420
431
#endif
421
432
422
433
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
+
423
456
byte tmp = shiftInOut (out != NULL ? out[i] : 0x5A );
424
457
#ifdef DUMP_COMMS
425
458
inbuf[i] = tmp;
426
459
#endif
427
460
if (in != NULL ) {
428
461
in[i] = tmp;
429
462
}
430
-
431
- delayMicroseconds (INTER_CMD_BYTE_DELAY); // Very important!
432
463
}
433
464
434
465
#ifdef DUMP_COMMS
@@ -473,14 +504,14 @@ class PsxController {
473
504
474
505
if (len >= 3 && len <= BUFFER_SIZE) {
475
506
// All commands have at least 3 bytes, so shift out those first
476
- shiftInOut (out, inputBuffer, 3 );
507
+ shiftInOut (out, inputBuffer, 3 , false );
477
508
if (isValidReply (inputBuffer)) {
478
509
// Reply is good, get full length
479
510
byte replyLen = getReplyLength (inputBuffer);
480
511
481
512
// Shift out rest of command
482
513
if (len > 3 ) {
483
- shiftInOut (out + 3 , inputBuffer + 3 , len - 3 );
514
+ shiftInOut (out + 3 , inputBuffer + 3 , len - 3 , true );
484
515
}
485
516
486
517
byte left = replyLen - len + 3 ;
@@ -493,7 +524,7 @@ class PsxController {
493
524
ret = inputBuffer;
494
525
} else if (len + left <= BUFFER_SIZE) {
495
526
// 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 );
497
528
ret = inputBuffer;
498
529
} else {
499
530
// Reply incomplete but not enough space provided
@@ -557,6 +588,32 @@ class PsxController {
557
588
558
589
559
590
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
+
560
617
/* * \brief Initialize library
561
618
*
562
619
* This function shall be called before any others, it will initialize the
0 commit comments