Wednesday, June 24, 2015

Using ADC to measure battery voltage and charge level

For proper control of a drone the flight control board need to know the charge level of the battery so that it may take necessary actions before the battery get depleted. Luckily we do not need to equip the board with any special sensor to achieve this as all PIC MCUs have the ADC (Analog-to-Digital Converter) module – we just need to know how to use it.

PIC ADC can convert voltages ranging between 0 and Vdd, which, in case of our MCU, is 3.3V. Obviously this is not enough to measure LiPo battery voltage which ranges from 4.2V to 12.6V for 1 to 3 cell battery – thus we need a voltage divider to bring it into an acceptable range. There are several requirements that the voltage divider should satisfy. First, it should bring low-side voltage to the range of 0 to 3.3V for all possible input voltages. Second, it should not drain too much power from the battery. Third, the power dissipated by the divider’s resistors should be within the acceptable range for these resistors. Finally, voltage divider should be able to provide sufficient current so that the Charge Holding Capacitor (CHOLD) on the input to ADC can fully charge during the sampling interval. The last requirement, to some extent, is at odds with the first three – we will discuss later on in this post what can be done to meet it.

On my board the voltage divider is represented by 2 resistors – 3.3 kOhm on the high-side and 1 kOhm on the low side (between the input to ADC and ground). This result in voltage dampening rate of 1/(3.3 + 1) ~ 0.2326, which results in maximum acceptable input voltage of 3.3V/0.2326 ~ 14.2V, which is well below the maximum voltage of 3-cell LiPo battery of 12.6V. For a 3-cell battery divider will consume about 12.6V/(3.3kOhm + 1kOhm) ~ 2.9mA, which is much smaller than the power consumption of the rest of the board and absolutely negligible as compared to the power consumption of the motors. At this current the high-side resistor of 3.3kOhm will dissipate about 30mW, which is well within the range of 125mW for 0805 resistor and 100mW of 0603, so here we are good as well. The numbers will be even smaller for 2- or 1-cell battery.

PIC ADC can operate at 10-bit or 12-bit precision – for our purpose we would select the 12-bit mode so that ADC will have enough precision to accurately represent voltages across the whole range of possible inputs (1- to 3-cell batteries). In a 12-bit mode ADC is capable of speed of up to 500 kilo-samples per second. To achieve this speed ADC requires the input to provide around 15mA to allow the CHOLD to fully charge during a fraction of a microsecond allowed for sampling the input. Battery voltage does not change that fast so we may safely reduce ADC speed to the minimum recommended sampling rate of 10 kilo-samples per second. Considering that by reducing speed we increased sampling interval roughly 50 times, a few milliamperes provides by the voltage divider should be enough to charge the CHOLD.
To measure voltage and, subsequently, discharge level of the battery, using the ADC we need to establish conversion factor – volts per LSB (least significant bit) of the ADC sample. In a 12-bit mode ADC sample is a number between 0 for input at the ground level and 4095 for input voltage being at the level of Vdd, which is 3.3V, thus the sample-to-voltage conversion factor for the ADC is ideally 3.3V/4095 = 0.0008059 V/LSB. Combining this with the voltage divider ratio we may obtain sample-to-input-voltage conversion ratio as 0.0008059/0.2325581 = 0.003465 V/LSB.

Now we may calculate several important ADC values corresponding to the following voltages:
Important Voltages
ADC Counts
Comment
1
289
Useful discharge range per cell
3.2
923
Cutoff discharge voltage per cell
5.7
1,645
Min voltage for 2-cell battery
8.8
2,540
Min voltage for 3-cell battery
We would like our battery management code to automatically identify number of cells of the battery – the last two entries in the table above serve exactly this role. The code assumes that if during initialization the battery voltage 9as measured by ADC) exceeds 8.8V then the battery is a 3-cell one. Otherwise, if the measured voltage is above 5.7V we assume that we have a 2-cell battery. Finally, if the voltage exceeds 3.2V we have a 1-cell battery. If it is lower than 3.2V we have a “special case” – the board is powered by the PIC Kit directly using the MCLR header and bypassing the voltage regulator. In this case the input voltage on the voltage divider will be what the voltage regulator passes back to the input and may be in the range of 2.5 to 2.7V. In the latter case the battery management code will report the charge level at 100% to avoid triggering immediate shutdown due to severe battery discharge.

Finally it is important to point out that the sample-to-input-voltage conversion ratio calculated above represent an ideal case – the voltage regulator provides exactly 3.3V and voltage divider resistors have resistance exactly at nominal values. In real life voltage regulator has some error in the output voltage and resistors typically are within +/-5% of the nominal value. Thus the conversion rate and various critical ADC sample values may need to be adjusted based upon the results of some testing. But that will be the task for later – for now we should have a look at the library responsible for working with ADC.

