PD4 display test: cycle diagnostic patterns, not just all-on
The real display's power-on test (JP1 jumper 5, firmware $B888) runs through several drawing sequences — a solid fill, a border+grid, more — each held briefly, to expose dead dots and addressing faults; it isn't just full on/off. Both vPLASMA and the replica firmware now cycle 5 representative patterns: solid, border+grid, horizontal stripes, vertical stripes, checkerboard. - VPlasmaDevice.ShowTestPattern(int) + TestPatternCount; the app cycles them on a 1.2s timer while jumper 5 is installed. - PlasmaDisplay::showTestPattern(int) + TEST_PATTERN_COUNT (shared logic); the Matrix Portal sketch's DOWN button toggles the cycling test. (The firmware's exact byte patterns live in the display's native scan-buffer layout, which we haven't mapped, so these stand in for that sequence rather than reproducing it byte-for-byte.) Verified: 29 C# tests pass; the C++ patterns render distinctly on the host. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,12 @@ static int demoScreen = 0;
|
||||
static uint32_t lastDemoStep = 0;
|
||||
static const uint32_t DEMO_MS = 2800;
|
||||
|
||||
// Panel test (DOWN button): cycle the diagnostic patterns.
|
||||
static bool testRunning = false;
|
||||
static int testPattern = 0;
|
||||
static uint32_t lastTestStep = 0;
|
||||
static const uint32_t TEST_MS = 1200;
|
||||
|
||||
// ---- helpers -------------------------------------------------------------
|
||||
|
||||
static bool frameHasFlash = false;
|
||||
@@ -120,6 +126,7 @@ void loop() {
|
||||
bool up = digitalRead(BUTTON_UP);
|
||||
if (upPrev == HIGH && up == LOW) { // pressed
|
||||
demoRunning = !demoRunning;
|
||||
testRunning = false;
|
||||
if (demoRunning) { demoScreen = 0; feedDemoScreen(0); demoScreen = 1; lastDemoStep = millis(); }
|
||||
else { display.reset(); }
|
||||
}
|
||||
@@ -129,19 +136,26 @@ void loop() {
|
||||
static bool downPrev = HIGH;
|
||||
bool down = digitalRead(BUTTON_DOWN);
|
||||
if (downPrev == HIGH && down == LOW) { // pressed
|
||||
testRunning = !testRunning;
|
||||
demoRunning = false;
|
||||
display.showTestPattern();
|
||||
if (testRunning) { testPattern = 0; display.showTestPattern(0); lastTestStep = millis(); }
|
||||
else { display.reset(); }
|
||||
}
|
||||
downPrev = down;
|
||||
#endif
|
||||
|
||||
// 3) Advance the demo on its timer.
|
||||
// 3) Advance the demo / test on their timers.
|
||||
uint32_t now = millis();
|
||||
if (demoRunning && now - lastDemoStep >= DEMO_MS) {
|
||||
feedDemoScreen(demoScreen);
|
||||
demoScreen = (demoScreen + 1) % plasmaDemoCount;
|
||||
lastDemoStep = now;
|
||||
}
|
||||
if (testRunning && now - lastTestStep >= TEST_MS) {
|
||||
testPattern = (testPattern + 1) % PlasmaDisplay::TEST_PATTERN_COUNT;
|
||||
display.showTestPattern(testPattern);
|
||||
lastTestStep = now;
|
||||
}
|
||||
|
||||
// 4) Blink phase for flashing pixels.
|
||||
if (now - lastBlink >= BLINK_MS) {
|
||||
|
||||
@@ -32,8 +32,25 @@ void PlasmaDisplay::setOrientation(Orientation o) {
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
void PlasmaDisplay::showTestPattern() {
|
||||
for (int i = 0; i < WIDTH * HEIGHT; ++i) pixels_[i] = PIX_LIT;
|
||||
// The real display's power-on test (JP1 jumper 5, firmware $B888) runs through
|
||||
// several drawing sequences — solid, border+grid, and more — to expose dead
|
||||
// dots and addressing faults. These stand in for that; the sketch cycles them.
|
||||
static bool testDot(int index, int x, int y) {
|
||||
switch (index) {
|
||||
case 0: return true; // all on
|
||||
case 1: return x == 0 || x == PlasmaDisplay::WIDTH - 1 || // frame
|
||||
y == 0 || y == PlasmaDisplay::HEIGHT - 1 ||
|
||||
(x % 16) == 0 || (y % 8) == 0; // + grid
|
||||
case 2: return ((y / 2) % 2) == 0; // horizontal stripes
|
||||
case 3: return ((x / 4) % 2) == 0; // vertical stripes
|
||||
default: return (((x / 8) + (y / 8)) % 2) == 0; // checkerboard
|
||||
}
|
||||
}
|
||||
|
||||
void PlasmaDisplay::showTestPattern(int index) {
|
||||
for (int y = 0; y < HEIGHT; ++y)
|
||||
for (int x = 0; x < WIDTH; ++x)
|
||||
pixels_[y * WIDTH + x] = testDot(index, x, y) ? PIX_LIT : 0;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,13 @@ class PlasmaDisplay {
|
||||
|
||||
PlasmaDisplay();
|
||||
|
||||
static const int TEST_PATTERN_COUNT = 5; // JP1 jumper-5 diagnostic patterns
|
||||
|
||||
void reset(); // power-on: dark glass, home cursor
|
||||
void feed(uint8_t b); // one received wire byte
|
||||
void feed(const uint8_t *buf, size_t n);
|
||||
void showTestPattern(); // all dots lit (JP1 jumper 5)
|
||||
void showTestPattern() { showTestPattern(0); } // all dots lit
|
||||
void showTestPattern(int index); // JP1 jumper 5: solid/grid/stripes/checker
|
||||
void setOrientation(Orientation o); // JP1 jumper 4
|
||||
|
||||
const uint8_t *frame() const { return pixels_; }
|
||||
|
||||
@@ -66,7 +66,9 @@ display listens), exactly as the cockpit drove the real panel.
|
||||
|
||||
- **UP** — toggle the built-in **firmware demonstration** (the real 10-screen
|
||||
PLASMADOT demo, `demo_screens.h`, extracted from the ROM).
|
||||
- **DOWN** — the **panel test pattern** (all dots lit; a dead-dot check).
|
||||
- **DOWN** — toggle the **panel test**: cycles diagnostic patterns (solid,
|
||||
border+grid, horizontal/vertical stripes, checkerboard) to expose dead dots
|
||||
and addressing faults — the multi-pattern sequence the real firmware runs.
|
||||
- **Power-on** briefly lights every dot (confirms both panels), then clears.
|
||||
|
||||
## Files
|
||||
|
||||
@@ -30,8 +30,10 @@ internal sealed class MainForm : Form
|
||||
|
||||
private readonly System.Windows.Forms.Timer _uiTimer = new() { Interval = 500 };
|
||||
private readonly System.Windows.Forms.Timer _demoTimer = new() { Interval = 2800 };
|
||||
private readonly System.Windows.Forms.Timer _testTimer = new() { Interval = 1200 };
|
||||
private int _selfTestPage;
|
||||
private int _demoScreen;
|
||||
private int _testPattern;
|
||||
|
||||
// ---- config panel controls -------------------------------------------
|
||||
private readonly ComboBox _portBox = new() { Width = 168, DropDownStyle = ComboBoxStyle.DropDownList };
|
||||
@@ -118,6 +120,7 @@ internal sealed class MainForm : Form
|
||||
_jp5.CheckedChanged += (_, _) => ApplyTestPattern();
|
||||
_jp6.CheckedChanged += (_, _) => ApplyDemo();
|
||||
_demoTimer.Tick += (_, _) => StepDemo();
|
||||
_testTimer.Tick += (_, _) => StepTestPattern();
|
||||
|
||||
// Default straps: jumper 2 installed = 9600 (the game's rate). Jumper 4
|
||||
// unstrapped = horizontal (the normal cockpit orientation).
|
||||
@@ -130,6 +133,7 @@ internal sealed class MainForm : Form
|
||||
{
|
||||
_uiTimer.Dispose();
|
||||
_demoTimer.Dispose();
|
||||
_testTimer.Dispose();
|
||||
_service.Dispose();
|
||||
_pipeService.Dispose();
|
||||
};
|
||||
@@ -288,16 +292,25 @@ internal sealed class MainForm : Form
|
||||
{
|
||||
if (_jp5.Checked)
|
||||
{
|
||||
_device.ShowTestPattern();
|
||||
PrependLog("Jumper 5 installed — panel test pattern (all dots lit)");
|
||||
PrependLog("Jumper 5 installed — panel test (cycling diagnostic patterns)");
|
||||
_testPattern = 0;
|
||||
_device.ShowTestPattern(0);
|
||||
_testTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
_testTimer.Stop();
|
||||
_device.Reset();
|
||||
PrependLog("Jumper 5 removed — test pattern cleared");
|
||||
}
|
||||
}
|
||||
|
||||
private void StepTestPattern()
|
||||
{
|
||||
_testPattern = (_testPattern + 1) % VPlasmaDevice.TestPatternCount;
|
||||
_device.ShowTestPattern(_testPattern);
|
||||
}
|
||||
|
||||
private void ApplyDemo()
|
||||
{
|
||||
if (_jp6.Checked)
|
||||
|
||||
@@ -197,21 +197,41 @@ public sealed class VPlasmaDevice
|
||||
FlushEvents();
|
||||
}
|
||||
|
||||
/// <summary>Number of diagnostic patterns the JP1 jumper-5 test cycles.</summary>
|
||||
public const int TestPatternCount = 5;
|
||||
|
||||
/// <summary>Show the first test pattern (all dots lit).</summary>
|
||||
public void ShowTestPattern() => ShowTestPattern(0);
|
||||
|
||||
/// <summary>
|
||||
/// Light every dot — the panel test pattern JP1 jumper 5 runs at power-on
|
||||
/// (the firmware's dead-dot check). Clear it with <see cref="Reset"/>.
|
||||
/// Paint diagnostic pattern <paramref name="index"/> (0..<see
|
||||
/// cref="TestPatternCount"/>-1). The real display's power-on test (JP1
|
||||
/// jumper 5, firmware <c>$B888</c>) runs through several drawing
|
||||
/// sequences — a solid fill, a border+grid, and more, each held briefly —
|
||||
/// to expose dead dots and addressing faults; these stand in for that.
|
||||
/// </summary>
|
||||
public void ShowTestPattern()
|
||||
public void ShowTestPattern(int index)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
for (int i = 0; i < _pixels.Length; ++i)
|
||||
_pixels[i] = PixelLit;
|
||||
for (int y = 0; y < Height; ++y)
|
||||
for (int x = 0; x < Width; ++x)
|
||||
_pixels[y * Width + x] = TestDot(index, x, y) ? PixelLit : (byte)0;
|
||||
_dirty = true;
|
||||
}
|
||||
FlushEvents();
|
||||
}
|
||||
|
||||
private static bool TestDot(int index, int x, int y) => index switch
|
||||
{
|
||||
0 => true, // all on
|
||||
1 => x == 0 || x == Width - 1 || y == 0 || y == Height - 1 // frame
|
||||
|| x % 16 == 0 || y % 8 == 0, // + grid
|
||||
2 => (y / 2) % 2 == 0, // horizontal stripes
|
||||
3 => (x / 4) % 2 == 0, // vertical stripes
|
||||
_ => ((x / 8) + (y / 8)) % 2 == 0, // checkerboard
|
||||
};
|
||||
|
||||
/// <summary>Feed <paramref name="count"/> received wire bytes.</summary>
|
||||
public void OnReceived(byte[] buffer, int count)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user