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
|
No comments:
Post a Comment