fix(sim): SSD1306 page-addressing mode (Tiny4kOLED/U8g2 OLED garbled)
SSD1306Core only handled horizontal/vertical addressing (0x20/0x21/0x22) and defaulted memMode to horizontal. Page-mode drivers (Tiny4kOLED on ATtiny85, U8g2 page buffer, classic SSD1306 libs) position the cursor with the single-byte commands 0xB0-0xB7 (page) and 0x00-0x0F / 0x10-0x1F (column nibbles) and rely on the SSD1306 power-on default of PAGE addressing — they never send 0x20. velxio ignored those cursor commands and advanced in horizontal mode, so every setCursor was a no-op and the hatching/border/text piled onto wrong rows -> garbled display. Fix: default memMode=2 (datasheet power-on) and handle the page/column-set commands. Adafruit_SSD1306 still works (it sends 0x20,0x00 + 0x21/0x22 explicitly). Verified: decoded the real ATTinyCore Tiny4kOLED I2C stream renders a clean border + '128x64'. Adds a page-addressing render test.
This commit is contained in:
parent
37b3fac978
commit
8c32b16589
|
|
@ -169,6 +169,36 @@ describe('SSD1306 — ImageData rendering (syncElement fix)', () => {
|
|||
expect(px[idxOff + 2]).toBe(0);
|
||||
});
|
||||
|
||||
it('page addressing (Tiny4kOLED): 0xB0+page / 0x00-0x1F col, no 0x20, cursor persists across data streams', () => {
|
||||
const el = makeOLEDElement();
|
||||
const sim = makeSim();
|
||||
PartSimulationRegistry.get('ssd1306')!.attachEvents!(el, sim as any, () => null);
|
||||
const device = sim._devices[0];
|
||||
|
||||
// Page-addressing setCursor: page 1, column 8 (col high nibble = 0x10,
|
||||
// col low nibble = 0x08). No 0x20 — relies on the power-on page-mode
|
||||
// default that Tiny4kOLED / U8g2-page / classic SSD1306 drivers assume.
|
||||
sendCommandStream(device, [0xb1, 0x10, 0x08]);
|
||||
// TinyWireM flushes its small buffer as distinct 16-byte I2C transactions,
|
||||
// so the column pointer MUST persist across separate data streams.
|
||||
sendDataStream(device, [0xff, 0x00]); // col 8, 9
|
||||
sendDataStream(device, [0x00, 0xff]); // col 10, 11 — cursor continued
|
||||
|
||||
const px = el.imageData.data;
|
||||
const lit = (row: number, col: number) => {
|
||||
const i = (row * 128 + col) * 4;
|
||||
return px[i] + px[i + 1] + px[i + 2] > 0;
|
||||
};
|
||||
// page 1 → rows 8..15; 0xff lights the whole 8-pixel column.
|
||||
expect(lit(8, 8)).toBe(true);
|
||||
expect(lit(15, 8)).toBe(true);
|
||||
expect(lit(8, 9)).toBe(false); // 0x00
|
||||
expect(lit(8, 10)).toBe(false); // 0x00 (start of 2nd stream)
|
||||
// col 11 lit proves the cursor advanced across the STOP/new transaction.
|
||||
expect(lit(8, 11)).toBe(true);
|
||||
expect(lit(15, 11)).toBe(true);
|
||||
});
|
||||
|
||||
it('fills all 1024 GDDRAM bytes via horizontal addressing', () => {
|
||||
const el = makeOLEDElement();
|
||||
const sim = makeSim();
|
||||
|
|
|
|||
|
|
@ -64,7 +64,12 @@ class SSD1306Core {
|
|||
private colEnd = 127;
|
||||
private pageStart = 0;
|
||||
private pageEnd = 7;
|
||||
private memMode = 0; // 0=horizontal, 1=vertical, 2=page
|
||||
// 0=horizontal, 1=vertical, 2=page. SSD1306 power-on default is PAGE
|
||||
// addressing (datasheet 10b). Adafruit_SSD1306 overrides it to horizontal
|
||||
// via 0x20,0x00; page-mode drivers (Tiny4kOLED, U8g2 page buffer) rely on
|
||||
// this default and never send 0x20 — so the default MUST be 2 or their
|
||||
// setCursor (0xB0-0xB7 + 0x00-0x1F) renders garbled.
|
||||
private memMode = 2;
|
||||
|
||||
// Multi-byte command accumulation
|
||||
private cmdBuf: number[] = [];
|
||||
|
|
@ -125,7 +130,19 @@ class SSD1306Core {
|
|||
this.page = this.pageStart;
|
||||
break;
|
||||
default:
|
||||
if (cmd >= 0x40 && cmd <= 0x7f) {
|
||||
// Page-addressing-mode cursor commands (single-byte). Used by
|
||||
// Tiny4kOLED / U8g2 page buffer / classic SSD1306 drivers whose
|
||||
// setCursor() does NOT use the 0x21/0x22 column/page-range commands.
|
||||
if (cmd >= 0xb0 && cmd <= 0xb7) {
|
||||
// set page start address (B0..B7 → page 0..7)
|
||||
this.page = cmd & 0x07;
|
||||
} else if (cmd <= 0x0f) {
|
||||
// set lower column nibble (0x00..0x0F)
|
||||
this.col = (this.col & 0xf0) | (cmd & 0x0f);
|
||||
} else if (cmd >= 0x10 && cmd <= 0x1f) {
|
||||
// set higher column nibble (0x10..0x1F)
|
||||
this.col = (this.col & 0x0f) | ((cmd & 0x0f) << 4);
|
||||
} else if (cmd >= 0x40 && cmd <= 0x7f) {
|
||||
/* display start line — visual, skip */
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue