Week 6

Electronics Production

Group assignment#

PCB production design rules#

The week started with an introduction by our TAs Qilmeg and Gert on how to submit a PCB design to a board house.

Group session at the Roland SRM-20 with our TA Gert
Group session at the Roland SRM-20 with our TA Gert

The key advice to retain:

  • The board flat and ensure no air bubbles to ensure no height differences in the cut
  • Swap the bits with the Allen key: 1/64" for the traces, 1/32" for the outline.
  • Ensure the PNG DPI matches the file, set the origin abotu 10 mm in front of the bottom-left corner
Walkthrough of the milling process in mods from the lab session

With our TA Qilmeg and Jason, we milled a Fab line test on the Roland SRM-20 with the 1/64" end mill. It’s a comb of 20 teeth whose traces and gaps grow from 0.001" to 0.020" in 0.001" steps.

The line test
The line test

The first 8 teeth came out imperfect detaching the PCB or missing all together. The first clean one was the 9th, at 0.009", meaning it would be the smallest feature our process can reliably cut.

My week 5 board used the fab rules of 0.016" traces with gaps of 0.020" or more, above this 0.009" limit.

The SRM-20 spindle with the 1/64" end mill
The SRM-20 spindle with the 1/64" end mill

Milling the IF-60 module#

To mill my IF-60 module from week 5, I exported it from KiCAD as Gerber files (copper, outline, drill) and went through gerber2img to become the PNGs that mods reads.

The mods program for the SRM-20: read png, PCB defaults, toolpath and the WebSocket device that talks to the mill
The mods program for the SRM-20: read png, PCB defaults, toolpath and the WebSocket device that talks to the mill

My milling process only worked on the third try. The first one, had rough edges on the right side of the PCB, which Anthony explained a bit that was too loosened.

The first milled IF-60 MODULE board, where the loose pin had not cut the lines cleanly on the lower right side of my board
The first milled IF-60 MODULE board, where the loose pin had not cut the lines cleanly on the lower right side of my board

The second iteration, the bit was dull and did not produce clean lines.

The third worked well albeit my outline PNG got mixed from a previous milling iteration, resulting in an offset of the cut but had no material impact on the board apart from cosmetic and being able to place it in the case designed for it.

(A huge thanks to Qilmeg for her invaluable support in this process and the many hours in the mill shop until we got the third milling correct!)

Vacuuming the copper dust after the traces pass
Vacuuming the copper dust after the traces pass

The advice we received from TAs on PCBs, is to also consider the case to keep it neat and avoid having desoldered or wires coming loose during transport.

With Claude connected to Fusion through MCP, I iterated the design to obtain a closed box lid that clicks on with snaps, and a window over the screem a press tab for SEL, a light tube for the online LED and the engravings on the case for some additional flair.

Fusion model of the IF-60 module and its snap-on case

Soldering and testing#

Resistors under the microscope: 1000 (100 Ω), 4991 (4.99 kΩ), 1002 (10 kΩ) and the 0 Ω jumper
Resistors under the microscope: 1000 (100 Ω), 4991 (4.99 kΩ), 1002 (10 kΩ) and the 0 Ω jumper

Once I had it milled, I soldered the XIAO RP2040, 13 resistors, the ONLINE LED, the SEL switch, the OLED socket and two 6-pin JST headers (thanks to Anthony who had them at the EECS shop!).

The stuffed board: XIAO RP2040, 13 resistors, the two JST headers, the OLED socket, the SEL switch and the ONLINE LED
The stuffed board: XIAO RP2040, 13 resistors, the two JST headers, the OLED socket, the SEL switch and the ONLINE LED

Since I was not at the archshop, I asked Claude whether it would be possible to do some testing without a multimeter to get a sense of what is working in the board.

It wrote an Arduino IDE sketch for the XIAO RP2040 using the same approach set up in week 3.

board_test.ino [ DOWNLOAD ]
Show source (47 lines)
// IF-60 module board test (v0.5): LED, SEL button, the four 5 V input dividers, OLED presence.
// Pin map from the schematic: D0 KBRQ, D1 PWR, D2 KBACK, D3 SO (inputs via 4.99k/10k dividers),
// D4 READY, D5 SI, D6 SCK (outputs via 100 R), D7 LED, D8 SEL (to GND), D9 SDA, D10 SCL.

const int LED = D7, SEL = D8, SDA_PIN = D9, SCL_PIN = D10;
const int INS[] = {D0, D1, D2, D3};
const char *IN_NAMES[] = {"KBRQ", "PWR", "KBACK", "SO"};

