Intelligent Universal Battery Charger/Cycler

By  Dr Mike Roberts

‘A valuable to tool for all who use rechargeable batteries’


Most of the common NiCad battery packs
1-8 cells
0-2500 maHr
Charge or cycle
Measurement of capacity


1-8 cell packs
Discharge/ charge current 0-250 ma
Discharge to 1.1 volt per cell
Discharge and charge times displayed
Charge time 1-16 Hrs (auto cut-off)
Data recording every 10 minutes (max. 7 days)
Serial (9600 baud) data downloading to a PC
Charger set-up held in non-volatile memory


This battery charger will be valuable to anyone who uses rechargeable batteries. It will handle most of the batteries used in model cars, aircraft and boats (up to 8 cells). The performance of the batteries can be seen simply by the display of the discharge time when cycling the batteries or in detail by downloading the voltage and current data to a PC.

Over the years I have accumulated (sorry!) rechargeable batteries of almost every imaginable capacity from 110 maH to 2000 maH. Hence I needed a charger where I could set the current within a wide range. I had also nearly lost a radio controlled glider due to one cell in the receiver pack not holding its charge and I had an electric flight power pack which did not seem to be giving full performance. So I thought it would also be useful if I could record the discharge performance of the battery before charging. These were the drivers for designing a universal charger where I could optionally discharge any of my batteries to see what remaining charge was there (and avoid ‘memory effect’?) and record the voltage/current during the cycle so I could examine the discharge/charge performance.

I hope that the design will also be of interest to those who are programming PIC microcontrollers. The alternative title for this article was going to be ‘Yes you can fit a Quart into a Pint Pot!’. See the software and input/output design below. I had to use several techniques to squeeze the hardware (I/O lines) and software into a PIC16C71.

Software design Input/Output

I start all my PIC projects with a check of how I will interface the PIC with the outside world. The first draft for this project was:

Battery voltage	 	1
Charge current	 	1
Discharge current		1


Display (8 data, 2 control)	10
Serial EEPROM	 	2
Serial link to PC	 	1
‘Charge’ output	 	1
‘Discharge’ output	 	1
Start-Stop input	 	1
No. of cells (binary)	 3
Charge time (hex)	 	4
                          Total	26

The PIC16C71 I planned to use has 13 I/O lines. So I needed to save a few lines and use multiplexing where possible.

The first saving was made by running the display in 4 bit mode rather than 8 bit. This saves 4 I/O lines. The drive subroutine has 8 more instructions and takes 10-20% longer to execute (including the time for the display to implement the instructions). The start-up code is also a little more complex. This is a small cost for a good saving in I/O lines.

For those who might want to use this 4 bit mode I have included the start-up and the control/data code in Listings 1 and 2. These listings can be compared to the 8 bit code in my ‘Putting PICs to work Part 1’ in issue 113 of Electronics. Note that PORTB bits 6,7 are kept high as these are the ‘charge’ and ‘discharge’ outputs. Also this code assumes the data is output on the lower nibble of port B. It can easily be modified for the upper nibble if required.

The next improvement came from deciding to enter the number of cells and the charge time using the same controls, prompted separately by the PIC. This saves 3 more lines. However this was not enough. So I had to move away from binary/hex input and use ‘up’ and ‘down’ and ‘next’. The total panel inputs are now 4, including the ‘start-stop’ button. This brought the total requirement down to 18 I/O lines.

I then looked at some simple multiplexing. The 4 data lines to the display spend most of their time doing nothing, and the display ignores them until directed to by the ‘enable’ line. So these 4 data lines could be also be used in their spare time as inputs to read the panel buttons. A single multiplexed data line/button is shown in Figure 1. The resistor ‘R’ is chosen to be low enough that it will generate a ‘low’ input against the PORTB ‘weak pull up’ of 0.1ma (set in the software) and high enough not to exceed the output current rating (20ma) when the I/O line is used as a high output to the display while a button is pressed.

I had now got down to a tantalising 14 I/O lines with a need to get to 13. The last challenge was to get the charge and discharge current signals into a single input. Here I just wondered "if you can ‘wire or’ digital signals I wonder if you can do the same with analogue signals?". The answer is ‘yes’ (see the circuit description). So I was down to 13 I/O lines and could get on with the programming.

Panel Input

