diff --git a/README.md b/README.md index 316f94d..4d142e1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Arduino PSX Controller -This project is a short of example on how to use an Arduino board as a controller for the Playstation 1 or 2. + +This is an Arduino library used to emulate a Playstation 1/2 controller, exclusively for the Leonardo / Micro / Pro Micro. ### Caveats @@ -9,7 +10,7 @@ This project is a short of example on how to use an Arduino board as a controlle It is **highly** recommended that you use an [Arduino Micro](https://store.arduino.cc/products/arduino-micro) or [comparable clone](https://www.aliexpress.com/item/1893729784.html). This is due to the fact that all necessary pins are easily broken out. Using a Leonardo or Pro Micro will require you to solder onto pre-existing LEDs, which can be difficult. -You will also need qty 2 (two) NPN signal transistors. I used 2N2222A, but many others will work. A similar N-Channel MOSFET, such as the 2N7000 will also work. +You will also need qty 1 (one) NPN signal transistor. I used 2N2222A, but many others will work. A similar N-Channel MOSFET, such as the 2N7000 will also work. Lastly, you will need a cable to plug into your Playstation. Options include: 1. Cutting the cord from an existing controller @@ -22,14 +23,14 @@ Let's get acquainted with the Playstation cable and color codes. It is always a ![Playstation Cable](/images/psx-cable-colors.png) -* **CIPO:** (Controller-In / Peripheral-Out) This is data from the controller to the console. It is held HIGH via a pull-up resistor inside the console. We will use one of the transistors to pull this line to GND. Also known as MISO. +* **CIPO:** (Controller-In / Peripheral-Out) This is data from the controller to the console. It is held HIGH via a pull-up resistor inside the console. We will use the transistor to pull this line to GND. Also known as MISO. * **COPI:** (Controller-Out / Peripheral-In) This is the data from the console to the controller. Also known as MOSI. * **7.6V:** This provides power for the rumble motors. See [Powering the Arduino](#powering-the-arduino). * **GND:** The common ground for the console, also known as 0V. * **3.3V:** This is power coming from the console, which typically powers the controller processor. See [Powering the Arduino](#powering-the-arduino). * **CS:** (Chip Select) This line goes LOW when the console is requesting data from a controller. This is how the console selects which player's controller to read from. Also known as SS. * **N/C:** This wire is not used (not connected). -* **ACK:** This is how the controller tells the console it has finished sending data. It is held HIGH via a pull-up resistor inside the console. The second transistor will pull this line to GND. +* **ACK:** This is how the controller tells the console it has finished sending data. It is held HIGH via a pull-up resistor inside the console. ## Arduino Wiring @@ -41,19 +42,23 @@ The example program follows the wiring below. Please refer to your specific tran * CIPO * COPI -* 7.6V -* GND * CS ### Pins that can be changed * ACK +### Power Pins + +* VIN +* 5V +* GND + ## Powering the Arduino There's a few options you have for powering the arduino. -### Option 1 (Recommended): 7.6V +### Option 1: (Recommended!) 7.6V to VIN Connect 7.6V to VIN on the Arduino, as shown in the diagram above. This will power the Arduino off the rumble line and at 5V. It is safe to plug in the USB connector at the same time using this method. @@ -69,108 +74,115 @@ Disconnect 7.6V and connect 3.3V from the Playstation to the 5V pin on your Ardu ## Basic Use -The example includes D-Pad controls, as well as X and O. Connect `A0`, `A1`, `A2`, `A3`, `A4`, or `A5` to GND to try it out. +### Initialization + +Include the header at the top of your code: ``` -button_state = 0; -if (digitalRead(A0) == LOW) { -button_state |= PS_LEFT; -} -if (digitalRead(A1) == LOW) { -button_state |= PS_DOWN; -} -if (digitalRead(A2) == LOW) { -button_state |= PS_UP; -} -if (digitalRead(A3) == LOW) { -button_state |= PS_RIGHT; -} -if (digitalRead(A4) == LOW) { -button_state |= PS_CIRCLE; -} -if (digitalRead(A5) == LOW) { -button_state |= PS_CROSS; +#include +``` + +This will automatically create an instance of the `PSX` object, which you will use later. + +Next, run the initialization function inside `setup`: + +``` +void setup() { + PSX.init(8); } ``` -To set a button, `BITWISE OR` the correct define with `button_state`. A button is released if it is not pressed during the loop. +The `init` function takes up to 3 arguments. -Valid defines are: +1. `ackPin`: This is the pin that the ACK line is attached to. Use the digital pin number, shown in the diagram above as the `D` numbers. + * The default is `8`. +2. `invertACK`: This allows you to invert the logic for the ACK pin. Set this to `false` when connected directly to the Arduino and use `true` when connected via a transistor. + * The default is `false`. +3. `invertCIPO`: This allows you to invert the logic for the CIPO pin. Set this to `false` when connected directly to the Arduino and use `true` when connected via a transistor. Please see [Bypassing the Transistor](#bypassing-the-transistor) + * The default is `true`. -``` -PS_SELECT -PS_L3 -PS_R3 -PS_START -PS_UP -PS_RIGHT -PS_DOWN -PS_LEFT -PS_L2 -PS_R2 -PS_L1 -PS_R1 -PS_TRIANGLE -PS_CIRCLE -PS_CROSS -PS_SQUARE -``` - -### Always On Buttons +#### Always Held Buttons For certain types of controllers (Pop'n Music and Guitar Hero come to mind), the game expects certain buttons to *always* be pressed. -To do so, change the following line from above: +To do so, run the method `setAlwaysHeld` using the button enums `BITWISE OR`'d together. ``` -button_state = 0; +void setup() { + PSX.init(8); + PSX.setAlwaysHeld(PS_INPUT::PS_LEFT | PS_INPUT::PS_DOWN | PS_INPUT::PS_RIGHT); +} ``` -to include the necessary buttons. In this example, Left, Down, and Right are being held permanently: +### Setting Buttons + +Buttons can be set easily using the `setButton` method. + +Set a button to `true` to press it and `false` to release it. ``` -button_state = PS_LEFT | PS_DOWN | PS_RIGHT; + // Press left button + PSX.setButton(PS_INPUT::PS_LEFT, true); + + // Release left button + PSX.setButton(PS_INPUT::PS_LEFT, false); ``` -### Additional Notes - -#### Changing the ACK Pin - -At the top of the `*.ino` file will be a section to change the ACK pin location. - +Valid options are as follows: ``` -/* USER CUSTOMIZABLE SETTINGS */ -#define ACK_PORT PORTB -#define ACK_DDR DDRB -#define ACK_PIN 4 // PB4 (Pin 8 on Micro) +PS_INPUT::PS_SELECT +PS_INPUT::PS_L3 +PS_INPUT::PS_R3 +PS_INPUT::PS_START +PS_INPUT::PS_UP +PS_INPUT::PS_RIGHT +PS_INPUT::PS_DOWN +PS_INPUT::PS_LEFT +PS_INPUT::PS_L2 +PS_INPUT::PS_R2 +PS_INPUT::PS_L1 +PS_INPUT::PS_R1 +PS_INPUT::PS_TRIANGLE +PS_INPUT::PS_CIRCLE +PS_INPUT::PS_CROSS +PS_INPUT::PS_SQUARE ``` -The pin number refers to the `PORT` number, not the standard Arduino number. Refer to the purple tags in the image above. +## Example -#### Bypassing the Transistors +A basic example is included. Navigate to the Example sketches in the Arduino IDE to run it. -For safety and performance reasons, I suggest using the transistors as shown above to prevent backfeeding voltage into your console. This has the potential to cause harm. +The example includes D-Pad controls, as well as O and X. Connect the following pins to GND to press the corresponding button: +|Pin|Button| +|:---:|:---:| +|A0|LEFT| +|A1|DOWN| +|A2|UP| +|A3|RIGHT| +|A4|O| +|A5|X| + +## Additional Notes + +### Bypassing the Transistor + +For safety and performance reasons, I suggest using the transistor as shown above to prevent backfeeding voltage into your console. This has the potential to cause harm. ***It has been noted by myself and others that bypassing the use of a transistor on the CIPO line may result in poor performance or corrupted data. It is not a software issue, but a result of electrical characteristics related to the Arduino, the length and impedance of your wires, and other factors. I will not be able to troubleshoot your situation if you go this route.*** -If you do not use the transistors and directly wire your Arduino to the console, the following lines must be changed. CIPO and ACK can be change individually. - -``` -#define INVERT_CIPO 1 // Set to 1 if CIPO is open-drain via a transistor -#define INVERT_ACK 1 // Set to 1 if ACK is open-drain via a transistor -``` - -to - -``` -#define INVERT_CIPO 0 // Set to 1 if CIPO is open-drain via a transistor -#define INVERT_ACK 0 // Set to 1 if ACK is open-drain via a transistor -``` - ## Credits +Some code used from [watterott's Arduino-Libs](https://github.com/watterott/Arduino-Libs). Specific lines are noted. + +``` +Copyright (c) 2011-2020 Watterott electronic (www.watterott.com) +All rights reserved. +``` + Wiring and protocol information from [Curious Inventor's page on the Playstation Controller](https://store.curiousinventor.com/guides/PS2) Code base adapted from [CrazyRedMachine's Ultiamte Pop'n Controller](https://github.com/CrazyRedMachine/UltimatePopnController/tree/PSX), which itself is based on [busslave's PSX_RECEIVER.cpp](https://nfggames.com/forum2/index.php?topic=5001.0). -Discord User GoroKaneda for getting me to work on and document this, as well as additional testing. \ No newline at end of file +Discord User GoroKaneda for getting me to work on and document this, as well as additional testing. + +Additional suggestions and feedback from [progmem](https://github.com/progmem). \ No newline at end of file diff --git a/examples/psx-basic-example/psx-basic-example.ino b/examples/psx-basic-example/psx-basic-example.ino new file mode 100644 index 0000000..1b3bc59 --- /dev/null +++ b/examples/psx-basic-example/psx-basic-example.ino @@ -0,0 +1,29 @@ +#include + +void setup() { + // Setup PSX + PSX.init(8); + + // Setup buttons + pinMode(A0, INPUT_PULLUP); + pinMode(A1, INPUT_PULLUP); + pinMode(A2, INPUT_PULLUP); + pinMode(A3, INPUT_PULLUP); + pinMode(A4, INPUT_PULLUP); + pinMode(A5, INPUT_PULLUP); +} + +void loop() { + + // Set buttons + PSX.setButton(PS_INPUT::PS_LEFT, digitalRead(A0) == LOW); + PSX.setButton(PS_INPUT::PS_DOWN, digitalRead(A1) == LOW); + PSX.setButton(PS_INPUT::PS_UP, digitalRead(A2) == LOW); + PSX.setButton(PS_INPUT::PS_RIGHT, digitalRead(A3) == LOW); + PSX.setButton(PS_INPUT::PS_CIRCLE, digitalRead(A4) == LOW); + PSX.setButton(PS_INPUT::PS_CROSS, digitalRead(A5) == LOW); + + // Send info + PSX.send(); + +} \ No newline at end of file diff --git a/images/micro-psx-wiring.png b/images/micro-psx-wiring.png index 625e8b6..22104de 100644 Binary files a/images/micro-psx-wiring.png and b/images/micro-psx-wiring.png differ diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..0069306 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,33 @@ +# Syntax Coloring Map for Arduino-PSX + +# Datatypes (KEYWORD1) +PSX KEYWORD1 + +# Methods and Functions (KEYWORD2) +init KEYWORD2 +setAlwaysHeld KEYWORD2 +setButton KEYWORD2 +send KEYWORD2 +isr KEYWORD2 + +# Enums (KEYWORD2) +PS_INPUT KEYWORD2 + +# Enum Variables (KEYWORD3) +PS_NONE KEYWORD3 +PS_SELECT KEYWORD3 +PS_L3 KEYWORD3 +PS_R3 KEYWORD3 +PS_START KEYWORD3 +PS_UP KEYWORD3 +PS_RIGHT KEYWORD3 +PS_DOWN KEYWORD3 +PS_LEFT KEYWORD3 +PS_L2 KEYWORD3 +PS_R2 KEYWORD3 +PS_L1 KEYWORD3 +PS_R1 KEYWORD3 +PS_TRIANGLE KEYWORD3 +PS_CIRCLE KEYWORD3 +PS_CROSS KEYWORD3 +PS_SQUARE KEYWORD3 \ No newline at end of file diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..5a29ea5 --- /dev/null +++ b/library.properties @@ -0,0 +1,10 @@ +name=Arduino-PSX +version=1.0 +author=Verox Zik +maintainer=Verox Zik <43590004+veroxzik@users.noreply.github.com> +sentence=Library to act as a Playstation 1/2 controller. +paragraph=This library allows an Arduino Leonardo, Micro, or Pro Micro to act as a Playstation 1/2 digital controller. +category=Device Control +url=https://github.com/veroxzik/arduino-psx-controller/ +architectures=avr +includes=arduino_psx.h \ No newline at end of file diff --git a/src/arduino_psx.cpp b/src/arduino_psx.cpp new file mode 100644 index 0000000..a145397 --- /dev/null +++ b/src/arduino_psx.cpp @@ -0,0 +1,112 @@ +#include "arduino_psx.h" + +ISR(SPI_STC_vect) { + PSX.isr(); +} + +void PSX_::init(int ackPin = 8, bool invACK = false, bool invCIPO = true) { + ackDDR = __digitalPinToDDRReg(ackPin); + ackPORT = __digitalPinToPortReg(ackPin); + ackPinMask = __digitalPinToBit(ackPin); + invertACK = invACK; + invertCIPO = invCIPO; + + if (invertACK) { + (*ackDDR) |= (1 << ackPinMask); // Set as output + (*ackPORT) &= ~(1 << ackPinMask); // Set LOW (open drain -- pullup on console) + } else { + (*ackDDR) &= ~(1 << ackPinMask); // Set as input + (*ackPORT) &= ~(1 << ackPinMask); // Ensure pullup is off + } + + if (invertCIPO) { + DDRB |= (1 << 3); // Set as output + PORTB &= ~(1 << 3); // Set LOW (open drain -- pullup on console) + } else { + DDRB &= ~(1 << 3); // Set as input + PORTB &= ~(1 << 3); // Ensure pullup is off + } + +#if (F_CPU == 16000000L) + SPCR |= (1 << SPR1); // Fosc/64 @16MHz==250KHz +#elif (F_CPU == 8000000L) + SPCR |= (1 << SPR2) | (1 << SPR1); // Fosc/32 @8MHz==250KHz +#endif + SPCR |= (1 << CPHA); // Setup @ leading edge, sample @ falling edge + SPCR |= (1 << CPOL); // Leading edge is falling edge, trailing edge is rising edge + SPCR &= ~(1 << MSTR); // MSTR bit is zero, SPI is slave + SPCR |= (1 << DORD); // Byte is transmitted LSB first, MSB last + SPCR |= (1 << SPE); // Enable SPI + SPCR |= (1 << SPIE); // Enable Serial Transfer Complete (STC) interrupt + SPDR = 0xFF; + + sei(); +} + +void PSX_::setButton(uint16_t button, bool state) { + if (state) { + buttonState |= button; + } else { + buttonState &= ~button; + } +} + +void PSX_::send() { + // Fill data array with button info + dataBuf[2] = ~(buttonState & 0xFF); + dataBuf[3] = ~((buttonState >> 8) & 0xFF); + + // Check SS and clear memcard flag if necessary + if ((PINB & (1 << 0)) > 0) { + if (memcardActive) { + memcardActive = false; + } + currentByte = 0; + if (!invertCIPO) { + DDRB &= ~(1 << 3); // Disable output + } + } +} + +void PSX_::isr() { + uint8_t recvByte = SPDR; + + if (recvByte == commandBuf[currentByte] && memcardActive == false) { + DDRB |= (1 << 3); // Enable output + if (invertCIPO) { + SPDR = ~dataBuf[currentByte]; + } else { + SPDR = dataBuf[currentByte]; + } + currentByte++; + if (currentByte < 5) { + _delay_us(8); // Necessary delay for PS1 (not needed for PS2) + // Set ACK low + if (invertACK) { + (*ackPORT) |= (1 << ackPinMask); + } else { + (*ackDDR) |= (1 << ackPinMask); // Enable output (default LOW) + } + _delay_us(1); + // Set ACK high + if (invertACK) { + (*ackPORT) &= ~(1 << ackPinMask); + } else { + (*ackDDR) &= ~(1 << ackPinMask); + } + } else { + DDRB &= ~(1 << 3); // Disable output + SPDR = 0xFF; + currentByte = 0; + } + } else { + if (recvByte == commandMemcard && currentByte == 0) { + memcardActive = true; + } + DDRB &= ~(1 << 3); // Disable output + SPDR = 0xFF; + currentByte = 0; + } +} + +PSX_ PSX; \ No newline at end of file diff --git a/src/arduino_psx.h b/src/arduino_psx.h new file mode 100644 index 0000000..00715dd --- /dev/null +++ b/src/arduino_psx.h @@ -0,0 +1,104 @@ +#ifndef PSX_h +#define PSX_h + +#if defined(ARDUINO) && ARDUINO >= 100 +#include "Arduino.h" +#else +#error "Arduino versions older than v1.0 are not supported." +#endif + +#include +#include +#include + +/* + Following section from Arduino-Libs (digitalWriteFast) + + Copyright (c) 2011-2020 Watterott electronic (www.watterott.com) + All rights reserved. +*/ + +#if (defined(ARDUINO_AVR_LEONARDO) || \ + defined(__AVR_ATmega16U4__) || \ + defined(__AVR_ATmega32U4__)) + +#define UART_RX_PIN (0) //PD2 +#define UART_TX_PIN (1) //PD3 + +#define I2C_SDA_PIN (2) //PD1 +#define I2C_SCL_PIN (3) //PD0 + +#define SPI_HW_SS_PIN (17) //PB0 +#define SPI_HW_MOSI_PIN (16) //PB2 +#define SPI_HW_MISO_PIN (14) //PB3 +#define SPI_HW_SCK_PIN (15) //PB1 + +#define __digitalPinToPortReg(P) \ +((((P) >= 0 && (P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PORTD : (((P) == 5 || (P) == 13) ? &PORTC : (((P) >= 18 && (P) <= 23)) ? &PORTF : (((P) == 7) ? &PORTE : &PORTB))) +#define __digitalPinToDDRReg(P) \ +((((P) >= 0 && (P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &DDRD : (((P) == 5 || (P) == 13) ? &DDRC : (((P) >= 18 && (P) <= 23)) ? &DDRF : (((P) == 7) ? &DDRE : &DDRB))) +#define __digitalPinToPINReg(P) \ +((((P) >= 0 && (P) <= 4) || (P) == 6 || (P) == 12 || (P) == 24 || (P) == 25 || (P) == 29) ? &PIND : (((P) == 5 || (P) == 13) ? &PINC : (((P) >= 18 && (P) <= 23)) ? &PINF : (((P) == 7) ? &PINE : &PINB))) +#define __digitalPinToBit(P) \ +(((P) >= 8 && (P) <= 11) ? (P) - 4 : (((P) >= 18 && (P) <= 21) ? 25 - (P) : (((P) == 0) ? 2 : (((P) == 1) ? 3 : (((P) == 2) ? 1 : (((P) == 3) ? 0 : (((P) == 4) ? 4 : (((P) == 6) ? 7 : (((P) == 13) ? 7 : (((P) == 14) ? 3 : (((P) == 15) ? 1 : (((P) == 16) ? 2 : (((P) == 17) ? 0 : (((P) == 22) ? 1 : (((P) == 23) ? 0 : (((P) == 24) ? 4 : (((P) == 25) ? 7 : (((P) == 26) ? 4 : (((P) == 27) ? 5 : 6 ))))))))))))))))))) + +#else +#error "This library does not support any processor besides the ATmega32U4 or ATmega16U4" +#endif + +/* End Arduino-Libs Section */ + +typedef enum { + PS_NONE = 0, + PS_SELECT = (1 << 0), + PS_L3 = (1 << 1), + PS_R3 = (1 << 2), + PS_START = (1 << 3), + PS_UP = (1 << 4), + PS_RIGHT = (1 << 5), + PS_DOWN = (1 << 6), + PS_LEFT = (1 << 7), + PS_L2 = (1 << 8), + PS_R2 = (1 << 9), + PS_L1 = (1 << 10), + PS_R1 = (1 << 11), + PS_TRIANGLE = (1 << 12), + PS_CIRCLE = (1 << 13), + PS_CROSS = (1 << 14), + PS_SQUARE = (1 << 15), +} PS_INPUT; + +class PSX_ { + + private: + uint16_t buttonState = 0; + uint16_t defaultState = 0; + bool invertCIPO = true; + bool invertACK = true; + volatile uint8_t *ackDDR; + volatile uint8_t *ackPORT; + volatile uint8_t ackPinMask; + + uint8_t dataBuf[5] = {0x41, 0x5A, 0xFF, 0xFF, 0xFF}; + uint8_t commandBuf[5] = {0x01, 0x42, 0x00, 0x00, 0x00}; + uint8_t commandMemcard = 0x81; + volatile bool memcardActive = false; + volatile uint8_t currentByte = 0; + + public: + void init(int ackPin, bool invACK = false, bool invCIPO = true); + void setAlwaysHeld(uint16_t buttons) { + defaultState = buttons; + buttonState = defaultState; + }; + + void setButton(uint16_t button, bool state); + void send(); + + void isr(); + +}; + +extern PSX_ PSX; + +#endif // PSX_h \ No newline at end of file diff --git a/src/arduino_psx/arduino_psx.ino b/src/arduino_psx/arduino_psx.ino deleted file mode 100644 index 56b3489..0000000 --- a/src/arduino_psx/arduino_psx.ino +++ /dev/null @@ -1,198 +0,0 @@ -/* USER CUSTOMIZABLE SETTINGS */ -#define ACK_PORT PORTB -#define ACK_DDR DDRB -#define ACK_PIN 4 // PB4 (Pin 8 on Micro) - - -/* RECOMMENDED DO NOT CHANGE */ -#define INVERT_CIPO 1 // Set to 1 if CIPO is open-drain via a transistor -#define INVERT_ACK 1 // Set to 1 if ACK is open-drain via a transistor - - -/* PSX DEFINE */ -#include -#include -#include -#define SPI_PORT PORTB -#define SPI_PINS PINB -#define SPI_DDR DDRB - -#define ATT_PIN 0 // ~CS -#define CMD_PIN 2 // COPI -#define DATA_PIN 3 // CIPO -#define CLK_PIN 1 // SCK -#define DATA_LEN 5 - -#define PS_SELECT (1 << 0) -#define PS_L3 (1 << 1) -#define PS_R3 (1 << 2) -#define PS_START (1 << 3) -#define PS_UP (1 << 4) -#define PS_RIGHT (1 << 5) -#define PS_DOWN (1 << 6) -#define PS_LEFT (1 << 7) -#define PS_L2 (1 << 8) -#define PS_R2 (1 << 9) -#define PS_L1 (1 << 10) -#define PS_R1 (1 << 11) -#define PS_TRIANGLE (1 << 12) -#define PS_CIRCLE (1 << 13) -#define PS_CROSS (1 << 14) -#define PS_SQUARE (1 << 15) -/* /PSX DEFINE */ - -/* PSX globals */ -#if INVERT_CIPO -uint8_t data_buff[DATA_LEN] = {~0x41, ~0x5A, ~0xFF, ~0xFF, ~0xFF}; //Reply. -#else -uint8_t data_buff[DATA_LEN] = {0x41, 0x5A, 0xFF, 0xFF, 0xFF}; //Reply. -#endif -uint8_t command_buff[DATA_LEN] = {0x01, 0x42, 0x00, 0x00, 0x00}; -uint8_t command_memcard = 0x81; -volatile bool memcard_active = false; - -volatile uint8_t curr_byte = 0; -volatile uint8_t next_byte = 0; - -uint8_t byte4; -uint8_t byte5; - -/* NOTE: a bit set to 1 is unpressed, a bit set to 0 is pressed */ -void convertInputToPSX(uint16_t state) { - - byte4 = ~(state & 0xFF); - byte5 = ~((state >> 8) & 0xFF); - -#if INVERT_CIPO - data_buff[2] = ~byte4; - data_buff[3] = ~byte5; -#else - data_buff[2] = byte4; - data_buff[3] = byte5; -#endif -} - -ISR(SPI_STC_vect) { - uint8_t inbyte = SPDR; - - if (inbyte == command_buff[curr_byte] && memcard_active == false) { - SPI_DDR |= (1 << DATA_PIN); // enable output - SPDR = data_buff[curr_byte]; - curr_byte++; - if (curr_byte < DATA_LEN) { - _delay_us(8); // Necessary delay for PS1 (not needed for PS2) - // Set ACK low -#if INVERT_ACK - ACK_PORT |= (1 << ACK_PIN); -#else - ACK_DDR |= (1 << ACK_PIN); // enable output -#endif - _delay_us(1); - // Set ACK high -#if INVERT_ACK - ACK_PORT &= ~(1 << ACK_PIN); // Release ACK -#else - ACK_DDR &= ~(1 << ACK_PIN); // disable output -#endif - - } else { - SPI_DDR &= ~(1 << DATA_PIN); // disable output - SPDR = 0xFF; - curr_byte = 0; - } - } else { - if (inbyte == command_memcard && curr_byte == 0) { - memcard_active = true; - } - SPI_DDR &= ~(1 << DATA_PIN); // disable output - SPDR = 0xFF; - curr_byte = 0; - } -} -/* /PSX globals */ - -uint16_t button_state = 0; - -void setup() { - - /* PSX setup */ -#if INVERT_ACK - ACK_DDR |= (1 << ACK_PIN); // output - ACK_PORT &= ~(1 << ACK_PIN); // set LOW (open drain -- pull-up on console) -#else - ACK_DDR &= ~(1 << ACK_PIN); // input - ACK_PORT &= ~(1 << ACK_PIN); // ensure pullup is off -#endif - -#if INVERT_CIPO - SPI_DDR |= (1 << DATA_PIN); // output - SPI_PORT &= ~(1 << DATA_PIN); //set LOW (open drain -- pull-up on console) -#else - SPI_DDR &= ~(1 << DATA_PIN); // input - SPI_PORT &= ~(1 << DATA_PIN); // ensure pullup is off -#endif - - //SPI setup - SPCR |= (1 << SPR1); // Fosc/64. @16MHz==250KHz. - SPCR |= (1 << CPHA); // Setup @ leading edge, sample @ falling edge. - SPCR |= (1 << CPOL); // Leading edge is falling edge, trailing edge is rising edge. - SPCR &= ~(1 << MSTR); // MSTR bit is zero, SPI is slave. - SPCR |= (1 << DORD); // Byte is transmitted LSB first, MSB last. - SPCR |= (1 << SPE); // Enable SPI. - SPCR |= (1 << SPIE); // Enable Serial Transfer Complete (STC) interrupt. - SPDR = 0xFF; - - sei(); // Enable global interrupts - /* /PSX setup */ - - /* --- --- --- --- --- --- */ - /* USER CODE STARTS HERE */ - /* --- --- --- --- --- --- */ - - pinMode(A0, INPUT_PULLUP); - pinMode(A1, INPUT_PULLUP); - pinMode(A2, INPUT_PULLUP); - pinMode(A3, INPUT_PULLUP); - pinMode(A4, INPUT_PULLUP); - pinMode(A5, INPUT_PULLUP); -} - -void loop() { - - button_state = 0; - if (digitalRead(A0) == LOW) { - button_state |= PS_LEFT; - } - if (digitalRead(A1) == LOW) { - button_state |= PS_DOWN; - } - if (digitalRead(A2) == LOW) { - button_state |= PS_UP; - } - if (digitalRead(A3) == LOW) { - button_state |= PS_RIGHT; - } - if (digitalRead(A4) == LOW) { - button_state |= PS_CIRCLE; - } - if (digitalRead(A5) == LOW) { - button_state |= PS_CROSS; - } - - /* --- --- --- --- --- --- */ - /* USER CODE ENDS HERE */ - /* --- --- --- --- --- --- */ - - convertInputToPSX(button_state); - - if ((SPI_PINS & (1 << ATT_PIN)) > 0) { - if (memcard_active) { - memcard_active = false; - } - curr_byte = 0; -#if INVERT_CIPO == 0 - SPI_DDR &= ~(1 << DATA_PIN); // input - SPI_PORT &= ~(1 << DATA_PIN); // ensure pullup is off -#endif - } -}