mirror of
https://github.com/bancika/diy-layout-creator
synced 2026-11-07 12:24:58 +10:00
minima fixes
This commit is contained in:
@@ -61,10 +61,10 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public enum ArduinoUnoVersion {
|
||||
LEONARDO("Leonardo"),
|
||||
REV3("UNO R3"),
|
||||
R4_WIFI("UNO R4 WiFi"),
|
||||
R4_MINIMA("UNO R4 Minima"),
|
||||
LEONARDO("Leonardo");
|
||||
R4_WIFI("UNO R4 WiFi");
|
||||
|
||||
private String label;
|
||||
|
||||
@@ -85,6 +85,12 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
public static Size BOARD_WIDTH = new Size(68.6d, SizeUnit.mm);
|
||||
public static Size BOARD_HEIGHT = new Size(53.4d, SizeUnit.mm);
|
||||
|
||||
// Top left corner of the Minima's 10-pin SWD connector, measured from the board's top left corner
|
||||
public static Size SWD_X = new Size(61.0d, SizeUnit.mm);
|
||||
public static Size SWD_Y = new Size(9.5d, SizeUnit.mm);
|
||||
public static Size SWD_WIDTH = new Size(2.5d, SizeUnit.mm);
|
||||
public static Size SWD_HEIGHT = new Size(6.0d, SizeUnit.mm);
|
||||
|
||||
// Pin names in sequence (44 pins total)
|
||||
public static final String[] PIN_NAMES = new String[] {
|
||||
// Power Header (0..7)
|
||||
@@ -116,6 +122,24 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
"MISO", "5V_ICSP", "SCK", "MOSI", "RST_ICSP", "GND_ICSP"
|
||||
};
|
||||
|
||||
// The Minima has no USB bridge chip either - the RA4M1 has native USB - so it drops the second
|
||||
// ICSP header, and without the WiFi module it has no OFF / GND / VRTC header. In their place it
|
||||
// carries a 10-pin SWD debug connector in the top right corner, modelled as a single node.
|
||||
public static final String[] PIN_NAMES_R4_MINIMA = new String[] {
|
||||
// Power Header (0..7)
|
||||
"BOOT", "IOREF", "RESET", "3.3V", "5V", "GND1", "GND2", "VIN",
|
||||
// Analog Header (8..13)
|
||||
"A0", "A1", "A2", "A3", "A4", "A5",
|
||||
// Digital Low (14..21)
|
||||
"D0 (RX)", "D1 (TX)", "D2", "D3 (~)", "D4", "D5 (~)", "D6 (~)", "D7",
|
||||
// Digital High (22..31)
|
||||
"D8", "D9 (~)", "D10 (~)", "D11 (~)", "D12", "D13", "GND3", "AREF", "SDA", "SCL",
|
||||
// Main ICSP Header (32..37, RA4M1)
|
||||
"MISO", "5V_ICSP", "SCK", "MOSI", "RST_ICSP", "GND_ICSP",
|
||||
// SWD Connector (38)
|
||||
"SWD"
|
||||
};
|
||||
|
||||
public ArduinoUno() {
|
||||
super();
|
||||
this.bodyColor = ARDUINO_TEAL;
|
||||
@@ -142,24 +166,44 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
}
|
||||
|
||||
/**
|
||||
* True for the Renesas RA4M1 generation, which shares a USB-C connector and the extra
|
||||
* OFF / GND / VRTC power header.
|
||||
* True for the Renesas RA4M1 generation, which shares a USB-C connector and the RA4M1 in place of
|
||||
* the ATmega328P.
|
||||
*/
|
||||
private boolean isR4() {
|
||||
return getVersion() == ArduinoUnoVersion.R4_WIFI || getVersion() == ArduinoUnoVersion.R4_MINIMA;
|
||||
}
|
||||
|
||||
/**
|
||||
* The second 2x3 ICSP header belongs to the USB bridge chip. The Leonardo's 32U4 and the Minima's
|
||||
* RA4M1 speak USB natively, so neither board carries it.
|
||||
*/
|
||||
private boolean hasBridgeIcspHeader() {
|
||||
return getVersion() == ArduinoUnoVersion.REV3 || getVersion() == ArduinoUnoVersion.R4_WIFI;
|
||||
}
|
||||
|
||||
private String[] getPinNames() {
|
||||
return getVersion() == ArduinoUnoVersion.LEONARDO ? PIN_NAMES_LEONARDO : PIN_NAMES;
|
||||
switch (getVersion()) {
|
||||
case LEONARDO:
|
||||
return PIN_NAMES_LEONARDO;
|
||||
case R4_MINIMA:
|
||||
return PIN_NAMES_R4_MINIMA;
|
||||
default:
|
||||
return PIN_NAMES;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getControlPointNodeName(int index) {
|
||||
// The WiFi shares the R3 pin table except for the reserved power header pin, which both R4
|
||||
// boards use for boot mode selection - the Minima says so in its own table
|
||||
if (index == 0 && getVersion() == ArduinoUnoVersion.R4_WIFI) {
|
||||
return "BOOT";
|
||||
}
|
||||
String[] pinNames = getPinNames();
|
||||
if (index >= 0 && index < pinNames.length) {
|
||||
return pinNames[index];
|
||||
}
|
||||
if (isR4()) {
|
||||
if (getVersion() == ArduinoUnoVersion.R4_WIFI) {
|
||||
if (index == 44) return "OFF";
|
||||
if (index == 45) return "GND";
|
||||
if (index == 46) return "VRTC";
|
||||
@@ -175,7 +219,9 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
|
||||
// Reference: Pin 0 (NC) is at (boardX + 220.0, boardY + 400.0) [1100 mils, 100 mils from bottom-left]
|
||||
// Board is 539.78px wide, 419.78px high
|
||||
int numPins = isR4() ? 47 : getPinNames().length;
|
||||
// Only the WiFi adds the OFF / GND / VRTC header on top of the names listed for the version
|
||||
int numPins =
|
||||
getVersion() == ArduinoUnoVersion.R4_WIFI ? PIN_NAMES.length + 3 : getPinNames().length;
|
||||
double[][] relativeOffsets = new double[numPins][2];
|
||||
|
||||
// Power header pins 0..7 (bottom left row)
|
||||
@@ -215,8 +261,8 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
relativeOffsets[36] = new double[] {icspX, icspY + 2 * spacing};
|
||||
relativeOffsets[37] = new double[] {icspX + spacing, icspY + 2 * spacing};
|
||||
|
||||
// Top-Left ICSP header (2x3 pins, 38..43) for ATmega16U2, absent on the Leonardo
|
||||
if (getVersion() != ArduinoUnoVersion.LEONARDO) {
|
||||
// Top-Left ICSP header (2x3 pins, 38..43) for ATmega16U2
|
||||
if (hasBridgeIcspHeader()) {
|
||||
double icsp2X = -new Size(0.28d, SizeUnit.in).convertToPixels(); // -56.0 px
|
||||
double icsp2Y = -new Size(1.77d, SizeUnit.in).convertToPixels(); // -354.0 px
|
||||
if (isR4()) {
|
||||
@@ -230,12 +276,22 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
relativeOffsets[43] = new double[] {icsp2X - 2 * spacing, icsp2Y + spacing};
|
||||
}
|
||||
|
||||
if (isR4()) {
|
||||
if (getVersion() == ArduinoUnoVersion.R4_WIFI) {
|
||||
relativeOffsets[44] = new double[] {-4 * spacing, 0}; // OFF
|
||||
relativeOffsets[45] = new double[] {-3 * spacing, 0}; // GND
|
||||
relativeOffsets[46] = new double[] {-2 * spacing, 0}; // VRTC
|
||||
}
|
||||
|
||||
// The SWD connector (38) gets a single point in the middle of its body, the way the Pi models
|
||||
// its flat-flex connectors, rather than ten points 1.27mm apart that nothing could reach
|
||||
if (getVersion() == ArduinoUnoVersion.R4_MINIMA) {
|
||||
relativeOffsets[38] = new double[] {
|
||||
SWD_X.convertToPixels() + SWD_WIDTH.convertToPixels() / 2.0
|
||||
- new Size(1.1d, SizeUnit.in).convertToPixels(),
|
||||
SWD_Y.convertToPixels() + SWD_HEIGHT.convertToPixels() / 2.0
|
||||
- new Size(2.0d, SizeUnit.in).convertToPixels()};
|
||||
}
|
||||
|
||||
rotatePoints(firstPoint, relativeOffsets);
|
||||
}
|
||||
|
||||
@@ -365,8 +421,12 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
|
||||
double raW = new Size(10d, SizeUnit.mm).convertToPixels();
|
||||
double raH = new Size(10d, SizeUnit.mm).convertToPixels();
|
||||
double raX = boardX + boardW - new Size(11.5d, SizeUnit.mm).convertToPixels() - raW;
|
||||
double raY = boardY + new Size(12d, SizeUnit.mm).convertToPixels();
|
||||
// The Minima carries the RA4M1 further from the top right corner than the WiFi does
|
||||
boolean minima = getVersion() == ArduinoUnoVersion.R4_MINIMA;
|
||||
double raShiftX = minima ? new Size(8d, SizeUnit.mm).convertToPixels() : 0;
|
||||
double raShiftY = minima ? new Size(4.5d, SizeUnit.mm).convertToPixels() : 0;
|
||||
double raX = boardX + boardW - new Size(11.5d, SizeUnit.mm).convertToPixels() - raW - raShiftX;
|
||||
double raY = boardY + new Size(12d, SizeUnit.mm).convertToPixels() + raShiftY;
|
||||
g2d.setColor(Color.BLACK);
|
||||
g2d.fill(new RoundRectangle2D.Double(raX, raY, raW, raH, 2, 2));
|
||||
g2d.setColor(Color.DARK_GRAY);
|
||||
@@ -374,6 +434,20 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
g2d.setColor(Color.WHITE);
|
||||
g2d.setFont(SILK_FONT_SMALL);
|
||||
StringUtils.drawCenteredText(g2d, "RA4M1", raX + raW / 2.0, raY + raH / 2.0, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
if (getVersion() == ArduinoUnoVersion.R4_MINIMA) {
|
||||
double swdW = SWD_WIDTH.convertToPixels();
|
||||
double swdH = SWD_HEIGHT.convertToPixels();
|
||||
double swdX = boardX + SWD_X.convertToPixels();
|
||||
double swdY = boardY + SWD_Y.convertToPixels();
|
||||
drawFpcConnector(g2d, swdX, swdY, swdW, swdH, true, "");
|
||||
|
||||
g2d.setColor(SILK_COLOR);
|
||||
g2d.setFont(SILK_FONT_SMALL);
|
||||
StringUtils.drawCenteredText(g2d, "SWD", swdX + swdW / 2.0,
|
||||
swdY + swdH + new Size(1.5d, SizeUnit.mm).convertToPixels(),
|
||||
HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
}
|
||||
} else if (getVersion() == ArduinoUnoVersion.LEONARDO) {
|
||||
// The 32U4 is a square TQFP-44 where the R3 carries its DIP-28
|
||||
double chipSize = new Size(0.4d, SizeUnit.in).convertToPixels();
|
||||
@@ -398,15 +472,19 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
StringUtils.drawCenteredText(g2d, "RST", btnX + btnW / 2.0,
|
||||
btnY + btnH + new Size(1.0d, SizeUnit.mm).convertToPixels(), HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
// The Minima prints its branding lower, in the space the WiFi gives to the radio module
|
||||
double brandShiftY = getVersion() == ArduinoUnoVersion.R4_MINIMA
|
||||
? new Size(24d, SizeUnit.mm).convertToPixels() : 0;
|
||||
|
||||
// Arduino Infinity Logo
|
||||
drawArduinoLogo(g2d, boardX + new Size(1.26d, SizeUnit.in).convertToPixels() - new Size(3.0d, SizeUnit.mm).convertToPixels(),
|
||||
boardY + new Size(0.4411d, SizeUnit.in).convertToPixels() - new Size(3.0d, SizeUnit.mm).convertToPixels());
|
||||
boardY + new Size(0.4411d, SizeUnit.in).convertToPixels() - new Size(3.0d, SizeUnit.mm).convertToPixels() + brandShiftY);
|
||||
|
||||
// Silkscreen text & branding
|
||||
g2d.setColor(SILK_COLOR);
|
||||
|
||||
double arduinoX = boardX + new Size(1.5d, SizeUnit.in).convertToPixels() - new Size(3.0d, SizeUnit.mm).convertToPixels();
|
||||
double arduinoY = boardY + new Size(0.775d, SizeUnit.in).convertToPixels() - new Size(4.0d, SizeUnit.mm).convertToPixels();
|
||||
double arduinoY = boardY + new Size(0.775d, SizeUnit.in).convertToPixels() - new Size(4.0d, SizeUnit.mm).convertToPixels() + brandShiftY;
|
||||
g2d.setFont(SILK_FONT_LARGE.deriveFont(SILK_FONT_LARGE.getSize2D() * 1.5f));
|
||||
StringUtils.drawCenteredText(g2d, "ARDUINO", arduinoX, arduinoY, HorizontalAlignment.CENTER, VerticalAlignment.CENTER);
|
||||
|
||||
@@ -433,8 +511,11 @@ public class ArduinoUno extends AbstractMakerBoard {
|
||||
|
||||
g2d.setTransform(oldTx);
|
||||
|
||||
// Draw header pins with continuity tracking
|
||||
drawPinHeader(g2d, 0, controlPoints.length, true, outlineMode, drawingObserver);
|
||||
// Draw header pins with continuity tracking; the Minima's SWD point sits inside the connector
|
||||
// body drawn above and gets no header pin of its own
|
||||
int headerPinCount = getVersion() == ArduinoUnoVersion.R4_MINIMA
|
||||
? controlPoints.length - 1 : controlPoints.length;
|
||||
drawPinHeader(g2d, 0, headerPinCount, true, outlineMode, drawingObserver);
|
||||
|
||||
g2d.setComposite(oldComposite);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.diylc.components.micro;
|
||||
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@@ -99,20 +100,44 @@ public class ArduinoUnoTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testR4MinimaMatchesR4WiFi() {
|
||||
// The Minima is the same board without the radio, so pin count and geometry are identical.
|
||||
public void testR4MinimaDropsBridgeIcspAndVrtcHeaders() {
|
||||
ArduinoUno wifi = new ArduinoUno();
|
||||
wifi.setVersion(ArduinoUno.ArduinoUnoVersion.R4_WIFI);
|
||||
ArduinoUno minima = new ArduinoUno();
|
||||
minima.setVersion(ArduinoUno.ArduinoUnoVersion.R4_MINIMA);
|
||||
|
||||
// The WiFi adds OFF / GND / VRTC to the 44 shared pins; the Minima has neither those nor the
|
||||
// six second-ICSP pins, and carries a single SWD node instead: 44 - 6 + 1 = 39
|
||||
Assert.assertEquals(47, wifi.getControlPointCount());
|
||||
Assert.assertEquals(47, minima.getControlPointCount());
|
||||
Assert.assertEquals(39, minima.getControlPointCount());
|
||||
Assert.assertEquals("OFF", wifi.getControlPointNodeName(44));
|
||||
Assert.assertEquals("SWD", minima.getControlPointNodeName(38));
|
||||
|
||||
// Both R4 boards use the R3's reserved power header pin for boot mode selection
|
||||
Assert.assertEquals("BOOT", minima.getControlPointNodeName(0));
|
||||
Assert.assertEquals("BOOT", wifi.getControlPointNodeName(0));
|
||||
|
||||
// The shield footprint is shared, so every header pin lands where the WiFi's does
|
||||
Assert.assertEquals(wifi.getBodyShape().getBounds2D(), minima.getBodyShape().getBounds2D());
|
||||
for (int i = 0; i < wifi.getControlPointCount(); i++) {
|
||||
for (int i = 0; i < 38; i++) {
|
||||
Assert.assertEquals("Pin " + i, wifi.getControlPoint(i), minima.getControlPoint(i));
|
||||
Assert.assertEquals("Pin " + i, wifi.getControlPointNodeName(i), minima.getControlPointNodeName(i));
|
||||
}
|
||||
|
||||
// The SWD connector sits in the top right quadrant, below the digital header
|
||||
Point2D swd = minima.getControlPoint(38);
|
||||
Rectangle2D bounds = minima.getBodyShape().getBounds2D();
|
||||
Assert.assertTrue("SWD should be on the board", bounds.contains(swd));
|
||||
Assert.assertTrue("SWD should be on the right half", swd.getX() > bounds.getCenterX());
|
||||
Assert.assertTrue("SWD should be on the top half", swd.getY() < bounds.getCenterY());
|
||||
Assert.assertTrue("SWD should be below the digital header",
|
||||
swd.getY() > minima.getControlPoint(14).getY());
|
||||
|
||||
for (int i = 0; i < minima.getControlPointCount(); i++) {
|
||||
String name = minima.getControlPointNodeName(i);
|
||||
Assert.assertNotNull("Pin " + i + " name should not be null", name);
|
||||
Assert.assertFalse("Pin " + i + " name should not be empty", name.trim().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,6 +155,9 @@ public class ArduinoUnoTest {
|
||||
Assert.assertEquals("Pin " + i, r3.getControlPoint(i), leonardo.getControlPoint(i));
|
||||
}
|
||||
|
||||
// The reserved power header pin is not a boot mode selector on the AVR boards
|
||||
Assert.assertEquals("NC", leonardo.getControlPointNodeName(0));
|
||||
|
||||
// I2C is shared with D2 / D3 on the Leonardo
|
||||
Assert.assertEquals("D2 (SDA)", leonardo.getControlPointNodeName(16));
|
||||
Assert.assertEquals("D3 (~, SCL)", leonardo.getControlPointNodeName(17));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Maker Board Roadmap
|
||||
clau# Maker Board Roadmap
|
||||
|
||||
Closing the gap between `org.diylc.components.micro` and what people building with
|
||||
microcontroller boards today actually reach for.
|
||||
|
||||
Reference in New Issue
Block a user