diff --git a/ergogen/config.yaml b/ergogen/config.yaml index e48c3e7..8f0a2ab 100644 --- a/ergogen/config.yaml +++ b/ergogen/config.yaml @@ -8,7 +8,7 @@ meta: # Required version of Ergogen engine: "4.1.0" # Version of the board - version: "1.0" + version: "1.1" author: binaryDiv # Define units and default values used throughout this file @@ -29,268 +29,409 @@ units: $default_spread: cx $default_padding: cy - # Vertical offset between function keys and primary keys - function_zone_offset: 3 + # Vertical offset between function key row and main zone + function_offset: 3 -# Define the points (i.e. keys) + # Horizontal offset/gap between function key groups (Esc, F1-F4, F5-F8, F9-F12) + function_gap: 6 + + # Distance between left and right half of the main zone + split_gap_width: 2cx + 3 + + # Radius of rotary encoder knob (just for visualization) + rotary_encoder_radius: 10 + +# Define the points (primarily the keys, but also some auxiliary points for +# placement of other components, e.g. rotary encoders and the controller board) points: key: # Tag all points as "is_key" unless overwritten tags: [ is_key ] + # Use 5V as VCC for all RGB LEDs, except for the first one in the chain + led_vcc_net: VCC zones: - # Primary keyboard zone - primary: + # Main keyboard zone + main: # Set anchor to fix placement on KiCad sheet anchor: - shift: [ 100, -150 ] + shift: [ 75, -175 ] # Key rows (from bottom to top) rows: - # Modifier row (Ctrl, ..., but excluding the thumb keys) + # Modifier row (Ctrl etc., but excluding the thumb keys) mods: - row_net: GP17 - led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" - tags: [ is_key, flip_led ] + row_net: ROW1 + led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" - # Bottom letter row (Shift, ZXCV...) + # Bottom letter row bottom: - row_net: GP16 - led_previous_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}" + row_net: ROW2 + led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}" + tags: [ is_key, flip_led ] - # Middle/home letter row (Caps Lock, ASDFG...) + # Home letter row home: - row_net: GP15 - led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" - tags: [ is_key, flip_led ] + row_net: ROW3 + led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" - # Top letter row (Tab, QWERT...) + # Top letter row top: - row_net: GP14 - led_previous_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}" - - # Number row (`, 12345...) - numbers: - row_net: GP13 - led_previous_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" + row_net: ROW4 + led_next_key: "{{zone.name}}_{{neighbor_col_right}}_{{row}}" tags: [ is_key, flip_led ] + # Number row + numbers: + row_net: ROW5 + led_next_key: "{{zone.name}}_{{neighbor_col_left}}_{{row}}" + # Key columns (from left to right) columns: - # Left-most column (`, Tab, Caps Lock, Shift, Ctrl) + # Left-most column (extra keys) + left: + key.column_net: COL1 + key.neighbor_col_right: zero + rows.numbers.led_next_key: main_left_top + rows.home.led_next_key: main_left_bottom + # Use placeholder to mark end of the RGB LED chain (unused net name) + rows.mods.led_next_key: END_OF_CHAIN + # Modifier column (`, Tab, Shift, etc.) zero: - key.column_net: GP8 + key.stagger: 1/8cy + key.column_net: COL2 + key.neighbor_col_left: left key.neighbor_col_right: one - rows.numbers.led_previous_key: function_esc_default - rows.home.led_previous_key: primary_zero_top - rows.mods.led_previous_key: primary_zero_bottom # First letter column (1, Q) one: - key.column_net: GP7 + key.stagger: 1/8cy + key.column_net: COL3 key.neighbor_col_left: zero key.neighbor_col_right: two # Second letter column (2, W) two: - key.column_net: GP6 + key.stagger: 1/8cy + key.column_net: COL4 key.neighbor_col_left: one key.neighbor_col_right: three rows.mods.skip: true # Third letter column (3, E) three: - key.column_net: GP5 + key.column_net: COL5 key.neighbor_col_left: two key.neighbor_col_right: four rows.mods.skip: true # Fourth letter column (4, R) four: - key.column_net: GP4 + key.stagger: -(1/4cy) + key.column_net: COL6 key.neighbor_col_left: three key.neighbor_col_right: five rows.mods.skip: true # Fifth letter column (5, T) five: - key.column_net: GP3 + key.stagger: -(1/4cy) + key.column_net: COL7 key.neighbor_col_left: four key.neighbor_col_right: six + rows.top.led_next_key: arrow_keys_up + rows.bottom.led_next_key: arrow_keys_down rows.mods.skip: true + + # (Gap with arrow keys in the middle: Matrix column 8) + # Sixth letter column (6, Y) six: - key.column_net: GP2 + key.spread: cx + split_gap_width + key.column_net: COL9 key.neighbor_col_left: five key.neighbor_col_right: seven + rows.home.led_next_key: arrow_keys_right rows.mods.skip: true # Seventh letter column (7, U) seven: - key.column_net: GP1 + key.stagger: 1/4cy + key.column_net: COL10 key.neighbor_col_left: six key.neighbor_col_right: eight rows.mods.skip: true # Eighth letter column (8, I) eight: - key.column_net: GP0 + key.stagger: 1/4cy + key.column_net: COL11 key.neighbor_col_left: seven key.neighbor_col_right: nine rows.mods.skip: true # Nineth letter column (9, O) nine: - key.column_net: GP25 + key.column_net: COL12 key.neighbor_col_left: eight key.neighbor_col_right: ten rows.mods.skip: true # Tenth letter column (0, P) ten: - key.column_net: GP24 + key.stagger: -(1/8cy) + key.column_net: COL13 key.neighbor_col_left: nine key.neighbor_col_right: eleven - rows.mods.skip: true + rows.mods.led_next_key: mirror_thumb_one # Eleventh letter column (-, [) eleven: - key.column_net: GP23 + key.stagger: -(1/8cy) + key.column_net: COL14 key.neighbor_col_left: ten key.neighbor_col_right: twelve - rows.mods.led_previous_key: thumb_mirror_one_default # Twelfth letter column (=, ]) twelve: - key.column_net: GP22 + key.stagger: -(1/8cy) + key.column_net: COL15 key.neighbor_col_left: eleven - rows.top.led_previous_key: primary_twelve_numbers - rows.bottom.led_previous_key: primary_twelve_home + rows.top.led_next_key: main_twelve_home + rows.bottom.led_next_key: main_twelve_mods # Thumb fan thumb: - # Position thumb keys based on the (non-existant) third key in the modifier row + # Position thumb keys based on the third key in the modifier row anchor: - ref: primary_two_mods - shift: [ 2, 0 ] + ref: main_one_mods + shift: [ cx + 1, 0 ] - # Rotate the thumb keys around the bottom-left corner of the key + # Zone-wide key settings key: - row_net: GP17 - tags: [ is_key, flip_led ] + row_net: ROW1 + # Rotate the thumb keys around the bottom-left corner of the key origin: [ -0.5cx, -0.5cy ] - splay: -4 + splay: -6 # Define thumb keys columns: one: - key.column_net: GP6 - key.mirror.column_net: GP24 - key.led_previous_key: primary_one_mods - key.mirror.led_previous_key: thumb_mirror_two_default + key.column_net: COL4 + key.mirror.column_net: COL12 + key.led_next_key: main_one_mods + key.mirror.led_next_key: mirror_thumb_two two: - key.column_net: GP5 - key.mirror.column_net: GP25 - key.led_previous_key: thumb_one_default - key.mirror.led_previous_key: thumb_mirror_three_default + key.column_net: COL5 + key.mirror.column_net: COL11 + key.led_next_key: thumb_one + key.mirror.led_next_key: mirror_thumb_three three: - key.column_net: GP4 - key.mirror.column_net: GP0 - key.led_previous_key: thumb_two_default - key.mirror.led_previous_key: thumb_mirror_four_default + key.column_net: COL6 + key.mirror.column_net: COL10 + key.led_next_key: thumb_two + key.mirror.led_next_key: mirror_thumb_four four: - key.column_net: GP3 - key.mirror.column_net: GP1 - key.led_previous_key: thumb_three_default - key.mirror.led_previous_key: thumb_four_default + key.column_net: COL7 + key.mirror.column_net: COL9 + key.led_next_key: thumb_three + key.mirror.led_next_key: thumb_center # Mirror the thumb fan mirror: - ref: primary_five_numbers - shift: [ 0.5cx, 0.5cx ] - distance: 1cx + ref: main_five_bottom + shift: [ 0.5cx, 0.5cy ] + distance: split_gap_width - # Function key row above the primary zone (Esc + 8 more keys) - # (Note that the keys are only named "f[1-8]" for convenience and are not a full set of F1-F12 keys. They can be - # used as F-keys when needed, but will more likely be used for macros and other special functions.) - function: - # Position function key row above primary zone with 3mm of vertical offset + # Single key centered between thumb keys + thumb_center: anchor: - ref: primary_zero_numbers - shift: [ 0, cy + function_zone_offset ] + # Center between the two inner thumb keys + aggregate.parts: + - thumb_four + - mirror_thumb_four + shift: [ 0, -0.25cy ] + # Key settings (instead of rows/columns) key: - row_net: GP12 + width: 1.5kcx + row_net: ROW1 + column_net: COL8 + led_next_key: thumb_four + tags: [ is_key, 1_5u ] + + # Arrow keys in the middle of the keyboard + # In the matrix, these keys act as 4 keys in a single column (column 8, row 2 to 5) + arrow_keys: + anchor: + # Center between the V and B keys, then shift down half a key + aggregate.parts: + - main_five_bottom + - main_six_bottom + shift: [ 0, -cy/2 ] + + # Assign all keys to the same column + key: + column_net: COL8 + + rows: + # Bottom row: Only the down key + bottom: + row_net: ROW2 + tags: [ is_key, flip_led ] + + # Center row: Left and right keys (shifted to the left to center them) + center: + adjust.shift: [ -cx/2, 0 ] + # Use ROW3 for the left key and ROW5 for the right key (overridden in columns) + row_net: ROW3 + + # Top row: Only the up key + top: + row_net: ROW4 + tags: [ is_key, flip_led ] + + columns: + # Left column: Up, left, down keys + left: + rows.top: + name: arrow_keys_up + led_next_key: main_six_top + rows.center: + name: arrow_keys_left + led_next_key: main_five_home + rows.bottom: + name: arrow_keys_down + led_next_key: main_six_bottom + + # Right column: Only right key (assigned to matrix row 5) + right: + rows.top.skip: true + rows.center: + name: arrow_keys_right + row_net: ROW5 + led_next_key: arrow_keys_left + rows.bottom.skip: true + + # Function key row above the main zone (Esc, F1-F12) + function: + # Position function key row above main zone with 3mm of vertical offset + anchor: + # To center the F keys on the top of the keyboard: + # - Get center point between keys 3 and 8 (they have the highest staggering) + # - Shift upwards by one key + an offset to have a small gap + # - Shift to the left by 7 keys (Esc, F1-F6) + the gaps between the F key groups. + aggregate.parts: + - main_three_numbers + - main_eight_numbers + shift: [ -6.5cx - 2 * function_gap, cy + function_offset ] + + # Single row zone: Assign row net + key: + row_net: ROW6 + tags: [ is_key, flip_led ] # Define function keys columns: # Escape key esc: - key.column_net: GP8 - key.led_previous_key: function_f1_default + key.shift: [ 0, -cy/8 ] + key.column_net: COL1 + key.led_next_key: function_f1 + # Esc is the first key in the RGB LED chain. + # The first LED needs to run on a lower voltage (see explanation below). + key.led_vcc_net: VCC_first_led - # Block of 4 keys with 6mm offset + # Block of 4 keys with gap f1: - key.spread: cx + 6 - key.column_net: GP7 - key.led_previous_key: function_f2_default + key.spread: cx + function_gap + key.column_net: COL2 + key.led_next_key: function_f2 f2: - key.column_net: GP6 - key.led_previous_key: function_f3_default + key.column_net: COL3 + key.led_next_key: function_f3 f3: - key.column_net: GP5 - key.led_previous_key: function_f4_default + key.column_net: COL4 + key.led_next_key: function_f4 f4: - key.column_net: GP4 - key.led_previous_key: function_f5_default + key.column_net: COL5 + key.led_next_key: function_f5 - # Block of 4 keys with 6mm offset + # Block of 4 keys with gap f5: - key.spread: cx + 6 - key.column_net: GP3 - key.led_previous_key: function_f6_default + key.spread: cx + function_gap + key.column_net: COL6 + key.led_next_key: function_f6 f6: - key.column_net: GP2 - key.led_previous_key: function_f7_default + key.column_net: COL7 + key.led_next_key: function_f7 + # Skip matrix column 8 (arrow keys) f7: - key.column_net: GP1 - key.led_previous_key: function_f8_default + key.column_net: COL9 + key.led_next_key: function_f8 f8: - key.column_net: GP0 - key.led_previous_key: status_led + key.column_net: COL10 + key.led_next_key: function_f9 - # Additional points for special components in the upper right corner: Status LED, reset button, rotary encoder - special: - # Align points with the function key zone + # Block of 4 keys with gap + f9: + key.spread: cx + function_gap + key.column_net: COL11 + key.led_next_key: function_f10 + f10: + key.column_net: COL12 + key.led_next_key: function_f11 + f11: + key.column_net: COL13 + key.led_next_key: function_f12 + f12: + key.column_net: COL14 + key.led_next_key: main_twelve_numbers + + # RP2040 Pico Mini controller board + controller: anchor: - ref: primary_ten_numbers - shift: [ 0, cy + function_zone_offset ] + # Place in the center of the keyboard, above the arrow keys + aggregate.parts: + - main_five_numbers + - main_six_numbers + shift: [ 0, 1.5 ] - # These are not actually keys + # This is an auxiliary point to place the controller, not an actual key key: - tags: [ no_key ] + tags: [ is_controller ] + width: 18 + height: 36 - # Define points - columns: - # Neopixel status LED (first in the chain, see comment in PCB footprints for details) - status_led: - key: - width: 3.4 - height: 3.0 + # Rotary encoder in the center of the keyboard (on top of the controller board) + rotary_center: + anchor: + ref: controller + rotate: -90 + key: + tags: [ is_rotary_encoder ] + width: 2 * rotary_encoder_radius + height: 2 * rotary_encoder_radius + # Matrix position of the switch: between F7 and F8 + row_net: ROW6 + column_net: COL8 - # Reset button - reset_button: - key: - shift: [ -0.25cx - 1, 0 ] - width: 6.5 - height: 6.5 - - # Rotary encoder in the upper right corner - rotary: - key: - row_net: GP12 - column_net: GP22 - shift: [ -0.25cx, 0 ] - width: 20.5 - height: 20.5 - tags: [ is_rotary_encoder ] + # Rotary encoder in the top right corner of the keyboard + rotary_top_right: + anchor: + ref: function_f12 + shift: [ cx + 2 + function_gap, -cy/8 ] + key: + tags: [ is_rotary_encoder ] + width: 2 * rotary_encoder_radius + height: 2 * rotary_encoder_radius + # Matrix position of the switch: Function row, right of F12 + row_net: ROW6 + column_net: COL15 # Generate outlines that can be used in the PCB and for 3D models outlines: # Outline of the key caps keys: + # 1u keys - what: rectangle - where: is_key + operation: stack + where: [ [ is_key, -1_5u ] ] size: [ kcx, kcy ] + # 1.5u keys + - what: rectangle + operation: stack + where: [ [ is_key, 1_5u ] ] + size: [ 1.5kcx, kcy ] # Rotary encoder with knob (2cm) rotary_encoder: @@ -299,46 +440,77 @@ outlines: size: [ 14, 14 ] - what: circle operation: stack - radius: 10 + radius: rotary_encoder_radius # Outline for the PCB - board: + board_outline: - what: polygon points: - - ref: primary_zero_numbers - shift: [ -0.5cx + 1, 1.5cy + function_zone_offset + 2 ] - - ref: primary_twelve_numbers - shift: [ 0.5cx - 1, 1.5cy + function_zone_offset + 2] - - ref: primary_twelve_mods - shift: [ 0.5cx - 1, -0.5cy + 2 ] - - ref: mirror_thumb_four - shift: [ 0.5cx, -0.5cy + 2 ] - - ref: thumb_four - shift: [ 0.5cx, -0.5cy + 2 ] - - ref: primary_zero_mods - shift: [ -0.5cx + 1, -0.5cy + 2 ] - expand: 4 + # Top left corner + - ref: &corner_top_left_anchor + - ref: main_left_numbers + affect: x + - ref: function_f1 + affect: y + - shift: [ -0.5kcx, 0.5kcy ] + # Extend all corners 4mm to the sides to get a 4mm border + shift: [ -4, 4 ] + + # Top right corner + - ref: &corner_top_right_anchor + - ref: main_twelve_numbers + affect: x + - ref: function_f12 + affect: y + - shift: [ 0.5kcx, 0.5kcy ] + shift: [ 4, 4 ] + + # Bottom right corner + - ref: + - ref: main_twelve_mods + shift: [ 0.5kcx, -0.5kcy ] + shift: [ 4, -4 ] + + # Bottom center-right corner (below thumb keys) + - ref: + - ref: mirror_thumb_four + shift: [ 0.5kcx, -0.5kcy ] + # Discard the rotation, so the next shift moves the corner straight down + affect: xy + shift: [ 0, -4 ] + + # Bottom center-left corner (below thumb keys) + - ref: + - ref: thumb_four + shift: [ 0.5kcx, -0.5kcy ] + # Discard the rotation, so the next shift moves the corner straight down + affect: xy + shift: [ 0, -4 ] + + # Bottom left corner + - ref: + - ref: main_left_mods + shift: [ -0.5kcx, -0.5kcy ] + shift: [ -4, -4 ] + + # Round off the corners with an 8mm radius fillet. + # NOTE: To get a smoother arc on the bottom of the board, do some post-processing in KiCad: + # 1. Remove the bottom center line and the two small fillet arcs next to it. + # 2. Select the two remaining bottom lines. Right click them and use "Shape Modification -> Extend Lines to Meet". + # 3. Right click the two angled lines again. Use "Shape Modification -> Fillet Lines..." with a 200mm radius. + fillet: 8 # Preview version of board with key caps and components for visualization board_preview: - - board + - board_outline - ^keys - # RP2040 controller board + + # RP2040 Pico Mini controller board - what: rectangle operation: stack - where: primary_eleven_numbers - size: [ 54, 20 ] - adjust.shift: [ 0, cy + function_zone_offset ] - # Status LED - - what: rectangle - operation: stack - where: special_status_led - size: [ 3.4, 3.0 ] - # Reset button - - what: rectangle - operation: stack - where: special_reset_button - size: [ 6.5, 6.5 ] + where: is_controller + size: [ 18, 36 ] + # Rotary encoder - what: outline name: rotary_encoder @@ -348,12 +520,12 @@ outlines: # Cutouts for the switches switch_cutouts: - what: rectangle - where: true + where: is_key size: 14 # Board outline with switch cutouts switch_plate: - - board + - board_outline - -switch_cutouts # Generate the PCB @@ -362,20 +534,48 @@ pcbs: # Define outline (edges) of the board based on the outlines defined above outlines: main: - outline: board + outline: board_outline # Define PCB components footprints: controller: - what: rp2040_purple - where: - ref: primary_eleven_numbers - shift: [ 0, cy + function_zone_offset ] - rotate: 90 + what: rp2040_pico_mini + where: is_controller params: + # Mount the controller board on the backside of the PCB orientation: down - # Use VBUS (5V from USB) as VCC net - VBUS: VCC + # Use 5V from USB as VCC net + 5V: VCC + # RGB LEDs: The chain starts with the Esc key + GP29: led_din_function_esc + # Matrix columns + GP27: COL1 + GP26: COL2 + GP25: COL3 + GP23: COL4 + GP22: COL5 + GP21: COL6 + GP20: COL7 + GP17: COL8 # Center column (arrow keys) + GP12: COL9 + GP11: COL10 + GP10: COL11 + GP9: COL12 + GP8: COL13 + GP7: COL14 + GP6: COL15 + # Matrix rows + GP13: ROW1 + GP14: ROW2 + GP19: ROW3 + GP18: ROW4 + GP15: ROW5 + GP28: ROW6 + # Rotary encoders + GP0: rotary_top_right_a + GP1: rotary_top_right_b + GP3: rotary_center_a + GP2: rotary_center_b choc_hotswap: what: choc_pretty @@ -385,10 +585,10 @@ pcbs: params: keycaps: true hotswap: true - from: "{{colrow}}" + from: "diode_{{name}}" to: "{{column_net}}" - diode: + key_diode: what: diode_smd where: is_key adjust: @@ -397,21 +597,21 @@ pcbs: resist: true params: side: B - from: "{{colrow}}" + from: "diode_{{name}}" to: "{{row_net}}" rotary_encoder: what: rotary_modified where: is_rotary_encoder params: - from: "{{colrow}}" + from: "diode_{{name}}" to: "{{column_net}}" - A: GP10 - B: GP11 + A: "{{name}}_a" + B: "{{name}}_b" C: GND - diode_rotary_encoder: - $extends: pcbs.eepyboard.footprints.diode + rotary_encoder_diode: + $extends: pcbs.eepyboard.footprints.key_diode where: is_rotary_encoder adjust: shift: [ 0, 0 ] @@ -426,8 +626,9 @@ pcbs: shift: [ 0, 4.7 ] params: side: B - din: "led_dout_{{led_previous_key}}" - dout: "led_dout_{{zone.name}}_{{colrow}}" + din: "led_din_{{name}}" + dout: "led_din_{{led_next_key}}" + VCC: "{{led_vcc_net}}" led_capacitor: what: cap_0805 @@ -438,12 +639,12 @@ pcbs: resist: true params: side: B - from: VCC + from: "{{led_vcc_net}}" to: GND led_chip_flipped: $extends: pcbs.eepyboard.footprints.led_chip - # Place flipped (rotated by 180°) LEDs in all rows *with* the flip_led tag + # Place flipped (rotated by 180°) LEDs in all rows with the flip_led tag where: [ [ is_key, flip_led ] ] adjust.rotate: 180 @@ -455,93 +656,82 @@ pcbs: rotate: 270 resist: true - # Additional first LED (not bound to a key) that serves as a status LED. - # Additionally, it runs on a reduced voltage to serve as a level converter for the next LED: The RP2040 runs on - # 3.3V, but the SK6812 runs on 5V and requires at least 3.5V (5V * 0.7) for the control signal. By reducing the - # power voltage with a diode, the threshold is reduced as well, so that the signal of the RP2040 is strong enough. - status_led_chip: - what: sk6812_mini_e - where: special_status_led - params: - side: B - VCC: VCC_status_led - din: GP9 - dout: led_dout_status_led - - status_led_capacitor: - what: cap_0805 - where: special_status_led - adjust: - shift: [ -6, -0.2 ] - rotate: 90 - params: - side: B - from: VCC_status_led - to: GND - - # Diode to reduce the voltage of the first LED chip by ~0.7V - status_led_diode: + # The first SK6812 chip in the chain has to run on a reduced voltage to serve as a makeshift level converter for + # all following RGB LEDs. + # This is needed because the RP2040 runs on 3.3V, but the SK6812 runs on 5V and requires at least 3.5V (5V * 0.7) + # for the control signal. By reducing the power voltage with a diode, the threshold is reduced as well, so that + # the signal of the RP2040 is strong enough. + first_led_voltage_diode: what: diode_smd - where: special_status_led + where: function_esc adjust: - shift: [ 0, 3.5 ] - rotate: 0 + shift: [ 9.5, 2.25 ] + rotate: -90 params: side: B from: VCC - to: VCC_status_led + to: VCC_first_led - reset_button: - what: button_6x6 - where: special_reset_button - params: - from: GND - to: RUN - - # Mounting holes + # Mounting holes: Top left and right corners mounting_hole_top_left: what: mountinghole_m2 where: - ref: function_esc - shift: [ -9, 11.5 ] - - mounting_hole_top_center: - what: mountinghole_m2 - where: - ref: function_f4 - shift: [ 12.5, 0 ] + ref: *corner_top_left_anchor + shift: [ 2, -2 ] mounting_hole_top_right: what: mountinghole_m2 where: - ref: primary_twelve_numbers - shift: [ 9, cy + function_zone_offset + 11.5 ] + ref: *corner_top_right_anchor + shift: [ -2, -2 ] + # Mounting holes: Top center, left and right of the controller + mounting_hole_controller_left: + what: mountinghole_m2 + where: + ref: main_five_numbers + shift: [ 0, 12 ] + + mounting_hole_controller_right: + what: mountinghole_m2 + where: + ref: main_six_numbers + shift: [ 0, 12 ] + + # Mounting holes: Bottom center, under columns five/six (but above the thumb keys) + mounting_hole_bottom_center_left: + what: mountinghole_m2 + where: + ref: main_five_bottom + shift: [ 0, -13 ] + + mounting_hole_bottom_center_right: + what: mountinghole_m2 + where: + ref: main_six_bottom + shift: [ 0, -13 ] + + # Mounting holes: Bottom left and right corners, under the second key columns mounting_hole_bottom_left: what: mountinghole_m2 where: - ref: primary_one_mods - shift: [ 10.5, -12 ] - - mounting_hole_bottom_center: - what: mountinghole_m2 - where: - ref: primary_six_bottom - shift: [ 0, -23 ] + ref: main_zero_mods + shift: [ 0, -13.5 ] mounting_hole_bottom_right: what: mountinghole_m2 where: - ref: primary_eleven_mods - shift: [ -10.5, -12 ] + ref: main_eleven_mods + shift: [ 0, -13.5 ] # Render text with project name and copyright onto the PCB copyright_text: what: text where: - ref: primary_six_bottom - shift: [ 0, -15 ] + ref: thumb_one + shift: [ 0, -16.5 ] + rotate: -0.3 # total rotation: -6.3 (including key rotation) params: text: |- - eepyBoard v1.0 by binaryDiv + eepyBoard v1.1 by binaryDiv (c) 2024 (MIT License) diff --git a/ergogen/footprints/rp2040_pico_mini.js b/ergogen/footprints/rp2040_pico_mini.js new file mode 100644 index 0000000..6186b7b --- /dev/null +++ b/ergogen/footprints/rp2040_pico_mini.js @@ -0,0 +1,182 @@ +// Pin header footprint for a small RP2040-based controller board from AliExpress called "Pico Mini RP2040". +// It has a similar footprint to the Pro Micro (NOT pin-compatible though!), but has an additional row of pins +// on the short edge of the board (basically forming a "U"). +// ------------------------------- +// Params +// orientation: if up (default), the controller will be on the front side of the PCB, otherwise on the back side + +module.exports = { + params: { + designator: 'MCU', + orientation: 'up', + + // Left side of pins (with USB port at the top), top to bottom + GP0: {type: 'net', value: 'GP0'}, + GP1: {type: 'net', value: 'GP1'}, + GP2: {type: 'net', value: 'GP2'}, + GP3: {type: 'net', value: 'GP3'}, + GP4: {type: 'net', value: 'GP4'}, + GP5: {type: 'net', value: 'GP5'}, + GP6: {type: 'net', value: 'GP6'}, + GP7: {type: 'net', value: 'GP7'}, + GP8: {type: 'net', value: 'GP8'}, + GP9: {type: 'net', value: 'GP9'}, + GP10: {type: 'net', value: 'GP10'}, + GP11: {type: 'net', value: 'GP11'}, + GP12: {type: 'net', value: 'GP12'}, + GP13: {type: 'net', value: 'GP13'}, + + // Bottom side of pins, left to right (i.e. between GP13 and GP19) + GP14: {type: 'net', value: 'GP14'}, + GP15: {type: 'net', value: 'GP15'}, + GP16: {type: 'net', value: 'GP16'}, + GP17: {type: 'net', value: 'GP17'}, + GP18: {type: 'net', value: 'GP18'}, + + // Right side of pins (with USB port at the top), top to bottom + '5V': {type: 'net', value: '5V'}, // 5V USB voltage + GND: {type: 'net', value: 'GND'}, + RST: {type: 'net', value: 'RST'}, // Reset pin: Pull down to reset + '3V3': {type: 'net', value: '3V3'}, // 3.3V voltage used by the MCU + GP29: {type: 'net', value: 'GP29'}, // also: ADC4 + GP28: {type: 'net', value: 'GP28'}, // also: ADC2 + GP27: {type: 'net', value: 'GP27'}, // also: ADC1 + GP26: {type: 'net', value: 'GP26'}, // also: ADC0 + GP25: {type: 'net', value: 'GP25'}, + GP23: {type: 'net', value: 'GP23'}, + GP22: {type: 'net', value: 'GP22'}, + GP21: {type: 'net', value: 'GP21'}, + GP20: {type: 'net', value: 'GP20'}, + GP19: {type: 'net', value: 'GP19'}, + }, + body: p => { + let sign, side, mirror; + + if (p.orientation === 'down') { + sign = -1; + side = 'B'; + mirror = 'mirror'; + } else { + sign = 1; + side = 'F'; + mirror = ''; + } + + const pins_left = [ + 'GP0', 'GP1', 'GP2', 'GP3', 'GP4', 'GP5', 'GP6', 'GP7', 'GP8', 'GP9', 'GP10', 'GP11', 'GP12', 'GP13', + ]; + const pins_bottom = [ + 'GP14', 'GP15', 'GP16', 'GP17', 'GP18', + ]; + const pins_right = [ + '5V', 'GND', 'RST', '3V3', 'GP29', 'GP28', 'GP27', 'GP26', 'GP25', 'GP23', 'GP22', 'GP21', 'GP20', 'GP19', + ]; + + // Functions to map coordinates from a "pin grid" to actual X/Y coordinates. + // The grid is defined as follows (assuming the board is in "up" orientation with the USB port on top): + // - The X coordinates go from 0 (left) to 6 (right) + // - The Y coordinates go from 0 (top) to 13 (bottom) + // This means the pin rows are on these coordinates: + // - Left side of pins: 0/0 to 0/13 + // - Right side of pins: 6/0 to 6/13 + // - Additional pins on the bottom (not counting the left and right pins): 1/13 to 5/13 + // These grid coordinates have to be mapped to footprint coordinates so that the board is centered, meaning + // the grid position 3/6.5 needs to be mapped to footprint coordinates 0/0. The grid is a standard 2.54mm grid. + function pin_pos_x(pin_number) { + // Use integers to avoid floating point issues + return sign * (254 * (pin_number - 3)) / 100; + } + function pin_pos_y(pin_number) { + // Use integers to avoid floating point issues + return (254 * (pin_number - 7) + 127) / 100; + } + + function pin_label(pin_name, pos_x, pos_y, pin_side) { + let offset_x = 0; + let offset_y = 0; + let offset_rotation = 0; + let justify = 'left'; + + if (pin_side === 'left') { + offset_x = -1.3 * sign; + justify = 'right'; + } else if (pin_side === 'right') { + offset_x = 1.3 * sign; + } else if (pin_side === 'bottom') { + offset_y = 1.7; + offset_rotation = -90 * sign; + } + + // Shorten the name of the "GPxx" pins to save a bit of space on the PCB + if (pin_name.startsWith('GP')) { + pin_name = pin_name.slice(2); + } + + return ` + (fp_text user ${pin_name} + (at ${pin_pos_x(pos_x) + offset_x} ${pin_pos_y(pos_y) + offset_y} ${p.r + offset_rotation}) + (unlocked yes) (layer ${side}.SilkS) + (effects (font (size 0.8 0.8) (thickness 0.15)) (justify ${justify} ${mirror})) + )`; + } + + function pin_pad(pad_number, pos_x, pos_y, pin_name) { + return ` + (pad ${pad_number} thru_hole circle + (at ${pin_pos_x(pos_x)} ${pin_pos_y(pos_y)} 0) + (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.Mask) + ${p[pin_name]} + )`; + } + + function render_pins() { + let pin_label_body = ''; + let pin_pad_body = ''; + let pad_number = 1; + + for (let i = 0; i < pins_left.length; i++) { + pin_label_body += pin_label(pins_left[i], 0, i, 'left'); + pin_pad_body += pin_pad(pad_number++, 0, i, pins_left[i]); + } + for (let i = 0; i < pins_bottom.length; i++) { + pin_label_body += pin_label(pins_bottom[i], 1 + i, 13, 'bottom'); + pin_pad_body += pin_pad(pad_number++, 1 + i, 13, pins_bottom[i]); + } + for (let i = 0; i < pins_right.length; i++) { + pin_label_body += pin_label(pins_right[i], 6, i, 'right'); + pin_pad_body += pin_pad(pad_number++, 6, i, pins_right[i]); + } + + return pin_pad_body + '\n' + pin_label_body; + } + + return ` + (module RP2040_PICO_MINI (layer F.Cu) (tedit 66FD3C6D) + ${p.at /* parametric position */} + + ${'' /* footprint reference */} + (fp_text reference "${p.ref}" (at 0 0) (layer ${side}.SilkS) ${p.ref_hide} + (effects (font (size 1.27 1.27) (thickness 0.15))) + ) + (fp_text value "" (at 0 0) (layer ${side}.SilkS) hide + (effects (font (size 1.27 1.27) (thickness 0.15))) + ) + + ${'' /* component outline */} + (fp_line (start -8.8 -18.0) (end 8.8 -18.0) (layer ${side}.SilkS) (width 0.15)) + (fp_line (start 8.8 -18.0) (end 8.8 18.0) (layer ${side}.SilkS) (width 0.15)) + (fp_line (start 8.8 18.0) (end -8.8 18.0) (layer ${side}.SilkS) (width 0.15)) + (fp_line (start -8.8 18.0) (end -8.8 -18.0) (layer ${side}.SilkS) (width 0.15)) + + ${'' /* illustration of the USB port overhang */} + (fp_line (start -4.3 -18.8) (end 4.3 -18.8) (layer Dwgs.User) (width 0.15)) + (fp_line (start 4.3 -18.8) (end 4.3 -11.8) (layer Dwgs.User) (width 0.15)) + (fp_line (start 4.3 -11.8) (end -4.3 -11.8) (layer Dwgs.User) (width 0.15)) + (fp_line (start -4.3 -11.8) (end -4.3 -18.8) (layer Dwgs.User) (width 0.15)) + + ${'' /* pin labels and pads */} + ${render_pins()} + ) + `; + } +} diff --git a/ergogen/footprints/rp2040_purple.js b/ergogen/footprints/rp2040_purple.js deleted file mode 100644 index d039206..0000000 --- a/ergogen/footprints/rp2040_purple.js +++ /dev/null @@ -1,140 +0,0 @@ -// Pin header footprint for a RP2040-based controller board from AliExpress. -// Doesn't really have a name, it's just "the purple one with USB-C". -// The pinout differs from the Raspberry Pi Pico! -// ------------------------------- -// Params -// orientation: if up (default), the controller will be on the front side of the PCB, otherwise on the back side - -module.exports = { - params: { - designator: 'MCU', - orientation: 'up', - - // Right side of pins (with USB port at the top), top to bottom - VBUS: {type: 'net', value: 'VBUS'}, - VIN: {type: 'net', value: 'VIN'}, - GND: {type: 'net', value: 'GND'}, - '3V3_EN': {type: 'net', value: '3V3_EN'}, - '3V3': {type: 'net', value: '3V3'}, - // (GND pin) - RUN: {type: 'net', value: 'RUN'}, // Reset pin: Pull down to reset - GP29: {type: 'net', value: 'GP29'}, // also: ADC3 - GP28: {type: 'net', value: 'GP28'}, // also: ADC2 - GP27: {type: 'net', value: 'GP27'}, // also: ADC1 - GP26: {type: 'net', value: 'GP26'}, // also: ADC0 - AGND: {type: 'net', value: 'AGND'}, - GP25: {type: 'net', value: 'GP25'}, - GP24: {type: 'net', value: 'GP24'}, - GP23: {type: 'net', value: 'GP23'}, - GP22: {type: 'net', value: 'GP22'}, - GP21: {type: 'net', value: 'GP21'}, - GP20: {type: 'net', value: 'GP20'}, - GP19: {type: 'net', value: 'GP19'}, - GP18: {type: 'net', value: 'GP18'}, - - // Left side of pins (with USB port at the top), top to bottom - GP0: {type: 'net', value: 'GP0'}, - GP1: {type: 'net', value: 'GP1'}, - GP2: {type: 'net', value: 'GP2'}, - GP3: {type: 'net', value: 'GP3'}, - GP4: {type: 'net', value: 'GP4'}, - // (GND pin) - GP5: {type: 'net', value: 'GP5'}, - GP6: {type: 'net', value: 'GP6'}, - GP7: {type: 'net', value: 'GP7'}, - GP8: {type: 'net', value: 'GP8'}, - GP9: {type: 'net', value: 'GP9'}, - GP10: {type: 'net', value: 'GP10'}, - GP11: {type: 'net', value: 'GP11'}, - GP12: {type: 'net', value: 'GP12'}, - // (GND pin) - GP13: {type: 'net', value: 'GP13'}, - GP14: {type: 'net', value: 'GP14'}, - GP15: {type: 'net', value: 'GP15'}, - GP16: {type: 'net', value: 'GP16'}, - GP17: {type: 'net', value: 'GP17'}, - }, - body: p => { - let def_neg, def_pos, side, mirror; - if (p.orientation === 'down') { - def_neg = ''; - def_pos = '-'; - side = 'B'; - mirror = 'mirror'; - } else { - def_neg = '-'; - def_pos = ''; - side = 'F'; - mirror = ''; - } - - const PINS_PER_SIDE = 20; - const PINS_PER_SIDE_HALF = 10; - - const pins_left = [ - 'GP0', 'GP1', 'GP2', 'GP3', 'GP4', 'GND', 'GP5', 'GP6', 'GP7', 'GP8', - 'GP9', 'GP10', 'GP11', 'GP12', 'GND', 'GP13', 'GP14', 'GP15', 'GP16', 'GP17', - ]; - const pins_right = [ - 'VBUS', 'VIN', 'GND', '3V3_EN', '3V3', 'GND', 'RUN', 'GP29', 'GP28', 'GP27', - 'GP26', 'AGND', 'GP25', 'GP24', 'GP23', 'GP22', 'GP21', 'GP20', 'GP19', 'GP18', - ]; - - function pin_pos_y(pin_number) { - // Fucking floating points... - return (254 * (pin_number - PINS_PER_SIDE_HALF) + 127) / 100; - } - - function pin_labels(pin_names, right_side) { - const sign = right_side ? def_pos : def_neg; - const justify = right_side ? 'right' : 'left'; - let pin_label_body = ''; - - for (let i = 0; i < PINS_PER_SIDE; i++) { - pin_label_body += ` - (fp_text user ${pin_names[i]} (at ${sign}7.62 ${pin_pos_y(i)} ${p.r}) (unlocked yes) (layer ${side}.SilkS) - (effects (font (size 0.8 0.8) (thickness 0.15)) (justify ${justify} ${mirror})) - )`; - } - return pin_label_body; - } - - function pin_pads(pin_names, right_side) { - const sign = right_side ? def_pos : def_neg; - const offset = right_side ? 21 : 1; - let pin_pad_body = ''; - - for (let i = 0; i < PINS_PER_SIDE; i++) { - pin_pad_body += ` - (pad ${i + offset} thru_hole circle (at ${sign}8.89 ${pin_pos_y(i)} 0) - (size 1.7526 1.7526) (drill 1.0922) (layers *.Cu *.Mask) ${p[pin_names[i]]} - )`; - } - return pin_pad_body; - } - - return ` - (module RP2040_USB_C_Purple (layer F.Cu) (tedit 66A6C4CD) - ${p.at /* parametric position */} - - ${'' /* footprint reference */} - (fp_text reference "${p.ref}" (at 0 0) (layer ${side}.SilkS) ${p.ref_hide} (effects (font (size 1.27 1.27) (thickness 0.15)))) - (fp_text value "" (at 0 0) (layer ${side}.SilkS) hide (effects (font (size 1.27 1.27) (thickness 0.15)))) - - ${'' /* component outline */} - (fp_line (start -10.16 -28.6) (end 10.16 -28.6) (layer ${side}.SilkS) (width 0.15)) - (fp_line (start 10.16 -28.6) (end 10.16 25.4) (layer ${side}.SilkS) (width 0.15)) - (fp_line (start 10.16 25.4) (end -10.16 25.4) (layer ${side}.SilkS) (width 0.15)) - (fp_line (start -10.16 25.4) (end -10.16 -28.6) (layer ${side}.SilkS) (width 0.15)) - - ${'' /* pin labels */} - ${pin_labels(pins_left, false)} - ${pin_labels(pins_right, true)} - - ${'' /* pin pads */} - ${pin_pads(pins_left, false)} - ${pin_pads(pins_right, true)} - ) - `; - } -}