Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Since not any consumer (especially the Raspberry Pi) can act as an SPI slave thi
## Currently supported hardware systems

* WPC95 & WPC -> 128x32
* Data East -> 128x32
* Data East -> 128x32 & 128x16
* Sega -> 128x32 & 192x64
* Stern Whitestar -> 128x32
* Stern SAM -> 128x32
Expand Down
Binary file not shown.
Binary file not shown.
44 changes: 31 additions & 13 deletions src/dmd_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,48 @@
#include "dmd_interface_alving.pio.h"
#include "dmd_interface_capcom.pio.h"
#include "dmd_interface_capcom_hd.pio.h"
#include "dmd_interface_de_x16_v1.pio.h"
#include "dmd_interface_de_x16_v2.pio.h"
#include "dmd_interface_desega.pio.h"
#include "dmd_interface_gottlieb.pio.h"
#include "dmd_interface_homepin.pio.h"
#include "dmd_interface_sega_hd.pio.h"
#include "dmd_interface_whitestar.pio.h"
#include "dmd_interface_wpc.pio.h"
#include "dmd_interface_homepin.pio.h"
#include "dmdreader_pins.h"
#include "hardware/gpio.h"
#include "hardware/pio.h"

// Derive divider from current clock so PIO runs at ~125 MHz reference
float dmd_interface_125mhz_clk_divider =
(float)(clock_get_hz(clk_sys)) / 125000000.0f; // scales automatically

// Init the DMD reader (dots) PIO program, common for all DMD types.
void dmd_reader_program_init(PIO pio, uint sm, uint offset, pio_sm_config c) {
// Set the IN pin, we don't use any other
sm_config_set_in_pins(&c, SDATA);
void dmd_reader_program_init(PIO pio, uint sm, uint offset, pio_sm_config c,
uint in_base_pin) {
sm_config_set_in_pins(&c, in_base_pin);

if (in_base_pin != SDATA_X16) {
// We only send, so disable the TX FIFO to make the RX FIFO deeper.
// Joining is not possible with data east x16
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);
} else {
// -- Data East 128x16 case --
// We need to set DOTCLK as the jump pin
sm_config_set_jmp_pin(&c, DOTCLK);

pio_gpio_init(pio, SDATA_X16); // Extra data line for Data East X16
pio_gpio_init(pio, SDATA_X16_PADDING); // used as a padding 0 bit

pio_sm_set_consecutive_pindirs(pio, sm, SDATA_X16, 1, false);
pio_sm_set_consecutive_pindirs(pio, sm, SDATA_X16_PADDING, 1, false);

// Make sure we run this sm with a 125MHz clk
sm_config_set_clkdiv(&c, dmd_interface_125mhz_clk_divider);
}
// Connect these GPIOs to this PIO block
pio_gpio_init(pio, DOTCLK);
pio_gpio_init(pio, SDATA);
pio_gpio_init(pio, DOTCLK);

// Set the pin direction at the PIO, handle pins seprately to support alphaDMD
// as well
Expand All @@ -42,9 +66,6 @@ void dmd_reader_program_init(PIO pio, uint sm, uint offset, pio_sm_config c) {
32 // autopush threshold
);

// We only send, so disable the TX FIFO to make the RX FIFO deeper.
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX);

// Load our configuration, do not yet start the program
pio_sm_init(pio, sm, offset, &c);
}
Expand All @@ -70,11 +91,8 @@ void dmd_framedetect_program_init(PIO pio, uint sm, uint offset,
false, // no autopush
0);

// Derive divider from current clock so PIO runs at ~125 MHz reference
uint32_t sys_hz = clock_get_hz(clk_sys); // e.g. 125/200/266 MHz
float target_hz = 125000000.0f; // PIO code designed for 125 MHz
float divider = (float)sys_hz / target_hz; // scales automatically
sm_config_set_clkdiv(&c, divider);
// Make sure we run this sm with a 125MHz clk
sm_config_set_clkdiv(&c, dmd_interface_125mhz_clk_divider);

// Load our configuration, do not yet start the program
pio_sm_init(pio, sm, offset, &c);
Expand Down
2 changes: 1 addition & 1 deletion src/dmd_interface_capcom.pio
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ rclk_loop:
wait 1 gpio RCLK
wait 0 gpio RCLK
jmp x-- rclk_loop
nop [3]
irq PLANE_START_IRQ
wait 1 gpio RDATA
.wrap
66 changes: 66 additions & 0 deletions src/dmd_interface_de_x16_v1.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.define DE 7
.define RDATA 6
.define RCLK 5
.define COLLAT 4
.define DOTCLK 3
.define SDATA 2
.define SDATA_X16_PADDING 1
.define SDATA_X16 0
.define FRAME_START_IRQ 5

.program dmd_reader_de_x16_v1

.wrap_target

set y, 31

irq clear FRAME_START_IRQ
wait irq FRAME_START_IRQ

start:
set x, 31