I was happy with my ‘next’ and ‘Start/Stop’ buttons but wanted to do something better than using ‘up’ and ‘down’ buttons to set the charge time and number of cells. I didn’t like the concept of having to press a button up to 8 times to get from one charge time to another. The solution is to use a ‘digital potentiometer’. When turned this gives two trains of on/off signals 90° out of phase. Train A leads train B when it is turned clockwise and follows when going anticlockwise. See Figure 2. There are various ways of reading this code. I used the logic that if A closes after both A and B are off (high) then this means ‘up’, and if B closes after both A and B are off then this means ‘down’. One benefit of this is that two separate ‘up’ and ‘down’ buttons could be used if desired without requiring any change in code.

From a programming viewpoint I read the four buttons (two buttons plus digital potentiometer) once every 10ms. This helps reduce the effects of contact bounce.

Data Recording

The main use of the EEPROM is to record the voltage and current information. However I also thought it would useful to store the number of cells, charge time and charge/cycle so if the charger was being used to repeatedly charge the same type of battery the unit would always power up to the previous settings.

In addition to recording the data the unit also needs to know how many data points there are. One option was to clear the data before each charge. A simpler solution (the one used) is to always record a ‘0’ voltage into the next data slot. Hence when downloading the data the program can just run until it sees a zero. I delayed the start of data recording by one minute. Otherwise pressing ‘start’ at the wrong time would record a useless data point and lose access to the previous set of data.

Assembly Code

A fair part of the code is what I now consider to be standard routines for driving the display, EEPROM, serial link and conversion subroutines (e.g. binary to decimal, binary to ASCII). Many of these have been covered here and in issue113 of Electronics. Code for driving the EEPROM will be covered next month. Soon after I put all these in I realised I was going to be pushed for space. I had created several ‘screens’ to collect the settings (number of cells, charge time, charge/cycle). This code was pretty thirsty on space. I was entering each character individually with two instructions (e.g. movlw ‘x’, call DOUT). I switched to using ‘table read’ which uses only one instruction per character once the overhead of the table calling routine has been invested. The Microchip application note AN556 gives a good explanation of how to do this. I made further savings by having a single input screen and moving the cursor to the data which has to be entered.

This was not enough! I then searched for sections of code that looked similar. I had separate routines for counting the charge and discharge hours, minutes and seconds. I combined these by introducing general second, minute and hour variables and copying these across as appropriate depending on whether it was in charge or discharge mode. I also had separate routines for sending the voltage as ASCII characters to the display or down the serial link. Both of these used code to convert the voltage from a 0-255 binary number to ACSII text characters 0-12.75. This was put into a subroutine.

I was so close and yet so far! I needed 15 more instructions over the 1024 capability. I was determined not to sacrifice any of the functionality. The final breakthrough came from the collections of four ‘nop’ instructions used for short delays in the EEPROM driver routines. Maybe these should be in a subroutine. But wait; a subroutine call uses two instructions and so does the return. Hence this subroutine only needs a ‘call’ and a ‘return’ statement. So why not just place a call on an existing ‘return’ statement? In order to maintain the independence of these routines I simply replaced the four ‘nop’s with a call to the ‘return’ statement of each routine. This looks peculiar but is fine. The processor does not care were the ‘return’ statement is. The only potential concern is that this introduces an additional level of subroutine call and one must not exceed the limits of the stack to record the program counters from each call (the PIC16C71 has 8 levels).

Software Overview

When I wrote my first (Z80) programmes I thought anyone who could fill more than 1000 lines of assembly code (about 15 pages of A4) must be insane. It is in fact quite easy to do this when one starts with a collection of standard routines. An analysis of this code gave the following breakdown:

Start-up								8
Data input							37
Interrupt routine including time update, running display			20
Data conversion (binary to ASCII etc.)				15
EEPROM input/output						14
Serial transfer capability						6

Arguably only the data input, interrupt routing and part of the serial transfer capability are special code. The rest (43%) are standard routines just ‘cut and paste’ from earlier projects.

Circuit description

Analogue Section (Figure 3b)

The analogue section controls the discharge/ charge as directed by the PIC to a current set by VR1, and converts the battery voltage and discharge/ charge current to 0-5v signals which can be read by the PIC.

