mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
tft
This commit is contained in:
@@ -505,25 +505,25 @@
|
||||
<releaseDate>2026-09-30 00:00:00.000 CET</releaseDate>
|
||||
<name></name>
|
||||
<changes>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Character LCD - 16x2 and 20x4, with I2C backpack or 16-pin parallel interface</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>0.96" OLED Display (SSD1306) - I2C and SPI</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>7-Segment Display - 1-digit 0.56 inch, 4-digit in 0.36 inch and 0.56 inch, and TM1637 driver module</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>8x8 LED Matrix (MAX7219)</description>
|
||||
<description>Character LCD - 16x2 and 20x4, with I2C backpack or 16-pin parallel interface</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>TFT Touch Screen (ILI9341) - 2.8 inch 240x320 with touch and SD card slot</description>
|
||||
<description>LED Bar Graph - 8, 10 and 12 segments</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>LED Matrix (MAX7219) - single 8x8, compact 8x8, 4-in-1 32x8</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>NeoPixel Jewel (WS2812B) - 7 LEDs</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
@@ -533,6 +533,22 @@
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>NeoPixel Stick (WS2812B) - 8 LEDs</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>NeoPixel Strip (WS2812B) - 30, 60 and 144 LEDs per meter, up to 144 LEDs</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>Nokia 5110 LCD (PCD8544)</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>0.96" OLED Display (SSD1306) - I2C and SPI</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>NEW_FEATURE</changeType>
|
||||
<description>TFT Display - 2.8 inch ILI9341, 1.8 inch ST7735, 1.54 inch ST7789, 1.28 inch round GC9A01</description>
|
||||
</org.diylc.appframework.update.Change>
|
||||
<org.diylc.appframework.update.Change>
|
||||
<changeType>IMPROVEMENT</changeType>
|
||||
<description>Bill of Materials now lists the selected variant of microcontroller boards and displays, so different versions of the same board are no longer merged into a single row</description>
|
||||
|
||||
@@ -524,43 +524,6 @@ public abstract class AbstractMakerBoard extends AbstractTransparentComponent<Vo
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to draw pin names beside a vertical column of pins, reading left to right as they do on
|
||||
* a row. A column has the whole pin pitch to stack its labels in rather than the width of one,
|
||||
* so unlike {@link #drawFlatRowPinLabels} it is the board, not the pitch, that limits how long a
|
||||
* name can be.
|
||||
*
|
||||
* @param g2d Graphics2D context (already transformed for board orientation)
|
||||
* @param x Unrotated first control point X coordinate (P0.getX())
|
||||
* @param y Unrotated first control point Y coordinate (P0.getY())
|
||||
* @param offsets Array of [x, y] relative offsets for all control points
|
||||
* @param startIndex Index of the first pin of the column
|
||||
* @param count Number of pins in the column
|
||||
* @param right True to print the labels to the right of the column, false to print them left
|
||||
* @param silkColor Silkscreen text color
|
||||
*/
|
||||
protected void drawFlatColumnPinLabels(Graphics2D g2d, double x, double y, double[][] offsets,
|
||||
int startIndex, int count, boolean right, Color silkColor) {
|
||||
if (offsets == null || offsets.length == 0) {
|
||||
return;
|
||||
}
|
||||
double labelOffset = PIN_ROW_FLAT_LABEL_OFFSET.convertToPixels();
|
||||
|
||||
g2d.setColor(silkColor);
|
||||
g2d.setFont(PIN_ROW_FLAT_FONT);
|
||||
|
||||
for (int i = startIndex; i < startIndex + count && i < offsets.length; i++) {
|
||||
String label = getSilkPinLabel(i);
|
||||
if (label == null || label.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
double pinX = x + offsets[i][0] + (right ? labelOffset : -labelOffset);
|
||||
double pinY = y + offsets[i][1];
|
||||
StringUtils.drawCenteredText(g2d, label, pinX, getLabelBaseline(g2d, pinY),
|
||||
right ? HorizontalAlignment.LEFT : HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to draw a complete PCB terminal block (green body + inner compartment lines + screw lugs)
|
||||
* that matches the visuals of the PCBTerminalBlock component.
|
||||
|
||||
@@ -71,8 +71,10 @@ public class TFTDisplay extends AbstractMakerBoard {
|
||||
// of zero means the module has no separate dark panel wider than its own lit area, so only the
|
||||
// screen is drawn, and a hole size of zero means the board has no mounting holes at all.
|
||||
// The round board is described by the same two outline figures as the others: its width is the
|
||||
// disc diameter and its length runs from the tab's outer edge to the bottom of the disc, so the
|
||||
// tab's projection is the difference between them and is not carried separately.
|
||||
// disc diameter and its length runs from the tab's outer edge to the far side of the disc, so
|
||||
// the tab's projection is the difference between them and is not carried separately. It is
|
||||
// the one variant whose header is at the bottom, so its board grows upwards from the pin row
|
||||
// rather than down from it.
|
||||
ILI9341_2_8("2.8\" ILI9341 240x320 (Touch + SD)", 50.0d, 86.0d, 43.2d, 57.6d, 69.1d, 3.0d,
|
||||
3.0d, 3.0d, 0d,
|
||||
new String[] {"VCC", "GND", "CS", "RESET", "DC", "MOSI (SDI)", "SCK", "LED", "MISO (SDO)",
|
||||
@@ -84,7 +86,7 @@ public class TFTDisplay extends AbstractMakerBoard {
|
||||
new String[] {"GND", "VCC", "SCL", "SDA", "RES", "DC", "CS", "BLK"}),
|
||||
GC9A01_1_28("1.28\" GC9A01 240x240 Round", 38.0d, 45.5d, 32.5d, 32.5d, 0d, 1.76d, 0d, 0d,
|
||||
22.9d,
|
||||
new String[] {"VCC", "GND", "SCL", "SDA", "DC", "CS", "RST"});
|
||||
new String[] {"RST", "CS", "DC", "SDA", "SCL", "GND", "VCC"});
|
||||
|
||||
private final String label;
|
||||
private final double boardWidthMm;
|
||||
@@ -211,8 +213,17 @@ public class TFTDisplay extends AbstractMakerBoard {
|
||||
return x - (px(controller.getBoardWidthMm()) - span) / 2.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top edge of the board. The rectangular boards hang below their header, which sits one offset
|
||||
* down from the top edge; the round one wears its tab at the bottom, so the board stands above
|
||||
* the row and the offset is measured up from the bottom edge instead.
|
||||
*/
|
||||
private double getBoardY(double y) {
|
||||
return y - px(getController().getHeaderOffsetMm());
|
||||
Controller controller = getController();
|
||||
if (controller.isRound()) {
|
||||
return y - px(controller.getBoardLengthMm() - controller.getHeaderOffsetMm());
|
||||
}
|
||||
return y - px(controller.getHeaderOffsetMm());
|
||||
}
|
||||
|
||||
private double[][] getRelativeOffsets() {
|
||||
@@ -240,16 +251,16 @@ public class TFTDisplay extends AbstractMakerBoard {
|
||||
double boardH = px(controller.getBoardLengthMm());
|
||||
|
||||
if (controller.isRound()) {
|
||||
// A disc with a rectangular tab on top. The tab has to be carried down to the disc's centre
|
||||
// line before the two are unioned: taken only as far as the top of the disc it would meet it
|
||||
// at a single tangent point and leave a notch either side of it. Below the line where the
|
||||
// disc is as wide as the tab, the disc governs the outline and only the projection shows.
|
||||
// A disc with a rectangular tab hanging below it. The tab has to be carried up to the disc's
|
||||
// centre line before the two are unioned: taken only as far as the bottom of the disc it
|
||||
// would meet it at a single tangent point and leave a notch either side of it. Above that
|
||||
// line the disc is wider than the tab and governs the outline, so only the projection shows.
|
||||
double discR = boardW / 2.0;
|
||||
double discCy = boardY + px(controller.getTabProjectionMm()) + discR;
|
||||
double discCy = boardY + discR;
|
||||
double tabW = px(controller.getTabWidthMm());
|
||||
Area body = new Area(new Ellipse2D.Double(boardX, discCy - discR, boardW, boardW));
|
||||
body.add(new Area(new Rectangle2D.Double(boardX + (boardW - tabW) / 2.0, boardY, tabW,
|
||||
discCy - boardY)));
|
||||
Area body = new Area(new Ellipse2D.Double(boardX, boardY, boardW, boardW));
|
||||
body.add(new Area(new Rectangle2D.Double(boardX + (boardW - tabW) / 2.0, discCy, tabW,
|
||||
boardY + boardH - discCy)));
|
||||
return body;
|
||||
}
|
||||
|
||||
@@ -318,11 +329,11 @@ public class TFTDisplay extends AbstractMakerBoard {
|
||||
Shape screen;
|
||||
|
||||
if (controller.isRound()) {
|
||||
// the lit circle is centred on the disc, which sits below the tab rather than on the
|
||||
// the lit circle is centred on the disc, which sits above the tab rather than on the
|
||||
// centre line of the bounding box
|
||||
double discR = boardW / 2.0;
|
||||
double discCx = boardX + discR;
|
||||
double discCy = boardY + px(controller.getTabProjectionMm()) + discR;
|
||||
double discCy = boardY + discR;
|
||||
screen = new Ellipse2D.Double(discCx - screenW / 2.0, discCy - screenH / 2.0, screenW,
|
||||
screenH);
|
||||
} else {
|
||||
@@ -349,9 +360,10 @@ public class TFTDisplay extends AbstractMakerBoard {
|
||||
|
||||
// The names go in the strip between the pin row and the panel, lying flat along the row as
|
||||
// the Nokia's do: the strip is 3.5 mm deep on the two smallest boards and a label stood on
|
||||
// end needs 4.7 mm.
|
||||
drawFlatRowPinLabels(g2d, x, y, getRelativeOffsets(), 0, controlPoints.length, true,
|
||||
SILK_COLOR);
|
||||
// end needs 4.7 mm. The round board carries its row at the bottom, so for it alone the
|
||||
// strip, and the names in it, are above the pins rather than below them.
|
||||
drawFlatRowPinLabels(g2d, x, y, getRelativeOffsets(), 0, controlPoints.length,
|
||||
!controller.isRound(), SILK_COLOR);
|
||||
}
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
|
||||
+41
-13
@@ -281,20 +281,26 @@ public class TFTDisplayTest {
|
||||
Assert.assertFalse(Controller.GC9A01_1_28.hasMountingHoles());
|
||||
}
|
||||
|
||||
/**
|
||||
* The round board reads RST first, which is to say leftmost. The array had been in the opposite
|
||||
* order, which looked plausible while the tab was drawn at the top and was wrong against the
|
||||
* part either way; turning the board over is what made it visible.
|
||||
*/
|
||||
@Test
|
||||
public void roundPinNamesComeFromTheController() {
|
||||
TFTDisplay display = of(Controller.GC9A01_1_28);
|
||||
String[] expected = new String[] {"VCC", "GND", "SCL", "SDA", "DC", "CS", "RST"};
|
||||
String[] expected = new String[] {"RST", "CS", "DC", "SDA", "SCL", "GND", "VCC"};
|
||||
for (int i = 0; i < expected.length; i++) {
|
||||
Assert.assertEquals("pin " + i, expected[i], display.getControlPointNodeName(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The outline is a disc with a narrower tab on it, so the corners of its bounding box are off the
|
||||
* board while the middle of the top edge is on it. Taking the tab only as far as the top of the
|
||||
* disc would have left a notch either side of the tangent point, which these corners would not
|
||||
* catch but the lower check would: low down, the disc is wider than the tab.
|
||||
* The outline is a disc with a narrower tab hanging below it, so the corners of its bounding box
|
||||
* are off the board while the middles of the top and bottom edges are on it. Taking the tab only
|
||||
* as far as the bottom of the disc would have left a notch either side of the tangent point,
|
||||
* which these corners would not catch but the centre-line check would: there the disc is wider
|
||||
* than the tab.
|
||||
*/
|
||||
@Test
|
||||
public void roundBodyIsADiscWithATab() {
|
||||
@@ -303,23 +309,45 @@ public class TFTDisplayTest {
|
||||
Rectangle2D bounds = body.getBounds2D();
|
||||
|
||||
Assert.assertEquals("disc diameter across", 38.0d, mm(bounds.getWidth()), 0.01d);
|
||||
Assert.assertEquals("tab tip to disc bottom", 45.5d, mm(bounds.getHeight()), 0.01d);
|
||||
Assert.assertEquals("disc top to tab tip", 45.5d, mm(bounds.getHeight()), 0.01d);
|
||||
|
||||
double x = bounds.getX();
|
||||
double y = bounds.getY();
|
||||
|
||||
Assert.assertTrue("the middle of the tab is off the board",
|
||||
Assert.assertTrue("the top of the disc is off the board",
|
||||
body.contains(x + px(19.0d), y + px(1.0d)));
|
||||
Assert.assertTrue("the middle of the tab is off the board",
|
||||
body.contains(x + px(19.0d), y + px(44.5d)));
|
||||
Assert.assertFalse("the top left corner is on the board",
|
||||
body.contains(x + px(1.0d), y + px(1.0d)));
|
||||
Assert.assertFalse("the top right corner is on the board",
|
||||
body.contains(x + px(37.0d), y + px(1.0d)));
|
||||
Assert.assertFalse("the bottom left corner is on the board",
|
||||
body.contains(x + px(1.0d), y + px(44.5d)));
|
||||
|
||||
// at the disc's centre line the board is wider than the tab, which is what proves the union
|
||||
// took the disc's outline and not the rectangle's
|
||||
// took the disc's outline and not the rectangle's; a third of the way further down only the
|
||||
// tab is left, and the same point is off the board
|
||||
Assert.assertTrue("the disc is no wider than its tab",
|
||||
body.contains(x + px(2.0d), y + px(26.5d)));
|
||||
Assert.assertTrue("the centre of the disc is off the board",
|
||||
body.contains(x + px(19.0d), y + px(26.5d)));
|
||||
body.contains(x + px(2.0d), y + px(19.0d)));
|
||||
Assert.assertFalse("the board is as wide as its disc below the centre line",
|
||||
body.contains(x + px(2.0d), y + px(30.0d)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Which edge the header sits on. Three of the boards hang below their pin row; the round one
|
||||
* wears its tab at the bottom, so it stands above its row instead -- the one case where the
|
||||
* offset is measured up from the bottom edge.
|
||||
*/
|
||||
@Test
|
||||
public void headerSitsOnTheEdgeItsBoardHangsFrom() {
|
||||
for (Controller controller : Controller.values()) {
|
||||
TFTDisplay display = of(controller);
|
||||
Rectangle2D bounds = display.getBodyShape().getBounds2D();
|
||||
Point2D firstPin = display.getControlPoint(0);
|
||||
double fromEdge = controller.isRound() ? bounds.getMaxY() - firstPin.getY()
|
||||
: firstPin.getY() - bounds.getY();
|
||||
|
||||
Assert.assertEquals(controller + " header offset", controller.getHeaderOffsetMm(),
|
||||
mm(fromEdge), 0.01d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,11 +432,24 @@ the netlist still spell them out.
|
||||
clock pin confirmed as `SCL` rather than the `SLC` typo some of these boards carry on their silk.
|
||||
|
||||
Two things about that outline are worth keeping. The body is the disc unioned with the tab, and the
|
||||
tab has to be carried down to the disc's centre line before the union: taken only as far as the top
|
||||
of the disc, the two shapes meet at a single tangent point and leave a notch either side of it. And
|
||||
the projection is not stored, being whatever the outline is longer than it is wide, so the round
|
||||
board is described by the same two figures as every other variant. The lit area is a 32.5 mm circle
|
||||
centred on the disc rather than on the bounding box, since the tab offsets one from the other.
|
||||
tab has to be carried to the disc's centre line before the union: taken only as far as the rim, the
|
||||
two shapes meet at a single tangent point and leave a notch either side of it. And the projection
|
||||
is not stored, being whatever the outline is longer than it is wide, so the round board is
|
||||
described by the same two figures as every other variant. The lit area is a 32.5 mm circle centred
|
||||
on the disc rather than on the bounding box, since the tab offsets one from the other.
|
||||
|
||||
One thing the flip turned up. The enum's pin array ran `VCC GND SCL SDA DC CS RST`, the reverse
|
||||
of the `RST CS DC SDA SCL GND VCC` recorded two paragraphs above, so the row was mirrored against
|
||||
the part. With the tab drawn at the top either order looked plausible -- the row has no landmark
|
||||
to be wrong against -- and turning the board over is what made it visible. The array now reads
|
||||
RST first, which is to say leftmost, and `roundPinNamesComeFromTheController` pins it there.
|
||||
|
||||
**The tab hangs below the disc, not above it.** That makes the round board the second in the
|
||||
package, after the Nokia, whose header is on the bottom edge and whose outline therefore grows
|
||||
upwards from control point zero -- so its header offset is measured up from the bottom edge where
|
||||
every other variant's is measured down from the top, and its pin names go above the row rather
|
||||
than below it. `headerSitsOnTheEdgeItsBoardHangsFrom` asserts the distinction for all four
|
||||
variants, because nothing else in the geometry would notice if the round one were flipped back.
|
||||
|
||||
1. `Controller` enum: `ILI9341_2_8("2.8\" ILI9341 240x320 (Touch + SD)")`,
|
||||
`ST7735_1_8("1.8\" ST7735 128x160")`, `ST7789_1_54("1.54\" ST7789 240x240")`,
|
||||
@@ -790,8 +803,12 @@ part name, then the variant list).
|
||||
6. **§8** regression samples and the `update.xml` block, last.
|
||||
|
||||
Eleven palette entries so far: the seven existing, plus the strip, the bar graph, the Jewel and the
|
||||
Nokia, plus the panel if appetite holds. The `update.xml` 6.7.0 block still lists only the original
|
||||
seven -- the strip, the bar graph, the Jewel and the Nokia have no entry yet.
|
||||
Nokia, plus the panel if appetite holds. The `update.xml` 6.7.0 block now carries all eleven,
|
||||
alphabetically as the 6.5.0 block lists its boards. Two of the entries it already had were stale
|
||||
rather than missing, and both were stale because a class had outgrown them: the matrix was still
|
||||
described as an 8x8 and the TFT as a touch screen with an SD slot, which is one variant of four
|
||||
and a part that was removed from the drawing. A twelfth entry is needed only if the panel is
|
||||
built.
|
||||
|
||||
## 10. Later slices, and why they are later
|
||||
|
||||
|
||||
Reference in New Issue
Block a user