(leonardo) playstation compatibility
This commit is contained in:
@@ -5,11 +5,23 @@
|
||||
#include <Keyboard.h>
|
||||
#else
|
||||
#include <EEPROM.h>
|
||||
#define WITH_PSX 1
|
||||
#if WITH_PSX == 1
|
||||
#include "ps2.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "POPNHID.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
/* 1 frame (as declared in POPNHID.cpp) on highspeed USB spec is 125µs */
|
||||
#define REPORT_DELAY 125
|
||||
#define MILLIDEBOUNCE 15
|
||||
#define REPORT_DELAY 120
|
||||
#else
|
||||
/* 1 frame (as declared in POPNHID.cpp) on fullspeed USB spec is 1ms */
|
||||
#define REPORT_DELAY 995
|
||||
#endif
|
||||
|
||||
#define MILLIDEBOUNCE 5
|
||||
POPNHID_ POPNHID;
|
||||
|
||||
/* Buttons + Lights declarations */
|
||||
@@ -26,6 +38,8 @@ const byte ButtonCount = sizeof(ButtonPins) / sizeof(ButtonPins[0]);
|
||||
const byte LightCount = sizeof(LightPins) / sizeof(LightPins[0]);
|
||||
Bounce buttons[ButtonCount];
|
||||
|
||||
uint16_t buttonsState = 0;
|
||||
|
||||
#if defined(ARDUINO_ARCH_SAM)
|
||||
/* Keypad declarations */
|
||||
const byte ROWS = 4;
|
||||
@@ -95,6 +109,24 @@ void setup() {
|
||||
if (lightMode > 4)
|
||||
lightMode = 2;
|
||||
POPNHID.setLightMode(lightMode);
|
||||
|
||||
#if WITH_PSX == 1
|
||||
PS2_MapInput(&buttonsState, (1<<0), PS2_TRIANGLE);
|
||||
PS2_MapInput(&buttonsState, (1<<1), PS2_CIRCLE);
|
||||
PS2_MapInput(&buttonsState, (1<<2), PS2_R1);
|
||||
PS2_MapInput(&buttonsState, (1<<3), PS2_CROSS);
|
||||
PS2_MapInput(&buttonsState, (1<<4), PS2_L1);
|
||||
PS2_MapInput(&buttonsState, (1<<5), PS2_SQUARE);
|
||||
PS2_MapInput(&buttonsState, (1<<6), PS2_R2);
|
||||
PS2_MapInput(&buttonsState, (1<<7), PS2_UP);
|
||||
PS2_MapInput(&buttonsState, (1<<8), PS2_L2);
|
||||
PS2_MapInput(&buttonsState, (1<<9), PS2_SELECT);
|
||||
PS2_MapInput(&buttonsState, (1<<10), PS2_START);
|
||||
PS2_AlwaysInput(PS2_LEFT|PS2_DOWN|PS2_RIGHT);
|
||||
|
||||
PS2_Init();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
//boot animation
|
||||
uint16_t anim[] = {1, 4, 16, 64, 256, 128, 32, 8, 2};
|
||||
@@ -107,21 +139,27 @@ void setup() {
|
||||
|
||||
/* LOOP */
|
||||
unsigned long lastReport = 0;
|
||||
uint32_t prevButtonsState = 0;
|
||||
uint16_t prevButtonsState = 0;
|
||||
bool modeChanged = false;
|
||||
void loop() {
|
||||
/* BUTTONS */
|
||||
uint32_t buttonsState = 0;
|
||||
buttonsState = 0;
|
||||
for (int i = 0; i < ButtonCount; i++) {
|
||||
buttons[i].update();
|
||||
int value = buttons[i].read();
|
||||
if (value != HIGH){
|
||||
buttonsState |= (uint32_t)1 << i;
|
||||
buttonsState |= (uint16_t)1 << i;
|
||||
} else {
|
||||
buttonsState &= ~((uint32_t)1 << i);
|
||||
buttonsState &= ~((uint16_t)1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
#if WITH_PSX == 1
|
||||
PS2_Task();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* USB DATA */
|
||||
if ( ( (micros() - lastReport) >= REPORT_DELAY) )
|
||||
{
|
||||
@@ -131,7 +169,7 @@ void loop() {
|
||||
|
||||
//check for HID-requested lightmode change
|
||||
POPNHID.updateLightMode();
|
||||
}
|
||||
}
|
||||
|
||||
/* LAMPS */
|
||||
uint8_t mode = POPNHID.getLightMode();
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
#include "ps2.h"
|
||||
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
/* USER CUSTOMIZABLE SETTINGS */
|
||||
#define ACK_PORT PORTD
|
||||
#define ACK_DDR DDRD
|
||||
#define ACK_PIN 5 // PD5 (TXLED)
|
||||
|
||||
/* RECOMMENDED DO NOT CHANGE */
|
||||
#define INVERT_CIPO 0 // Set to 1 if CIPO is open-drain via transistor (recommended)
|
||||
#define INVERT_ACK 0 // Set to 1 if ACK is open-drain via transistor (recommended)
|
||||
|
||||
/* END OF USER CUSTOMIZABLE SETTINGS */
|
||||
|
||||
// Stores a constructed packet for the PS2.
|
||||
uint16_t Data = 0;
|
||||
// List of available PS2 inputs.
|
||||
PS2_InputList_t *PS2Input = NULL;
|
||||
// Current PS2 state.
|
||||
void (*PS2Handler)(uint8_t) = NULL;
|
||||
|
||||
|
||||
|
||||
void PS2_Acknowledge(void) {
|
||||
// Burn a few cycles before acknowledging
|
||||
asm volatile(
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
);
|
||||
#if INVERT_ACK == 0
|
||||
ACK_DDR |= (1<<ACK_PIN);
|
||||
#else
|
||||
ACK_DDR &= ~(1<<ACK_PIN);
|
||||
#endif
|
||||
// 40 cycles of delay should give us the same delay as a real PS1 controller
|
||||
asm volatile(
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
"nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"
|
||||
);
|
||||
#if INVERT_ACK == 0
|
||||
ACK_DDR &= ~(1<<ACK_PIN);
|
||||
#else
|
||||
ACK_DDR |= (1<<ACK_PIN);
|
||||
#endif
|
||||
}
|
||||
|
||||
void PS2_Listen(uint8_t in);
|
||||
void PS2_Addressed(uint8_t in);
|
||||
void PS2_HeaderFinished(uint8_t in);
|
||||
void PS2_LowerSent(uint8_t in);
|
||||
|
||||
uint8_t memory_card_timeout = 0;
|
||||
|
||||
void PS2_MemoryCardTimeout(uint8_t in) {
|
||||
if (memory_card_timeout) --memory_card_timeout;
|
||||
if (!memory_card_timeout)
|
||||
PS2Handler = PS2_Listen;
|
||||
}
|
||||
|
||||
void PS2_MemoryCardID2(uint8_t in) {
|
||||
// If we receive a 0x01 here, revert back to the listener
|
||||
--memory_card_timeout;
|
||||
if (in == 0x01) {
|
||||
memory_card_timeout = 0;
|
||||
PS2_Listen(in);
|
||||
return;
|
||||
}
|
||||
PS2Handler = PS2_MemoryCardTimeout;
|
||||
}
|
||||
|
||||
void PS2_MemoryCardID1(uint8_t in) {
|
||||
// If we receive a 0x01 here, revert back to the listener
|
||||
--memory_card_timeout;
|
||||
if (in == 0x01) {
|
||||
memory_card_timeout = 0;
|
||||
PS2_Listen(in);
|
||||
return;
|
||||
}
|
||||
PS2Handler = PS2_MemoryCardID2;
|
||||
}
|
||||
|
||||
// Memory card addressed; ignore input
|
||||
void PS2_MemoryCardAddressed(uint8_t in) {
|
||||
memory_card_timeout = 8;
|
||||
if (in == 'R')
|
||||
memory_card_timeout = 138;
|
||||
if (in == 'W')
|
||||
memory_card_timeout = 136;
|
||||
|
||||
PS2Handler = PS2_MemoryCardID1;
|
||||
}
|
||||
|
||||
// Idle state.
|
||||
void PS2_Listen(uint8_t in) {
|
||||
if (in == 0x81) {
|
||||
DDRB &= ~0x08;
|
||||
PS2Handler = PS2_MemoryCardAddressed;
|
||||
}
|
||||
// Report as a digital controller when addressed
|
||||
if (in == 0x01) {
|
||||
DDRB |= 0x08;
|
||||
#if INVERT_CIPO == 1
|
||||
SPDR = ~0x41;
|
||||
#else
|
||||
SPDR = 0x41;
|
||||
#endif
|
||||
PS2Handler = PS2_Addressed;
|
||||
PS2_Acknowledge();
|
||||
}
|
||||
}
|
||||
|
||||
// When polling is requested, begin responding
|
||||
void PS2_Addressed(uint8_t in) {
|
||||
if (in == 0x42) {
|
||||
#if INVERT_CIPO == 1
|
||||
SPDR = ~0x5A;
|
||||
#else
|
||||
SPDR = 0x5A;
|
||||
#endif
|
||||
PS2Handler = PS2_HeaderFinished;
|
||||
PS2_Acknowledge();
|
||||
}
|
||||
}
|
||||
|
||||
// After end-of-header sent, send the first byte
|
||||
void PS2_HeaderFinished(uint8_t in) {
|
||||
uint8_t *data = (uint8_t *)&Data;
|
||||
|
||||
#if INVERT_CIPO == 1
|
||||
SPDR = ~(*data);
|
||||
#else
|
||||
SPDR = (*data);
|
||||
#endif
|
||||
PS2Handler = PS2_LowerSent;
|
||||
PS2_Acknowledge();
|
||||
}
|
||||
|
||||
// After first byte sent, send the second and go back to listening.
|
||||
void PS2_LowerSent(uint8_t in) {
|
||||
uint8_t *data = (uint8_t *)&Data + 1;
|
||||
|
||||
#if INVERT_CIPO == 1
|
||||
SPDR = ~(*data);
|
||||
#else
|
||||
SPDR = (*data);
|
||||
#endif
|
||||
PS2Handler = PS2_Listen;
|
||||
PS2_Acknowledge();
|
||||
}
|
||||
|
||||
void PS2_Init(void) {
|
||||
cli();
|
||||
|
||||
#if INVERT_ACK == 0
|
||||
ACK_PORT &= ~(1<<ACK_PIN);
|
||||
#else
|
||||
ACK_PORT |= (1<<ACK_PIN);
|
||||
#endif
|
||||
PS2_Acknowledge();
|
||||
// Set MISO as an output pin
|
||||
DDRB |= 0x08;
|
||||
// Setup data on falling edge, sample on rising edge (SPI mode 3)
|
||||
SPCR = (1 << CPOL) | (1 << CPHA)
|
||||
// Transmit LSB first
|
||||
| (1 << DORD)
|
||||
// Enable interrupts for SPI
|
||||
| (1 << SPIE)
|
||||
// Enable SPI
|
||||
| (1 << SPE);
|
||||
|
||||
// Set the first byte up
|
||||
|
||||
#if INVERT_CIPO == 1
|
||||
SPDR = 0x00; // 0xFF;
|
||||
#else
|
||||
SPDR = 0xFF;
|
||||
#endif
|
||||
PS2Handler = PS2_Listen;
|
||||
sei();
|
||||
}
|
||||
|
||||
// Update the stored data packet
|
||||
void PS2_Task(void) {
|
||||
if (PINB & 0x01)
|
||||
{
|
||||
|
||||
#if INVERT_CIPO == 1
|
||||
SPDR = 0x00;
|
||||
#else
|
||||
SPDR = 0xFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
PS2_InputList_t *map = PS2Input;
|
||||
uint16_t new_data = 0;
|
||||
|
||||
while(map) {
|
||||
if (!map->input || (*map->input & map->mask))
|
||||
new_data |= map->buttons;
|
||||
|
||||
map = map->parent;
|
||||
}
|
||||
|
||||
Data = ~new_data;
|
||||
}
|
||||
|
||||
void PS2_MapInput(uint16_t *input, uint16_t mask, PS2_INPUT buttons) {
|
||||
PS2_InputList_t *child = calloc(1, sizeof(PS2_InputList_t));
|
||||
|
||||
child->input = input,
|
||||
child->mask = mask,
|
||||
child->buttons = buttons,
|
||||
child->parent = PS2Input;
|
||||
|
||||
PS2Input = child;
|
||||
}
|
||||
|
||||
void PS2_AlwaysInput(PS2_INPUT buttons) {
|
||||
PS2_InputList_t *child = calloc(1, sizeof(PS2_InputList_t));
|
||||
|
||||
child->input = NULL,
|
||||
child->mask = 0,
|
||||
child->buttons = buttons,
|
||||
child->parent = PS2Input;
|
||||
|
||||
PS2Input = child;
|
||||
}
|
||||
|
||||
// When a transfer is complete, determine what to do next
|
||||
ISR(SPI_STC_vect) {
|
||||
uint8_t input = SPDR;
|
||||
if (input == 0x01 && (!memory_card_timeout)) PS2Handler = PS2_Listen;
|
||||
PS2Handler(SPDR);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
#if defined(ARDUINO_ARCH_AVR)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
typedef enum {
|
||||
PS2_NC = 0,
|
||||
PS2_SELECT = (1 << 0),
|
||||
PS2_L3 = (1 << 1),
|
||||
PS2_R3 = (1 << 2),
|
||||
PS2_START = (1 << 3),
|
||||
PS2_UP = (1 << 4),
|
||||
PS2_RIGHT = (1 << 5),
|
||||
PS2_DOWN = (1 << 6),
|
||||
PS2_LEFT = (1 << 7),
|
||||
PS2_L2 = (1 << 8),
|
||||
PS2_R2 = (1 << 9),
|
||||
PS2_L1 = (1 << 10),
|
||||
PS2_R1 = (1 << 11),
|
||||
PS2_TRIANGLE = (1 << 12),
|
||||
PS2_CIRCLE = (1 << 13),
|
||||
PS2_CROSS = (1 << 14),
|
||||
PS2_SQUARE = (1 << 15),
|
||||
} PS2_INPUT;
|
||||
|
||||
typedef struct PS2_InputList_t PS2_InputList_t;
|
||||
|
||||
struct PS2_InputList_t {
|
||||
uint16_t *input; // Input source; NULL is always true
|
||||
uint16_t mask; // Mask to check; (*input & mask)
|
||||
PS2_INPUT buttons; // OR the following if true
|
||||
PS2_InputList_t *parent;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
void PS2_Init(void);
|
||||
void PS2_Task(void);
|
||||
|
||||
void PS2_MapInput(uint16_t *input, uint16_t mask, PS2_INPUT buttons);
|
||||
void PS2_AlwaysInput(PS2_INPUT buttons);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user