The key here is the ‘wire or’ of the charge and discharge currents to a single analogue signal equal to the larger of the two. Both the charge and discharge current amplifiers use the same 2.7ohm sensing resistor R12. With a gain of 6.2 a current of 256ma gives 5volts, which is the full scale analogue input and held as 256 in binary, hence requiring no scaling in the PIC. (Full scale is actually 255 corresponding to 4.98 to 5.00v). The current sensing resistor is in the positive supply so the battery negative can be at earth potential. The ‘current’ is amplified by two of the operation amplifiers in IC5 to give negative going voltages with respect to the battery positive, representing the discharge/ charge currents. During discharge the ‘charge’ current gives a positive signal as does the ‘discharge’ signal during charge. The diodes D5 and D6 ignore the positive signals and select the most negative output, taking current from the constant current generator IC6. This is then inverted and turned into a positive signal relative to 0v by IC5d.

The current is controlled by TR1,2 driven by IC3,4. These latter ICs have the useful feature of having a ‘strobe’ input. This can be used to clamp or restrict the output voltage. If both are clamped to the -12v rail the battery will be neither charged nor discharged. By releasing the clamp (turning TR 3,5 or TR 4,6 off) the battery can be charged or discharged. The current is set by potentiometer VR1. The amplifiers drive the measured current to this value. Zener diode D7 is included to limit the current in the event that both the charge and discharge amplifiers are turned on at the same time (may happen during power-up).

Digital Section (Figure 3c)

The EEPROM is connected to PORTA. PORTA pin 4 is an open collector output which is ideal for the data line. This avoids conflict if both the PIC and the EEPROM try to send data at the same time.

The serial data link uses a standard MAX232 converter. The capacitors C19 to C22 are the right way round. They connect to the +/- 10 volt rails generated internally for the serial transmission.

Since the display is operated in 4 bit mode there are no connections to pins 7-10 inclusive. VR2 adjusts the display contrast/ viewing angle.


I usually install components in height order. So start with the 3 wire links, resistors, IC socket (for the PIC), capacitors etc. Leave the voltage regulators IC1,2 and TR1,2 until last. When everything else is in place fit the voltage regulators, pushed as close as they can down onto the PCB. Bend at least one lead of each under the board to strengthen the connection as these support this end of the PCB. Then ‘Z’ bend the leads so their bodies can go flat onto the heatsink. Drill and tap (3mm) the heat sink (see Figure 4), then use the heatsink as a template to accurately solder TR1,2 in place. If you do not want to tap these holes then it should be possible to fit nuts to longer bolts. The holes are aligned with gaps in the fins.

Drill the front and rear panels as in Figures 5,6. The PCB is held at one end using 14mm spacers from the base. Mount the spacers on the PCB and assemble to mark the locations for the holes in the base. The transformer bolts onto the base. There should be 5mm space all round.

I fitted the display using M2.5 counter sunk head bolts adjusted to be flush with the face of the display and glued these in place with a glue gun (take care not to get glue on the display face). One benefit of this method is that minor adjustments of the position can be made afterwards by applying a soldering iron to the back of the bolt heads - remelting the glue. Alternately longer bolts could be used through the panel.

Lastly connect the display and panel potentiometers, buttons and LED (D2). The new displays in the Maplin catalogue include backlit versions incorporating a current limiting resistor for 5 volt operation via connections 15,16. Run an earth connection from the mains chassis plug to the enclosure base and to the PCB earth connection point.

The serial lead is made up with a normal audio coax connection to the 3.5mm mono plug with the other end configured as follows:

			9pin D	25pin D
ground / '-' 		5	7
data/signal		2	3

connect DTR, DSR	4,6	20,6
connect RTS, CTD	7,8	4,5

Note that all the ‘connect’ links need to be in place. Some 25/9 way adapters do not include a full set of internal links - hence it is better to go direct to the appropriate ‘D’ connector.


On power up the display should show:

Charge 8Cells 16hr

Turning the ‘up/down’ digital potentiometer changes ‘Charge’ to ‘Cycle’. Press ‘Next’ to move the cursor under the ‘8’. Adjust as necessary using the ‘up/down’ pot. Press ‘Next’ again to adjust the charge time. At any time during the above pressing ‘st/st’ (start/stop) starts the cycle/charge. Alternatively if you wish to adjust the data again press ‘Next’ again (to show ‘Download?’) and then a second time to get back to the beginning.

During the cycle/charge the display will show (for example):

8C 10.65V C 0:00 16h 50ma D 0:00

