diff --git a/CMakeLists.txt b/CMakeLists.txt index 53be8d86..431422d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ pico_generate_pio_header(${PROJECT_NAME} src/dmd_interface_wpc.pio) pico_generate_pio_header(${PROJECT_NAME} src/dmd_interface_whitestar.pio) pico_generate_pio_header(${PROJECT_NAME} src/dmd_interface_spike.pio) pico_generate_pio_header(${PROJECT_NAME} src/dmd_interface_sam.pio) +pico_generate_pio_header(${PROJECT_NAME} src/dmd_interface_desega.pio) # Add the standard library to the build target_link_libraries(${PROJECT_NAME} pico_stdlib) diff --git a/src/dmd_interface_desega.pio b/src/dmd_interface_desega.pio new file mode 100644 index 00000000..0e0e3b1f --- /dev/null +++ b/src/dmd_interface_desega.pio @@ -0,0 +1,130 @@ +.define DE 7 +.define RDATA 6 +.define RCLK 5 +.define COLLAT 4 +.define DOTCLK 3 +.define SDATA 2 + + +.program dmd_reader_desega + +; Send using an external clock on the SPI interface + + ; initialize y with 4096 = number of pixels (128x32) + set x, 1 + in x, 1 + in null, 13 + mov y, isr + +.wrap_target + + mov x, y ; load number of pixels + mov isr, null ; reset shift counter + + irq clear 4 + wait irq 4 + + +; this is the loop for the most significant bit +dotloop1: + wait 0 gpio DOTCLK ; falling edge + wait 1 gpio DOTCLK ; raising edge + in null 2 ; padding + in pins 1 ; read pin data + in null 1 + jmp x-- dotloop1 + + mov x, y ; load number of pixels + mov isr, null ; reset shift counter + +; There's an empty line between LSB and HSB plane + wait 0 gpio RCLK + wait 1 gpio RCLK + +; this is the loop for the least significant bit +; in this loop, the bit value is multipled by 2 by shifting it one bit +dotloop2: + wait 0 gpio DOTCLK ; falling edge + wait 1 gpio DOTCLK ; raising edge + in null 3 ; padding + in pins 1 ; read pin data + jmp x-- dotloop2 + + +.wrap + + +.program dmd_framedetect_desega + +.wrap_target + +; Data East/Sega frame start detection +; ~7.86µs is the longest state in which DE can be low without it being a new frame +; Each nop [31] = 32 cycles = 256 ns @125 MHz +; We go with 10 µs to be safe and confirm the start of a new frame, which is 256ns * 39 = 9.98 µs +wait_low: + wait 0 gpio DE ; Wait for DE to go low + + set x, 20 ; Use x as storage for 20 iterations + +delay_loop: + nop [31] + nop [31] + jmp x-- delay_loop ; 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 + irq 4 ; Pin remained low long enough → trigger IRQ + + wait 1 gpio DE ; Wait again for it to go high before restarting cycle + jmp wait_low ; went high, time to go back to wait_low + +.wrap + + +% c-sdk { +static inline void dmd_reader_desega_program_init(PIO pio, uint sm, uint offset) { + pio_sm_config c = dmd_reader_desega_program_get_default_config(offset); + + // Set the IN pin, we don't use any other + sm_config_set_in_pins(&c, 2); // SDATA + + // Set the pin direction at the PIO + pio_sm_set_consecutive_pindirs(pio, sm, 5, 1, false); // RCLK + pio_sm_set_consecutive_pindirs(pio, sm, 2, 2, false); // SDATA, DOTCLK + + // Connect these GPIOs to this PIO block + pio_gpio_init(pio, 5); // RCLK + pio_gpio_init(pio, 3); // DOTCLK + pio_gpio_init(pio, 2); // SDATA + + // Shifting to left matches the customary MSB-first ordering of SPI. + sm_config_set_in_shift( + &c, + false, // Shift-to-right = false + true, // Autopull enabled + 32 // Autopull 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); +} +%} + +% c-sdk { +static inline void dmd_framedetect_desega_program_init(PIO pio, uint sm, uint offset) { + pio_sm_config c = dmd_framedetect_desega_program_get_default_config(offset); + + // Set the pin direction at the PIO + pio_sm_set_consecutive_pindirs(pio, sm, 7, 1, false); // DE + + // Connect this GPIO to this PIO block + pio_gpio_init(pio, 7); // DE + + // Load our configuration, do not yet start the program + pio_sm_init(pio, sm, offset, &c); +} +%} diff --git a/src/dmd_interface_whitestar.pio b/src/dmd_interface_whitestar.pio index 44ca09d7..ef62e3a6 100644 --- a/src/dmd_interface_whitestar.pio +++ b/src/dmd_interface_whitestar.pio @@ -109,4 +109,4 @@ static inline void dmd_framedetect_whitestar_program_init(PIO pio, uint sm, uint // Load our configuration, do not yet start the program pio_sm_init(pio, sm, offset, &c); } -%} \ No newline at end of file +%} diff --git a/src/dmd_reader.c b/src/dmd_reader.c index 58a8a0c6..82eccb66 100644 --- a/src/dmd_reader.c +++ b/src/dmd_reader.c @@ -17,6 +17,7 @@ #include "dmd_interface_whitestar.pio.h" #include "dmd_interface_spike.pio.h" #include "dmd_interface_sam.pio.h" +#include "dmd_interface_desega.pio.h" // should CRC32 checksum be caculated and sent with each frame #define USE_CRC @@ -93,6 +94,7 @@ typedef struct __attribute__((__packed__)) block_pix_crc_header_t #define DMD_WHITESTAR 2 #define DMD_SPIKE1 3 #define DMD_SAM 4 +#define DMD_DESEGA 5 // Line oversampling #define LINEOVERSAMPLING_NONE 1 @@ -363,30 +365,38 @@ int detect_dmd() if ((dotclk > 450000) && (dotclk < 550000) && (de > 3800) && (de < 4000) && - (rdata > 115) && (rdata < 130)) - { + (rdata > 115) && (rdata < 130)) { printf("WPC detected\n"); spi_notify_onoff(DMD_WPC); return DMD_WPC; + } else if ((dotclk > 640000) && (dotclk < 700000) && (de > 5000) && (de < 5300) && - (rdata > 70) && (rdata < 85)) - { + (rdata > 70) && (rdata < 85)) { printf("Stern Whitestar detected\n"); spi_notify_onoff(DMD_WHITESTAR); return DMD_WHITESTAR; + } else if ((dotclk > 1000000) && (dotclk < 1100000) && (de > 8000) && (de < 8400) && (rdata > 240) && (rdata < 270)) { printf("Stern Spike1 detected\n"); spi_notify_onoff(DMD_SPIKE1); return DMD_SPIKE1; + } else if ((dotclk > 1000000) && (dotclk < 1100000) && (de > 8000) && (de < 8400) && (rdata > 60) && (rdata < 70)) { printf("Stern SAM detected\n"); spi_notify_onoff(DMD_SAM); return DMD_SAM; + + } else if ((dotclk > 640000) && (dotclk < 700000) && + (de > 5000) && (de < 5300) && + (rdata > 3800) && (rdata < 4000)) { + printf("Data East/Sega detected\n"); + spi_notify_onoff(DMD_DESEGA); + return DMD_DESEGA; } spi_notify_onoff(1); @@ -584,11 +594,11 @@ bool init() lcd_width = 128; lcd_height = 32; - lcd_bitsperpixel = 2; + lcd_bitsperpixel = 2; // Whitestar is 2bpp lcd_pixelsperbyte = 8 / lcd_bitsperpixel; lcd_planesperframe = 2; // in Whitestar, there's a MSB and a LSB plane lcd_lineoversampling = LINEOVERSAMPLING_WHITESTAR; // in Whitestar each line is sent twice - lcd_mergeplanes = MERGEPLANES_ADDSHIFT; // required for correct 2bpp merge + //lcd_mergeplanes = MERGEPLANES_ADDSHIFT; // required for correct 2bpp merge } else if (dmd_type == DMD_SPIKE1) { dmd_pio = pio0; offset = pio_add_program(dmd_pio, &dmd_reader_spike_program); @@ -634,6 +644,28 @@ bool init() lcd_planesperframe = 1; // in SAM there is one planes lcd_lineoversampling = LINEOVERSAMPLING_SAM; // with 4x line oversampling lcd_mergeplanes = MERGEPLANES_ADD; + } else if (dmd_type == DMD_DESEGA) { + dmd_pio = pio0; + offset = pio_add_program(dmd_pio, &dmd_reader_desega_program); + dmd_sm = pio_claim_unused_sm(dmd_pio, true); + dmd_reader_desega_program_init(dmd_pio, dmd_sm, offset); + printf("Data East/Sega DMD reader initialized\n"); + + // The framedetect program just runs and detects the beginning of a new frame + frame_pio = pio0; + offset = pio_add_program(frame_pio, &dmd_framedetect_desega_program); + frame_sm = pio_claim_unused_sm(frame_pio, true); + dmd_framedetect_desega_program_init(frame_pio, frame_sm, offset); + pio_sm_set_enabled(frame_pio, frame_sm, true); + printf("Data East/Sega frame detection initialized\n"); + + lcd_width = 128; + lcd_height = 32; + lcd_bitsperpixel = 2; // Data East/ Sega is 2bpp + lcd_pixelsperbyte = 8 / lcd_bitsperpixel; + lcd_planesperframe = 2; // in DE/Sega, there's a MSB and a LSB plane + lcd_lineoversampling = LINEOVERSAMPLING_WHITESTAR; // in DE/Sega each line is sent twice + lcd_mergeplanes = MERGEPLANES_ADDSHIFT; // required for correct 2bpp merge } else { printf("Unknown DMD type, aborting\n"); return false;