Using the HLVD Module in Swordfish Basic

teaserMany of the 18F-series micros have an HIGH/LOW-VOLTAGE DETECT (HLVD) module that can be used to monitor VDD and warn if it has decreased below a specified voltage or increased above a specified voltage.  This is a great technique for monitoring the supply voltage of a micro that is directly battery powered.  It has several advantages over using an ADC channel for the purpose:

    1. No port pins are needed to implement.
    2. No external components are required and no power is consumed by an external voltage divider.
    3. The HLVD module uses an internal band-gap reference and draws a maximum of 45 µA when enabled.  It can be disabled except when needed to reduce even this tiny current draw.
    4. It can generate an interrupt when VDDis below or above (depending on how it's configured) the selected set point.



There are however a couple limitations:

    1. It can only be used to measure VDD, so it cannot be used to monitor a battery supplying power through a voltage regulator.
    2. There are only 16 steps over the measurement range with manufacturing tolerances on the band-gap reference and internal resistive voltage divider, so the settings are somewhat granular.  The trip point settings are shown below for an 18F2520. Please note the trip point settings depend on the device used.  Consult the data sheet.


The instructions for using the HLVD are spelled out in the data sheet:

22.2 HLVD Setup
The following steps are needed to set up the HLVD module:

    1. Write the value to the HLVDL<3:0> bits that selects the desired HLVD trip point.
    2. Set the VDIRMAG bit to detect high voltage (VDIRMAG = 1) or low voltage (VDIRMAG = 0).
    3. Enable the HLVD module by setting the HLVDEN bit.
    4. Clear the HLVD interrupt flag (PIR2<2>), which may have been set from a previous interrupt.
    5. Enable the HLVD interrupt, if interrupts are desired, by setting the HLVDIE and GIE bits (PIE2<2> and INTCON<7>). An interrupt will not be generated until the IRVST bit is set.


At first glance, the procedure for using the HVLD module appears daunting, requiring configuring several registers of the PIC and setting up interrupts.  It's actually pretty simple to use.  In this article, I will cover a polled implementation rather than an interrupt-driven one.  Additional information may be found in the data sheet in the section on the HIGH/LOW-VOLTAGE DETECT (HLVD) module, which is 22 for the 18F2520.

Swordfish makes it simple to access the PIC's registers.  The register names are pre-configured as variable names.  For example, the HLVD setup is done using the HLVDCON register.  If you start typing hlvdc... when you complete the word, it will snap to all caps -  HLVDCON - meaning Swordfish recognizes it.  So setting it up is as easy as typing HLVDCON = %00011011.

The first step above is configuring the trip point in the HLVDCON register.  This is slightly confusing because the trip point settings aren't shown in section 22; they are contained in Table 26-4 in section 26 as the footnote under the HLVDCON register indicates.

Table 26-4

An aside here.  The percent sign above indicates to the compiler that %00011011 is a binary number.  HLVDCON = 27 or HLVDCON = $1B mean exactly the same thing but are shown as decimal and hex values respectively.  So why use the binary format?  Each bit in the register has a specific meaning, so it's easier to specify the setting bit by bit.  You can easily tell from the binary representation which bits are set - the red dots show the values selected.

The lower 4 bits of the HLVDCON register come from the above table to set the trip voltage.  The upper four bits are configured according to Register 22-1 - HLVDCON.  We don't need to know the register number, since Swordfish already knows its name.  On a chip other than the 18F2520, the register number may even be different, but Swordfish knows which register to access because it's defined in the setup file for each chip.
Register 22-1

A couple of these need some explanation:

    • Bit 6 is unused.  The value written to it doesn't matter.
    • Bit 5 is a flag indicating that the internal voltage reference is stable.  Setting (writing a 1) or clearing (writing a 0) does not affect operation.
    • Bits 3 - 0 are the set-point, shown in table 26-4 (for the 18F2520).  For our test, %1011 was selected, giving a nominal trip point of 3.9 volts, but it can range from 3.7v - 4.1v depending on chip tolerances.

So the first step in using the HLVD module is configuring the HLVDCON register, which is simple:

HLVDCON = %00011011

This sets the trip to occur when VDD falls below the trip point, enables the HLVD module and sets to trip point to approximately 3.9 volts.

We've actually covered the first three (of five) steps from the data sheet and the hard part is done.  Not too tough so far!

If VDD falls below the voltage threshold we have defined, the HLVD interrupt flag will be set (i.e., equal to 1).  Since this is a polled implementation of the HVLD module, no interrupt will be generated.  Instead, we have to read another register to determine if the threshold has been crossed.  We'll read bit 2 in the register named PIR2.  Since we can't be sure what happened before our software came along, we first have to clear (set the bit equal to 0) before we read it to tell if the threshold has been crossed. 

Wow, this is a busy register - PIR2: PERIPHERAL INTERRUPT REQUEST (FLAG) REGISTER 2 - many things get reported in this register as shown below.

PIR2 copy

Oscillators and timers and EEPROM...all kinds of things.  But we're only interested in bit<2>, HLVDIF (High Low Voltage Detect Interrupt Flag).  The rest of the parameters here don't matter to us.  This register just contains status flags so it probably wouldn't hurt if we changed all of them but in general, it's best to modify only the bits we're interested in in a register to prevent unexpected results.  Fortunately, Swordfish makes it easy to read and write specific bits in a register (or any variable for that matter).

x = PIR2.bits(2) will read the value of only bit 2 in the register.

PIR2.bits(2) = 0 will make the bit equal to 0 (i.e, clear the bit)

PIR2.bits(2) = 1 will make the bit equal to 1 (i.e., set the bit)

So now we know how to clear the bit initially, and read the bit to tell if the HLVD threshold has been crossed.  This handles #4 in the instructions on how to use the HLVD module.

I want to poll the HLVD register to tell if the threshold has been crossed rather than generate an interrupt. A simple check can be added to the main program loop to check or the code can be put in a subroutine and called as desired.  So step #5 above isn't needed.  We've got all the pieces to use the HLVD module.

I wrote a simple program to test this all out.  One LED toggles each time through the program loop to indicate the program is running, and the other LED illuminates whenever VDD is below the set threshold.


*  Name    : HLVD Test                                                      *
*  Author  : Jon Chandler                                                   *
*  Notice  : Copyright (c) 2013 Creative Commons 3.0 sa-by-nc               *
*          : All Rights Reserved                                            *
*  Date    : 1/8/2013                                                       *
*  Version : 1.0                                                            *
*  Notes   : Poll the HLVD register to detect low Vdd voltage.              *
*          : Register settings may vary with device.                        *
Device = 18f2520
Clock = 20
Include ("utils.bas")
Dim LEDYellow As PORTB.3
Dim LEDBlue As PORTC.0
LEDBlue = 0
LEDYellow = 0
LEDBlue = 1
LEDYellow = 1
HLVDCON =%00011011      'see section 22.0 for setup and table 26-4 for voltage setting 
                        'set to 4 volts in this case
PIR2.bits(2) = 0        'clear HLVD interrupt flag
'PIE2.bits(2) = 1        'enable HLVD interrupt (section 22.2, sentence 5)
'intcon.bits(7) = 1     'enable HLVD interrupt (section 22.2, sentence 5)
                        'not needed for polled result    
While 1 = 1
    If PIR2.bits(2) = 1 Then          'check HLVD interrupt
            LEDBlue = 0                'LED = ON
            PIR2.bits(2) = 0          'clear HVLD interrupt
            LEDBlue = 1                'LED = OFF
    End If
    Toggle (LEDYellow)

Pretty simple, don't you think?

Testing The Program

 In order to test the program, VDD must be varied.  If you have a real deal PICkit 2 (not all clones support this) or a PICkit 3, VDD can be varied.  The picture below is the PICkit 2 GUI screen.   The supply voltage can be varied in the box shown.

NOTE:  This voltage indicated is only accurate if the PICkit 2 has been calibrated to the USB port being used.


In my test program, the HLVD range was <1011> which sets the voltage to a nominal range of 3.90 volts, with a minimum voltage of 3.70 to a maximum of 4.10 volts.  Using my adjustable bench supply and reading VDD accurately with a Fluke 45 bench meter, the threshold to activate the HLVD interrupt was found to be 3.85 volts at a temperature of about 65°F.  There appears to be a slight amount of hysteresis of around 0.05 volts - the voltage has to rise slightly above the threshold before the interrupt will clear.


When is a Battery Dead?

Determining the threshold for "low battery" depends on the battery chemistry and the load drawn by the circuit.  A typical battery discharge curve is shown below.  The voltage is somewhat flat in the operating range of the battery but starts to fall rapidly when the battery is depleted.  The challenge is to get the maximum life from the battery but still provide a warning in time to allow whatever action is necessary before the battery is dead.

The red axes shows about 90% of the battery capacity has been used and the cell voltage (for this particular type of battery) is about 1.04 volts.  The blue line shows about 95% of the battery capacity used and the cell voltage is about 0.99 volts.  The cell voltage is dropping rapidly at this point.

A three cell pack would provide 4.8 volts with fresh batteries.  The 90% point would be 3.12 volts, and the 95% point would be 2.97 volts.  How does this fit with the HLVD thresholds of the PIC18F2520?

HLVDL<3:0> = 0111 provides a range of 2.96 to 3.28 volts.  That exceeds the range of 90- 95% of capacity used and may not provide much warning of a low battery depending on chip tolerances.

the next stop of HLVDL<3:0> = 1000 would provide a range from 3.22 to 3.56 volts.  This would provide a warning at between 60 and 85% of battery depletion.  This would provide more warning of low batteries at the risk of loosing 40% of battery life.

Battery Discharge Curve

Posted: 6 years 7 months ago by lespic #12920
lespic's Avatar
Useful info , I seem to have jumped from 16F's to 32MX (chipKIT).
Posted: 6 years 7 months ago by bitfogav #12919
bitfogav's Avatar
Great info there Jon
Posted: 6 years 7 months ago by Jon Chandler #12905
Jon Chandler's Avatar
I did forget to mention one important point. You must keep in mind the allowable operating voltage of the micro in determining what low-level threshold to use.

For my tests, I used an 18F2520, and it was operating below 3.85 volts. What is the allowable supply voltage range for this part? According to the graph from the data sheet, it's 4.2 volts. The chip may work below this voltage but it's not guaranteed. It may fail in strange ways below this voltage.

For a battery-powered application, the 18LF2520 would be a better choice. But look carefully at the graph below. The maximum allowed clock speed is a function of supply voltage. The LF version will operate down to 2 a maximum clock speed of 4 MHz.

A better choice for a battery-powered application would be the 18F25K22. It will operate down to 1.8 volts at a clock speed of 20 MHz.

Posted: 6 years 7 months ago by Jon Chandler #12906
Jon Chandler's Avatar
There was an off-line question about my battery life estimation. I realize this may be unclear. Consulting the illustration below, the battery discharge curve shows voltage vs some unit of time for a constant current load. At t = 0, the battery is fresh and the voltage is about 1.6 volts per cell. At t = 8, the voltage has dropped to 1.1 volts per cell and by t = 9.5, the voltage is less than 1 volt per cell and falling quickly. At t = 10, the cell voltage is 0.8 volts and there's little juice left in the battery.

This was a convenient curve to pick. The red numbers at the top of the graph show percentage of life used. At t = 10, the battery is essentially dead - 100% of life used.

So we can look at any point along the curve and estimate the life remaining based on the cell voltage. This is an estimate that depends on the cell type, the load current and the nature of the load. The best estimates will be based on a curve at approximately the same load as your circuit.

Posted: 6 years 1 month ago by Breixo #13395
Breixo's Avatar
Hi, Jon,
Congratullations and thanks for this wonderful article.
At the begining you say: "This is a great technique for monitoring the supply voltage of a micro that is directly battery powered."
Before to read your article I was planning to use the 'Ultra Low-Power Wake-up (ULPWU)' input pin to detect the low voltage wall power supply and the HLDV input to monitor the low voltage battery but after to read your article I've some troubles as if both signals are or not compatibles and they can do the functions as I had thought.

Can you write any comment about that?. (I use the 18F87K22)

Thanks and best regards,
Posted: 6 years 1 month ago by RangerBob #13413
RangerBob's Avatar
Good article again Jon!
It can only be used to measure VDD, so it cannot be used to monitor a battery supplying power through a voltage regulator.

Not through the Voltage regulator, but can be darn useful to watch *around* the regulator!

On some (most?) devices HLVD can use an external pin (for example on 18F27J50 its RA5/AN4/C1INC/SS1/HLVDIN/RCV/RP2). Set HLVDL<3:0> to ‘1111’. Then you can connect an external voltage source before your voltage regulator to HLVDIN using a external voltage divider to get it to the correct 1.2V trip point.

I typically use this setup to monitor an external DC supply in battery backed devices, whereby the PIC can select and control its own power sources (either DC or Battery). As an interrupt is fired when HLVD is triggered you can respond very quickly in switching from the DC supply to the battery, before the voltage regulator bulk capacitance is exhausted.

I have some example code for this here: ... light=hlvd
Posted: 6 years 1 month ago by Jon Chandler #13415
Jon Chandler's Avatar
Hmmmm.... Looking at the data sheet for the 18F4550 shows the internal voltage reference as 1.2 volts.

I remember considering the possibility of using the external input to the HLVD module to monitor a battery pre-regulator but I concluded that the absolution maximum rating of Vdd +0.3 volts would be exceeded if the voltage divider was sized to the proper range. In looking at the 18F2520 data sheet, it says the HLVD input is compared against " the internal voltage reference" but does not specify what the voltage is (in my brief search using an iPad). It seems like I was thinking the trip point was 4.096 or something like that...

If the reference voltage is 1.2 volts, the voltage divider can be scaled so that a fully charged battery voltage won't exceed Vdd and this should work great. Check what the reference voltage is – it may vary between chips.
Posted: 6 years 1 month ago by RangerBob #13419
RangerBob's Avatar
Yup. In every 18F device I've used with HLVD it's been 1.2V for the bandgap reference which like you say, when carefully scaled with an external divider works a treat!
Posted: 6 years 1 month ago by Jon Chandler #13420
Jon Chandler's Avatar
I clearly had a brain fart on using the external HLVD input. I think I had the mistaken notion that the input was compared to Vdd, which would make it unless without exceeding absolute maximum ratings.

Comparing against a 1.2v reference leaves plenty of room to accommodate higher battery voltage. You do lose use of a port pin (for the HLVD input) and you have a voltage divider drawing some power so you do give up some of the advantages of the HLVD module.j

Forum Activity

Member Access