Please help support the site by donating at the link below.https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8ZRU34U47BESW
/* MCP4251 8-bit SPI digital potentiometer, ATmega168 *-bit microcontroller */#include <avr/io.h>#define F_CPU 8000000UL#include <util/delay.h>#include <avr/interrupt.h>#include <inttypes.h>#define LED_PORT PORTC // port, data direction, and input register definitions#define LED_DDR DDRC#define ATTENTION_PORT PORTC#define ATTENTION_DDR DDRC#define CS_PORT PORTC#define CS_DDR DDRC#define BUTTON0_PIN PIND#define PSP_CROSS PC1 // I/O pin definitions#define PSP_SQUARE PC2#define PSP_TRIANGLE PD2#define PSP_CIRCLE PD3#define PSP_UP PD4#define PSP_DOWN PD5#define PSP_LEFT PD6#define PSP_RIGHT PD7#define PSP_START PB0#define PSP_SELECT PB1#define PSP_HOME PB6#define PSP_L1 PB7#define PSP_R1 PC0#define STATUS_LED_GREEN PC5 // green LED cathode on PC5; indicates psx controller connected#define COMMAND PB3 // SPI MOSI; sends commands f-rom AVR to DSC#define DATA PB4 // SPI MISO; receives incoming data f-rom DSC; 1k external pull-up resistor required#define CLOCK PB5 // SPI SCK; serial clock controlled by AVR, DATA is read on rising edge#define ATTENTION PC3 // initiates and closes each data packet transmission#define CS PC4 // chip select for MCP4251#define SS PB2 // SPI slave select pin; set as output to ensure AVR remains SPI master#define BUTTON0 PD1 // button to connect/disconnect potentiometer terminals within the MCP4251#define PS_SELECT 0 // psx_data[0] byte#define PS_L3 1#define PS_R3 2#define PS_START 3#define PS_UP 4#define PS_RIGHT 5#define PS_DOWN 6#define PS_LEFT 7#define PS_L2 0 // psx_data[1] byte#define PS_R2 1#define PS_L1 2#define PS_R1 3#define PS_TRIANGLE 4#define PS_CIRCLE 5#define PS_CROSS 6#define PS_SQUARE 7#define Y_POT_ADDRESS 0x00 // MCP4251 wiper 0 register address (datasheet p.48)#define X_POT_ADDRESS 0x10 // MCP4251 wiper 1 register address (datasheet p.48)#define TCON_ADDRESS 0x40 // MCP4251 terminal control register (datasheet p.35)#define TCON_CONNECT_ALL 0xFF // connect all six potentiometer terminals within the MCP4251 (datasheet p.36)#define TCON_DISCONNECT_ALL 0x88 // disconnect all six potentiometer terminals within the MCP4251 (datasheet p.36)/* SR0 (status register 0) bit defines */#define DSC_CONNECT_STATUS_BIT 0#define TCON_CONNECT_STATUS_BIT 1#define BUTTON0_STATUS_BIT 2//SPE enables SPI hardware; DORD sets data order to LSB first; MSTR selects master mode; CPOL sets clock polarity (high when idle)//CPHA selects data setup on leading (falling) edge of clock, read on trailing (rising) edge; SPR1, SPI2X sets clock frequency prescale factor at 32 (250kHz clock)#define PSX_SPI_CONFIG SPCR = 0x7E;\ SPSR |= _BV(SPI2X); //SPE enables SPI hardware; DORD low sets data order to MSB first; MSTR selects master mode; CPOL low sets clock polarity (low when idle)//CPHA low selects data setup on trailing (falling) edge of clock, read on leading (rising) edge; SPR1, SPI2X sets clock frequency prescale factor at 32 (250kHz clock)#define MCP4251_SPI_CONFIG SPCR = 0x52;\ SPSR |= _BV(SPI2X);// macro for mapping button presses f-rom psx controller to psp#define MAP_DIGITAL_CONTROL(in_byte, in_bit, out_byte, out_bit){\ (~in_byte & _BV(in_bit)) ? (out_byte &= ~_BV(out_bit)) : (out_byte |= _BV(out_bit));\ }/* init() sets all registers and enables necessary interrupts */void init(void){ /*Set pin I/O registers*/ DDRB |= _BV(SS) | _BV(COMMAND) | _BV(CLOCK) | _BV(PSP_START) | _BV(PSP_SELECT) | _BV(PSP_HOME) | _BV(PSP_L1); DDRC |= _BV(STATUS_LED_GREEN) | _BV(ATTENTION) | _BV(CS) | _BV(PSP_R1) | _BV(PSP_CROSS) | _BV(PSP_SQUARE); DDRD |= _BV(PSP_TRIANGLE) | _BV(PSP_CIRCLE) | _BV(PSP_UP) | _BV(PSP_DOWN) | _BV(PSP_LEFT) | _BV(PSP_RIGHT); PORTD |= _BV(PSP_TRIANGLE) | _BV(PSP_CIRCLE) | _BV(PSP_UP) | _BV(PSP_DOWN) | _BV(PSP_LEFT) | _BV(PSP_RIGHT) | _BV(BUTTON0); PORTC |= _BV(STATUS_LED_GREEN) | _BV(ATTENTION) | _BV(CS) | _BV(PSP_R1) | _BV(PSP_CROSS) | _BV(PSP_SQUARE); PORTB |= _BV(SS) | _BV(COMMAND) | _BV(CLOCK) | _BV(PSP_START) | _BV(PSP_SELECT) | _BV(PSP_HOME) | _BV(PSP_L1);}/* Core AVR-DSC send/receive communication function; takes command_byte, returns single data byte f-rom controller. */uint8_t psx_comm(uint8_t command_byte){ SPDR = command_byte; // Write command byte to SPI data register to begin transmission (pg. 175) while(!(SPSR & _BV(SPIF))); // Conditional loop to prevent write collision _delay_us(30); // Delay between byte transmissions return(SPDR); // Return byte f-rom controller in shift register receive buffer (pg. 175)}/* AVR-MCP4251 write command plus data to MCP4251 register */void write_mcp4251_reg(uint8_t register_address, uint8_t data){ CS_PORT &= ~_BV(CS); // lower MCP4251 chip select line _delay_us(2); // delay required by datasheet SPDR = register_address; // write first 8-bit command+address prefix while(!(SPSR & _BV(SPIF))); // wait for transmission complete flag to set SPDR = data; // write data for x-axis potentiometer while(!(SPSR & _BV(SPIF))); // wait for transmission complete flag to set _delay_us(2); // delay again for no good reason CS_PORT |= _BV(CS); // return chip select line high}/* Functions for remapping buttons between controllers */void OR_map(uint8_t source_byte, uint8_t destination_byte, uint8_t source_bit, uint8_t destination_bit){ if(~source_byte & _BV(source_bit)) destination_byte &= ~_BV(destination_bit); }/* Function to simplify configuration of psx controller; requires 4 command bytes, loop_bytes sets number of bytes following the default 6 */uint8_t config_comm(uint8_t byte2, uint8_t byte4, uint8_t byte5, uint8_t byte6, uint8_t loop_bytes){ uint8_t config_id = 0x00; ATTENTION_PORT &= ~_BV(ATTENTION); _delay_us(16); psx_comm(0x01); config_id = psx_comm(byte2); psx_comm(0x00); psx_comm(byte4); psx_comm(byte5); psx_comm(byte6); for(uint8_t x = 0; x < loop_bytes; x++) psx_comm(0x00); _delay_us(16); ATTENTION_PORT |= _BV(ATTENTION); return(config_id);}int main(void){ uint8_t psx_data[6]; // array stores data f-rom psx controller uint8_t SR0 = 0x00; // status register 0 uint8_t psx_config_counter = 0x00; // counter to increment psx controller config sequence uint8_t psx_config_id = 0x00; // mode ID given by psx controller, 0x41 = digital, 0x73 = analog uint8_t left_analog_x = 0x80; uint8_t left_analog_y = 0x80; uint8_t right_analog_x = 0x80; uint8_t right_analog_y = 0x80; init(); // set important AVR configuration registers _delay_ms(200); MCP4251_SPI_CONFIG; // configure AVR SPI registers for MCP4251 communication write_mcp4251_reg(TCON_ADDRESS, TCON_CONNECT_ALL); // initialize MCP4251 by setting TCON to connect all potentiometer terminals internally SR0 |= _BV(TCON_CONNECT_STATUS_BIT); _delay_us(16); /* Infinite while loop*/ while(1){ psx_data[0] = 0xFF; // reset data variables to inactive values psx_data[1] = 0xFF; left_analog_x = 0x80; left_analog_y = 0x80; right_analog_x = 0x80; right_analog_y = 0x80; PSX_SPI_CONFIG; // configure AVR SPI registers for psx communication _delay_us(4); if(SR0 & _BV(DSC_CONNECT_STATUS_BIT)){ // test if a controller is connected and configured ATTENTION_PORT &= ~_BV(ATTENTION); // begin communication with psx controller by lowering select line _delay_us(16); // required delay between select line low and data transmission psx_comm(0x01); // byte 0; standard controller address header psx_config_id = psx_comm(0x42); // byte 1; standard polling command if(psx_comm(0x00) == 0x5A){ // byte 2; standard header, data reply should always be 0x5A, tests connection status for(uint8_t x = 0; x < ((psx_config_id & 0x0F) << 1); x++) // loop loads data array with mode specific byte count psx_data[x] = psx_comm(0x00); _delay_us(16); // delay following data transmission before ATTENTION returns high ATTENTION_PORT |= _BV(ATTENTION); // end communication with DSC LED_PORT &= ~_BV(STATUS_LED_GREEN); // green status LED on (controller connected) if(psx_config_id == 0x73){ // if psx controller is in analog mode... right_analog_x = psx_data[2]; // copy x/y axis values f-rom joysticks into variables right_analog_y = psx_data[3]; left_analog_x = psx_data[4]; left_analog_y = psx_data[5]; } } else{ SR0 &= ~_BV(DSC_CONNECT_STATUS_BIT); // if 0x5A not received in command byte 2, controller != connected; set status register LED_PORT |= _BV(STATUS_LED_GREEN); // green status LED off (controller not connected) } } if(!(SR0 & _BV(DSC_CONNECT_STATUS_BIT))){ // this switch handles the configuration commands to send over six main loops when the DSC is first plugged in switch(psx_config_counter){ case 0: psx_config_id = config_comm(0x42, 0x00, 0x00, 0x00, 0x00); psx_config_counter = 1; break; // standard initial polling request, 5 bytes total case 1: psx_config_id = config_comm(0x43, 0x01, 0x00, 0x00, 0x00); psx_config_counter = 2; break; // enter config mode, 5 bytes total case 2: psx_config_id = config_comm(0x44, 0x01, 0x03, 0x00, 0x03); psx_config_counter = 3; break; // enable analog mode and lock controller, 9 bytes total case 3: psx_config_id = config_comm(0x4D, 0x00, 0x01, 0xFF, 0x03); psx_config_counter = 4; break; // vibration motor control byte mapping case 4: psx_config_id = config_comm(0x43, 0x00, 0x5A, 0x5A, 0x03); psx_config_counter = 5; break; // exit config mode case 5: psx_config_id = config_comm(0x42, 0x00, 0x00, 0x00, 0x03); SR0 |= _BV(DSC_CONNECT_STATUS_BIT); psx_config_counter = 0; break; } // test will reset configuration switch if a communication error occurs or controller is disconnected before full configuration is complete if((psx_config_id == 0x00) || (psx_config_id == 0xFF)){ psx_config_counter = 0; SR0 &= ~_BV(DSC_CONNECT_STATUS_BIT);} } MCP4251_SPI_CONFIG; // configure AVR SPI registers for MCP4251 communication _delay_us(4); write_mcp4251_reg(X_POT_ADDRESS, left_analog_x); // write value f-rom psx controller left analog x-axis to wiper 0 register in MCP4251 _delay_us(2); write_mcp4251_reg(Y_POT_ADDRESS, left_analog_y); // write value f-rom psx controller left analog y-axis to wiper 1 register in MCP4251 MAP_DIGITAL_CONTROL(psx_data[0], PS_UP, PORTD, PSP_UP); // map digital button presses f-rom psx controller to psp MAP_DIGITAL_CONTROL(psx_data[0], PS_DOWN, PORTD, PSP_DOWN); // all buttons are active low MAP_DIGITAL_CONTROL(psx_data[0], PS_LEFT, PORTD, PSP_LEFT); MAP_DIGITAL_CONTROL(psx_data[0], PS_RIGHT, PORTD, PSP_RIGHT); MAP_DIGITAL_CONTROL(psx_data[0], PS_SELECT, PORTB, PSP_SELECT); MAP_DIGITAL_CONTROL(psx_data[0], PS_START, PORTB, PSP_START); MAP_DIGITAL_CONTROL(psx_data[0], PS_R3, PORTB, PSP_HOME); MAP_DIGITAL_CONTROL(psx_data[1], PS_CROSS, PORTC, PSP_CROSS); MAP_DIGITAL_CONTROL(psx_data[1], PS_SQUARE, PORTC, PSP_SQUARE); MAP_DIGITAL_CONTROL(psx_data[1], PS_TRIANGLE, PORTD, PSP_TRIANGLE); MAP_DIGITAL_CONTROL(psx_data[1], PS_CIRCLE, PORTD, PSP_CIRCLE); MAP_DIGITAL_CONTROL(psx_data[1], PS_R1, PORTC, PSP_R1); MAP_DIGITAL_CONTROL(psx_data[1], PS_L1, PORTB, PSP_L1); if(~psx_data[1] & _BV(PS_L2)){ (PORTD &= ~_BV(PSP_LEFT));} // map secondary shoulder buttons to left/right directional buttons if(~psx_data[1] & _BV(PS_R2)){ (PORTD &= ~_BV(PSP_RIGHT));} if(right_analog_x > 178){ (PORTD &= ~_BV(PSP_CIRCLE));} // define right analog dead zone and map movements to digital buttons else if(right_analog_x < 78){ (PORTC &= ~_BV(PSP_SQUARE));} if(right_analog_y > 178){ (PORTC &= ~_BV(PSP_CROSS));} else if(right_analog_y < 78){ (PORTD &= ~_BV(PSP_TRIANGLE));} // the following checks for a single press of button0 and toggles the connection of the potentiometer terminals within the MCP4251 if(~BUTTON0_PIN & _BV(BUTTON0)){ if(!(SR0 & _BV(BUTTON0_STATUS_BIT))){ // BUTTON0_STATUS_BIT indicates whether or not the button is already being pressed if(SR0 & _BV(TCON_CONNECT_STATUS_BIT)){ // if poterntiometer terminals are already connected... write_mcp4251_reg(TCON_ADDRESS, TCON_DISCONNECT_ALL); SR0 &= ~_BV(TCON_CONNECT_STATUS_BIT); // ...disconnect potentiometer terminals and set status bit } else if(!(SR0 & _BV(TCON_CONNECT_STATUS_BIT))){ // if poterntiometer terminals are already disconnected... write_mcp4251_reg(TCON_ADDRESS, TCON_CONNECT_ALL); SR0 |= _BV(TCON_CONNECT_STATUS_BIT); // ...connect potentiometer terminals and set status bit } SR0 |= _BV(BUTTON0_STATUS_BIT); // set BUTTON0_STATUS_BIT flag so we know the button press has been recognized } } if(BUTTON0_PIN & _BV(BUTTON0)) // if the button is high (default state)... SR0 &= ~_BV(BUTTON0_STATUS_BIT); // ...reset BUTTON0_STATUS_BIT to wait for next press _delay_ms(15); //delay for roughly 60 samples per second } // while(1)} // main()
Robin I r sorrieez.
I can't see the pictures. can you try to put them on different site then photobucket. I tried two different browers btw. Thanks
photos come out all fine for me and i tried on opera and firefox.
Blizzrad have u mad a post on the pinouts for the PS2 controller...
Very good job at writing down all the pins and adding your hints to it. Also very nice solder job you did there, I specially liked your D-Pad, I thought of doing it that way too, but I don't use kynar.
What do you use? Is there something you think is better?
How does this thing draw power? or does it need any?
No, not yet. I am working on the pictures now along with some text descriptions. I should have it all posted sometime this weekend. I will just add it on to this thread instead of starting a new one. I don't want to clutter up the forums, and the Dualshock pinouts are only loosely PSP related.