Skip to content

Commit

Permalink
Fix small issues and compartmentalize pcb-modules
Browse files Browse the repository at this point in the history
  • Loading branch information
hypadr1v3 committed Jul 28, 2023
1 parent c31cfe6 commit 17c3941
Show file tree
Hide file tree
Showing 13 changed files with 1,904 additions and 1,789 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Click any of the links below to access a detailed walkthrough of how they were d

| Recipe | What It Can Do |
| ------------- | ------------- |
| [SD Card Reader](./sd_card_reader/) | Specialized microcontroller, SD card connector, bundles, custom board shapes, general workflow|
| [SD Card Reader](./sd_card_reader/) | Specialized controller, SD card connector, bundles, custom board shapes, general workflow|
| [Battery Charger](./battery_charger_design/) | LiPo battery charger, LDO voltage regulator, USB-C connector, JST battery connector, resistors, capacitors, detailed checks, export to KiCad |
| [USB-C Cable Tester](./usb_c_cable_tester/) | Test points, programmatic parts placement, USB-C connectors, LEDs, coin cell battery, circuit introspection |
| [BLE-mote Wireless IOT Sensor Board](./ble_mote_esp32_iot_board/) | Microcontroller, pin assignment (supports/requires), parametric design, design optimization, copper pour, net-classes, stackup |
Expand Down
49 changes: 31 additions & 18 deletions sd_card_reader/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,69 +26,82 @@ We want our design to be able to:
#### Implementation Plan
To accomplish this, we will use:

* A microcontroller: Microchip USB2240-AEZG-06
* A controller: Microchip USB2240-AEZG-06
* A power regulator: ROHM Semicon BD433M5FP-CE2
* An EEPROM: ST Microelectronics M24C04-WMN6TP
* A USB C male connector: HRO Electronics TYPE-C-31-G-03
* An SD card connector: XUNPU SD-101
* Miscellaneous generic components

## Components
Most of our main components (like our microcontroller) don't exist in the OCDB so we will define these ourselves. Fortunately, we don't have to build the landpattern definitions and pin definitions as our parts exist in the parts database.
Most of our main components (like our controller) don't exist in the OCDB so we will define these ourselves. Fortunately, we don't have to build the landpattern definitions and pin definitions as our parts exist in the parts database.

We want to define each of our main components in their own separate modules so we can reuse them later on for different projects. Thus, each component is put into a `pcb-module`
We can look for parts using the component search. Since, we want to manufacture this board from JLCPCB, we should look for components that have the property `vendor_part_numbers.lcsc`.

![:3](imgs/compsearch.png)

We want to define each of our main components in their own separate modules so we can reuse them later on for different projects. Thus, each component is put into a `pcb-module`.

#### Microcontroller
For this project, we're going to use a Microchip USB2240. For each module we're going to follow a workflow:

* See what inputs and outputs we want and define them as pins and ports.
* We want to use bundles (ports) for pins that are commonly connected together. We can look for bundles in the OCDB by opening `ocdb/utils/bundles.stanza`
* We can also create our own bundles if they don't exist in the OCDB. `sd-card-uhs-1-connector` is an example
```
port power : power
port i2c : i2c
port usb-in : usb-2
port sd-card-conn : sd-card-uhs-1-connector
pin LED
```
* We can also create our own bundles if they don't exist in the OCDB. `sd-card-uhs-1-connector` is an example
```
public pcb-bundle sd-card-uhs-1-connector:
pin CMD ; Command/Response
port power : power ; 3.3v and 2 ground pins
pin CLK ; Clock
port DAT : pin[4] ; Data pins (last pin also used as card detect)
pin CD ; Card detect
pin WP ; Write protect
```

* Instantiate the exact part we want using a database part query
* An alternative to making a query could be searching this part in the component search and clicking "Create component" after selecting a suitable component. We can define component properties here instead of the pcb-module
* An alternative to making a query could be searching for this part in the component search and clicking "Create component" after selecting a suitable component.
```
inst usb2240 : database-part(["manufacturer" => "Microchip", "mpn" => "USB2240-AEZG-06"])
```

