Squashed commit of the following:

commit 13bfa6c6f9e911420e83d2e051a2aa95857359d8
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Tue Mar 4 18:10:42 2025 +0100

    Finish post

commit 5ded765f53b3551ae8ddd466004ca7026c1a90f8
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Tue Mar 4 17:41:23 2025 +0100

    Writing post

commit 98487c174c8f9150f6e5e33922ee75460b100926
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Tue Mar 4 15:08:27 2025 +0000

    Quick fix

commit cadc04083d02e4e157f780d8a358e22e0c597b37
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Fri Feb 28 10:32:43 2025 +0000

    publish ecmwf wms post

commit 05b6311fd6e92e1d8fadd79c5651e06bbf2696c9
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Mon Feb 24 16:04:20 2025 +0000

    Update feed.xml

commit ac09cc1d39306de6226d66a72fc67c11aba4ad0f
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Thu Feb 20 08:54:57 2025 +0000

    Make bound states an animation

commit b16485972c81f3f0bd1dd9deda5ea3b114344f25
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 18:26:20 2025 +0000

    Squashed commit of the following:

    commit ddd4a9145fb903eba795f3dea9402268a099e651
    Author: Tom <thomas.hodson@ecmwf.int>
    Date:   Sun Feb 16 18:26:06 2025 +0000

        Make executable code snippets post

commit 00ff594495a1741d2666703367b10fd82e5c2048
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 17:36:09 2025 +0000

    Update new_post.py

commit ff965f1037e0a94d466dc51f5570f56f26990d2c
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 17:35:17 2025 +0000

    Update new_post.py

commit 5b3c86ee9a95d314286935ddc54ca0a306755280
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 17:32:29 2025 +0000

    Update new_post.py

commit 6f0d2a006abe8b9eea0a82b17425144e91e1965c
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 17:26:35 2025 +0000

    Don't open new posts in the browser just print the url

commit c05b03e8d16929d3a614a841919357c1a9eb427f
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Thu Feb 20 08:54:31 2025 +0000

    Update 2025-02-16-usb-c-psu-monitor-board-bringup.md

commit 718c56544a5ef3f49bfc86e1adab9fbf467111ff
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 21:02:35 2025 +0000

    Write post

commit 26de5b5515fec3fdb84fd397b34b6027c9dfee44
Author: Tom <thomas.hodson@ecmwf.int>
Date:   Sun Feb 16 17:26:14 2025 +0000

    Create 2025-02-16-usb-c-psu-monitor-board-bringup.md
This commit is contained in:
Tom 2025-03-04 18:11:11 +01:00
parent 8758b49a65
commit 37dbad1eed
14 changed files with 5004 additions and 2 deletions

View File