// minimal software I2C, only to see whether the OLED answers (D9/D10 are not a hardware I2C pair)
void sda(bool h) { pinMode(SDA_PIN, h ? INPUT_PULLUP : OUTPUT); if (!h) digitalWrite(SDA_PIN, LOW); delayMicroseconds(5); }
void scl(bool h) { pinMode(SCL_PIN, h ? INPUT_PULLUP : OUTPUT); if (!h) digitalWrite(SCL_PIN, LOW); delayMicroseconds(5); }
bool i2cProbe(uint8_t addr) {
  sda(1); scl(1); sda(0); scl(0);                       // start
  uint8_t b = addr << 1;
  for (int i = 7; i >= 0; i--) { sda(b >> i & 1); scl(1); scl(0); }
  sda(1); scl(1); bool ack = digitalRead(SDA_PIN) == LOW; scl(0);
  sda(0); scl(1); sda(1);                               // stop
  return ack;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED, OUTPUT); pinMode(SEL, INPUT_PULLUP);
  for (int i = 0; i < 4; i++) pinMode(INS[i], INPUT);
  pinMode(D4, OUTPUT); pinMode(D5, OUTPUT); pinMode(D6, OUTPUT);   // outputs held low
  digitalWrite(D4, LOW); digitalWrite(D5, LOW); digitalWrite(D6, LOW);
}

void loop() {
  static unsigned long t = 0; static bool led = false; static bool lastSel = false;
  bool pressed = digitalRead(SEL) == LOW;
  if (pressed != lastSel) { Serial.println(pressed ? "SEL pressed" : "SEL released"); lastSel = pressed; }
  if (millis() - t > 1000) {
    t = millis(); led = !led;
    digitalWrite(LED, pressed ? HIGH : led);
    Serial.print("LED "); Serial.print(led ? "on " : "off");
    Serial.print(" | SEL "); Serial.print(pressed ? "DOWN" : "up  ");
    for (int i = 0; i < 4; i++) { Serial.print(" | "); Serial.print(IN_NAMES[i]); Serial.print("="); Serial.print(digitalRead(INS[i])); }
    pinMode(SDA_PIN, INPUT); pinMode(SCL_PIN, INPUT); delay(2);      // no internal pull-up: only the OLED's resistors can pull these high
    int sdaIdle = digitalRead(SDA_PIN), sclIdle = digitalRead(SCL_PIN);
    Serial.print(" | SDA idle="); Serial.print(sdaIdle); Serial.print(" SCL idle="); Serial.print(sclIdle);
    int found = -1;
    for (int a = 0x08; a < 0x78 && found < 0; a++) if (i2cProbe(a)) found = a;
    Serial.print(" | I2C scan: ");
    if (found < 0) Serial.println("nothing"); else { Serial.print("device at 0x"); Serial.println(found, HEX); }
  }
}

The check is meant to send a status line once a second over USB, from there it became apparent the soldering for D7 to the LED needed rework.

The SEL button also had a constant connection, which I suspect was one leg touching the ground copper.

With no typewriter connected, each voltage divider’s 10 kΩ resistor holds its line at 0. Any line reading 1 would mean a missing resistor. All four (R5, R7, R9, R13 (10 kΩ)) read 0.

The OLED didn’t answer. Using the arduino sketch, Claude wrote a second test program that makes use of the screen's pull-up resistor on each line, which brings a connected line back to 3.3 V. By pulling each line to 0 V and letting go, I could see whether it was connected to the screen: SDA bounced back, while SCL stayed at 0 V.

line_test.ino [ DOWNLOAD ]
Show source (19 lines)
// For each line: drive LOW, release (no internal pull-up), time how long until it reads HIGH again.
// Connected to the OLED's pull-up resistor -> back high within a few microseconds.
// Floating (unsoldered XIAO pin or open J2 leg) -> stays low much longer or never returns.
const int PINS[] = {D9, D10}; const char *NAMES[] = {"D9 (SDA)", "D10 (SCL)"};
void setup() { Serial.begin(115200); }
void loop() {
  for (int i = 0; i < 2; i++) {
    int p = PINS[i];
    pinMode(p, OUTPUT); digitalWrite(p, LOW); delay(5);
    pinMode(p, INPUT);
    unsigned long t0 = micros(); long dt = -1;
    while (micros() - t0 < 20000) { if (digitalRead(p)) { dt = micros() - t0; break; } }
    Serial.print(NAMES[i]); Serial.print(": ");
    if (dt < 0) Serial.print("stays LOW (floating or held low)");
    else { Serial.print("back HIGH in "); Serial.print(dt); Serial.print(" us"); }
    Serial.print(i == 0 ? "   |   " : "\n");
  }
  delay(1000);
}

Completing them and debugging the tool is next on my list to test it for my final project.

For now at least, the heavy lifting of the milling which took one full evening and the second day due to three milling was at least completed. Phew...