* Go through the pins using the design explorer (you can find this by opening the sidebar and clicking "Explorer" under the "Views" section)
* Define each pin's properties with the help of the part's datasheet and the [JITX documentation](https://docs.jitx.com/reference/utilities/properties.html)
* Create and connect components that would be consistent for every usecase for this design (pull-up resistors and decoupling capacitors etc.)
* For this design, we always need a 24MHz crystal so we define it with the same workflow.
* `bypass-cap-strap` creates a capacitor which uses `short-trace`. This hints that we want our PCB trace to be as short as possible to reach both pads
* It's best to define pins we're not using as a `no-connect`
* Create and connect components that would be consistent for every use case for this design (pull-up resistors and decoupling capacitors etc.)
* For this design, we always need a 24MHz crystal so we define it with the same workflow
* `bypass-cap-strap` creates a capacitor which uses `short-trace`. The `short-trace` is a hint to the layout that we want our PCB trace to be as short as possible between both pads
* It's best to define pins we're not using as a `no-connect` to avoid ERC errors in the design flow
* The parameter `"_exist" => ["vendor_part_numbers.lcsc"]` allows us to specify that we require the part to have an LCSC number

#### USB C male connector
Our application uses USB 2 so we want to convert USB C to USB 2. We require a resistor on the CC pin so that our device can be detected.
Our application uses USB2 so we want to use the USB2 protocol over the USB-C connection.

## Bundles
Bundles allow us to define a set of pins which are commonly used together and allow us to view the design at a higher level. Bundles also make connecting pins easy and foolproof. For example, we can create a bundle called `sd-card-uhs-1-connector` and define every pin required for an SD card. Now we can use this to connect our microcontroller to the SD Card connector within a single `net` statement.
Bundles allow us to define a set of pins which are commonly used together and allow us to view the design at a higher level. Bundles also make connecting pins easy and foolproof. For example, we can create a bundle called `sd-card-uhs-1-connector` and define every pin required for an SD card. Now we can use this to connect our controller to the SD Card connector within a single `net` statement.

We can create bundles within bundles too to further abstract our code.
We can create bundles within bundles too to further simplify our code.

## Main Design

We want to define a custom shape for our board with 5mm x 2mm notches so we create the board using `Polygon`
We want to define a custom shape for our board with 5mm x 2mm notches so we create the board using a Polygon shape:
```
val board-shape = Polygon([Point(-15.0,-15.0), Point(10.0,-15.0), Point(10.0,-13.0), Point(15.0,-13.0), Point(15.0, 13.0), Point(10.0, 13.0), Point(10.0, 15.0), Point(-15.0,15.0)])
```

We also need to use this same shape for our ground copper planes
We also can to use this same shape for our ground copper ground planes which extend from edge to edge.
```
geom(gnd) :
copper-pour(LayerIndex(0), isolate = 0.1, rank = 1, orphans = true) = board-shape
copper-pour(LayerIndex(1), isolate = 0.1, rank = 1, orphans = true) = board-shape
```

Now that all our components and the board is defined, we can move on to placing and connecting our modules together. We use the `dims` function to get the dimensions of our custom board shape and use it for placement.
Now that all our components and the board are defined, we can move on to placing and connecting our modules together. We use the `dims` function to get the dimensions of our custom board shape and use it for placement.
```
val d = dims(board-shape)
```
Expand Down Expand Up @@ -139,15 +152,15 @@ net (status-led.out gnd)

#### Layout

With all our components and connections defined, now we can place our components where we want on the board. Then we can select pads (shift+click) or select whole pads (press `a`) and then press `q` to route traces through those pads. We can use vias where necessary by pressing `v` and then dragging from a pad.
With all our components and connections defined, now we can place our components where we want on the board. Then we can select single pads (`click`), extend the selection of pads (`shift+click`) or select all pads connected to the pad we first selected (press `a`) and then press `q` to route traces through those pads.

![:3](imgs/jitx-layout-and-route.png)

#### Export to KiCad

Now that we're happy with the layout, we can export the design straight into KiCad.

First, let's go into `helpers.stanza`, set KiCad as our CAD tool, and add a mapping so that the custom LCSC field get's exported with our components:
First, let's go into `helpers.stanza`, set KiCad as our CAD tool, and add a mapping so that the custom LCSC field gets exported as a component property:
```
val export-field-mapping = [
"LCSC" => "LCSC"
Expand All @@ -165,7 +178,7 @@ Then we build the project with `Ctrl + Enter`, and export with `export-design()`

Let's open up KiCad, open the project, and open the schematic + board viewers. The first thing to do is to click the "Update PCB with changes made to schematic" button in the board view to ensure everything has synced.

Then, change the silkscreen position and add vias as deemed necessary.
Then we need to change the reference texts' silkscreen location as necessary.

![:3](imgs/kicad-done.png)

Expand Down
23 changes: 23 additions & 0 deletions sd_card_reader/bundles.stanza
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#use-added-syntax(jitx)

defpackage bundles:
import core
import collections
import jitx
import jitx/commands
import ocdb/utils/defaults
import ocdb/utils/landpatterns
import ocdb/utils/box-symbol
import ocdb/utils/bundles
import ocdb/utils/property-structs
import ocdb/utils/generic-components
import ocdb/utils/generator-utils
import ocdb/utils/symbols

public pcb-bundle sd-card-uhs-1-connector:
pin CMD ; Command/Response
port power : power ; 3.3v and 2 ground pins
pin CLK ; Clock
port DAT : pin[4] ; Data pins (last pin also used as card detect)
pin CD ; Card detect
pin WP ; Write protect
35 changes: 35 additions & 0 deletions sd_card_reader/components/bd433.stanza
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#use-added-syntax(jitx)

defpackage components/bd433:
import core
import collections
import jitx
import jitx/commands
import ocdb/utils/defaults
import ocdb/utils/landpatterns
import ocdb/utils/box-symbol
import ocdb/utils/bundles
import ocdb/utils/property-structs
import ocdb/utils/generic-components
import ocdb/utils/generator-utils
import ocdb/utils/symbols
import bundles
import helpers

public pcb-module voltage-regulator :
port power-in : power
port power-out : power

inst reg : database-part(["mpn" => "BD433M5FP-CE2", "manufacturer" => "ROHM Semicon"])

property(reg.rated-temperature) = min-max(-40.0, 150.0)
property(reg.mounting) = "smd"
property(reg.VCC.power-pin) = PowerPin(min-max(4.0,42.0))
property(reg.VOUT.power-supply-pin) = PowerSupplyPin(min-max(3.2, 3.37), 500.0e-3)

net (reg.FIN power-in.gnd power-out.gnd)
net (reg.VCC power-in.vdd)
net (reg.VOUT power-out.vdd)

bypass-cap-strap(reg.VCC, power-in.gnd, ["capacitance" => 0.1e-6, "_exist" => ["vendor_part_numbers.lcsc"]])
bypass-cap-strap(reg.VOUT, power-out.gnd, ["capacitance" => 0.1e-6, "_exist" => ["vendor_part_numbers.lcsc"]])
49 changes: 49 additions & 0 deletions sd_card_reader/components/m24c04.stanza
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#use-added-syntax(jitx)

defpackage components/m24c04:
import core
import collections
import jitx
import jitx/commands
import ocdb/utils/defaults
import ocdb/utils/landpatterns
import ocdb/utils/box-symbol
import ocdb/utils/bundles
import ocdb/utils/property-structs
import ocdb/utils/generic-components
import ocdb/utils/generator-utils
import ocdb/utils/symbols
import bundles
import helpers

public pcb-module eeprom :
port power : power
port i2c : i2c

inst eeprom : database-part(["mpn" => "M24C04-WMN6TP", "manufacturer" => "STMicroelectronics"])

property(eeprom.rated-temperature) = min-max(-65.0, 130.0)
val vcc = 3.3

property(eeprom.SDA) = DigitalIO(OpenCollector(min-max(0.0, 0.4), 1.0e-3), min-max(-0.45,0.3 * vcc), min-max(0.7 * vcc, vcc + 1.0), eeprom.VCC, eeprom.VSS, 2.0e-6)
property(eeprom.SCL) = DigitalInput(min-max(-0.45,0.3 * vcc), min-max(0.7 * vcc, vcc + 1.0), eeprom.VCC, eeprom.VSS, 2.0e-6)

property(eeprom.WC_NOT) = DigitalInput(min-max(-0.45,0.3 * vcc), min-max(0.7 * vcc, vcc + 1.0), eeprom.VCC, eeprom.VSS, 2.0e-6)

no-connect(eeprom.NC)
; We only have one EEPROM chip so we leave these floating
no-connect(eeprom.E1)
no-connect(eeprom.E2)

net (eeprom.VCC power.vdd)
net (eeprom.VSS power.gnd)

net (eeprom.SDA i2c.sda)
net (eeprom.SCL i2c.scl)

; Put pull up on SDA and SCL
res-strap(eeprom.SDA, power.vdd, ["resistance" => 5.0e3, "_exist" => ["vendor_part_numbers.lcsc"]])
res-strap(eeprom.SCL, power.vdd, ["resistance" => 5.0e3, "_exist" => ["vendor_part_numbers.lcsc"]])

; We don't want write operations to the EEPROM so we set WC_NOT high
res-strap(eeprom.WC_NOT, power.vdd, ["resistance" => 5.0e3, "_exist" => ["vendor_part_numbers.lcsc"]])
Loading

0 comments on commit 17c3941

Please sign in to comment.