dotloop:
; Because of PIO limitations, we read in 4 bit: px 1 as 000v and px 2 as 0v00 (where v is either 0 or 1 depending on the SDATA state)
; The first bit of px 2 always contains a padding 0, thanks to GPIO 1 because it's pulled low permanently.
; in pins 3 = (px 1 -> v)0(v <- px 2). Here the zero in the middle is the padding bit (GPIO 1), and the 1s on either side are the actual pixel values.
; This lets us create a unique combination which is either lit up (pixval 1) or off (pixval 0)
; possible values here are 0000, 0001 and 0100
wait 1 gpio DOTCLK ; rising edge
in null 3 ; 000 padding for 4bpp
in pins 3 ; read value of px 1(GPIO2), padding 0 (GPIO1) and px 2 (GPIO0) as they're sampled on the same DOTCLK edge
in null 2 ; 00 padding for 4bpp
wait 0 gpio DOTCLK ; falling edge
jmp x-- dotloop

set x, 7 ; loop 32 times
no_lsb_msb_padding:
in null 32
jmp x-- no_lsb_msb_padding

jmp y-- start

.wrap

; Frame detection program runs in parallel to the reader program and signals the start of a new frame using an IRQ.
.program dmd_framedetect_de_x16_v1
.wrap_target

; V1 (TMNT & Checkpoint) Data East 128x16 frame detection
; When RDATA goes high, check if DE is high as well. If this is the case, we are in the middle of a frame.
; Finally, skip 16 rows forward to find the starting row.

detect_loop:
wait 0 gpio RDATA
wait 1 gpio RDATA
jmp pin, prepare_skip_loop ; jmp pin is DE
jmp detect_loop

prepare_skip_loop:
set x, 16 ; 16 rows
skip_loop:
wait 0 gpio DE
wait 1 gpio DE
jmp x-- skip_loop
irq FRAME_START_IRQ
.wrap
77 changes: 77 additions & 0 deletions src/dmd_interface_de_x16_v2.pio
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
.define DE 7
.define RDATA 6
.define RCLK 5
.define COLLAT 4
.define DOTCLK 3
.define SDATA 2
.define SDATA_X16_PADDING 1
.define SDATA_X16 0
.define FRAME_START_IRQ 5

.program dmd_reader_de_x16_v2

wait irq FRAME_START_IRQ ; we only check for an IRQ once because of limitations

.wrap_target

start:
set x, 31
dotloop:
; Because of PIO limitations, we read in 4 bit: px 1 as 000v and px 2 as 0v00 (where v is either 0 or 1 depending on the SDATA state)
; The first bit of px 2 always contains a padding 0, thanks to GPIO 1 because it's pulled low permanently.
; in pins 3 = (px 1 -> v)0(v <- px 2). Here the zero in the middle is the padding bit (GPIO 1), and the 1s on either side are the actual pixel values.
; This lets us create a unique combination which is either lit up (pixval 1) or off (pixval 0)
; possible values here are 0000, 0001 and 0100
wait 1 gpio DOTCLK ; rising edge
in null 3 ; 000 padding for 4bpp
in pins 3 ; read value of px 1(GPIO2), padding 0 (GPIO1) and px 2 (GPIO0) as they're sampled on the same DOTCLK edge
in null 2 ; 00 padding for 4bpp
wait 0 gpio DOTCLK ; falling edge
jmp x-- dotloop
jmp !y skip
jmp y-- start

skip:
mov x, osr ; copy 8192 to x
lsb_msb_check:
jmp pin reload_dotloop_y ; if DOTCLK is high, it means we will have a valid LSB + MSB row
jmp x-- lsb_msb_check ; loop for ~65.5 µs (based on 125MHz clkdiv)

set x, 5 ; 0b101
set y, 31
no_lsb_msb_padding:
; Shift in 0101 0101 32 times. We record 64 pixels like this as the missing MSB row.
in x, 4
in x, 4
jmp y-- no_lsb_msb_padding

mov y, null
jmp start

reload_dotloop_y:
set y, 1

.wrap

; Frame detection program runs in parallel to the reader program and signals the start of a new frame using an IRQ.
.program dmd_framedetect_de_x16_v2
.wrap_target

; Data East 128x16 frame detection
; When DE goes low, check for the next rising edge of DE. If DE goes high again after ~16 µs -> frame start.

wait_low:
wait 0 gpio DE ; Wait for DE to go low
mov x, osr ; Use x as storage for 3000 iterations

delay_loop:
jmp x-- delay_loop ; Decrement x and repeat until zero

; After ~16 µs, check if still low
jmp pin, frame_start ; If DE went high early → we found the start
wait 1 gpio DE
jmp wait_low ; DE didn't go high early → restart

frame_start:
irq FRAME_START_IRQ
.wrap
4 changes: 2 additions & 2 deletions src/dmd_interface_desega.pio
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ dotloop:

wait_low:
wait 0 gpio DE ; Wait for DE to go low
set x, 20 ; Use x as storage for 20 iterations
set x, 31 ; Use x as storage for 32 iterations

delay_loop:
nop [31]
nop [31]
jmp x-- delay_loop ; Decrement x and repeat until zero
jmp x-- delay_loop [31] ; Decrement x and repeat until zero

; After ~10 µs, check if still low
jmp pin, wait_low ; If pin went high early → back to wait_low
Expand Down
Loading
Loading