The ‘:’ of the appropriate time (‘C’ = charge, ‘D’ = discharge) will pulse once per second to show if it is charging or discharging. A first idea of the state of the battery can be seen from the discharge time. When the cycle/charge has finished or has to be terminated early press ‘st/st’. The display will show ‘Download?’. Press ‘st/st’ again to download the data via the serial link, or ‘Next’ to get straight back to the beginning. At the end of downloading the display will show ‘Done’ for a couple of seconds before reverting to the beginning.

To receive the data a PC serial port should be set up (e.g. via Windows Terminal.exe) to:

Baud		9600
Data bits	8
Parity		None
Flow Control	None

Also in Settings/ Terminal Preferences set CR > CR/LF for inbound. The data is loaded (using ‘Receive Text File’) into a *.txt file. The format of the data is: ‘voltage’, tab, ‘current’, CR, repeating. This can then be ‘cut and paste’ into a spreadsheet (or with some spreadsheets can be opened direct) for plotting/analysis. Figure 7 gives an example plot for a 700mah 8 cell transmitter battery pack.

The author can supply a simple software (OK for DOS/Windows) to receive the data if required.

Printed Circuit Board, Programmed PIC, * parts

A set of non-Maplin parts (PCB, Programmed PIC, digital potentiometer and matching miniature potentiometer (VR1)) are available from the author for £25 including postage. A disc with software for receiving the data into a text file (plus the assembly code Listings 1 and 2 here and the assembly code from Putting PICs to work article from Electronics issue 113) is available from the author for £5 including postage.

Dr M P Roberts, 4 Thames Avenue, Guisborough, Cleveland, TS14 8AD

Parts List


R1	270	1	M270
R2,4	1k0	2	M1k0
R3,5	2m2	2	M2M2
R6	470	1	M470R
22,23,26,27,32	10k	13	M10K
R11	15 3W	1	W15R
R12	2.7	1	M2R7
R14	62k	1	M62K
R15	68	1	M68R
R17	1m8	1	M1M8
R18	75k	1	M75K
R21,24	4k7	2	M4K7
R25	3k0	1	M3K0
R28,29,30,31	2k2	4	M2K2

VR1	10k	1	*
VR2	5K	1	WR41U


14,15,16	0.1uF  63v	11	DT98G
C2,10	2200nF 35v	2	AT64U
C4,8,12,19,20,21,22	22uF 35v	7	AT56L
C17,18	12pF	2	WX45Y


D1	WO2		1	AQ96E
D2	LED red		1	QW86E
D3,4	1N4001		1	QL73Q
D5,6	1N4148		2	QL80B
D7	BZY88C 6v8	1	QH10L

IC1	7805		1	AV16S
IC2	7912		1	AV20W
IC3,4	CA3140E		2	QH29G
IC5	LM348N		1	AV43W
IC6	LM334N		1	WQ32K
IC7	24LC16B/P		1	AD23A
IC8	Pre-programmed PIC	1	*
IC9	MAX232		1	FD92A

TR1,2	BD139		1	QF07H
TR3,4	BC557		1	QQ16S
TR5,6	BC547		1	QQ14Q


	Transformer 15 + 15v 15W	1	DH57M

X1	3.2768MHz crystal	1	FY86T

	16 x 2 Display		1	NT57M

	IC socket		1	FJ66W

	Heat sink		1	HQ70M
	TO126 insulator		2	WR26D
	TO220 insulator		2	WR23A

	LED clip		1	YH62S

	Enclosure		1	XJ27E

	3mm knobs		2	JZ86T

	3.5mm plug		1	HF80B
	3.5mm socket		1	CX93B
	2.5MM plug		1	HH62S
	2.5mm socket		1	JK10L
	9/25 way D socket	1	RK61R/YQ49D
	9/25 way hood		1	FP27E/FP29G
	Mains inlet		1	HL15R
	Insulation cover for above	1	JK66W

	Mains lead		1	MK41U
	Ribbon cable		1	XR06G
	Earth wire		1	XR38R
	Coax			2	XR16S

SW1	Sub Min Toggle		1	FH00A
SW2	Sub Min Push black	1	JM01B
SW3	Sub Min Push red	1	JM47B
SW4	Digital potentiometer	1	*

	PCB			1	*

	M2.5 12mm CS bolts	1	BF40T
	M3 10mm PH bolts	1	JY22Y
	M2.5 nuts		1	JD61R
	M3 nuts			1	JD62S
	M3 treaded spacers	1	FG38R

Contact Details   Home