• monitor Vdd on a 12F675

    PICBASIC PRO program that allows you to monitor Vdd on a 12F675 using only one pin for external reference. This allows you to take action when the supply voltage drops. Written by Frank Miller.

    ' VOLTAGE MONITOR is an unusual circuit in that the monitoring is done backwards!
    ' There are situations when you want to monitor your own battery voltage and
    ' shut down the circuit at a critical voltage. The program gives you a 3 digit
    ' output from "430" (+4.30 volts) to "600" (+6.00 volts) which makes it easy to
    ' act on just about any voltage. The beauty of the program is in its ability to
    ' accurately calculate an offset for a voltage reference since they are all
    ' different when you look at the exact reference voltage, (eg. a 3.3v regulator
    ' can be as low as 3.247 volts which would throw off your reading significantly).
    ' The only critical part of this circuit is during the actual programming. You
    ' must ensure you are powering the circuit with exactly 5.00 volts! After that,
    ' it is fully calibrated to read any voltage at any time. Accuracy AND display
    ' resolution down to 1/100 volt!

    ' The conventional method for doing this trick would require using the 1 and only
    ' Vref pin (GPIO.1) on the 12F675 and another GPIO pin for the ADC reading.
    ' That's too many pins, plus, you can't IN CIRCUIT PROGRAM if you have a voltage
    ' constantly going to GPIO.1's "CLOCK" pin. You would have to disconnect this
    ' voltage source every time you dump a new program with this routine attached.
    ' Obviously, this is not a good way to go.

    ' The other method would be to power the chip with an LDO (Low Drop Out) regulator.
    ' A great Vreg is the LM1117 at 3.3v that will stay regulated down to 4.37 volts
    ' of course "brown-out" is to be disabled and you need to use two 1% resistors to
    ' form an accurate voltage divider for the input. Remember that the voltage
    ' reference chip (LM1117) would be powering the chip so that means that the 1024
    ' steps of the 10-bit ADC can not go above 3.3 volts. Because the +5v battery is
    ' over this ADC ceiling of 3.3 volts, you have to give the ADC input something
    ' less via the divider network. This is still not a great way to monitor voltage.

    ' Here's a better way to monitor your own voltage using only an inexpensive LDO
    ' 3.3 volt regulator (LM-1117), only one pin (any of the 6 available), and a few
    ' lines of code. Of course you must have a PIC with EEPROM. This routine was
    ' written specifically for the 12F675 since there are so few pins to work with.

    ' First set the ADCON0 to use Vdd for the 'reference'. And yes, it'll be changing
    ' all the time which in turn, changes the 10-bit scale. Hang-on... it gets real
    ' simple, real fast. This is where things go backwards. The uP reads the +3.3v
    ' all the time and never changes, however the ADC result will change because the
    ' ratio of the ADC ladder is changing due to Vdd changing! This is what's backwards.

    ' There are three "knowns" or "givens" so we can easily solve for the fourth.
    ' The three "givens" are: "REFERENCE" at 3.3 volts, "RESOLUTION" at 1024 and the
    ' "ADC" reading. All we need to do is solve for the 4th or "X". The trick is to
    ' use a little high school math to simply "Solve for X". This is not complicated1

    ' ADC Resolution "X"
    ' -------------- : ------------------
    ' ADC Result Voltage Reference

    ' Do the typical "cross multiply" to get a one line formula of:
    ' (Resolution) x (Voltage Reference) = (ADC Result_x)

    ' Now to plug in some numbers and divide by the "X":
    ' (This example of all four sections filled in equates to a +5.00 volt battery)

    ' 1024 "X"
    ' ------ : ------
    ' 665 3.3 (665 is an ADC result of an exact 5 volt power supply)

    ' Now we have 665_x = 3325. Now solve for X by dividing 3325 by 665 = +5.00v
    ' Since the RESOLUTION and VOLTAGE REFERENCE don't change we have a constant of
    ' 3325 and only need one line of code after the ADC finishes, but we're also
    ' dealing with a decimal point which we can't work with. The section below
    ' labeled "Calculate" does repetitive division using the "//" (modulus) function.
    ' It doesn't give you the digits to the right of the decimal, rather, you get
    ' what's left over from the first division as a whole number. You then do it
    ' again and again to get three whole numbers. I've multiplied the first digit by
    ' 100 to make room for two "place settings" for simple addition at the end of a
    ' "tens" digit (the second division) and lastly, the third digit being the "units"
    ' We jus add 'em right up. If the number is +4.27 volts, the three digits would
    ' be "400" + "20" "7". Your program just looks for the three digits to manipulate
    ' when to act on any voltage. Simple!

    ' This program will create an "offset" to compensate for one of the "knowns" that
    ' really IS NOT a great known... the voltage regulator! It can vary significantly
    ' since the spec is quite loose for our purposes, however, it will not vary once
    ' you have it in circuit. The problem is you can't rely on the rated 3.3 volt
    ' value. This is why we will run the calibration step in the middle of the program
    ' on the VERY FIRST run through. It will create an offset and store it in memory
    ' for any subsequent cold start. Because the actual programming is critical on
    ' this calibration step, you MUST verify you have a very solid +5.00 volts on the
    ' chip. In effect, the program assumess it's getting 5 volts during the routine.

    ' That's about it... the last few notes are for connections to the chip, etc. If
    ' you have any feedback or questions, please email to Frank Miller at
    ' mail@pulsar.gs

    ' NOTE: Before dumping the program to the 12F675, make sure you have EXACTLY +5v
    ' on the board since the calibration step is run first to correct the variance
    ' on the 3.3 volt regulator. This "offset" value is subsequently put into EEPROM
    ' for read-back on any future power-up, maintaining accurate voltage readings.
    ' If you notice your voltage readout is off a 'tad' from your volt meter reading
    ' it means your programming voltage was off by that same amount.

    ' LCD is SEETRON (Scott Edwards 4-Line LCD Serial LCD backpack) and powered
    ' from the Epic programmer. (It's ok... not much of a load!) You can't easily
    ' use the incoming 5v supply to power the LCD because when the supply
    ' volts drop below 4.6v, the LCD gets goofy.

    ' CONNECTIONS TO IC PINS:
    ' Pin 2: LCD serial backpack
    ' Pin 3: LM-1117 Low Drop Out voltage regulator

    ' For demonstration purposes, you'll notice that the progam is using 462 bytes
    ' of space, almost half of the 1000 total, however, in use there is no need for
    ' knowing this info, so the program space drops dramatically in half.


    Code:
    '****************************************************************
    '*  Name    : 12F675 One Pin Voltage Monitor.BAS                        *
    '*  Author  : Frank Miller "Pulsar"                             *
    '*  Email   : mail@PulsarProFX.com                                    *
    '*  Date    : 7/30/2004                                         *
    '*  Version : 1.0                                               *
    '****************************************************************
    
    
    
    '-------------------------------------------------------------------------------
    VARIABLES:   
        Sample      var word        ' ADC voltage reading (Pin 3 set by ADCON0)
        Group       var word        ' Collection of 20 ADC readings to average
        Base        var word        ' Sample voltage reading x 5 volts
        Volts       var WORD        ' Manipulated as a 2 digit reading (46 = +4.6v)
        Volts_1     var word        ' 100's (1st voltage digit)
        Volts_2     var word        ' 10's (2nd voltage digit)
        Volts_3     var word        ' 1's (3rd voltage digit)
        Remain      var word        ' Remainder used 
        loop_cnt        var byte        ' Used in ADC routine to capture 20 readings
        LCD         var GPIO.5      ' 4-Line "Scott Edwards LCD" backpack on pin 2
    
        TRISIO = %010000            ' LCD (pin 1 = gpio.5) and Vref (pin 3 = gpio.4)
        eeprom 0, [0, 0]            ' Program forces first 2 memory locations to "0"
     
    LCD_SETUP:                      ' Print labels once for a flicker-free display    
        SEROUT LCD, 4, [12, 14]     ' 4 = baud, 12 = clear screen, 14 = backlight on 
        serout LCD, 4, [16, 144, "  BASE = "]  ' 16 = put cursor at "144" location
        serout LCD, 4, [16, 164, "SAMPLE = "]  ' 20 characters per line
        serout LCD, 4, [16, 184, " VOLTS = "]  ' 4th line would start at "204" 
        
        '---------------------------------------------------------------------------
        ANSEL =  %00110010      ' Analog Select Register (page 42)
                                ' MSB 0 = n/a                             "0"
                                '     0 = FRC select (bit 3)              "011"
                                '     1 = FRC select (bit 2)
                                '     1 = FRC select (bit 1)
                                '     0 = analog input select (bit 4 of 4) "0010"
                                '     0 = analog input select (bit 3 of 4)
                                '     1 = analog input select (bit 2 of 4)
                                '     0 = analog input select (bit 1 of 4)
        '---------------------------------------------------------------------------
        ADCON0 = %10001101      ' ADC input on GPIO.4/AN3/pin3 (page 41)
                                ' MSB 1 = Right Justify                   "1"    
                                '     0 = Vdd as voltage reference        "0"
                                '     0 = n/a                             "00"
                                '     0 = n/a
                                '     1 = select channel 03 (bit 2 of 2)  "11"
                                '     1 = select channel 03 (bit 1 of 2)
                                '     0 = conversion status (see routine) "0"
                                ' LSB 1 = A/D converter module is on      "1"
    
    '-------------------------------------------------------------------------------
    ADC: 
        Let Sample = 0                  ' Clear any prior values
        Let Group = 0
        for loop_cnt = 1 to 20              ' Take 20 readings for better accuracy
        
        ADCON0.1 = 1                    ' Setup for manual ADC technique
        NOT_DONE:
            if ADCON0.1 = 1 then not_done ' Waiting for completion of cycle
            pause 10                    ' Give the reading a little settling time
            sample.highbyte = ADRESH    
            Sample.lowbyte = ADRESL     
    
        pause 10        
        let Group = Group + Sample      ' Collecting 20 samples
        next loop_cnt                       ' Continue back up for another reading
        let sample = Group/20           ' Accurate averaged reading to work with
    
    '-------------------------------------------------------------------------------
    CHECK_CALIBRATION:                  ' Check for a stored value for BASE
    
        Read 0, base.byte0              ' Read EEPROM memory locations 0 & 1 to form
        read 1, base.byte1              ' a 16-bit word from 2 bytes for "BASE" value
                                        
        if base = 0 then                ' If nothing there, do the calibration
            Base = sample * 5           ' Sample x 5! Set power supply exactly to 5v!
            Write 0, BASE.byte0         ' Put lower BASE value into EEPROM address 0
            write 1, base.byte1         ' Put higher BASE value into EEPROM address 1
        endif                           ' End of capturing the BASE value
    
    '-------------------------------------------------------------------------------
    CALCULATE:
    
        Let Volts_1 = Base / sample       ' This is the FIRST of three digits
        Let Volts_1 = Volts_1 * 100       ' Make a place holder for 2nd & 3rd digits
        Let remain = base // sample * 10  ' Get the modulus (//) and multiply x 10
        '------------------------------- 
        Let Volts_2 = Remain / sample     ' This is the SECOND of three digits
        Let Volts_2 = Volts_2 * 10        ' Make a place holder for 2nd & 3rd digits
        let remain = remain // sample * 10' Get the modulus (//) and multiply x 10
        '------------------------------- 
        Let Volts_3 = remain / Sample     ' This is the THIRD of three digits
        
        Let Volts=Volts_1+Volts_2+volts_3 ' 3-digit number with 1/100 volt accuracy
        
    DISPLAY: 
        serout LCD, 4, [16, 153, #base]
        serout LCD, 4, [16, 173, #Sample]
        serout LCD, 4, [16, 193, #volts]
    
    goto adc