summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHombreLaser <buran@silosneeded.com>2025-04-04 22:48:40 -0600
committerHombreLaser <buran@silosneeded.com>2025-04-04 22:48:40 -0600
commitb187ed942ffc6cf19189ce7833fc7da5ed4d39ff (patch)
tree91ba88b0ceb1fc8328040571f18817c3f1150007
parent7a91ce0d8f4d7b7f003a27cf0e10e9151079f840 (diff)
Add picoshock devlog #1
-rw-r--r--_posts/en/2025-04-04-picoshock_devlog_1.md193
-rw-r--r--assets/images/2025/minicom.jpgbin0 -> 31352 bytes
2 files changed, 193 insertions, 0 deletions
diff --git a/_posts/en/2025-04-04-picoshock_devlog_1.md b/_posts/en/2025-04-04-picoshock_devlog_1.md
new file mode 100644
index 0000000..e9e336a
--- /dev/null
+++ b/_posts/en/2025-04-04-picoshock_devlog_1.md
@@ -0,0 +1,193 @@
+---
+layout: post
+title: "PicoShock Devlog #1"
+lang: en
+date: 2025-04-04
+tags: ["Programming", "PicoShock"]
+---
+
+Let's just say that I like [fightsticks](https://silosneeded.com/en/2023/12/hori_fighting_stick_restoration). I also like
+the PlayStation 2, if the amount of articles tangentially related to it aren't revealing enough. Besides fightsticks and the
+Playstation 2, I also like Playstation 2 fighting games, specially SoulCalibur 2.
+
+So, creating a custom fightstick, powered by the extremely powerful RP2040 microcontroller in the spirit of other projects like
+[GP-2040CE](https://github.com/OpenStickCommunity/GP2040-CE?ref=silosneeded.com), the [PhobGCC](https://github.com/PhobGCC?ref=silosneeded.com) and its distant
+cousin, the [pico rectangle](https://github.com/JulienBernard3383279/pico-rectangle?ref=silosneeded.com) seemed so enticing to me.
+
+This idea came about more or less 2 years ago, in the same time frame I began building my own fight sticks. I searched for
+similar projects or, at least, a library that could allow me to interface with the Playstation 2 as a controller. Basically,
+I wanted to know if someone more intelligent than me had already taken the mantle of building the ultimate Playstation 2 controller.
+
+Let's just say, I couldn't find such projects. But there are some similar ones, like
+[pico memcard](https://github.com/dangiu/PicoMemcard?ref=silosneeded.com),
+which is a PSX memory card powered by an RP2040 microcontroller (both memory cards and controllers in the first Playstation consoles
+spoke the same protocol, and the Playstation 2 protocol is, let's say, a superset of the Playstation 1's) and the
+[ps1 mouse](https://github.com/Franticware/usb-to-ps1-mouse-pro?ref=silosneeded.com), a raspberry pi pico powered USB to PSX adapter that allows one to use a USB mouse as a Playstation mouse, which, yes,
+[it did exist](https://en.wikipedia.org/wiki/PlayStation_Mouse?ref=silosneeded.com).
+
+My main objective, and the project I'll be making (and hope to finish) is a dualshock protocol implementation library: reasonably
+high level and in C, allowing for projects both in C and C++. MicroPython compatibility could be in the roadmap, too.
+
+It will be under the MIT license because I ultimately want to make the GP2040-CE firmware Playstation 2 (and maybe 1) compatible,
+and GP2040-CE is licensed under these terms.
+
+This license also, sadly, doesn't let me derive my work off of pico memcard, as it is licensed under the GPL, so I've been studying
+instead the ps1 mouse's PIO (more on that later) source code. But... it's esoteric.
+
+Let this series of posts bare witness to my success (or failure) on making this project. If I don't achieve this, well, let them
+be a mark of shame forever sitting on my blog. But hell, I really would like to have a universal fightstick which I could use
+on my favorite console, with zero latency. Because the RP2040 is just that powerful (refer to the GP2040-CE's latency tests)
+
+# What I've done
+
+Yesterday I formally began my journey. I've been researching the Pico's Programmable Input/Output (PIO for short) feature
+for a while now, and made an (unfinished) mock project to familiarize myself with the debugging and build process of pico projects.
+
+I started with a simple program, that reads the command pin (the Playstation -> controller communication bus) and outputs
+to the screen what it has read:
+
+```c
+#include <stdint.h>
+#include <stdio.h>
+#include "pico/stdlib.h"
+
+#include <stdio.h>
+#include "pico/stdlib.h"
+#include "hardware/pio.h"
+#include "dualshock.pio.h"
+
+#define COMMAND_PIN 27
+
+int main() {
+ stdio_init_all();
+ PIO pio = pio0;
+ uint offset = pio_add_program(pio, &dualshock_program);
+ uint sm = pio_claim_unused_sm(pio, true);
+
+ dualshock_program_init(pio, sm, offset, COMMAND_PIN);
+
+ while (true) {
+ uint32_t data = pio_sm_get(pio, sm);
+ printf("%x\n", data);
+ sleep_ms(1000);
+ }
+}
+```
+
+<br>
+As you can see, I reference some mystical functions like `pio_add_program` and `pio_claim_unused_sm`. These are SDK functions that
+work in conjunction with the PIO feature I talked about.
+
+## PIO Primer
+
+In the world of microcontrollers there are usually two ways of talking to a device: bitbang whatever data it transmits to us or
+use specific hardware usually programmed by a third party vendor that allows us to communicate in a specific protocol,
+like I2C or SPI.
+
+The first one has been the most used in the world of hobbyist electronics using arduino. Bitbanging is, basically, using the CPU to
+try and time reads and writes to the device. If the device we want to communicate with sends us bytes on a, let's say, 1 MHz clock
+cycle, then we do our best to make our CPU receive that byte in the time the device expects us to, same for writes.
+
+This is inefficient and hard, but, in the world of the PS2, has been done: Sukko Pera's
+[PSX Newlib](https://github.com/SukkoPera/PsxNewLib?ref=silosneeded.com) and Bill Porter's
+[arduino library](https://github.com/madsci1016/Arduino-PS2X?ref=silosneeded.com) come to mind.
+
+If we read the source code of this projects, we can see the use of `sleep` directives and the like, to try and time every read
+and write.
+
+PIO allows us to not do that. Instead, it provides us a hardware specialized for input and output, consisting of state machines,
+two FIFOs for reading and writing data and several shift registers to, well, shift data around. We program this
+hardware under a pretty basic, but powerful, assembly language.
+
+For what I've learned about PIO (remember, I'm not an expert) it handles timing by running on a fraction of the frequency of
+the RP2040 main CPU clock, which, if I remember correctly, runs at 125 MHz. This fraction can be 1, by the way, meaning it
+can run at the full speed of the CPU. But in our case, and, quoting the [source](https://store.curiousinventor.com/guides/PS2?ref=silosneeded.com)
+I'll probably base all of my work on, the dualshock 2 runs at a frequency of 500 KHz.
+
+# My (pretty basic) PIO program
+
+So now that we know PIO, what have I written in it? Well, let's remember that I want to read the data the playstation has sent
+me and output it on the screen. In PIO terms, I want to read a byte off of my input pin, shift this byte to the ISR, and then push
+whatever is in the ISR to my TX (read) FIFO, for it to be consumed by the pico's main CPU.
+
+```asm
+.program dualshock
+
+.wrap_target
+ in pins, 8 ; Read 8 bits from the input pin, and
+ ; shift them to the Input Shift Register
+ push ; Push the data present in the Input Shift Register
+ ; to the RX FIFO.
+.wrap
+
+% c-sdk {
+ void dualshock_program_init(PIO pio, uint sm, uint offset, uint command_pin) {
+ pio_sm_config cfg = dualshock_program_get_default_config(offset);
+ sm_config_set_in_pins(&cfg, command_pin);
+ sm_config_set_in_shift(&cfg, false, false, 8);
+ sm_config_set_clkdiv_int_frac(&cfg, 250, 0x00);
+ }
+%}
+```
+
+Here it is, my masterpiece. Pretty basic, right? But I think it was a pretty good exercise in getting to know the PIO feature
+and actually using it (later we'll see the Cmake file, to show you how you can build your project taking a PIO program in mind).
+
+Everything below the c-sdk is code that will be injected to the header file the assembler will generate with this file.
+In it, we configure our PIO state machine: the pin we'll use as the command pin (I.E., the one we'll be reading),
+the direction we'll shift bits on (in this case, the playstation 2 speaks a Least significant bit protocol, in layman terms,
+we'll read what it has to say from left to right) and its clock frequency: 125/250 = 0.5 MHz, or, 500 KHz. I think. I broke
+my head trying to come up with this, let's just say the documentation on clock dividers was hard to parse, for me, at least.
+
+## Building this abomination
+
+The cmake file is as follows:
+
+```cmake
+cmake_minimum_required(VERSION 4.0)
+
+include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
+
+project(pico_shock C CXX ASM)
+
+pico_sdk_init()
+
+add_executable(main main.c)
+
+pico_generate_pio_header(main ${CMAKE_SOURCE_DIR}/dualshock.pio)
+
+target_link_libraries(main pico_stdlib hardware_pio)
+
+pico_enable_stdio_uart(main 1)
+
+pico_add_extra_outputs(main)
+```
+
+<br>
+We only care about the `pico_generate_pio_header(main ${CMAKE_SOURCE_DIR}/dualshock.pio)` line. This tells make to
+use the pio assembler to, well, assemble our pio program and generate the `dualshock.pio.h` file we include in our C program.
+
+# Results
+
+Well, the PS2 is speaking to us in chinese:
+
+![Minicom screenshot](/assets/images/2025/minicom.jpg)
+
+None of this means anything per the Curious Inventor guide, and, it's expected. I was hoping to at least get a 0x00 byte, which
+it seems to be the first byte the console sends in every packet. But no.
+
+It's expected because the PS2 speaks in a (albeit heavily modified) duplex SPI protocol: we receive and read data at the same
+time, regulated by a clock pulse, and also using a chip select (attention, in DualShock terms) and acknowledge signals.
+
+# What comes after
+
+For now, I want to receive actual, useful data. I hope I can achieve this by only implementing the clock signal, but of course,
+I'll have to implement the other ones (data, attention and acknowledge) sooner or later.
+
+The pico-examples repository provides a really relevant example: an
+[SPI implementation in PIO](https://github.com/raspberrypi/pico-examples/tree/master/pio/spi?ref=silosneeded.com).
+I've been basing most of my current work on it, and the C code seems specially interesting: particularly the way it reads
+(and writes) data passed by the PIO state machine.
+
+It seems that using `pio_sm_get` and friends is the sucker's way of doing it. Well, I only hope the debugger plays nice, because
+I'm gonna need it to poke at what I'll be actually receiving from the PIO state machine.
diff --git a/assets/images/2025/minicom.jpg b/assets/images/2025/minicom.jpg
new file mode 100644
index 0000000..207c9d5
--- /dev/null
+++ b/assets/images/2025/minicom.jpg
Binary files differ