Skip to content

Commit

Permalink
Merge branch 'pmc+/development' into pmc+/release
Browse files Browse the repository at this point in the history
  • Loading branch information
dangiu committed Jun 3, 2023
2 parents b31d37a + e11ce0f commit 9ea6468
Show file tree
Hide file tree
Showing 15 changed files with 636 additions and 523 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[submodule "no-OS-FatFS-SD-SPI-RPi-Pico"]
path = no-OS-FatFS-SD-SPI-RPi-Pico
url = git@github.com:carlk3/no-OS-FatFS-SD-SPI-RPi-Pico.git
url = https://github.com/carlk3/no-OS-FatFS-SD-SPI-RPi-Pico.git
17 changes: 16 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico_w)

include(pico_sdk_import.cmake)

project(picomemcard_project C CXX ASM)
Expand All @@ -15,6 +17,7 @@ pico_generate_pio_header(PicoMemcard ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)

add_definitions(-DNO_PICO_LED) # prevent SD SPI library from using LED
add_subdirectory(no-OS-FatFS-SD-SPI-RPi-Pico/FatFs_SPI PicoMemcard)
add_subdirectory(poc_examples)

# Example source
target_sources(PicoMemcard PUBLIC
Expand All @@ -35,6 +38,18 @@ target_include_directories(PicoMemcard PUBLIC

pico_enable_stdio_uart(PicoMemcard 1) # enable only UART stdio

target_link_libraries(PicoMemcard pico_stdlib pico_multicore pico_time hardware_pio tinyusb_device tinyusb_board FatFs_SPI)
target_link_libraries(
PicoMemcard
pico_stdlib
pico_multicore
pico_time
hardware_pio
tinyusb_device
tinyusb_board
FatFs_SPI

pico_cyw43_arch_none # If you do not need the TCP/IP stack but wish to use the on-board LED on a Pico W
hardware_adc
)

pico_add_extra_outputs(PicoMemcard)
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Memory card images must be exactly 128KB (131072 bytes) in size. PicoMemcard and
For other file formats, try using [MemcardRex] for converting to the desired output.

* **PicoMemcard** only supports a single image which must be named exactly `MEMCARD.MCR`.
* **PicoMemcard+** supports hundreds of images. Each image must be named `N.MCR` where `N` is an integer number (e.g. `0.MCR`, `1.MCR`...). On boot the first image loaded will always be the one with the lowest number.
* **PicoMemcard+** supports hundreds of images. Each image must be named `N.MCR` where `N` is an integer number (e.g. `0.MCR`, `1.MCR`...). On boot your previously loaded image will be reloaded, unless it's a fresh card then `0.MCR` will be loaded.

Inside `docs/images` you can find two memory card images. One has a couple of saves on it so you can test if everything works correctly, the other is completely empty.

Expand All @@ -122,11 +122,11 @@ Additionally this method does not work on PS2 Memory Cards and Controllers are w
## Syncing Changes
Generally speaking, new data written to PicoMemcard (e.g. when you save) is permanently stored only after a short period of time (due to hardware limitation). The on board LED indicates whether all changes have been stored or not, in particular:
* On Rapsbery Pi Pico the LED will be on when all changes have been saved, off otherwise.
* On RP2040-Zero the LED will be green when all changes have been saved, yellow otherwise
* On RP2040-Zero the LED will be solid green when all changes have been saved, red otherwise.

Unlike **PicoMemcard+** that tries to write new changes as soon as possible, **PicoMemcard** will generally do it only after a period of inactivity (around 5 seconds). If you want to force **PicoMemcard** to immediately sync you can press `START + SELECT + TRIANGLE`.

**Attention**: after you save your game, make sure to wait for the LED to be green before turning off the console otherwise you might lose your more recent progress!
**Attention**: after you save your game, make sure to wait for the LED to be solid green before turning off the console otherwise you might lose your more recent progress!

## 3D-Printed Case
I've finally designed a 3D-printable case for the different PicoMemcard PCBs. It helps inserting correctly the PCB and ensuring that the the connection to the PSX is optimal. The same result, albeit more janky, can be achieved using a folded sheet of paper as a spacer.
Expand All @@ -153,15 +153,15 @@ In particular, the RP2040-Zero has RGB LED that provides a more clear output. Th
| Status | Pico | RP2040-Zero
| --- | --- | --- |
| Failed to read memory card image | Slow blinking led | Red blinking led
| Data not fully synced (do not turn off PSX)| Led off | Yellow led |
| Data synced | Led on | Green led |
| Data not fully synced (do not turn off PSX)| Led off | Red led (or flashing red and green) |
| Data synced | Led on | Green solid led |

### PicoMemcard+ Status
| Status | Pico | RP2040-Zero
| --- | --- | --- |
| Failed to read SD card | Blinking led | Red blinking led
| Data not fully synced (do not turn off PSX)| Led off | Yellow led |
| Data synced | Led on | Green led |
| Data not fully synced (do not turn off PSX)| Led off | Red led (or flashing red and green) |
| Data synced | Led on | Green solid led |
| Memory Card image changed | Three fast blinks | Single blue blink |
| Memory Card image not changed (end of list) | Nine fast blinks | Single orange blink |
| New Memory Card image created | Multiple very fast blinks | Single light blue blink |
Expand Down
17 changes: 10 additions & 7 deletions inc/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@
#define PICO
//#define RP2040ZERO

/* Invert red and green. Uncomment this if the LED colours for your RP2040 Zero are incorrect. */
#define INVERT_RED_GREEN

/* PSX Interface Pinout */
#ifdef PICO
#define PIN_DAT 5
#define PIN_CMD PIN_DAT + 1 // must be immediately after PIN_DAT
#define PIN_SEL PIN_CMD + 1 // must be immediately after PIN_CMD
#define PIN_CLK PIN_SEL + 1 // must be immediately after PIN_SEL
#define PIN_ACK 9
#ifdef PICO // TODO remove/find way to include this into pio code
//#define PIN_DAT 5
//#define PIN_CMD PIN_DAT + 1 // must be immediately after PIN_DAT
//#define PIN_SEL PIN_CMD + 1 // must be immediately after PIN_CMD
//#define PIN_CLK PIN_SEL + 1 // must be immediately after PIN_SEL
//#define PIN_ACK 9
#endif

#ifdef RP2040ZERO
#ifdef RP2040ZERO // TODO remove/find way to include this into pio code
#define PIN_DAT 9
#define PIN_CMD PIN_DAT + 1 // must be immediately after PIN_DAT
#define PIN_SEL PIN_CMD + 1 // must be immediately after PIN_CMD
Expand Down
4 changes: 4 additions & 0 deletions inc/led.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ void led_output_mc_change();
void led_output_end_mc_list();
void led_output_new_mc();

int32_t is_pico_w();
void init_led(uint32_t pin);
void set_led(uint32_t pin, uint32_t level);

#endif
3 changes: 2 additions & 1 deletion inc/memcard_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
bool memcard_manager_exist(uint8_t* filename);
uint32_t memcard_manager_count();
uint32_t memcard_manager_get(uint32_t index, uint8_t* out_filename);
#define memcard_manager_get_first(out_filename) memcard_manager_get(0, (out_filename))
#define memcard_manager_get_initial(out_filename) memcard_manager_get(memcard_manager_get_prev_loaded_memcard_index(), (out_filename))
uint32_t memcard_manager_get_prev_loaded_memcard_index();
uint32_t memcard_manager_get_next(uint8_t* filename, uint8_t* out_nextfile);
uint32_t memcard_manager_get_prev(uint8_t* filename, uint8_t* out_prevfile);
uint32_t memcard_manager_create(uint8_t* out_filename);
Expand Down
2 changes: 2 additions & 0 deletions poc_examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(controller_simulator)
add_subdirectory(memcard_sniffer)
22 changes: 22 additions & 0 deletions poc_examples/controller_simulator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.13)

include(${CMAKE_SOURCE_DIR}/pico_sdk_import.cmake)

project(controller_simulator C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()

add_executable(controller_simulator)

pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/psxSPI.pio)

target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/controller_simulator.c
)

