mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
oled tweak
This commit is contained in:
@@ -4958,6 +4958,15 @@
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-10-11T00:00:00.0000000</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>0.96" SSD1306 SPI (8-Pin)</name>
|
||||
<values>
|
||||
<entry><string>Size</string><diylc.displays.OLEDDisplay_-Version>SSD1306_0_96</diylc.displays.OLEDDisplay_-Version></entry>
|
||||
<entry><string>Interface</string><diylc.displays.OLEDDisplay_-OLEDInterface>SPI_8Pin</diylc.displays.OLEDDisplay_-OLEDInterface></entry>
|
||||
</values>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-10-11T00:00:00.0000000</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>0.91" SSD1306 I2C</name>
|
||||
<values>
|
||||
@@ -4985,6 +4994,15 @@
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-10-11T00:00:00.0000000</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>0.91" SSD1306 SPI (8-Pin)</name>
|
||||
<values>
|
||||
<entry><string>Size</string><diylc.displays.OLEDDisplay_-Version>SSD1306_0_91</diylc.displays.OLEDDisplay_-Version></entry>
|
||||
<entry><string>Interface</string><diylc.displays.OLEDDisplay_-OLEDInterface>SPI_8Pin</diylc.displays.OLEDDisplay_-OLEDInterface></entry>
|
||||
</values>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-10-11T00:00:00.0000000</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>1.3" SH1106 I2C</name>
|
||||
<values>
|
||||
@@ -5012,6 +5030,15 @@
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-10-11T00:00:00.0000000</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
<org.diylc.core.Template>
|
||||
<name>1.3" SH1106 SPI (8-Pin)</name>
|
||||
<values>
|
||||
<entry><string>Size</string><diylc.displays.OLEDDisplay_-Version>SH1106_1_3</diylc.displays.OLEDDisplay_-Version></entry>
|
||||
<entry><string>Interface</string><diylc.displays.OLEDDisplay_-OLEDInterface>SPI_8Pin</diylc.displays.OLEDDisplay_-OLEDInterface></entry>
|
||||
</values>
|
||||
<defaultFlag>false</defaultFlag>
|
||||
<createdOn>2026-10-11T00:00:00.0000000</createdOn>
|
||||
</org.diylc.core.Template>
|
||||
</list>
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
@@ -24,6 +24,7 @@ package org.diylc.components.displays;
|
||||
import java.awt.Color;
|
||||
import java.awt.Composite;
|
||||
import java.awt.Font;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.AffineTransform;
|
||||
@@ -67,6 +68,12 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
public static Color ACTIVE_AREA_COLOR = Color.decode("#060D15");
|
||||
public static Color PIXEL_BLUE = Color.decode("#00D4FF");
|
||||
|
||||
// Clearance a name keeps from the edge of its pad. The shared drawFlatRowPinLabels sits its
|
||||
// labels 2.04 mm off the pin centre, which the 3.5 mm strip the Nokia and the TFT print into can
|
||||
// afford; the 0.96" has 2.27 mm between header and glass and the 0.91" SPI board 2.5 mm, where
|
||||
// that offset drops the text onto the display.
|
||||
public static Size LABEL_GAP = new Size(0.5d, SizeUnit.mm);
|
||||
|
||||
public static final String[] PIN_NAMES_I2C = new String[] {"GND", "VCC", "SCL", "SDA"};
|
||||
|
||||
/**
|
||||
@@ -89,6 +96,17 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
public static final String[] PIN_NAMES_SPI_NO_CS =
|
||||
new String[] {"GND", "VCC", "SCL", "SDA", "RES", "DC"};
|
||||
|
||||
/**
|
||||
* The eight-pin board, which is the only one of these to bring the controller's logic rail out
|
||||
* beside the module's input: VIN takes 4.8 - 5.2 V and VDD the 2.8 - 3.3 V the core runs on.
|
||||
* They are separate rails and must not be tied together, so the extra pad is a different board
|
||||
* rather than a different silkscreen -- as is the order, which follows the controller's own pin
|
||||
* numbering and so starts at the data line and leaves the supplies to the far end, the reverse
|
||||
* of where the other boards print them.
|
||||
*/
|
||||
public static final String[] PIN_NAMES_SPI_SPLIT_SUPPLY =
|
||||
new String[] {"SDA", "SCL", "DC", "RES", "CS", "VDD", "VIN", "GND"};
|
||||
|
||||
private Version version = Version.SSD1306_0_96;
|
||||
private OLEDInterface oledInterface = OLEDInterface.I2C_4Pin;
|
||||
private Display screen = Display.VALUE;
|
||||
@@ -161,9 +179,9 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
/**
|
||||
* The board the two properties name between them. On the 0.96" and the 1.3" the interface only
|
||||
* changes how many pins the header carries, but the 0.91" is sold as two different boards -- a
|
||||
* 38 x 12 mm one with a four-pin column on its left edge and a 38 x 20 mm one with a seven-pin
|
||||
* row on top -- so the outline, the header edge, the hole pattern and the panel's position all
|
||||
* come from the pair rather than from the size alone.
|
||||
* 35.8 x 12 mm one with a four-pin column on its left edge and a 32.5 x 20.5 mm one with a
|
||||
* seven-pin row on top -- so the outline, the header edge, the hole pattern and the panel's
|
||||
* position all come from the pair rather than from the size alone.
|
||||
*/
|
||||
private Layout getLayout() {
|
||||
return getVersion().getLayout(getOledInterface());
|
||||
@@ -238,6 +256,66 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
px(layout.getBoardWidthMm()), px(layout.getBoardLengthMm()), 8, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* The names the module prints in the strip between its header and its glass. They lie flat and
|
||||
* read the way the board does, as the Nokia's and the TFT's do, rather than standing on end
|
||||
* across the row the way the Arduino boards print theirs -- no strip here is deep enough for
|
||||
* that. The 0.91" I2C board is the one whose header is a column, so its names lie to the right
|
||||
* of the pins instead of below them, and a name that would reach the glass is left off.
|
||||
*/
|
||||
private void drawPinNames(Graphics2D g2d, double boardX, double boardY) {
|
||||
Layout layout = getLayout();
|
||||
double[][] offsets = getRelativeOffsets();
|
||||
Point2D p0 = controlPoints[0];
|
||||
double gap = LABEL_GAP.convertToPixels();
|
||||
|
||||
// What a label has to clear is the header body drawPinHeader fills, a square reaching one
|
||||
// pixel further than PIN_SIZE either side of the pin rather than the pin itself; measured off
|
||||
// the pin alone the names would be printed onto the pads.
|
||||
double padHalf = Math.round(PIN_SIZE.convertToPixels()) + 1;
|
||||
|
||||
g2d.setFont(PIN_ROW_FLAT_FONT);
|
||||
g2d.setColor(SILK_COLOR);
|
||||
FontMetrics metrics = g2d.getFontMetrics();
|
||||
|
||||
// One reference glyph rather than each label's own box, so a row of them sits on one line.
|
||||
// Every name on these boards is upper case and none carries a descender, so a box centred on
|
||||
// the cap height lands each of them in the same place.
|
||||
double capHeight = g2d.getFont().createGlyphVector(g2d.getFontRenderContext(), "X")
|
||||
.getVisualBounds().getHeight();
|
||||
|
||||
boolean column = layout.isHeaderOnLeftEdge();
|
||||
double glassEdge = column ? boardX + px(layout.getPanelLeftMm())
|
||||
: boardY + px(layout.getPanelTopMm());
|
||||
|
||||
for (int i = 0; i < offsets.length; i++) {
|
||||
String label = getSilkPinLabel(i);
|
||||
if (label == null || label.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
double pinX = p0.getX() + offsets[i][0];
|
||||
double pinY = p0.getY() + offsets[i][1];
|
||||
double room = glassEdge - (column ? pinX : pinY) - padHalf;
|
||||
double extent = column ? metrics.stringWidth(label) : capHeight;
|
||||
if (extent > room) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A gap off the pad, or the middle of the strip where the strip cannot afford the whole gap:
|
||||
// the 0.96" leaves 1.13 mm between its pads and its glass to print 0.73 mm of lettering in,
|
||||
// and splitting what is left over is the only placement that clears both.
|
||||
double offset = padHalf + Math.min(gap, (room - extent) / 2.0);
|
||||
|
||||
if (column) {
|
||||
StringUtils.drawCenteredText(g2d, label, pinX + offset, pinY, HorizontalAlignment.LEFT,
|
||||
VerticalAlignment.CENTER);
|
||||
} else {
|
||||
StringUtils.drawCenteredText(g2d, label, pinX, pinY + offset + capHeight / 2.0,
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Graphics2D g2d, ComponentState componentState, boolean outlineMode, Project project,
|
||||
IDrawingObserver drawingObserver) {
|
||||
@@ -299,18 +377,21 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
g2d.draw(new RoundRectangle2D.Double(glassX, glassY, glassW, glassH, 4, 4));
|
||||
|
||||
// The lit area is offset within the panel horizontally -- the driver's bonding region takes
|
||||
// one end of the glass -- and centred across it. Both are figures of the panel part rather
|
||||
// than of the board it is mounted on, so the two 0.91" boards share them.
|
||||
// one end of the glass -- and sits high in it vertically, the bonding region running along
|
||||
// the bottom on every one of these panels. Following the TFT boards, its top is read from
|
||||
// the board's top edge, which is the edge it is measured from on a module in hand.
|
||||
double activeW = px(version.getActiveWidthMm());
|
||||
double activeH = px(version.getActiveLengthMm());
|
||||
double activeX = glassX + px(version.getActiveLeftInPanelMm());
|
||||
double activeY = glassY + (glassH - activeH) / 2.0;
|
||||
double activeY = boardY + px(layout.getActiveTopMm());
|
||||
g2d.setColor(ACTIVE_AREA_COLOR);
|
||||
g2d.fill(new RoundRectangle2D.Double(activeX, activeY, activeW, activeH, 2, 2));
|
||||
|
||||
MakerBoardPainter.drawScreenText(g2d,
|
||||
new Rectangle2D.Double(activeX, activeY, activeW, activeH), getPixelColor(), getScreen(),
|
||||
getName(), getValueForDisplay());
|
||||
|
||||
drawPinNames(g2d, boardX, boardY);
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
@@ -343,14 +424,18 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
}
|
||||
|
||||
public enum OLEDInterface {
|
||||
// Labels stay short because they reach the BOM's value column through getVariantLabel and are
|
||||
// printed on the lit area, which the 0.91" has little of: "SPI, 7-pin" and "SPI, 6-pin" were
|
||||
// tried and the glass stopped printing anything at all. The pin array belongs here rather than
|
||||
// on the board, because all three sizes bring the same lines out in the same order and only the
|
||||
// interface decides which set that is.
|
||||
// Every SPI label states its pin count, which is the only thing that separates the three of
|
||||
// them; a bare "SPI" beside "SPI-6" would read as an omission on a BOM row and the reader
|
||||
// would have to know which board it meant. Labels stay short because they reach the BOM's
|
||||
// value column through getVariantLabel and are printed on the lit area, which the 0.91" has
|
||||
// little of: "SPI, 7-pin" and "SPI, 6-pin" were tried and the glass stopped printing anything
|
||||
// at all. The pin array belongs here rather than
|
||||
// on the board, because a row is the same at all three sizes and only the interface decides
|
||||
// which row that is.
|
||||
I2C_4Pin("I2C", PIN_NAMES_I2C),
|
||||
SPI_7Pin("SPI", PIN_NAMES_SPI),
|
||||
SPI_6Pin("SPI-6", PIN_NAMES_SPI_NO_CS);
|
||||
SPI_6Pin("SPI-6", PIN_NAMES_SPI_NO_CS),
|
||||
SPI_7Pin("SPI-7", PIN_NAMES_SPI),
|
||||
SPI_8Pin("SPI-8", PIN_NAMES_SPI_SPLIT_SUPPLY);
|
||||
|
||||
private final String label;
|
||||
private final String[] pinNames;
|
||||
@@ -365,22 +450,25 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
}
|
||||
|
||||
public enum Version {
|
||||
// The panel and its lit area belong to the display part, so they are carried here and shared
|
||||
// by both of a size's boards; everything that belongs to a PCB is on the Layout beside them.
|
||||
// Lit areas are the nominal pixel count at the panel's dot pitch -- 128 x 64 at 0.17 mm,
|
||||
// 128 x 32 at 0.175 mm, 128 x 64 at 0.23 mm -- which makes them derivations rather than
|
||||
// The panel and its lit area belong to the display part, so their sizes are carried here and
|
||||
// shared by both of a size's boards; everything that belongs to a PCB is on the Layout beside
|
||||
// them, and so is where the lit area sits down the board, which the two 0.91" boards disagree
|
||||
// on.
|
||||
// The 0.96"'s and the 1.3"'s panels are measured off a module, and so is the 0.96"'s lit
|
||||
// area; the other lit areas are the nominal pixel count at the panel's dot pitch -- 128 x 32
|
||||
// at 0.175 mm and 128 x 64 at 0.23 mm -- which makes those derivations rather than
|
||||
// measurements, noted so they are not built upon.
|
||||
//
|
||||
// The lit area's offset inside the panel is measured on the 0.91" and happens to be the
|
||||
// centred position on the other two; oneCentredPanel in the test pins that down, so a later
|
||||
// correction to a panel or a board cannot leave a stale offset behind.
|
||||
SSD1306_0_96("0.96\" SSD1306 128x64", 26.7d, 19.3d, 21.744d, 10.864d, 2.478d,
|
||||
new Layout(27.0d, 27.0d, 1.8d, false, 1.5d, 2.0d, 0.15d, 3.85d)),
|
||||
// centred position on the other two; centredPanelsStoreTheCentredOffsets in the test pins
|
||||
// that down, so a later correction to a panel or a board cannot leave a stale offset behind.
|
||||
SSD1306_0_91("0.91\" SSD1306 128x32", 30.0d, 12.0d, 22.384d, 5.584d, 2.1d,
|
||||
new Layout(38.0d, 12.0d, 1.5d, true, 0d, 0d, 5.0d, 0d),
|
||||
new Layout(38.0d, 20.0d, 1.5d, false, 2.5d, 2.0d, 1.25d, 4.0d)),
|
||||
SH1106_1_3("1.3\" SH1106 128x64", 31.4d, 16.7d, 29.42d, 14.7d, 0.99d,
|
||||
new Layout(35.4d, 33.5d, 1.5d, false, 2.0d, 3.0d, 2.0d, 8.4d));
|
||||
new Layout(35.8d, 12.0d, 1.5d, true, 0d, 0d, 5.0d, 0d, 2.35d),
|
||||
new Layout(32.5d, 20.5d, 1.5d, false, 2.5d, 2.0d, 1.25d, 4.0d, 6.6d)),
|
||||
SSD1306_0_96("0.96\" SSD1306 128x64", 26.7d, 19.26d, 21.74d, 11.0d, 2.48d,
|
||||
new Layout(27.0d, 27.0d, 1.6d, false, 2.0d, 2.0d, 0.15d, 3.87d, 6.14d)),
|
||||
SH1106_1_3("1.3\" SH1106 128x64", 35.4d, 23.0d, 29.42d, 14.7d, 2.99d,
|
||||
new Layout(35.4d, 33.5d, 1.5d, false, 2.5d, 3.0d, 0d, 5.25d, 7.35d));
|
||||
|
||||
private final String label;
|
||||
private final double panelWidthMm;
|
||||
@@ -418,10 +506,11 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
public double getActiveLeftInPanelMm() { return activeLeftInPanelMm; }
|
||||
|
||||
/**
|
||||
* The two SPI headers share a board. That is a derivation rather than a measurement: a 6-pin
|
||||
* module is a PCB of its own with one fewer pad, and only its outline would say whether it
|
||||
* matches the 7-pin board it is modelled on. The header is centred on the width either way, so
|
||||
* the shorter row sits correctly on the board it borrows.
|
||||
* The three SPI headers share a board. That is a derivation rather than a measurement: the
|
||||
* 6-pin and 8-pin modules are PCBs of their own, one pad short and one pad long, and only
|
||||
* their outlines would say whether either matches the 7-pin board they are modelled on. The
|
||||
* header is centred on the width in every case, so a shorter or longer row still sits
|
||||
* correctly on the board it borrows.
|
||||
*/
|
||||
public Layout getLayout(OLEDInterface oledInterface) {
|
||||
return oledInterface == OLEDInterface.I2C_4Pin ? i2cLayout : spiLayout;
|
||||
@@ -434,9 +523,7 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
* different boards on the 0.91".
|
||||
*
|
||||
* <p>Hole positions are an inset from each of the two edges nearest a hole, following the TFT
|
||||
* boards. The 0.96"'s pattern was given as a 24 mm centre-to-centre spacing instead, which on
|
||||
* its 27 mm board is this inset; {@code holePatternMatchesTheSpecifiedSpacing} keeps the two
|
||||
* readings in step.
|
||||
* boards.
|
||||
*/
|
||||
public static class Layout {
|
||||
|
||||
@@ -448,10 +535,11 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
private final double holeSizeMm;
|
||||
private final double panelLeftMm;
|
||||
private final double panelTopMm;
|
||||
private final double activeTopMm;
|
||||
|
||||
Layout(double boardWidthMm, double boardLengthMm, double headerOffsetMm,
|
||||
boolean headerOnLeftEdge, double holeInsetMm, double holeSizeMm, double panelLeftMm,
|
||||
double panelTopMm) {
|
||||
double panelTopMm, double activeTopMm) {
|
||||
this.boardWidthMm = boardWidthMm;
|
||||
this.boardLengthMm = boardLengthMm;
|
||||
this.headerOffsetMm = headerOffsetMm;
|
||||
@@ -460,6 +548,7 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
this.holeSizeMm = holeSizeMm;
|
||||
this.panelLeftMm = panelLeftMm;
|
||||
this.panelTopMm = panelTopMm;
|
||||
this.activeTopMm = activeTopMm;
|
||||
}
|
||||
|
||||
public double getBoardWidthMm() { return boardWidthMm; }
|
||||
@@ -469,6 +558,12 @@ public class OLEDDisplay extends AbstractMakerBoard {
|
||||
public double getPanelLeftMm() { return panelLeftMm; }
|
||||
public double getPanelTopMm() { return panelTopMm; }
|
||||
|
||||
/**
|
||||
* Top of the lit area measured from the board's top edge, following the TFT boards, which is
|
||||
* the edge it is measured from on a module in hand.
|
||||
*/
|
||||
public double getActiveTopMm() { return activeTopMm; }
|
||||
|
||||
/** Clearance from the edge the header sits on, whichever edge that is. */
|
||||
public double getHeaderOffsetMm() { return headerOffsetMm; }
|
||||
|
||||
|
||||
+166
-33
@@ -24,6 +24,9 @@ package org.diylc.components.displays;
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.diylc.appframework.miscutils.ConfigurationManager;
|
||||
import org.diylc.common.Display;
|
||||
@@ -92,10 +95,10 @@ public class OLEDDisplayTest {
|
||||
Layout i2c = Version.SSD1306_0_91.getLayout(OLEDInterface.I2C_4Pin);
|
||||
Layout spi = Version.SSD1306_0_91.getLayout(OLEDInterface.SPI_7Pin);
|
||||
|
||||
Assert.assertEquals("I2C width", 35.8d, i2c.getBoardWidthMm(), 0.01d);
|
||||
Assert.assertEquals("I2C length", 12.0d, i2c.getBoardLengthMm(), 0.01d);
|
||||
Assert.assertEquals("SPI length", 20.0d, spi.getBoardLengthMm(), 0.01d);
|
||||
Assert.assertEquals("the two share a long edge", i2c.getBoardWidthMm(),
|
||||
spi.getBoardWidthMm(), 0.01d);
|
||||
Assert.assertEquals("SPI width", 32.5d, spi.getBoardWidthMm(), 0.01d);
|
||||
Assert.assertEquals("SPI length", 20.5d, spi.getBoardLengthMm(), 0.01d);
|
||||
Assert.assertTrue("the I2C column should stand on the left edge", i2c.isHeaderOnLeftEdge());
|
||||
Assert.assertFalse("the SPI row should lie along the top edge", spi.isHeaderOnLeftEdge());
|
||||
Assert.assertFalse("the I2C board has no mounting holes", i2c.hasMountingHoles());
|
||||
@@ -127,6 +130,16 @@ public class OLEDDisplayTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void controlPointsAreTheEightPinRowInOrder() {
|
||||
OLEDDisplay display = of(Version.SSD1306_0_96, OLEDInterface.SPI_8Pin);
|
||||
String[] expected = new String[] {"SDA", "SCL", "DC", "RES", "CS", "VDD", "VIN", "GND"};
|
||||
Assert.assertEquals(expected.length, display.getControlPointCount());
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
Assert.assertEquals("pin " + i, expected[i], display.getControlPointNodeName(i));
|
||||
}
|
||||
}
|
||||
|
||||
/** Every size is sold in every interface, so no size may carry a shorter pin array. */
|
||||
@Test
|
||||
public void everyInterfaceExistsAtEverySize() {
|
||||
@@ -137,6 +150,8 @@ public class OLEDDisplayTest {
|
||||
of(version, OLEDInterface.SPI_7Pin).getControlPointCount());
|
||||
Assert.assertEquals(version + " SPI-6", 6,
|
||||
of(version, OLEDInterface.SPI_6Pin).getControlPointCount());
|
||||
Assert.assertEquals(version + " SPI-8", 8,
|
||||
of(version, OLEDInterface.SPI_8Pin).getControlPointCount());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +179,32 @@ public class OLEDDisplayTest {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The eight-pin board carries the same seven lines as the seven-pin one plus a second supply,
|
||||
* so what has to hold is that the set has grown by exactly VDD and nothing has been dropped --
|
||||
* the order cannot be compared, because this row follows the controller's pin numbering and runs
|
||||
* the other way. VDD and VIN are separate rails, so neither may be renamed to the other or to
|
||||
* the VCC the shorter rows print, which a set comparison would let through if both were spelled
|
||||
* the same.
|
||||
*/
|
||||
@Test
|
||||
public void theEightPinBoardAddsOnlyTheLogicSupply() {
|
||||
Set<String> seven = new HashSet<String>(Arrays.asList(OLEDInterface.SPI_7Pin.getPinNames()));
|
||||
Set<String> eight = new HashSet<String>(Arrays.asList(OLEDInterface.SPI_8Pin.getPinNames()));
|
||||
|
||||
Assert.assertEquals("no pin names repeat in the row", 8,
|
||||
OLEDInterface.SPI_8Pin.getPinNames().length);
|
||||
Assert.assertEquals("no pin names repeat in the row", 8, eight.size());
|
||||
Assert.assertTrue("VIN replaces the shorter rows' VCC", eight.contains("VIN"));
|
||||
Assert.assertTrue("the logic rail is the pin that is added", eight.contains("VDD"));
|
||||
Assert.assertFalse("VCC belongs to the rows with one supply", eight.contains("VCC"));
|
||||
|
||||
seven.remove("VCC");
|
||||
eight.remove("VIN");
|
||||
eight.remove("VDD");
|
||||
Assert.assertEquals("the signal lines are the seven-pin row's", seven, eight);
|
||||
}
|
||||
|
||||
/** Switching either property must resize the array, not leave the longer one's points behind. */
|
||||
@Test
|
||||
public void switchingInterfaceResizesTheControlPoints() {
|
||||
@@ -175,6 +216,8 @@ public class OLEDDisplayTest {
|
||||
Assert.assertEquals(7, display.getControlPointCount());
|
||||
display.setOledInterface(OLEDInterface.SPI_6Pin);
|
||||
Assert.assertEquals(6, display.getControlPointCount());
|
||||
display.setOledInterface(OLEDInterface.SPI_8Pin);
|
||||
Assert.assertEquals(8, display.getControlPointCount());
|
||||
display.setOledInterface(OLEDInterface.I2C_4Pin);
|
||||
Assert.assertEquals(4, display.getControlPointCount());
|
||||
}
|
||||
@@ -244,13 +287,15 @@ public class OLEDDisplayTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Each lit area is its pixel count at the panel's dot pitch, so its aspect is the pixel aspect:
|
||||
* a 2:1 letterbox on the two 128 x 64 panels and 4:1 on the 128 x 32. This is the figure the
|
||||
* 0.96" was drawn wrong against for as long as its panel was near-square.
|
||||
* A lit area's aspect is very nearly the pixel aspect: a 2:1 letterbox on the two 128 x 64
|
||||
* panels and 4:1 on the 128 x 32. This is the figure the 0.96" was drawn wrong against for as
|
||||
* long as its panel was near-square. The two derived panels sit exactly on it because that is
|
||||
* how they were arrived at; the measured 0.96" comes out a little under, and the tolerance here
|
||||
* is wide enough to allow that and nothing like a transposition.
|
||||
*/
|
||||
@Test
|
||||
public void litAreaMatchesThePixelAspect() {
|
||||
Assert.assertEquals(2.0d, aspect(Version.SSD1306_0_96), 0.01d);
|
||||
Assert.assertEquals(2.0d, aspect(Version.SSD1306_0_96), 0.05d);
|
||||
Assert.assertEquals(4.0d, aspect(Version.SSD1306_0_91), 0.01d);
|
||||
Assert.assertEquals(2.0d, aspect(Version.SH1106_1_3), 0.01d);
|
||||
}
|
||||
@@ -269,6 +314,16 @@ public class OLEDDisplayTest {
|
||||
version.getActiveLengthMm() <= version.getPanelLengthMm());
|
||||
Assert.assertTrue(version + " lit area starts off the panel",
|
||||
version.getActiveLeftInPanelMm() >= 0);
|
||||
|
||||
for (OLEDInterface oledInterface : OLEDInterface.values()) {
|
||||
Layout layout = version.getLayout(oledInterface);
|
||||
String board = version + " " + oledInterface;
|
||||
Assert.assertTrue(board + " lit area starts above its panel",
|
||||
layout.getActiveTopMm() >= layout.getPanelTopMm());
|
||||
Assert.assertTrue(board + " lit area runs off the bottom of its panel",
|
||||
layout.getActiveTopMm() + version.getActiveLengthMm() <= layout.getPanelTopMm()
|
||||
+ version.getPanelLengthMm());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,24 +395,50 @@ public class OLEDDisplayTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* The 0.91" SPI board centres its lit area on the board's height, which is how it was measured,
|
||||
* rather than hanging the panel below the header the way the taller boards do.
|
||||
* The 0.91" SPI board's panel offset of 1.25 mm is exactly centred on its 32.5 mm width -- two
|
||||
* independent readings agreeing, which is what makes this worth asserting. The vertical pair do
|
||||
* not agree: the panel hangs below the header rather than sitting centred on the height, so
|
||||
* there is nothing to cross-check down the board.
|
||||
*/
|
||||
@Test
|
||||
public void theSmallSpiBoardCentresItsLitArea() {
|
||||
public void theSmallSpiBoardCentresItsPanelAcrossTheBoard() {
|
||||
Version version = Version.SSD1306_0_91;
|
||||
Layout layout = version.getLayout(OLEDInterface.SPI_7Pin);
|
||||
double litTop = layout.getPanelTopMm()
|
||||
+ (version.getPanelLengthMm() - version.getActiveLengthMm()) / 2.0;
|
||||
Assert.assertEquals(layout.getBoardLengthMm() / 2.0,
|
||||
litTop + version.getActiveLengthMm() / 2.0, 0.01d);
|
||||
Assert.assertEquals("the panel is off centre across the board",
|
||||
(layout.getBoardWidthMm() - version.getPanelWidthMm()) / 2.0, layout.getPanelLeftMm(),
|
||||
0.01d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Every lit area is measured down from its board's top edge rather than centred on the panel:
|
||||
* the driver's bonding region takes the bottom of the glass on all of these, so the lit area
|
||||
* sits high in the frame and centring it would drop it -- by 1.86 mm on the 0.96", 0.86 mm on
|
||||
* the 0.91" I2C board, 0.61 mm on its SPI sibling and 2.05 mm on the 1.3". The centred position
|
||||
* is asserted beside each measurement because a stale one would still land inside the panel and
|
||||
* clear every pin and hole.
|
||||
*/
|
||||
@Test
|
||||
public void measuredLitAreasSitHighInTheirPanels() {
|
||||
assertLitAreaTop(Version.SSD1306_0_96, OLEDInterface.I2C_4Pin, 6.14d, 8.0d);
|
||||
assertLitAreaTop(Version.SSD1306_0_91, OLEDInterface.I2C_4Pin, 2.35d, 3.208d);
|
||||
assertLitAreaTop(Version.SSD1306_0_91, OLEDInterface.SPI_7Pin, 6.6d, 7.208d);
|
||||
assertLitAreaTop(Version.SH1106_1_3, OLEDInterface.I2C_4Pin, 7.35d, 9.4d);
|
||||
}
|
||||
|
||||
private static void assertLitAreaTop(Version version, OLEDInterface oledInterface,
|
||||
double measuredMm, double centredMm) {
|
||||
Layout layout = version.getLayout(oledInterface);
|
||||
Assert.assertEquals(version + " measured from the top edge", measuredMm,
|
||||
layout.getActiveTopMm(), 0.01d);
|
||||
Assert.assertEquals(version + " centring would drop it", centredMm, layout.getPanelTopMm()
|
||||
+ (version.getPanelLengthMm() - version.getActiveLengthMm()) / 2.0, 0.01d);
|
||||
}
|
||||
|
||||
/**
|
||||
* The panel is wide enough on every one of these boards to share the holes' horizontal span, so
|
||||
* the vertical axis is the only one that can keep them apart -- a symmetric check would fail on
|
||||
* a board that is drawn correctly. The 0.91" SPI board is the tight one, clearing each hole row
|
||||
* by 0.5 mm.
|
||||
* a board that is drawn correctly. The 0.91" SPI board is the tight one, clearing its
|
||||
* header-side holes by 0.5 mm.
|
||||
*/
|
||||
@Test
|
||||
public void panelClearsTheMountingHolesVertically() {
|
||||
@@ -381,16 +462,16 @@ public class OLEDDisplayTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* The strip between a top-edge row and the panel below it. On the 0.96" it is 2.05 mm, which is
|
||||
* the whole reason that board carries no silkscreen: a flat label needs about 2.7 mm and would
|
||||
* cross the panel's top edge. The 1.3" has 6.9 mm and could print its names, which is noted in
|
||||
* the plan rather than done here.
|
||||
* The strip between a top-edge row and the panel below it, which is what the pin names have to
|
||||
* fit into. The 0.96"'s 2.27 mm is the shallowest of them and is why these boards place their
|
||||
* own labels a fixed gap off the pad: the shared helper's 2.04 mm offset is pitched for the
|
||||
* 3.5 mm the Nokia and the TFT have and would print the names onto the glass.
|
||||
*/
|
||||
@Test
|
||||
public void panelClearsThePinRow() {
|
||||
Assert.assertEquals("0.96\"", 2.05d, stripDepthMm(Version.SSD1306_0_96), 0.01d);
|
||||
Assert.assertEquals("0.96\"", 2.27d, stripDepthMm(Version.SSD1306_0_96), 0.01d);
|
||||
Assert.assertEquals("0.91\" SPI", 2.5d, stripDepthMm(Version.SSD1306_0_91), 0.01d);
|
||||
Assert.assertEquals("1.3\"", 6.9d, stripDepthMm(Version.SH1106_1_3), 0.01d);
|
||||
Assert.assertEquals("1.3\"", 3.75d, stripDepthMm(Version.SH1106_1_3), 0.01d);
|
||||
}
|
||||
|
||||
private static double stripDepthMm(Version version) {
|
||||
@@ -435,17 +516,69 @@ public class OLEDDisplayTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* The 0.96"'s hole pattern was specified as a 24 mm centre-to-centre spacing in both directions
|
||||
* and is stored as the equivalent inset, following the TFT boards. If its outline is ever
|
||||
* corrected the inset has to be recomputed, and this is what says so.
|
||||
* Every board prints its pin names, and none of them prints one on the glass. The strip they go
|
||||
* in is 2.27 mm on the 0.96" against the 3.5 mm the sibling displays print into, so this is the
|
||||
* check that the tighter placement actually clears the panel -- no geometry assertion here would
|
||||
* notice a label drawn over the display, and on the 0.96" the shared helper's offset would put
|
||||
* one there.
|
||||
*
|
||||
* <p>The silkscreen colour is repointed at a colour nothing else on these boards draws in, which
|
||||
* is what makes the count unambiguous: mounting holes are filled in the canvas's white and would
|
||||
* otherwise be indistinguishable from lettering. Appearance constants are deliberately not
|
||||
* final so that they can be overridden at runtime, which is what this leans on.
|
||||
*/
|
||||
@Test
|
||||
public void holePatternMatchesTheSpecifiedSpacing() {
|
||||
Layout layout = Version.SSD1306_0_96.getLayout(OLEDInterface.I2C_4Pin);
|
||||
Assert.assertEquals("across", 24.0d,
|
||||
layout.getBoardWidthMm() - 2 * layout.getHoleInsetMm(), 0.01d);
|
||||
Assert.assertEquals("down", 24.0d,
|
||||
layout.getBoardLengthMm() - 2 * layout.getHoleInsetMm(), 0.01d);
|
||||
public void pinNamesAreDrawnAndStayOffThePanel() {
|
||||
Color silk = AbstractMakerBoard.SILK_COLOR;
|
||||
AbstractMakerBoard.SILK_COLOR = Color.MAGENTA;
|
||||
try {
|
||||
for (Version version : Version.values()) {
|
||||
for (OLEDInterface oledInterface : OLEDInterface.values()) {
|
||||
Layout layout = version.getLayout(oledInterface);
|
||||
OLEDDisplay display = of(version, oledInterface);
|
||||
int[] pixels = MakerBoardTestSupport.renderPixels(display);
|
||||
int side = (int) Math.round(Math.sqrt(pixels.length));
|
||||
|
||||
Rectangle2D outline = display.getBodyShape().getBounds2D();
|
||||
Rectangle2D glass = new Rectangle2D.Double(
|
||||
outline.getX() + mmToPx(layout.getPanelLeftMm()),
|
||||
outline.getY() + mmToPx(layout.getPanelTopMm()),
|
||||
mmToPx(version.getPanelWidthMm()), mmToPx(version.getPanelLengthMm()));
|
||||
|
||||
int printed = 0;
|
||||
int onGlass = 0;
|
||||
for (int i = 0; i < pixels.length; i++) {
|
||||
if (!isSilk(pixels[i])) {
|
||||
continue;
|
||||
}
|
||||
printed++;
|
||||
if (glass.contains(i % side, i / side)) {
|
||||
onGlass++;
|
||||
}
|
||||
}
|
||||
|
||||
String board = version + " " + oledInterface;
|
||||
Assert.assertTrue(board + " prints no pin names at all", printed > 0);
|
||||
Assert.assertEquals(board + " prints a pin name on the panel", 0, onGlass);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
AbstractMakerBoard.SILK_COLOR = silk;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Antialiasing thins the lettering where it meets whatever is behind it, so a label clipping the
|
||||
* glass by a fraction of a millimetre arrives dimmed rather than at full strength. Matching the
|
||||
* hue rather than the value catches those too: nothing else drawn on these boards -- the blue
|
||||
* PCB, the cyan pads, the dark glass, the lit text, the gold and white of a mounting hole --
|
||||
* leaves red and blue both well ahead of green.
|
||||
*/
|
||||
private static boolean isSilk(int argb) {
|
||||
int red = (argb >> 16) & 0xFF;
|
||||
int green = (argb >> 8) & 0xFF;
|
||||
int blue = argb & 0xFF;
|
||||
return red > 60 && blue > 60 && red > green * 1.5 && blue > green * 1.5;
|
||||
}
|
||||
|
||||
/** Without this the BOM collapses six different modules into one or two rows. */
|
||||
@@ -453,10 +586,10 @@ public class OLEDDisplayTest {
|
||||
public void bomValueCarriesSizeAndInterface() {
|
||||
Assert.assertEquals("0.96\" SSD1306 128x64, I2C",
|
||||
of(Version.SSD1306_0_96, OLEDInterface.I2C_4Pin).getValueForDisplay());
|
||||
Assert.assertEquals("1.3\" SH1106 128x64, SPI",
|
||||
Assert.assertEquals("1.3\" SH1106 128x64, SPI-7",
|
||||
of(Version.SH1106_1_3, OLEDInterface.SPI_7Pin).getValueForDisplay());
|
||||
|
||||
java.util.Set<String> values = new java.util.HashSet<String>();
|
||||
Set<String> values = new HashSet<String>();
|
||||
for (Version version : Version.values()) {
|
||||
for (OLEDInterface oledInterface : OLEDInterface.values()) {
|
||||
values.add(of(version, oledInterface).getValueForDisplay());
|
||||
|
||||
Reference in New Issue
Block a user