From e058653fc27e6b60e65d312e164c74b27319c790 Mon Sep 17 00:00:00 2001 From: Jan Vos Date: Tue, 11 Aug 2026 14:20:01 +0200 Subject: [PATCH 1/6] DMA sniffing instead of slow crc32 lib --- src/dmdreader.cpp | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/dmdreader.cpp b/src/dmdreader.cpp index a47cd79a..fed162ee 100644 --- a/src/dmdreader.cpp +++ b/src/dmdreader.cpp @@ -119,6 +119,7 @@ uint8_t *framebuf3; uint8_t *current_framebuf; uint8_t *framebuf_to_send; +uint8_t dummy_sniff_dst[1]; uint32_t frame_crc = 0; uint32_t crc_previous_frame = 0; bool detected_0_1_0_1 = false; @@ -142,9 +143,11 @@ uint frame_offset; // DMA uint dmd_dma_channel; +uint dma_sniff_channel; uint spi_dma_channel; dma_channel_config dmd_dma_channel_cfg; +dma_channel_config dma_sniff_channel_cfg; dma_channel_config spi_dma_channel_cfg; volatile bool spi_dma_running = false; @@ -624,6 +627,16 @@ void dmd_dma_reset() { dmd_set_and_enable_new_dma_target(); } +/** + * @brief Sniffs the processed array to prepare for CRC32 extraction + * + */ +void dmd_set_and_enable_new_dma_sniffer() { + dma_channel_transfer_from_buffer_now(dma_sniff_channel, current_framebuf, + loopback ? source_bytes : target_bytes); + dma_channel_wait_for_finish_blocking(dma_sniff_channel); // waiting is required. +} + /** * @brief Handles DMD DMA requests by switching between the buffers * @@ -890,8 +903,12 @@ void dmd_dma_handler() { memcpy(current_framebuf, processingbuf, loopback ? source_bytes : target_bytes); - frame_crc = - crc32(0, current_framebuf, loopback ? source_bytes : target_bytes); + // frame_crc = + // crc32(0, current_framebuf, loopback ? source_bytes : target_bytes); + dmd_set_and_enable_new_dma_sniffer(); + + frame_crc = dma_hw->sniff_data; + dma_hw->sniff_data = 0xFFFFFFFF; // always clean after sniffing. switch_buffers(); @@ -1505,6 +1522,28 @@ bool dmdreader_init(bool return_on_no_detection) { irq_set_exclusive_handler(DMA_IRQ_0, dmd_dma_handler); irq_set_enabled(DMA_IRQ_0, true); #endif + + // CRC32 DMA sniffer for DMD reader + dma_sniff_channel = dma_claim_unused_channel(true); + dma_sniff_channel_cfg = dma_channel_get_default_config(dma_sniff_channel); + channel_config_set_transfer_data_size(&dma_sniff_channel_cfg, DMA_SIZE_8); + channel_config_set_read_increment(&dma_sniff_channel_cfg, true); + channel_config_set_write_increment(&dma_sniff_channel_cfg, false); + + // (bit-reverse) CRC32 specific sniff set-up + channel_config_set_sniff_enable(&dma_sniff_channel_cfg, true); + dma_sniffer_set_data_accumulator(0xFFFFFFFF); + dma_sniffer_set_output_reverse_enabled(true); + dma_sniffer_enable(dma_sniff_channel, DMA_SNIFF_CTRL_CALC_VALUE_CRC32R, true); + + dma_channel_configure( + dma_sniff_channel, &dma_sniff_channel_cfg, + dummy_sniff_dst, // The (unchanging) dummy write address + current_framebuf, // The (unchanging) read address + 0, // We do not know the transfer count yet + false // Do not yet start! + ); + // Finally start DMD reader PIO program and DMA dmd_set_and_enable_new_dma_target(); pio_sm_set_enabled(frame_pio, frame_sm, true); From bda3a22cefcb8e3e491c617137633f4fed05e985 Mon Sep 17 00:00:00 2001 From: Jan Vos Date: Tue, 11 Aug 2026 15:17:58 +0200 Subject: [PATCH 2/6] delete crc32 lib. --- src/crc32.cpp | 89 ----------------------------------------------- src/crc32.h | 8 ----- src/dmdreader.cpp | 3 -- 3 files changed, 100 deletions(-) delete mode 100644 src/crc32.cpp delete mode 100644 src/crc32.h diff --git a/src/crc32.cpp b/src/crc32.cpp deleted file mode 100644 index 453ca5a5..00000000 --- a/src/crc32.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-1996 Mark Adler - * For conditions of distribution and use, see copyright notice in zlib.h - */ - -/* $Id: crc32.c,v 1.1 2007/10/07 20:47:35 matthias Exp $ */ - -#include "crc32.h" - -/* ======================================================================== - * Table of CRC-32's of all single-byte values (made by make_crc_table) - */ -static uint32_t crc_table[256] = { - 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, - 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, - 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, - 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, - 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, - 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, - 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, - 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, - 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, - 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, - 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, - 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, - 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, - 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, - 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, - 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, - 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, - 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, - 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, - 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, - 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, - 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, - 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, - 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, - 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, - 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, - 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, - 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, - 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, - 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, - 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, - 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, - 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, - 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, - 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, - 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, - 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, - 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, - 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, - 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, - 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, - 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, - 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, - 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, - 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, - 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, - 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, - 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, - 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, - 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, - 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, - 0x2d02ef8dL}; - -#define DO1(buf) crc = crc_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); -#define DO2(buf) \ - DO1(buf); \ - DO1(buf); -#define DO4(buf) \ - DO2(buf); \ - DO2(buf); -#define DO8(buf) \ - DO4(buf); \ - DO4(buf); - -uint32_t crc32(uint32_t crc, const uint8_t *buf, uint32_t len) { - if (!buf) return (0L); - crc = crc ^ 0xffffffffL; - while (len >= 8) { - DO8(buf); - len -= 8; - } - if (len) do { - DO1(buf); - } while (--len); - return (crc ^ 0xffffffffL); -} diff --git a/src/crc32.h b/src/crc32.h deleted file mode 100644 index b9e0e359..00000000 --- a/src/crc32.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CRC32_H -#define CRC32_H - -#include - -uint32_t crc32(uint32_t crc, const uint8_t *buf, uint32_t len); - -#endif \ No newline at end of file diff --git a/src/dmdreader.cpp b/src/dmdreader.cpp index fed162ee..c13e0b9a 100644 --- a/src/dmdreader.cpp +++ b/src/dmdreader.cpp @@ -4,7 +4,6 @@ #include #include -#include "crc32.h" #include "dmd_counter.h" #include "dmd_interface.h" #include "dmdreader_pins.h" @@ -903,8 +902,6 @@ void dmd_dma_handler() { memcpy(current_framebuf, processingbuf, loopback ? source_bytes : target_bytes); - // frame_crc = - // crc32(0, current_framebuf, loopback ? source_bytes : target_bytes); dmd_set_and_enable_new_dma_sniffer(); frame_crc = dma_hw->sniff_data; From 27daef67f36bb43d79cf491ec01f05c932814c53 Mon Sep 17 00:00:00 2001 From: Jan Vos Date: Tue, 11 Aug 2026 16:55:59 +0200 Subject: [PATCH 3/6] readme additions - additional garbage clean up and update platformio platform --- README.md | 23 +++++----- docs/install-openocd.md | 39 ---------------- platformio.ini | 4 +- scripts/program-pbos.sh | 15 ------ scripts/program-pico.sh | 2 - scripts/raspberrypi-swd-dmdreader.cfg | 12 ----- signals.md | 66 --------------------------- 7 files changed, 14 insertions(+), 147 deletions(-) delete mode 100644 docs/install-openocd.md delete mode 100755 scripts/program-pbos.sh delete mode 100755 scripts/program-pico.sh delete mode 100644 scripts/raspberrypi-swd-dmdreader.cfg delete mode 100644 signals.md diff --git a/README.md b/README.md index f3c8e6f7..0bf2f60d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ # Read and display pinball DMD data -This project allows to read the contents of a pinball DMD using the Raspberry Pi Pico. +This project allows to read the contents of a pinball DMD using the Raspberry Pi Pico. Both the RP2040 and RP2350 are supported. ## Hardware -The Pi Pico is directly connected to the 6 DMD data lines. Communication between the Pi Pico and the consumer is implemented via SPI with an additional IRQ line. -On this IRQ line, the Pico signals that new data is available and consumer must start the data transfer. -Since not any consumer (especially the Raspberry Pi) can act as an SPI slave this method is used. +The Pico is directly connected to the DMD data lines. Communication between the Pico and the consumer is implemented via SPI with an additional IRQ line. +On this IRQ line, the Pico signals that new data is available and consumer must start the data transfer whenever possible. +Since most consumers (especially the Raspberry Pi) are not real-time, this method is used. +Because of this, the consumer has full flexibility of setting an SPI CLK frequency. The Pico in this case is real-time, and will be able to keep up easily. -## Officially supported hardware systems +## Officially supported and tested hardware systems * WPC95 & WPC -> 128x32 * Data East -> 128x32 & 128x16 @@ -27,14 +28,14 @@ Since not any consumer (especially the Raspberry Pi) can act as an SPI slave thi ## Reading data When reading data, we assume the data is sent correctly. -This means we can read a full frame, containing a predefined amount of bits per pixel. +DMDreader sends complete frames only, containing a predefined amount of bits per pixel. The process is as follows: - - Wait for a frame to start (DMD frame detect program) - - Read frame (Pixel loop) - - Construct frame based on pixel loop data and system specific code + - Wait for a frame to start (DMD frame detect PIO program) + - Read frame (DMD dotloop PIO program) + - Construct frame based on captured data (dmd_dma_handler IRQ) ## License -This project has been forked from https://github.com/pinballpower/code_dmd after that project changed its license from MIT to GPL v3 on 2022-05-02. -So, the license of this fork is GPL v3. \ No newline at end of file +This project was once forked from https://github.com/pinballpower/code_dmd, but has undergone many changes and additions. +Since the original project changed its license from MIT to GPL v3 on 2022-05-02, the license of this fork is now also GPL v3. \ No newline at end of file diff --git a/docs/install-openocd.md b/docs/install-openocd.md deleted file mode 100644 index da97e859..00000000 --- a/docs/install-openocd.md +++ /dev/null @@ -1,39 +0,0 @@ -# OpenOCD - -To upload software to the RP2040, OpenOCD is needed. - -## Installation -``` -sudo apt install automake autoconf build-essential texinfo libtool libftdi-dev libusb-1.0-0-dev -git clone --recurse-submodules --depth 1 --branch rp2040 https://github.com/raspberrypi/openocd.git -cd openocd/ -./bootstrap -./configure --enable-ftdi --enable-sysfsgpio --enable-bcm2835gpio -make -j 4 -sudo make install -``` - -## Configuration -sudo vi /usr/local/share/openocd/scripts/interface/raspberrypi-swd.cfg - -``` -# Use RPI GPIO pins -adapter driver bcm2835gpio - -bcm2835gpio_speed_coeffs 146203 36 - -# SWD swclk swdio -# Header pin numbers -bcm2835gpio_swd_nums 23 22 - -transport select swd - -adapter speed 1000 -``` - -## Usage - -``` -openocd -f interface/raspberrypi-swd.cfg -f target/rp2040.cfg -``` - diff --git a/platformio.ini b/platformio.ini index 956a868b..4eef6051 100644 --- a/platformio.ini +++ b/platformio.ini @@ -2,14 +2,14 @@ default_envs = ppucdmd [env:ppucdmd] -platform = https://github.com/mkalkbrenner/platform-raspberrypi#issue-112 +platform = https://github.com/maxgerhardt/platform-raspberrypi#79ce473e1a8010ed8096388690222fd2c94858a6 framework = arduino board = pico board_build.core = earlephilhower board_build.filesystem_size = 0.5m [env:ppucdmd2] -platform = https://github.com/mkalkbrenner/platform-raspberrypi#issue-112 +platform = https://github.com/maxgerhardt/platform-raspberrypi#79ce473e1a8010ed8096388690222fd2c94858a6 framework = arduino board = rpipico2 board_build.core = earlephilhower diff --git a/scripts/program-pbos.sh b/scripts/program-pbos.sh deleted file mode 100755 index f51f77b9..00000000 --- a/scripts/program-pbos.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -if [ "$1" == "" ]; then - echo "start with $0 hostname to program RPO2040 on PBOS" - exit 1 -fi - -echo "Stopping DMDReader..." -sshpass -p pbos scp dmdreader.elf root@$1:/firmware -sshpass -p pbos ssh -l root $1 'pkill dmdreader' -sleep 5 -sshpass -p pbos ssh -l root $1 'pkill -KILL dmdreader' -echo "Programming RP2040..." -sshpass -p pbos ssh -l root $1 'cd /firmware; openocd -f raspberrypi-swd-dmdreader.cfg -f target/rp2040.cfg -c "program dmdreader.elf verify reset exit"' -echo "Rebooting..." -sshpass -p pbos ssh -l root $1 'reboot' diff --git a/scripts/program-pico.sh b/scripts/program-pico.sh deleted file mode 100755 index 5413957c..00000000 --- a/scripts/program-pico.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -openocd -f raspberrypi-swd-dmdreader.cfg -f target/rp2040.cfg -c "program dmdreader.elf verify reset exit" diff --git a/scripts/raspberrypi-swd-dmdreader.cfg b/scripts/raspberrypi-swd-dmdreader.cfg deleted file mode 100644 index f7fe7f56..00000000 --- a/scripts/raspberrypi-swd-dmdreader.cfg +++ /dev/null @@ -1,12 +0,0 @@ -# Use RPI GPIO pins -adapter driver bcm2835gpio - -bcm2835gpio_speed_coeffs 146203 36 - -# SWD swclk swdio -# Header pin numbers -bcm2835gpio_swd_nums 23 22 - -transport select swd - -adapter speed 1000 diff --git a/signals.md b/signals.md deleted file mode 100644 index 6c28168f..00000000 --- a/signals.md +++ /dev/null @@ -1,66 +0,0 @@ -# DMD Signals and operation - -## Signals - -|Signal|Name|Usage| -|---|---|---| -|DE|display enable|Turn on/off display| -|RDATA|row data|set first row| -|RCLK|row clock|next row| -|COLLAT|cloumn latch|activate transmitted data for current column| -|DOTCLK|dot clock|next column| -|SDATA|dot data|pixel data| - -## Timing/Frequencies - -|System|Pixel (column) Clock|Line clock|Screen rate| -|---|---|---|---| -|WPC|1MHz|3.9kHz|125Hz| -|WhiteStar|1MHz/500kHz|2.577kHz|78Hz| -|SAM|4.7MHz|2kHz|62.5Hz| -|Spike 1|4.8MHz|3.8kHz|63.5Hz| - -## Operation WPC - -Column data is sent serialized using SDATA SCLK signals. -It takes 128us to transfer the 128bits of a line. -However, about 50% of the time, there is no signal/clock -active. - -Lines are stepped by a RCLK signal. RDATA is low. If RDATA -is H when the RCLK signal is received, line is being -reset to the first line - -## Operation Whitestar - -WhiteStar works differently than WPC. In Whitestar, each line -is sent twice first with 500kHz pixel clock, then again with -1MHz pixel clock. While this might seem strange, this allows -to use 2 bit brightness data with only 2 cycles. - -## Operation SAM - -On SAM systems each line is displayed 4 times: -|line|display time| -|---|---| -|1|83.2us| -|2|166.4us| -|3|41.55us (least significant)| -|4|208us (most significant)| - -This makes parsing of these a bit more complicated as there is no -"standard" LSB->MSB order as you would expect - - -## Operation Spike 1 - -Spike 1 supports 16 levels of brightness. They are displayed in 4 planes each -using different timing: -|plane|diplsay time| -|---|---| -|1|1.05ms (least significant)| -|2|2.1ms| -|3|4.2ms| -|4|8.4ms| - -A full screen consist of these 4 planes and therefore is displayed for 15.75ms \ No newline at end of file From 08e9a96a4254cbd2aa614cbe292a5ea930cf6783 Mon Sep 17 00:00:00 2001 From: Jan Vos Date: Tue, 11 Aug 2026 22:59:05 +0200 Subject: [PATCH 4/6] source pointer is set during processing code --- src/dmdreader.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dmdreader.cpp b/src/dmdreader.cpp index c13e0b9a..e17fe0ed 100644 --- a/src/dmdreader.cpp +++ b/src/dmdreader.cpp @@ -1535,10 +1535,10 @@ bool dmdreader_init(bool return_on_no_detection) { dma_channel_configure( dma_sniff_channel, &dma_sniff_channel_cfg, - dummy_sniff_dst, // The (unchanging) dummy write address - current_framebuf, // The (unchanging) read address - 0, // We do not know the transfer count yet - false // Do not yet start! + dummy_sniff_dst, // The (unchanging) dummy write address + NULL, // Source pointer is set during dmd_dma_handler + 0, // We do not know the transfer count yet + false // Do not yet start! ); // Finally start DMD reader PIO program and DMA From 2c242474836dd59a993dc19803be1db79cfbf415 Mon Sep 17 00:00:00 2001 From: Jan Vos Date: Tue, 11 Aug 2026 23:12:52 +0200 Subject: [PATCH 5/6] fix some comments for better clarity --- src/dmdreader.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/dmdreader.cpp b/src/dmdreader.cpp index e17fe0ed..c45a0ae1 100644 --- a/src/dmdreader.cpp +++ b/src/dmdreader.cpp @@ -627,13 +627,15 @@ void dmd_dma_reset() { } /** - * @brief Sniffs the processed array to prepare for CRC32 extraction + * @brief Takes the freshly processed frame array and runs it through a dummy + * DMA transfer. This is required to sniff the CRC32. * */ -void dmd_set_and_enable_new_dma_sniffer() { +void dmd_prepare_dma_sniffer() { dma_channel_transfer_from_buffer_now(dma_sniff_channel, current_framebuf, loopback ? source_bytes : target_bytes); - dma_channel_wait_for_finish_blocking(dma_sniff_channel); // waiting is required. + dma_channel_wait_for_finish_blocking( + dma_sniff_channel); // waiting is required. } /** @@ -902,10 +904,10 @@ void dmd_dma_handler() { memcpy(current_framebuf, processingbuf, loopback ? source_bytes : target_bytes); - dmd_set_and_enable_new_dma_sniffer(); + dmd_prepare_dma_sniffer(); frame_crc = dma_hw->sniff_data; - dma_hw->sniff_data = 0xFFFFFFFF; // always clean after sniffing. + dma_hw->sniff_data = 0xFFFFFFFF; // always clean after sniffing! switch_buffers(); @@ -1535,10 +1537,10 @@ bool dmdreader_init(bool return_on_no_detection) { dma_channel_configure( dma_sniff_channel, &dma_sniff_channel_cfg, - dummy_sniff_dst, // The (unchanging) dummy write address + dummy_sniff_dst, // Destination pointer NULL, // Source pointer is set during dmd_dma_handler 0, // We do not know the transfer count yet - false // Do not yet start! + false // Do not yet start ); // Finally start DMD reader PIO program and DMA From 08f78095e33d094d5f9cba7ca8be17fc39deece8 Mon Sep 17 00:00:00 2001 From: Jan Vos Date: Wed, 12 Aug 2026 00:03:23 +0200 Subject: [PATCH 6/6] more readme additions --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0bf2f60d..cc6073ca 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ -# Read and display pinball DMD data +# Read pinball DMD data This project allows to read the contents of a pinball DMD using the Raspberry Pi Pico. Both the RP2040 and RP2350 are supported. -## Hardware +## Pico hardware setup -The Pico is directly connected to the DMD data lines. Communication between the Pico and the consumer is implemented via SPI with an additional IRQ line. -On this IRQ line, the Pico signals that new data is available and consumer must start the data transfer whenever possible. -Since most consumers (especially the Raspberry Pi) are not real-time, this method is used. -Because of this, the consumer has full flexibility of setting an SPI CLK frequency. The Pico in this case is real-time, and will be able to keep up easily. +The Pico is directly connected to all DMD data lines. Communication between the Pico and the consumer is implemented via SPI with an additional IRQ line. On this IRQ line, the Pico signals that a new frame is available, notifying the consumer to start the data transfer as soon as possible. The consumer acts as the SPI master, so it controls the clock frequency used to retrieve the data. -## Officially supported and tested hardware systems +This architecture accommodates consumers (especially Linux hosts like the Raspberry Pi) that are not real-time systems. + +## Officially supported pinball hardware systems * WPC95 & WPC -> 128x32 * Data East -> 128x32 & 128x16 @@ -37,5 +36,9 @@ The process is as follows: ## License -This project was once forked from https://github.com/pinballpower/code_dmd, but has undergone many changes and additions. -Since the original project changed its license from MIT to GPL v3 on 2022-05-02, the license of this fork is now also GPL v3. \ No newline at end of file +This project has been forked from https://github.com/pinballpower/code_dmd +Since the original project changed its license from MIT to GPL v3 on 2022-05-02, the license of this fork is now also GPL v3. + +## Project credits + +DMDreader would not exist without the foundational work done by [Daniel Matuschek](https://github.com/pinballpower). Since then, the project has grown significantly due to extensive contributions from [Markus Kalkbrenner](https://github.com/mkalkbrenner) and [Jan Vos](https://github.com/pastorl69), who have delivered numerous bug fixes, performance optimizations, and support for additional manufacturers. \ No newline at end of file