diff options
-rw-r--r-- | _posts/en/2025-04-12-picoshock_devlog_2.md | 240 | ||||
-rw-r--r-- | assets/images/2025/minicom_2.png | bin | 0 -> 40596 bytes |
2 files changed, 240 insertions, 0 deletions
diff --git a/_posts/en/2025-04-12-picoshock_devlog_2.md b/_posts/en/2025-04-12-picoshock_devlog_2.md new file mode 100644 index 0000000..0a35c91 --- /dev/null +++ b/_posts/en/2025-04-12-picoshock_devlog_2.md @@ -0,0 +1,240 @@ +--- +layout: post +title: "PicoShock Devlog #2" +lang: en +date: 2025-04-12 +tags: ["Programming", "PicoShock"] +--- + +These last couple of weeks I've kept my work on picoshock somewhat consistent, and I've also learnt a lot, really. + +For starters, my first log entry was filled to the brim with mistakes, some pretty dumb ones, too: + +- I didn't even start the PIO program. +- I used `pio_sm_get` instead of `pio_sm_get_blocking`: the difference between the two is that the second + one waits for the RX FIFO to actually have some data to read. The first one, when the FIFO is empty, + has undefined behavior. That explains the readings I was getting: the program didn't even start and as such, + the FIFO wasn't getting filled. +- The folks at the [raspberry pi forum](https://forums.raspberrypi.com/viewtopic.php?p=2309324) explained to me + the nuances of clock frequencies in PIO programs: although it can be the case that I want a precise clock frequency + matching that of the device I wish to interface (I'll have to do this when implementing the console mode), it sometimes is + not, and it's better to actually set the frequency higher. + +So the days I've been working on pico shock I've had a single goal: receive coherent data, as per the dualshock protocol. +I've implemented the clock and acknowledge signals, too. So now, my PIO program looks like this: + +```asm +.program dualshock +.side_set 1 + +wait 0 pin 2 side 1 ;; Wait for clock to go low, pull ACK high. +.wrap_target +wait_for_att: + set x, 8 side 1 ;; Set bit counter: we want to read a byte. + wait 0 pin 1 side 1 ;; Wait for ATT. PS2 wants to tell us something. + +read_loop: + wait 0 pin 2 side 1 ;; When clock drops from low to high, + wait 1 pin 2 side 1 ;; read the values. + in pins, 1 side 1 ;; Read the bit sent by the PS2. + jmp x-- read_loop side 1 ;; Decrement bit counter. + +acknowledge: + push [15] side 1 ;; We've read a byte, wait for 12us before pulling ACK low. + nop [14] side 1 + nop side 0 [5] ;; Pull ACK low. +.wrap + +% c-sdk { + void dualshock_program_init(PIO pio, uint sm, uint offset, uint command_pin, uint data_pin, uint ack_pin) { + pio_sm_config cfg = dualshock_program_get_default_config(offset); + + sm_config_set_in_pins(&cfg, command_pin); + pio_sm_set_consecutive_pindirs(pio, sm, ack_pin, 1, false); + sm_config_set_out_pins(&cfg, data_pin, 1); + sm_config_set_sideset_pins(&cfg, ack_pin); + sm_config_set_in_shift(&cfg, true, false, 8); + sm_config_set_clkdiv_int_frac(&cfg, 50, 0); + pio_gpio_init(pio, ack_pin); + pio_sm_init(pio, sm, offset, &cfg); + } +%} +``` + +It has grown quite a bit. + +As per my understanding, `sm_config_set_in_pins(&cfg, command_pin)` sets both the pin and its 5 consecutive ones as input pins. +The pin you pass as an argument is called the base pin. As such, following the command pin I wired the clock and after that, +the attention pins. I could be wrong here, too, but, for example, to wait on a pin other than the command, you refer to +your pin with its bitmask. For example, `0b00000` is a 5 bit number. The first refers to the base, the second to the next one, +etc. But, I have my doubts on this: in wait instructions, for example, if you write `wait 0 pin 0` you're waiting for your +base pin to pull low... I think. + +As the pin following the command one is the ATT pin, that's my pin 1, and clock is my pin 2. So, I hope that: + +```asm +wait 0 pin 2 +``` + +Means that I'm waiting for my clock to pull low. + +I've implemented ACK as a side set pin: it's normally set high, but after reading a byte, I must wait 12us and then pull it low +for (half, I'm still not sure how I will do that, meanwhile I'll try for a full PS2 clock cycle) a clock cycle. + +I'm also reading a single bit with every `in` instruction, as the playstation 2 pulls the clock low and then high and after that, +sends a bit. + +# Frequencies + +If you've read my thread in the forum you probably know by now what I mean by me using the clock divider feature wrong. It wasn't +actually because I wasn't setting the frequency I intended to (500 KHz) but because in reality I want to set it higher. So: +`sm_config_set_clkdiv_int_frac(&cfg, 250, 0)` became `sm_config_set_clkdiv_int_frac(&cfg, 50, 0)`. I'm now setting the PIO frequency +as, hopefully, 2.5 MHz. This means that `nop side 0`, the instruction that pulls ACK low, is wrong. As 2.5 MHz is 5 times faster +than 500 KHz, so, this instruction should be `nop side 0 [5]` instead. 5 cycles for us is a cycle for the PS2. + +# What has changed? + +Well, now it seems I'm actually getting something out of the PS2, kind of. The printf sentence of my C program prints +the number 800000 endlessly, which is off by 23 bits to the right (oh, yes, I want the ISR FIFO to shift to the right, +I thought that LSB protocols shifted it to the left. Another one of the countless silly mistakes of my first post). +The number I'm actually looking for is 1. Or 0x01 in hex notation. + +# What comes next + +Well, now that I've set up the state machine's frequency right, and receiving something that looks like relevant data, I +now have to prove my hypothesis: should I shift everything I receive from the PS2 23 bits to the right to make sense of it? +For this, I'll implement the data (out) pin, send something, and see if the number the PS2 sends me as a response is different +from 800000. If it is, I'll shift that 23 bits to the right and see if it coincides with the protocol. + +Until next time! This is the hard part: after this, it probably won't be a walk in the park but I'm pretty certain the C portion of +the library won't be that hard to write. + +# Another update + +Well, I was planning to post this on saturday, but I forgot, and when I had some free time to keep working on this, well, let's say +I've made some progress. + +## Updates to dualshock.pio + +First, another fix to my pio program: + +```asm +;; Before +wait_for_att: + set x, 8 side 1 ;; Set bit counter: we want to read a byte. + +;; After + wait_for_att: + set x, 7 side 1 +``` + +It seems that me making stupid mistakes is a constant on this, oh well. Don't be too harsh on me. + +I also implemented the data bus in pio: + +```asm +.program dualshock_data + +.wrap_target +wait_for_output: + jmp !osre wait_for_att ;; Stall while we don't have data to output + jmp wait_for_output + +wait_for_att: + set x, 7 ;; Set bit counter: we want to send a byte. + wait 0 pin 0 ;; Wait for ATT. + +out_loop: + wait 0 pin 1 ;; When clock drops from low to high, + wait 1 pin 1 ;; send our data. + out pins, 1 ;; Read the bit sent by the PS2. + jmp x-- out_loop ;; Decrement bit counter. +.wrap + +% c-sdk { + void dualshock_data_program_init(PIO pio, uint sm, uint offset, uint data_pin, + uint att_pin) { + pio_sm_config cfg = dualshock_data_program_get_default_config(offset); + + sm_config_set_in_pins(&cfg, att_pin); + sm_config_set_out_pins(&cfg, data_pin, 1); + sm_config_set_out_shift(&cfg, true, true, 8); + sm_config_set_clkdiv_int_frac(&cfg, 50, 0); + pio_sm_init(pio, sm, offset, &cfg); + } +%} +``` + +Yes, a shameful copy paste of what I already had for the command pin, but ignoring acknowledge and using out instead of in. + +## Updates to the main loop + +Well, now I have access to a data bus, now I'm actually reading a byte, let's see what we get: + +```bash +# Trust me, this is what minicom was printing, but I didn't take a screenshot, +# and I'm still not using version control so I don't want to revert anything I have. +# We'll see what's new on the C program shortly. +1000000 +1000000 +1000000 +# etc... +``` + +Well, that's an interesting number. More interesting than 800000, that's for sure. + +Let's write this number in binary: +`0000 0001 0000 0000 0000 0000 0000 0000` + +Huh. Interesting... We're waiting a 1, this is by no means a one... but, if we shift it 24 bits to the +right... it is. So I told myself, well, I'm getting a 1, sort of. If I send data to the PS2 and if when +shifting its responses 24 bits to the right we get the numbers we're expecting... that would +mean I'm now getting the right numbers from the PS2, So that's what we're going to do: + +```c +int main() { +// PIO configuration, etc. You know the drill. + + // We don't want to read weird values on the first loop. + uint32_t data = 0xff; + + while (true) { + //sleep_ms(1000); + + if ((data >> 24) == 0x01) + pio_sm_put_blocking(data_pio, data_sm, 0xff); + + if ((data >> 24) == 0x42) + pio_sm_put_blocking(data_pio, data_sm, 0x41); + + if ((data >> 24) == 0x00) + pio_sm_put_blocking(data_pio, data_sm, 0x5a); + + data = pio_sm_get_blocking(pio, sm); + + printf("%x\n", data); + } +} +``` + +This is a rather crude pattern matching, I know. I got rid of the sleep directive to print more precise numbers to the +console. Yes, they're going to be printed really fast in succession, but they would be a more accurate representation of this +loop. And it's nothing that scrolling the terminal up can't fix. + + + +Well... those numbers seem pretty familiar. According to the [Curious Inventor guide](https://store.curiousinventor.com/guides/PS2), +this back and forth communication with the PS2 should look like this: + +|Byte # | 1| 2| 3| 4| 5| +|-------|----|----|----|----|----| +|Command|0x01|0x42|0x00|0x00|0x00| +|-------|----|----|----|----|----| +|Data |0xff|0x41|0x5a|0xff|0xff| + +Well, minus the last 2 data bytes which we're not sending (we're matching 0x00 with 0x5a) all of the command bytes are exactly these, +when shifted 24 bits to the right, of course. This means that everything I read from the RX FIFO must be shifted 24 bits to the +right, no biggie. + +Well, I'm pretty stoked. And this means that now comes the boring part: parsing all those bytes and sending appropriate responses. +So exciting. diff --git a/assets/images/2025/minicom_2.png b/assets/images/2025/minicom_2.png Binary files differnew file mode 100644 index 0000000..9d59b4e --- /dev/null +++ b/assets/images/2025/minicom_2.png |