Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the acknowledge pin #13

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/PsxControllerBitBang.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,19 @@ class PsxControllerBitBang: public PsxController {
return PsxController::begin ();
}
};

template <uint8_t PIN_ATT, uint8_t PIN_CMD, uint8_t PIN_DAT, uint8_t PIN_CLK,
uint8_t PIN_ACK>
class PsxControllerBitBangWithAck:
public PsxControllerBitBang<PIN_ATT, PIN_CMD, PIN_DAT, PIN_CLK> {
private:
DigitalPin<PIN_ACK> ack;

public:
virtual boolean begin () override {
ack.config (INPUT, HIGH); // Enable pull-up
initAckPin (PIN_ACK);

return PsxControllerBitBang<PIN_ATT, PIN_CMD, PIN_DAT, PIN_CLK>::begin ();
}
};
14 changes: 14 additions & 0 deletions src/PsxControllerHwSpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,17 @@ class PsxControllerHwSpi: public PsxController {
return PsxController::begin ();
}
};

template <uint8_t PIN_ATT, uint8_t PIN_ACK>
class PsxControllerHwSpiWithAck: public PsxControllerHwSpi<PIN_ATT> {
private:
DigitalPin<PIN_ACK> ack;

public:
virtual boolean begin () override {
ack.config (INPUT, HIGH); // Enable pull-up
initAckPin (PIN_ACK);

return PsxControllerHwSpi<PIN_ATT>::begin ();
}
};
69 changes: 63 additions & 6 deletions src/PsxNewLib.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,8 @@ enum GunconStatus {
GUNCON_OTHER_ERROR
};

EMPTY_INTERRUPT (psxAcknowledgeEmptyISR);

/** \brief PSX Controller Interface
*
* This is the base class implementing interactions with PSX controllers. It is
Expand All @@ -307,6 +309,13 @@ class PsxController {
*/
static const byte BUFFER_SIZE = 32;

/** \brief Acknowledge interrupt bitmask
*
* Bitmask for the acknowledge pin's interrupt in EIFR.
* If we didn't acquire an interrupt, the mask is zero.
*/
uint8_t ackBitmask = 0x00;

/** \brief Internal communication buffer
*
* This is used to hold replies received from the controller.
Expand Down Expand Up @@ -404,6 +413,32 @@ class PsxController {
*/
virtual byte shiftInOut (const byte out) = 0;

/** \brief Initialize support for the acknowledge pin
*
* If the acknowledge pin can generate a hardware interrupt, its interrupt
* flag is used to listen for the controller acknowledging that it is ready
* to receive the next byte.
*
* If this method is not called or the given pin cannot generate a hardware
* interrupt, we will wait a fixed amount of time and assume the controller
* is ready by then.
*
* \param[in] ackPin Pin number of the acknowledge pin
*/
void initAckPin (uint8_t ackPin) {
uint8_t interruptNum = digitalPinToInterrupt (ackPin);
if (interruptNum != NOT_AN_INTERRUPT) {
// We only care about the interrupt flag in EIFR, not about actually
// handling the interrupt. Therefore we attach an empty ISR and revert
// the interrupt mask after attaching it.
uint8_t orgMask = EIMSK;
attachInterrupt (interruptNum, psxAcknowledgeEmptyISR, RISING);
// Assume EIMSK and EIFR use the same bit fields.
ackBitmask = EIMSK & ~orgMask;
EIMSK = orgMask;
}
}

/** \brief Transfer several bytes to/from the controller
*
* This function transfers an array of <i>command</i> bytes to the
Expand All @@ -413,22 +448,44 @@ class PsxController {
* \param[out] in The data bytes returned by the controller, must be sized
* to hold at least \a len bytes
* \param[in] len The amount of bytes to be exchanged
* \param[in] waitForAck Whether we should wait for an acknowledgement before
* transferring the first byte
*/
void shiftInOut (const byte *out, byte *in, const byte len) {
void shiftInOut (const byte *out, byte *in, const byte len, bool waitForAck) {
#ifdef DUMP_COMMS
byte inbuf[len];
#endif

for (byte i = 0; i < len; ++i) {
if (ackBitmask) {
if (waitForAck) {
// Wait for acknowledge interrupt flag to be set.
unsigned long start = micros ();
while (!(EIFR & ackBitmask)) {
if (micros () - start >= INTER_CMD_BYTE_DELAY * 2) {
// Timeout: probably no controller present.
break;
}
}
}
// Reset acknowledge flag.
EIFR = ackBitmask;
} else {
if (waitForAck) {
// Wait for a fixed amount of time such that the controller is
// ready for the next byte.
delayMicroseconds (INTER_CMD_BYTE_DELAY);
}
}
waitForAck = true;

byte tmp = shiftInOut (out != NULL ? out[i] : 0x5A);
#ifdef DUMP_COMMS
inbuf[i] = tmp;
#endif
if (in != NULL) {
in[i] = tmp;
}

delayMicroseconds (INTER_CMD_BYTE_DELAY); // Very important!
}

#ifdef DUMP_COMMS
Expand Down Expand Up @@ -473,14 +530,14 @@ class PsxController {

if (len >= 3 && len <= BUFFER_SIZE) {
// All commands have at least 3 bytes, so shift out those first
shiftInOut (out, inputBuffer, 3);
shiftInOut (out, inputBuffer, 3, false);
if (isValidReply (inputBuffer)) {
// Reply is good, get full length
byte replyLen = getReplyLength (inputBuffer);

// Shift out rest of command
if (len > 3) {
shiftInOut (out + 3, inputBuffer + 3, len - 3);
shiftInOut (out + 3, inputBuffer + 3, len - 3, true);
}

byte left = replyLen - len + 3;
Expand All @@ -493,7 +550,7 @@ class PsxController {
ret = inputBuffer;
} else if (len + left <= BUFFER_SIZE) {
// Part of reply is still missing and we have space for it
shiftInOut (NULL, inputBuffer + len, left);
shiftInOut (NULL, inputBuffer + len, left, true);
ret = inputBuffer;
} else {
// Reply incomplete but not enough space provided
Expand Down