@ -0,0 +1,90 @@
---
title: USB-C PSU Monitor Board Bringup
layout: post
excerpt: Finally testing boards I got back manufactured last year.
assets: /assets/blog/usb-c-psu-monitor-board-bringup
thumbnail: /assets/blog/usb-c-psu-monitor-board-bringup/thumbnail.jpg
social_image: /assets/blog/usb-c-psu-monitor-board-bringup/thumbnail.jpg
alt:
---
Quick recap of my [USB-C PSU build](/projects/usbc_charging_station): I have these USB-C supply boards from aliexpress that take 24v and supply up to 100W using the standard USB-C PD profiles. I designed this little board to add enable/disable with a mosfet and power monitoring with an INA219. In the end, many of these boards will go into a lasercut case with a Meanwell 240W 24V PSU and a microcontroller to turn some of them off if the combined draw gets too high.
<figure class="multiple">
<img src="/assets/images/2024/usbc_psu/pcb_top.jpeg">
<img src="/assets/projects/usbc_power_supply/thumbnail.svg" class="invertable">
<img class="wide" src="/assets/images/2024/usbc_psu/soldered_up.jpeg">
</figure>
I've had these boards back from the board house for multiple months and have only just gotten around to testing them. Here's the sequence of tests I went through, trying to rule out basic errors before moving onto things that might kill it.
1. Supply 3V through a lab psu with a variable supply. Initially with a very low current limit, ramp the current limit to check for dead shorts.
2. Connect up I2C and see if the INA219 responds.
3. Find the INA219 on I2C at address 64 (0b1000000) which makes sense assuming the floating address pins have a pulldown resistor to ground (see table below).
| A1 | A0 | ADDRESS |
| --- | --- | ------- |
| GND | GND | 1000000 |
| GND | VS+ | 1000001 |
| GND | SDA | 1000010 |
| GND | SCL | 1000011 |
| | | |
| VS+ | GND | 1000100 |
| VS+ | VS+ | 1000101 |
| VS+ | SDA | 1000110 |
| VS+ | SCL | 1000111 |
| SDA | GND | 1001000 |
| SDA | VS+ | 1001001 |
| SDA | SDA | 1001010 |
| SDA | SCL | 1001011 |
| SCL | GND | 1001100 |
| SCL | VS+ | 1001101 |
| SCL | SDA | 1001110 |
| SCL | SCL | 1001111 |
Those two solder pads on the board let you connect A1 to GND or SDA and A0 to VDD or SCL. Looking at this now, I realise that since both pins have a pulldown to ground. I should have just set it up so each `ADDR_SEL_X` pin could be connected to either SDA or SCL which would have given me 9 possible addresses. This way I only get 6 which should nevertheless be enough because I currently only have 5 boards. I can always use two I2C ports.
Next lets bridge some of these address select pads to check the address actually changes. In the image below I've connected SEL_0 to GND and SEL_1 to 3v3. This should set the address to 65 / 0b1000001 / 0x41.
<figure class="multiple">
<img src="{{ page.assets }}/addr_sel_schematic.svg" class="invertable">
<img src="{{ page.assets }}/addr_sel.png">
<img src="{{ page.assets }}/addr_sel_set.png">
</figure>
Great that works too.
Next I hooked the output up to a 10 Ohm power resistor and ramped the input voltage to see how much voltage would be dropped over the MOSFET, seems fine! A touch test of the MOSFET also felt good even at 100W.
<figure class="two-wide">
<img src="{{ page.assets }}/resistor_load.png">
<img src="{{ page.assets }}/hotplate_load.jpg">
</figure>
Finally I switched to the other board with the USB-C psu attached, ramped the supply to 24v and connected a hotplate as a test load. All seems good! Now I just need to get it all integrated into the case!
Next step was soldering up these modules, I'm going for 3 of the 100w modules and 5 of the 65w modules. I don't want to order more of the custom PCBs so I've put the 65 modules in banks of 2 and 3 where each bank has a monitor pcb and then one for each of the 100W modules.
<figure class="two-wide">
<img src="{{ page.assets }}/soldered_on.jpg">
<img src="{{ page.assets }}/soldered_up_65w.jpg">
</figure>
Ok bring up done!
I updated the laser cut case design to 4mm ply instead of 3mm plywood. This made it feel much more solid in the hand. The 3mm version felt quick flimsy. I also cut out a circle of orange acrylic which I'm going to embed flush into the front panel. It gives the display this lovely warm orange cast that looks like some old VFD display. The front panel itself is a scrap bit of some hardwood from the workshop. I think it was an offcut from the table saw so it wasn't uniformly 4mm throughout but the laser went through it happily and the thickness variation isn't noticeable,
<figure class="two-wide">
<img src="{{ page.assets }}/orange_screen_test.jpg">
<img src="{{ page.assets }}/orange_screen_in_situ.jpg">
</figure>
Next job is to finally finish the module that holds all the supply PCBs and mechanically couples them the rear panel and to a heatsink and fan.
Here's a sneak peak of that in CAD, the pit at the top is a guide to draw air up through both the central heatsinks and a little bit over the top of each bank of PCBs.
<figure>
<img src="/assets/projects/usbc_power_supply/new_airflow_design.png">
</figure>

View File

@ -252,6 +252,9 @@ figure.multiple {
margin: 0;
padding: 0;
}
> .wide {
width: calc(100% - 0.5em);
}
}
.image-grid-4x4 {

View File

@ -12,14 +12,15 @@ input_path, output_path = sys.argv[1], sys.argv[2]
# convert to 64bit floats from 0 - 1
d = np.asarray(Image.open(input_path).convert("RGBA")).astype(np.float64) / 255.0
print("Top left corner colour: ", d[0,0])
#decompose channels
# r,g,b,a = d.T
color = d[:, :, :3]
# The amount of white in each pixel
white = np.array([1.,1.,1.])
white_amount = np.min(color, axis = 2)
white = np.array([0.69803922, 0.69803922, 0.69803922])
white_amount = np.min(color / white, axis = 2)
alpha = 1 - white_amount
premultiplied_new_color = (color - (1 - alpha)[:, :, None] * white[None, None, :])

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB