Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
dangiu committed Aug 2, 2022
2 parents 4efe8b7 + 7932fb9 commit 899767b
Show file tree
Hide file tree
Showing 23 changed files with 766 additions and 444 deletions.
9 changes: 8 additions & 1 deletion .gitignore
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,11 @@ build/
.vscode/.cortex*

# FatFS documentation
ff14b/documents
ff14b/documents

# VSCode
.vscode/settings.json

# Clion
/.idea
/cmake-build-debug
16 changes: 0 additions & 16 deletions .vscode/settings.json

This file was deleted.

4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ add_executable(PicoMemcard)

pico_set_linker_script(PicoMemcard ${CMAKE_SOURCE_DIR}/memmap.ld)
pico_generate_pio_header(PicoMemcard ${CMAKE_CURRENT_LIST_DIR}/psxSPI.pio)
pico_generate_pio_header(PicoMemcard ${CMAKE_CURRENT_LIST_DIR}/ws2812.pio)


# Example source
target_sources(PicoMemcard PUBLIC
${CMAKE_SOURCE_DIR}/src/led.c
${CMAKE_SOURCE_DIR}/src/lfs_disk.c
${CMAKE_SOURCE_DIR}/src/lfs_flash_handler.c
${CMAKE_SOURCE_DIR}/src/main.c
Expand All @@ -38,6 +40,6 @@ target_include_directories(PicoMemcard PUBLIC

pico_enable_stdio_uart(PicoMemcard 1) # enable only UART stdio

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

pico_add_extra_outputs(PicoMemcard)
103 changes: 70 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,76 +7,118 @@ PicoMemcard allows you to build your own supercharged PSX Memory Card that can b
* Allows to copy saves to/from any other memory card (using original PSX file manager)
* Allows to play burned CDs (thanks to [FreePSXBoot])
* Cheaper than an original memory card
* Can store hudreds of memory card images

## Bill of materials
* **Raspberry Pi Pico** (around $5)
* One of:
* Custom [PicoMemcard PCB](#picomemcard-pcb)
* Broken/Counterfeit/Original PSX Memory Card (counterfeit ones can be found on AliExpress for around $2-3)
* PSX/PS2 Controller Cable
* Nothing, if you are a mad man and feel like soldering cables directly to your PSX (would not recommend).
* CAT5 network cable and a bit of creativity.

Basically anything that will allow you to interface with the memory card slot pins will do. If you have a broken contoller you can cut off the cable and use that since controllers and memory cards share the same bus. Of course, plugging your memory card into the controller slot will prevent you from using 2 controllers at the same time.

[This guy] assembled one using a 3d-printed plastic shell and simple CAT5 network cable. I belive this is the cheapest method anybody has come up until now.

In total building a PicoMemcard wil cost you less than buying a used original Memory Card!

## Video
Video of newest release coming soon.

<!-- TODO add new video -->

[![PicoMemcard](https://img.youtube.com/vi/Sie0kzmnJJw/0.jpg)](https://www.youtube.com/watch?v=Sie0kzmnJJw)

## PicoMemcard PCB
<img src="./docs/PCBs.jpg" alt="Custom PCBs" width="800">

This is the custom PCB designed and manufactured specifically for this application. It makes it much easier to build PicoMemcard since you don't need to cut up another memory card and all the soldering pads are easily accessible. Still, you will need a soldering iron to assemble the device.
These are the custom PCBs designed and manufactured specifically for this application. They make it much easier to build PicoMemcard since you don't need to cut up another memory card and all the soldering pads are easily accessible. Still, you will need a soldering iron to assemble the device.

The Raspberry Pi Pico must sit flush on top of the PCB, so use the soldering pads instead of the through-holes (see right side of the picture). Personally, I used a couple of instant-glue drops to fix it in place and then soldered the different pads.
The Raspberry Pi Pico or RP2040-Zero must sit flush on top of the PCB, so use the soldering pads instead of the through-holes (see right side of the picture). You can use some electrical tape to hold the board in place while you solder the first pins.

I've designed the PCB with a bit of future-proofeness in mind. Although not yet implemented, it supports the installation of an SD SPI expansion board to allow the Pico to read/write data to a microSD card and store multiple memory card images (see [Future Development](#future-development) section).
<img src="./docs/PCBs.jpg" alt="Custom PCBs" width="800">

Since I've already received so many requests and I'm starting to lose track of them, I created a Google Form where you can [request one]. It also allows me to understand how many PCBs I should manufacture. If you do request one, make sure to specify a way for me to contact you back in order to get shipping information and stuff.
At the moment there are 3 variations of the PCB, going from left to right: $5)
* PCB compatible with Raspberry Pi Pico supporting SD Card.
* PCB compatible with RP2040-Zero supporting SD Card.
* Mini PCB compatible with RP2040-Zero not supporting SD Card. This has the same size as the original chip inside Memory Cards so it can be simply dropped in place.

Before filling the form, keep in mind that the cheapest way to build PicoMemcard is using a counterfeit memory card from Aliexpress. The custom PCB makes the building process much easier and gets you a more professional-looking product, but the features you get are the same.
If you want to request one you can do it [from here]. I plan to eventually make the PCB design open source as well so people can more easily get access to it.

<img src="./docs/PCB-Demo.jpg" alt="Custom PCB in use" width="800">
<img src="./docs/PCB-demo.jpg" alt="Custom RP2040-Zero PCB in use" width="800">

In the picture above you can see the PCB plugged into a PSOne. I'm already working on a 3d-printed enclosure instead of using paper sheets as a spacer.
In the picture above you can see the full-size RP2040-Zero PCB plugged into a PSOne. I'm finally getting a 3d printer to work on a custom enclosure. In the meantime you can either use some paper sheets as spacers or the top side of a memory card shell to help align the PCB as shown in the picture.

## Wiring
The wiring diagram below shows how to wire the Pico and a counterfeit memory card. For the other cases (wiring directly to the PSX or using a controller cable) the pins on the Pico are the same, the pinout of the PSX/controller can be found on [psx-spx]. The image shows the bottom side of the memory card with the cover removed.
Raspberry Pi Pico and RP2040-Zero require different pin usage for full compatibility.

The wiring diagrams below show how to wire respectively a Pico and an RP2040-Zero to a counterfeit memory card. For the other cases (wiring directly to the PSX or using a controller cable) the pins on Pico and RP2040-Zero are the same, the pinout of the PSX/controller can be found on [psx-spx]. The images show the bottom side of the memory card with the cover removed.

<img src="./docs/wiring_bg.svg" alt="Wiring Diagram" width="800">
<img src="./docs/wiring-pico.svg" alt="Pico Wiring Diagram" width="800">

<img src="./docs/wiring-RP2040-Zero.svg" alt="RP2040-Zero Wiring Diagram" width="800">

The dashed line on the PCB of the memory card is where you should cut a groove deep enough to disconnect the original circuitry from the traces. The yellow squares above the line indicate where you should scrape away the protective film in order to expose the copper traces and solder the wires onto them.

Finally the area at the bottom of the memory card is where you can cut a hole to feed the wires through connecting them to the Pico.
If you are using an RP2040-Zero you can also cut away part of the original Memory Card circuitry to fit it inside the original shell.

The connections in the PicomMemcard+ area are optional and only required if you want to build **PicoMemcard+**.

## PicoMemcard vs PicoMemcard+
Since some features cannot be implmented when using only a Pico/RP2040-Zero but require additional hardware, I've decided to split the project in two versions:
* **PicoMemcard** simplest device to build, only requires a Raspberry Pi Pico / RP2040-Zero
* **PicoMemcard+** more complex but capable device. Supports additional features such as multiple memory card images but requires additional hardware (namely the microSD SPI expansion board).

This approach was choosen because many people want a solution that is as cheap as possible and don't care about additional features. Conversely, others would like to have additional features even if it means spending a bit more money.

The current release of PicoMemcard+ additionally to all the features of PicoMemcard supports:
* Trasparent write-back: the sync operation performed to save new data no longer reults in the memory card appearing as if was briefly disconnected.
* Multiple memory card image switchable through controller input.

## Installation
1. Download the latest [release].
2. While pressing the 'BOOTSEL' button on your Raspberry Pi Pico, plug it into your computer.
1. Download the latest [release] for your board (Raspberry Pi Pico and RP2040-Zero require different binaries).
2. While pressing the 'BOOTSEL' or 'BOOT' button on your board, plug it into your computer.
3. Drag and drop the PicoMemcard release onto your Raspberry Pi Pico.
4. PicoMemcard should appear on your PC as a USB drive.
5. Upload a memory card image to your PicoMemcard.

## Transfering Data
As of the current release, uploading data to PicoMemcard requires some precise steps:
* The image of the memory card to upload must be called exactly `MEMCARD.MCR`. Uploading anything else will not result in any errors but PicoMemcard will not save the uploaded data to flash. After rebooting the device the old data will still be present.
* The image must be 128KB (131072 bytes) in size which is precisely the size of an original Memory Card.
* After the image has been uploaded, the device **must be safely ejected** in order for the data to be actually imported correctly. This is a limitation of the current implementation (see [Design](#design) section).
Memory card images must be exactly 128KB (131072 bytes) in size. PicoMemcard and PicoMemcard+ only support files with `.MCR` extensions. However, `.MCR` and `.MCD` extensions are interchangable and can be converted to one another simply via renaming.
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 `0.MCR`.

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.

**ATTENTION:**
## Switching Image
On **PicoMemcard+** you can switch the active memory card image with the following inputs:
* `START + SELECT + DPAD UP` will switch to the next image (e.g from `1.MCR` to `2.MCR`).
* `START + SELECT + DPAD DOWN` will switch to the previous image (e.g from `1.MCR` to `0.MCR`).

**Attention**: this method only works on PSX if the controller used to provide the input is plugged in the same slot as PicoMemcard (exactly under it). Using a controller from a different slot will have no effect.

Additionally this method does not work on PS2 Memory Cards and Controllers are wired on a different bus.

## 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

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!

## General Warnings
I would recommend to never plug PicoMemcard both into the PC (via USB) and the PSX at the same time! Otherwise the 5V provided by USB would end up on the 3.3V rail of the PSX. I'm not really sure if this could cause actual damage but I would avoid risking it.

If you really need to have the Pico plugged into both the USB and PSX (e.g. for debugging purposes), disconnect the 3.3V line from the VBUS pin. In this way you can power on the Pico using a simple USB phone charger or by plugging it into your PC.

## Future Development
As of now PicoMemcard is still in very early development stages and only tested on a PSOne Pal model (SCPH-102, bios version 4.4). It **should** work on any PSX model. If you want to try it on your PSX any feedback would be much appreciated.
As a disclamer, I don't take any responsability for what will happen to your console when using PicoMemcard/PicoMemcard+.

I've tried to make the project as accessible as possible by using the least amount of hardware but this comes with a few limitations, in particular regarding the flash storage of the Pico. In the future I want to add the possibility to use a microSD card by adding a microSD SPI module, this would improve on the following:
* The very limited storage preventing from having multiple memory card images at the same time and being able to switch them using a button on the Pico.
* The complex codebase using multiple filesystems (see [Design](#design) section).
* The brief downtime periods PicoMemcard may have while writing new data to flash memory, appearing as if the Memory Card was briefly disconnected.
* The need to perform safe ejection as a way to signal the Pico to transfer the data from the virtual FAT disk to the flash filesystem (see [Design](#design) section).
## Future Development
PicoMemcard has been tested on numerous PSX and PS2 models and **should** work on all of them. For now it only supports PSX memory cards emulation (even on PS2 hardware). PS2 memory card emulation is the next logical step.

## Design
For people interested in understanding how PicoMemcard works I provide a more extensive explanation in [this post].
Expand All @@ -98,11 +140,6 @@ For people interested in understanding how PicoMemcard works I provide a more ex
[PulseView]: https://sigrok.org/wiki/PulseView
[release]: https://github.com/dangiu/PicoMemcard/releases/latest
[this post]: https://dangiu.github.io/2022/05/13/picomemcard.html
[request one]: https://forms.gle/f6XHtz6W5fn5qDZV7


<!--
TODO
Add FAQ? Is it necessary?
Add Changelog
-->
[from here]: https://forms.gle/f6XHtz6W5fn5qDZV7
[This guy]: https://github.com/MrSVCD/PicoPSX_3D
[MemcardRex]: https://github.com/ShendoXT/memcardrex
Binary file removed docs/PCB-Demo.jpg
Binary file not shown.
Binary file added docs/PCB-demo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/PCBs.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/wiring-RP2040-Zero.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/wiring-pico.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion docs/wiring_bg.svg

This file was deleted.

32 changes: 32 additions & 0 deletions inc/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef __CONFIG_H__
#define __CONFIG_H__

/* Global configuration options for PicoMemcard */
#define TUD_MOUNT_TIMEOUT 3000 // max time (in ms) before giving up on MSC mode (USB) and starting memcard simulation
#define MSC_WRITE_SYNC_TIMEOUT 1 * 1000 // time (in ms) expired since last MSC write before exporting RAM disk into LFS
#define IDLE_AUTOSYNC_TIMEOUT 5 * 1000 // time (in ms) the memory card must be inactive before automatic sync from RAM to LFS
#define MEMCARD_FILE_NAME "memcard.mcr" // name of memory card file image

/* Board targeted by build */
#define PICO
//#define RP2040ZERO


/* 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
#endif

#ifdef RP2040ZERO
#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
#define PIN_CLK PIN_SEL + 1 // must be immediately after PIN_SEL
#define PIN_ACK 13
#endif

#endif
10 changes: 10 additions & 0 deletions inc/led.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef __LED_H__
#define __LED_H__

#include "pico/stdlib.h"

void led_init();
void led_output_sync_status(bool out_of_sync);
void led_blink_error(int amount);

#endif
2 changes: 0 additions & 2 deletions inc/lfs_disk.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#define LFS_BLOCK_COUNT 70
#define LFS_BLOCK_SIZE 4096

#define MEMCARD_FILE_NAME "memcard.mcr" // name of memcard file inside system

/* LittleFS filesystem configuration */
extern const struct lfs_config LFS_CFG;

Expand Down
41 changes: 26 additions & 15 deletions inc/memory_card.h
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
#include <stdint.h>
#include <stdbool.h>

#define MC_SEC_SIZE 128 // size of single sector in bytes
#define MC_SIZE MC_SEC_SIZE * 1024 // size of memory card in bytes
#define MC_MAX_SEC 0x3ff
#define MC_FLAG_BYTE_DEF 0x08 // bit 3 set = new memory card inserted
#define MC_SEC_SIZE 128 // size of single sector in bytes
#define MC_SEC_COUNT 1024 // number of sector in one memory card
#define MC_SIZE MC_SEC_SIZE * MC_SEC_COUNT // size of memory card in bytes
#define MC_FLAG_BYTE_DEF 0x08 // bit 3 set = new memory card inserted

#define MC_ID1 0x5A
#define MC_ID2 0x5D
Expand All @@ -19,20 +19,31 @@
#define MC_BAD_SEC 0xFF
#define MC_BAD_CHK 0x4E

/* Error codes */
#define MC_OK 0
#define MC_MOUNT_ERR 1
#define MC_FILE_OPEN_ERR 2
#define MC_FILE_READ_ERR 3
#define MC_FILE_WRITE_ERR 4
#define MC_FILE_SIZE_ERR 5
#define MC_NO_INIT 6

typedef struct {
uint8_t flag_byte;
uint8_t data[MC_SIZE];
uint8_t* data;
bool out_of_sync;
uint32_t last_operation_timestamp;
} MemoryCard;

uint32_t memory_card_init(MemoryCard* mc);
bool memory_card_is_sector_valid(MemoryCard* mc, uint32_t sector);
uint8_t* memory_card_get_sector_ptr(MemoryCard* mc, uint32_t sector);
void memory_card_set_sync(MemoryCard* mc, bool out_of_sync);
bool memory_card_get_sync(MemoryCard* mc);
void memory_card_update_timestamp(MemoryCard* mc);
uint32_t memory_card_sync(MemoryCard* mc);
uint32_t memory_card_reset_seen_flag(MemoryCard* mc);
} memory_card_t;

uint32_t memory_card_init(memory_card_t* mc);
uint32_t memory_card_import(memory_card_t* mc, uint8_t* file_name);
bool memory_card_is_sector_valid(memory_card_t* mc, uint32_t sector);
uint8_t* memory_card_get_sector_ptr(memory_card_t* mc, uint32_t sector);
void memory_card_set_sync(memory_card_t* mc, bool out_of_sync);
bool memory_card_get_sync(memory_card_t* mc);
void memory_card_update_timestamp(memory_card_t* mc);
bool memory_card_sync_required(memory_card_t* mc);
uint32_t memory_card_sync(memory_card_t* mc);
void memory_card_reset_seen_flag(memory_card_t* mc);

#endif
Loading

0 comments on commit 899767b

Please sign in to comment.