Almost any voltage with QC
You might also want to check :
- the XIAO/MicroPython version of this board (tested on RP2040)
- the QC protocol documentation
Kicad 8 projects and Arduino code : QC_Fab.zip
QuickCharge using tiny412
The QuickCharge protocol lets you “negociate” voltage through USB on smart power supplies.
This page is a short documentation about my journey testing QuickCharge capabilities. I did it for myself and mainly for learning purposes.
I like things small and efficient. This is why this project is ATTiny412-based. (could actually be even smaller/simpler processor). The board was redesigned to make it very “Fab-able”.
You might also want to check the XIAO QC version, allowing the same protocol to be used through MicroPython, as a portable selectable power supply with rotary encoder.
Long story short (TL;DR;)
The code was written to be as compatible as possible :
- If what’s needed is just a fixed QC2.0 standard voltage (
5V
,9V
,12V
or20V
) , set theTARGET_DCV
(e.g. to120
as it’s configured in dcV) and the code will use QC2.0 only, maximizing compatibility with power supplies and minimizing needed program memory. - If you need a finer value, the code will use QC3.0 and get the closest value as a target reference. (be aware QC3 allows only
0.2V
steps, not finer) - If you don’t set
TARGET_DCV
(for example, commenting it), the code will look at the potentiometer pin and use it as a target. This also means that if you declareTARGET_DCV
to get a fixed value, you don’t actually need to populate the board with a potentiometer as the code won’t look at it. - Depending on whether you declared
USECLASS
to beCLASSB
(for 3.6V-20V range) orCLASSA
(for 3.6V-12V range), the potentiometer will cover the desired range. This allows finer adjustment forCLASSA
power supply (max 12V).
All the “fast chargers” tested so far are compatible, even when only declared as PD
(not explicitely QC
) - read dedicated section to know the difference. However, some appeared to be CLASSA
only (12V max) when used with QC
even though they can deliver up to 20V when used in PD
mode. Also, some were able to ramp up to 20V
in continuous mode but not as QC2.0 standard voltage (asking for 19.9V will to the trick, for example). Strange.
- The code was modified to let you see things : the interval between each step was very much increased (200ms instead of 5ms).
- It was a previous test on my first prototype board : the code jumps directly to 12V via QC2.0 and then switches to coutinuous (steps) mode, increasing (very slowly) the voltage by 0.2V steps (you don’t see all of them because my display is very slow to refresh actual voltage). This will actually happen so fast that it will look nearly instantaneous (after the 2sec handshake, though).
QC protocol
Please refer to the dedicated documentation page if you want to learn more about the QC protocol.
The board
The schematic is pretty simple (USB only view) :
D+
0.6V
voltage is realized by a resistor bridge (R1
andR2
) when the corresponding pin is set as an input. When set as an output, the pin imposes its own voltage that can be0V = LOW
(actually never used) or3.3V = HIGH
D-
0.6V
is realized by theADC
(code will automatically select available internal reference). Instead of changing theADC
value to get the0V
or3.3V
, we simply set the pin as a digital output when needed.- two
1k
resistors protect the lines in case something goes wrong
The full schematic includes :
- a
10k
(or reasonably close, not critical) potentiometer -RV1
- to provide a selectable input readable by an analog pin - a resistor bridge (
R6
andR7
) to divide theVBUS
voltage by11
: we’ll do the more complex maths later. (see notes about ADC on the sense pin) - a
3.3V
regulator and its capacitor : this value has been chosen to ease the output voltages values onD+/D-
- and of course our cute and lovely little
atTiny412
and itsUPDI
access for programming
Before you go further, I strongly recommend that you have a look at the code and know your goal as you might not want to populate the entire board depending on your usage.
I packed everything together. And I still use thick traces [min 0.64mm, usually more, especially on power lines]. Thick traces are easier to mill (with more than reasonable 0.5mm clearance), more robust and handle more current and force. So why would we wear out our bits removing more copper?
Hero video
And this is working just great!
Notes and comments
An arduino library exists but doesn’t take advantage of DAC and will be more resources/hardware/pins-consuming.
“Indication” LED
I added an “indication” led that lets me have a very rough idea of the voltage : with a 10k
resistor the led will be very shy at 5V and be more visible when approaching 20V. This value might be adapted for ClassA. (3k3
or 4k7
probably are good values for ClassB, I didn’t test)
Stronger power lines
I put a quite thick solder cover on the power lines between my USB connector and output terminal block. This is because I want to make sure that my traces will handle high currents without resistance. It makes my board uglier but more reliable.
Resistor divider, ADC timing and maths
R6 = 47k
and R7 = 4k7
form a resistor divider by a factor 11 : those values were chosen to ease components list (10x multiples are very common in stocks) and let VBUS
reach 20V (or more) safely :
- you might also use
10k
and1k
for example but this will sink more current (~2mA at 20V) - or
100k
and10k
but that might require small code tweaks
Using high impedance resistance devider will sink less current but will maybe require small ADC timing adjustments and/or parallel small capacitor (e.g. 100nF
in parallel with R7
) to avoid noise on the sensing pin.
This voltage sensor is used as a feedback.
Getting the value in dcV from the voltage divider requires some maths :
- voltage reference is simply the 3.3V to ease the code : so 33dcV is 1023 (10bits ADC range)
- the voltage divider provides a factor of 1/11 for explained reasons
So we should compute a 11x33/1023 ~= 0.355 multiplication to get the actual voltage in dcV from the ADC conversion. This is achieved by approximating 0.355
by ( 1 + 1/2 - 1/16 - 1/32 + 1/64) / 4
, that is “quite” easy to compute with limited computational resources. This code complexification is the (not-that-high-)price to pay for high code compatibility and versatility on a chip with limited resources.
“Universal” UPDI access
I put a “double” foot print on the board to reach the UPDI pin : you can either solder a 2 pins 2.54mm SMD header to get your GND-UPDI contacts, or solder a unique through hole pin. This last solution gives more robustness for testing purposes and will work fine if you connect your GND reference into the terminal block (or if you’re connected to a USB port with the same GND reference).
I actually solder a UPDI connector for testing purposes only. On my end board I usually simply program my code by pressing a wire on the UPDI pad and leave it unpopulated.
Drilled USB connector versions
I added a drilled USB connector version. I’m not a huge fan of using those, especially for power-related boards or testing purposes.
Updated USB connector version
You can solder a USB-C module (for example those). Check pinout before! This is my new favorite version.
Do NOT connect a through holes USB-A connector (you will have to force it AND the pinout is reversed!)
Wide power regions
I intentionally created wide power regions on the top : this allows one to solder wires instead of populating with a terminal block connector.
Conclusion
This - very - small board allows me to get - almost - any voltage using very minimalistic hardware and works on most smart power supply. Playing around with my different chargers let me know more about their real (and sometimes surprising) characteristics. When all I need is 12V
, I now can get it easily on - almost? - any charger (even external batteries).
This gives me food for thought about my box of “chargers I keep just in case” I need a specific voltage…
Is it about to become old XX’s century stuffs?
Files
Kicad 8 projects and Arduino code : QC_Fab.zip