pico_enable_stdio_uart(${PROJECT_NAME} 1)

target_link_libraries(${PROJECT_NAME} pico_stdlib hardware_pio)

pico_add_extra_outputs(${PROJECT_NAME})
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,11 @@ PIO pio = pio0;

uint smSelMonitor;
uint smCmdReader;
uint smAckSender;
uint smDatWriter;

uint offsetSelMonitor;
uint offsetCmdReader;
uint offsetDatWriter;
uint offsetAckSender;

static uint count = 0;

Expand All @@ -62,48 +60,48 @@ void cancel_ack() {

void process_joy_req(uint8_t next_byte) {
printf("\n%.2x ", next_byte);
write_dat_LSB_blocking(pio, smDatWriter, ID_LO); // write lower ID byte
write_byte_blocking(pio, smDatWriter, ID_LO); // write lower ID byte

next_byte = read_cmd_byte_blocking(pio, smCmdReader);
next_byte = read_byte_blocking(pio, smCmdReader);
if(next_byte != 0x42) {
printf("\nWaiting for %.2x but received %.2x\n", 0x42, next_byte);
return;
} else {
write_dat_LSB_blocking(pio, smDatWriter, ID_HI); // write upper ID byte
write_byte_blocking(pio, smDatWriter, ID_HI); // write upper ID byte
printf("%.2x ", next_byte);
}

next_byte = read_cmd_byte_blocking(pio, smCmdReader);
next_byte = read_byte_blocking(pio, smCmdReader);
if(next_byte != 0x00) {
printf("\nWaiting for %.2x but received %.2x\n", 0x00, next_byte);
return;
} else {
switch (count) {
case 0:
write_dat_LSB_blocking(pio, smDatWriter, R_PRESSED); // fake right d-pad being pressed
write_byte_blocking(pio, smDatWriter, R_PRESSED); // fake right d-pad being pressed
break;
case 2:
write_dat_LSB_blocking(pio, smDatWriter, L_PRESSED); // fake left d-pad being pressed
write_byte_blocking(pio, smDatWriter, L_PRESSED); // fake left d-pad being pressed
break;
case 1:
case 3:
default:
write_dat_LSB_blocking(pio, smDatWriter, NO_PRESSED); // release all buttons
write_byte_blocking(pio, smDatWriter, NO_PRESSED); // release all buttons
break;
}
printf("%.2x ", next_byte);
}

next_byte = read_cmd_byte_blocking(pio, smCmdReader);
next_byte = read_byte_blocking(pio, smCmdReader);
if(next_byte != 0x00) {
printf("\nWaiting for %.2x but received %.2x\n", 0x00, next_byte);
return;
} else {
write_dat_LSB_blocking(pio, smDatWriter, NO_PRESSED); // other buttons are all inactive
write_byte_blocking(pio, smDatWriter, NO_PRESSED); // other buttons are all inactive
printf("%.2x ", next_byte);
}

next_byte = read_cmd_byte_blocking(pio, smCmdReader);
next_byte = read_byte_blocking(pio, smCmdReader);
cancel_ack(); // last byte being received, no need to send more data
if(next_byte != 0x00) {
printf("\nWaiting for %.2x but received %.2x\n", 0x00, next_byte);
Expand All @@ -124,27 +122,23 @@ int main() {

offsetSelMonitor = pio_add_program(pio, &sel_monitor_program);
offsetCmdReader = pio_add_program(pio, &cmd_reader_program);
offsetAckSender = pio_add_program(pio, &ack_sender_program);
offsetDatWriter = pio_add_program(pio, &dat_writer_program);

smSelMonitor = pio_claim_unused_sm(pio, true);
smCmdReader = pio_claim_unused_sm(pio, true);
smAckSender = pio_claim_unused_sm(pio, true);
smDatWriter = pio_claim_unused_sm(pio, true);

dat_writer_program_init(pio, smDatWriter, offsetDatWriter, PIN_DAT, PIN_CLK);
ack_sender_program_init(pio, smAckSender, offsetAckSender, PIN_ACK);
cmd_reader_program_init(pio, smCmdReader, offsetCmdReader, PIN_CMD);
cmd_reader_program_init(pio, smCmdReader, offsetCmdReader, PIN_CMD, PIN_ACK);
sel_monitor_program_init(pio, smSelMonitor, offsetSelMonitor, PIN_SEL);


/* Enable all SM simultaneously */
uint32_t smMask = (1 << smSelMonitor) | (1 << smCmdReader) | (1 << smAckSender) | (1 << smDatWriter);
uint32_t smMask = (1 << smSelMonitor) | (1 << smCmdReader) | (1 << smDatWriter);
pio_enable_sm_mask_in_sync(pio, smMask);

uint32_t i = 0;
while(true) {
uint8_t item = read_cmd_byte_blocking(pio, smCmdReader);
uint8_t item = read_byte_blocking(pio, smCmdReader);

if(item == 0x01)
process_joy_req(item);
Expand Down
22 changes: 22 additions & 0 deletions poc_examples/memcard_sniffer/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.13)

include(${CMAKE_SOURCE_DIR}/pico_sdk_import.cmake)

project(memcard_sniffer C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()

add_executable(memcard_sniffer)

pico_generate_pio_header(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/psxSPI.pio)

target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/memcard_sniffer.c
)

pico_enable_stdio_uart(${PROJECT_NAME} 1)

target_link_libraries(${PROJECT_NAME} pico_stdlib hardware_pio)

pico_add_extra_outputs(${PROJECT_NAME})
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ int main() {
smDatReader = pio_claim_unused_sm(pio, true);

dat_reader_program_init(pio, smDatReader, offsetDatReader, PIN_DAT);
cmd_reader_program_init(pio, smCmdReader, offsetCmdReader, PIN_CMD);
cmd_reader_program_init(pio, smCmdReader, offsetCmdReader, PIN_CMD, PIN_ACK);
sel_monitor_program_init(pio, smSelMonitor, offsetSelMonitor, PIN_SEL);

/* Enable all SM simultaneously */
Expand All @@ -77,8 +77,8 @@ int main() {

/* Samping phase */
for(int i = 0; i < BUFF_LEN; ++i) {
cmdBuffer[index] = read_byte_blocking(pio, smCmdReader);
datBuffer[index] = read_byte_blocking(pio, smDatReader);
cmdBuffer[i] = read_byte_blocking(pio, smCmdReader);
datBuffer[i] = read_byte_blocking(pio, smDatReader);
}

/* Printing results */
Expand Down
Loading

0 comments on commit 9ea6468

Please sign in to comment.