AVR-based Li-Ion battery charger

If you ever worked with lithium batteries, you probably know the TP4056 – a chip for charging lithium batteries with CC-CV profile, two LEDs for state-of-charge indication, temperature input and so on. You can find it relatively cheaply on Ebay or similar sites, either as standalone IC or on a small and very practical breakout board with a microUSB input. I also made an advanced version of one of these boards.

However, it is not configurable – it’s max voltage is fixed to 4.20 V and the charging current is set with a resistor. But what if you want to charge one of these ‘Li-HV’ cells, which have a Vmax of 4.35 V? Or what if you want to do a “storage charge” to only 3.9 V? Or what if you need to change the current? Changing the resistor every time you want to change the current is not very practical…. And since I work with lithium batteries a lot and I often need some of the things above, I came up with this board:

AVR Li-Ion charger, top view (the color wires are going to an MCP9701 temperature sensor)
AVR Li-Ion charger, bottom view

It’s a buck converter controlled by an ATtiny44 with software adjustable voltage and current limiting, cutoff current, precharge current, min and max temperatures and so on. These parameters are stored in the ATtiny’s EEPROM and it is very simple to change them – another IC on this board is the CH330, which is a variation of the well known CH340 USB-to-UART converter, just this time it is in an SO-08 package. So what you do is you plug this into your computer’s USB and fire up a serial terminal (I use YAT) with a baudrate of 19200 and voilá – a configuration portal shows up. Then you can change any parameters according to your needs (see the picture below). After you unplug the board from the USB, connect a battery and plug this into a powered USB (like a wallwart) and it starts charging according to the presets in EEPROM.

printscreen from a serial terminal (purple is text sent by the device, blue is text sent by the user)

Principle of operation

schematic, revision 1.0

My goal was to keep this board as small as possible while maintaining usability, so the topology of the buck converter is really simple. The ATtiny drives the gate of a P-channel FET directly with an inverted PWM signal. Because of size, I wanted small inductor and capacitors, which means as-fast-as-possible PWM. For this reason, the ATtiny is clocked from a 20 MHz crystal, which results in a 78 kHz 8-bit PWM.

The analogue part has two nice tricks. Firstly, when most people measure something with the ADC on, let’s say, an Arduino board, they use the AVCC as a reference – this means that the +5 V from USB is used as the top of the ADC range. However, the USB was not designed to be used as an analog reference, and it is very imprecise. And for charging lithium batteries, you need an accuracy of about ±50 mV, which means I had to utilize an MCP1501 – a 4.096 V reference, connected to PA0. This is the first trick – the ATtiny’s pin PA0 can be used as an AREF for the ADC. Also, with a 4.096 V reference, one LSB is exactly 4 mV, which makes calculations much easier for the CPU.

Another thing – about which not many people are aware – is that the tiny44 has differential inputs with selectable gain of 1x or 20x. This is how I did the high-side current sensing without any additional components – a simple 100 mΩ shunt resistor is sensed by a differential input with a gain of 20. So the full scale current is 2 A, which is also the max output from a USB port. So everything fits together nicely.

Once the device is plugged into USB, the LED start blinking blue. Now if the user connects a battery, the device switches to charging mode and starts charging, according to the parameters. However, if no battery is connected and the user send anything to the device via serial, it switches to configuration mode and prints the configuration portal. Here, the user can change any of the parameters listed, as long as it is withing given margins.

Here are short explanations about the charging variables

Variable nameDescription
VmaxMaximal charging voltage of the battery
VpreIf the battery has lower initial voltage than this, the charging process will start with a precharge, which is a slow charge intended for batteries with undervoltage.
ImaxMaximal charging current. This also limits how much current will be drawn from you USB, so if you have just 1 A USB sockets, you can set this to for example 800 mA.
IcutCutoff current – once the charging current drops below this value´, the charge is considered finished. Usually 10 % of Imax.
IprePrecharging current – if the battery goes thru precharge, this current value will be used. Usually 10 % of Imax.
TminMinimal temperature for charging.
TmaxMaximal temperature for charging.

State of charge indication

Of course I wanted this to show the current stage of the charging process. The most convenient way is probably with LEDs, however since I had only a single pin left, I had to use the WS2812, which is an RGB LED with an integrated driver. Slightly overkill, sure, but in the end it enables a lot of colors:

  • blue blinking – waiting for the user to connect battery or initiate configuration mode (by sending anything over UART to the device)
  • blue on – configuration mode
  • purple color – precharge
  • red color – constant current
  • orange color – constant voltage
  • green color – charging finished
  • red blinking – error (temperature too low/high)

Possible future updates:

  • micro USB input
  • (Arduino-like) UART bootloader, so that the firmware can be changed without using a dedicated programmer (with this, you could for example upload firmware for charging NiMH batteries just by plugging it into USB)
  • it would be interesting to make the LED change colors gradually from purple to red to orange as the charging process progresses
  • add charging timeout


  • Since the code takes 4076 bytes out of the 4096 available (that is 99.5 % of the program space) on the ATtiny44, it’s important to set the -Os flag (optimize for size) in the Makefile/project properties. Otherwise, the code will be too large to fit onto the tiny44.


Another handy device I have been using for a few months now. The programming was surprisingly challenging, given the relative simplicity of operation.

As always, all source files are on GitHub.

1 thought on “AVR-based Li-Ion battery charger”

Leave a Reply

Your email address will not be published. Required fields are marked *