fix(epaper): wrap RAM Y counter at window end (tri-colour red plane)
The 2.9" tri-colour ESP32 alert badge rendered the red ALERT pill as white: the red plane (0x26) was received but landed out of bounds and was dropped. GxEPD2_3C writes the 0x24 (black) plane then the 0x26 (red) plane WITHOUT re-seeking the RAM address counter between them — it relies on the SSD168x counter wrapping back to the window start after the last byte of the window. Our decoder advanced Y past the window end instead of wrapping, so every 0x26 byte hit y >= rows and was discarded (red_ram stayed all-init). Mirror the hardware: when the X cursor wraps at the end of a row, advance Y with a wrap at the active window boundary (yrange), honouring the data-entry Y direction. Applied identically to the worker slave, the browser decoder, and the Python golden reference so the three stay in lockstep. No regression on the mono panels (their counter is re-seeked per plane, so the wrap is a no-op for them); verified the tri-colour pill now renders red and the 2.9" weather / 2.13" clock / 1.54" hello panels are unchanged.
This commit is contained in:
parent
9ba8687743
commit
3bb6f95a67
|
|
@ -301,18 +301,29 @@ class Ssd168xEpaperSlave:
|
|||
if 0 <= self._x_byte < self._ram_bpr and 0 <= self._y < self._ram_rows:
|
||||
plane[self._y * self._ram_bpr + self._x_byte] = byte
|
||||
x_inc = (self._entry_mode & 0x01) == 0x01
|
||||
y_inc = (self._entry_mode & 0x02) == 0x02
|
||||
end_of_row = False
|
||||
if x_inc:
|
||||
if self._x_byte < self._xrange[1]:
|
||||
self._x_byte += 1
|
||||
else:
|
||||
self._x_byte = self._xrange[0]
|
||||
self._y += 1
|
||||
end_of_row = True
|
||||
else:
|
||||
if self._x_byte > self._xrange[0]:
|
||||
self._x_byte -= 1
|
||||
else:
|
||||
self._x_byte = self._xrange[1]
|
||||
self._y += 1
|
||||
end_of_row = True
|
||||
if end_of_row:
|
||||
# Advance Y, WRAPPING at the window boundary like the SSD168x RAM
|
||||
# address counter. Some drivers (e.g. GxEPD2_3C) write the 0x24 then
|
||||
# the 0x26 plane without re-seeking the counter, relying on this
|
||||
# wrap so the second plane lands in the window.
|
||||
if y_inc:
|
||||
self._y = self._yrange[0] if self._y >= self._yrange[1] else self._y + 1
|
||||
else:
|
||||
self._y = self._yrange[1] if self._y <= self._yrange[0] else self._y - 1
|
||||
|
||||
|
||||
# ── UC8159c (ACeP 7-colour 5.65" GoodDisplay GDEP0565D90) ───────────────────
|
||||
|
|
|
|||
|
|
@ -374,19 +374,32 @@ export class SSD168xDecoder {
|
|||
}
|
||||
// Auto-increment per data_entry_mode (default 0x03: X+, then Y+ at end of row).
|
||||
const xInc = (this.entryMode & 0x01) === 0x01;
|
||||
const yInc = (this.entryMode & 0x02) === 0x02;
|
||||
let endOfRow = false;
|
||||
if (xInc) {
|
||||
if (this.xByte < this.xrange[1]) {
|
||||
this.xByte += 1;
|
||||
} else {
|
||||
this.xByte = this.xrange[0];
|
||||
this.y += 1;
|
||||
endOfRow = true;
|
||||
}
|
||||
} else {
|
||||
if (this.xByte > this.xrange[0]) {
|
||||
this.xByte -= 1;
|
||||
} else {
|
||||
this.xByte = this.xrange[1];
|
||||
this.y += 1;
|
||||
endOfRow = true;
|
||||
}
|
||||
}
|
||||
if (endOfRow) {
|
||||
// Advance Y, WRAPPING at the window boundary like the SSD168x RAM address
|
||||
// counter. Some drivers (e.g. GxEPD2_3C) write the 0x24 then the 0x26
|
||||
// plane without re-seeking the counter, relying on this wrap so the
|
||||
// second plane lands in the window.
|
||||
if (yInc) {
|
||||
this.y = this.y >= this.yrange[1] ? this.yrange[0] : this.y + 1;
|
||||
} else {
|
||||
this.y = this.y <= this.yrange[0] ? this.yrange[1] : this.y - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,17 +312,26 @@ class SSD168xDecoder:
|
|||
plane[self._y * self._ram_bpr + self._x_byte] = byte
|
||||
# Auto-increment per data_entry_mode (default x+, then y+ at end of row).
|
||||
x_inc = (self._entry_mode & 0x01) == 0x01 # bit0: 1 = X+
|
||||
# entry_mode bit1: Y direction; bit2: which counter advances first.
|
||||
# For the default 0x03, X advances; once it hits xrange[1], wrap and Y++.
|
||||
y_inc = (self._entry_mode & 0x02) == 0x02 # bit1: 1 = Y+
|
||||
end_of_row = False
|
||||
if x_inc:
|
||||
if self._x_byte < self._xrange[1]:
|
||||
self._x_byte += 1
|
||||
else:
|
||||
self._x_byte = self._xrange[0]
|
||||
self._y += 1
|
||||
end_of_row = True
|
||||
else:
|
||||
if self._x_byte > self._xrange[0]:
|
||||
self._x_byte -= 1
|
||||
else:
|
||||
self._x_byte = self._xrange[1]
|
||||
self._y += 1
|
||||
end_of_row = True
|
||||
if end_of_row:
|
||||
# Advance Y, WRAPPING at the window boundary like the SSD168x RAM
|
||||
# address counter. Some drivers (e.g. GxEPD2_3C) write the 0x24 then
|
||||
# the 0x26 plane without re-seeking the counter, relying on this wrap
|
||||
# so the second plane lands in the window.
|
||||
if y_inc:
|
||||
self._y = self._yrange[0] if self._y >= self._yrange[1] else self._y + 1
|
||||
else:
|
||||
self._y = self._yrange[1] if self._y <= self._yrange[0] else self._y - 1
|
||||
|
|
|
|||
Loading…
Reference in New Issue