All the functionality related to managing ADC and reporting on battery status consolidated in theADC Library. As usually, all the definitions specific to the board (like the MCU pin selected for ADC) and to the MCU are consolidated in the ADCProfile.h header. ADCLogal.h header file and corresponding ADCLocal.c code file define and implement variables and constants local to the library. ADC count constants calculated above are also defined in ADCLocal.c code file.

Initialization of the ADC module and establishing cell count of the battery performed in the ADCInit(…) function defined in the ADCInit.c code file. Every configuration setting implemented in ADCInit(…) is well documented by the comments provided in code. Additional information about configuring ADC is available in PIC24EP512GP806 datasheet and Section16 “Analog-to-Digital Converter” of the Family Reference Guide. Contrary to other initialization routines in the libraries that we discussed thus far, ADCInit(…) does not enable ADC module – it is already enabled in the common initialization routine Init() defined in the Init.c code file. The reason for this is specific to PIC MCUs and explained in the comment notes in the body of Init() function.

PIC24EP512GP806 MCU provides 16 buffers to store conversion results. Using ADC configuration parameters they could be split into two banks – lower half and upper half. Using interrupt control configuration for ADC, we may instruct module to raise interrupt not after each sample is converted, but when 8 conversions are performed and one of the buffer banks filled in with new samples. This configuration reduces the number of interrupts and provide MCU more time to read data from the buffer. This configuration allows to give ADC interrupt a relatively low priority of 3 without the danger of missing some of the samples.

_AD1Interrupt() routine defined in the ADCISR.c code file is responsible for processing ADC interrupts. Results of ADC conversion are accumulated in the _ADCSampleSum variable. When 256 samples accumulated (takes about 25.6 milliseconds with ADC running at 10 kHz), the ISR routine calculates their average and update the _ADCValue variable. It then divides the value in the accumulator by 2 and drops the count of samples in the accumulator to 128. Thus the next update of the _ADCValue variable with the new value will take place when 128 new samples are added to the accumulator bringing the count of samples to be averaged again to 256. This mechanism reduces the frequency of battery status update to about 78 Hz as well as implements a form of a simple IIR low-pass filter. The goal of this is to eliminate juddering of the battery charge measurements due to immediate changes in throttle level to allow the board to make decisions based upon the average battery charge level.

ADC.h header and corresponding ADC.c code file provide definition and implementation of the API functions exposed by the library. Battery voltage and charge level are calculated in the function ADCGetBatteryStatus(…) based upon the last available sample from the ADC interrupt routine, provided in the variable _ADCValue. This variable is of type “int” and thus being read and written atomically (in one operation) so we do not have to protect access to this variable with the critical section.

Project 19-ADC provides sample code to demonstrate reading battery parameters and expose them through the logging interface. After programming the board with the firmware produced by this project I ran a test capturing measurements at different levels of battery discharge. At the same time I was capturing the actual battery voltage using digital voltmeter. Results of the test presented in the following table:
Raw
Charge
V (ADC) 
V (D.V.)
Error
V/LSB
V (Adj.)
Error (Adj)
2365
0.90
8.19
8.17
0.30%
0.003455
8.17
0.00%
2364
0.90
8.19
8.16
0.38%
0.003452
8.17
0.08%
2216
0.64
7.68
7.66
0.24%
0.003457
7.66
0.06%
2214
0.64
7.67
7.65
0.28%
0.003455
7.65
0.02%
2194
0.60
7.60
7.58
0.29%
0.003455
7.58
0.01%
2192
0.60
7.60
7.57
0.33%
0.003453
7.57
0.03%
0.003454
As it follows from the analysis of the data, using ideal conversion value of 0.003465 V/LSB calculated based upon the nominal values of resistors and output voltage of the voltage regulator results in a slightly higher value of battery voltage calculated based upon the ADC data then the real voltage of the battery. The error is not very significant – around 0.3%. However, if we recalculate value of the conversion factor based upon the experimental data by averaging it across several test points, the error drops by the order of magnitude to a negligible level. Based upon this new value of the conversion factor, ADC counts corresponding to important voltage levels were also re-calculated and constants in the code updated correspondingly. Recalculated values presented in the following table:
Important Voltages
ADC Counts
Comment
1
289
Useful discharge range per cell
3.2
926
Cutoff discharge voltage per cell
5.7
1,650
Min voltage for 2-cell battery
8.8
2,547
Min voltage for 3-cell battery

This post happened to be rather long, but we managed to review all the aspects of measuring battery voltage and discharge level. In the next post we will review soft- and hard-iron calibration of the HMC5983 magnetometer.

No comments:

Post a Comment