• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

2 voltmeters using a single microcontroller

Status
Not open for further replies.

BM8

New Member
Hi all,

I need some help with a certain digital voltmeter circuit design. I should start by saying that I’m new to electronics so any detailed explanation would be highly appreciated.

I’m trying to make a circuit to measure voltage from 2 DC sources in series, using 1 microcontroller. Here is a sketch of the circuit that I'm trying to complete.


upload_2017-9-11_14-36-42.png

I have some concerns:
  1. Where do I connect the grounds? Grounds for both DC sourcs are different so I can’t connect them both to the same ground as the microcontroller. And by the way, does the MCU have a ground pin? If yes what do I connect to it GRN1, GRN2, GRN3, or all?
  2. I have a doubt about the protection of a 5V microcontroller with the voltage dividers. Are they well sized for both batteries? Specially for DC2, because the voltage in the circuit increases from DC1 to DC2.
  3. Is it possible to take measurements with only one oscillator or do I need a second one?
  4. Is a pic microconntroller suitable for this application? Other propositions?
  5. Any other recommendations?
Thank you in advance for your help..
 

Nigel Goodwin

Super Moderator
Most Helpful Member
You've connected grounds directly across the bottom battery, and have now destroyed it - and possibly burn your home to the ground.

You can't measure two different voltages like that, the easiest way (with your existing circuit) is to remove GND2 and it's battery connection and connect the bottom of R2 to GND3 - you may also need to increase the value of R1 as well, to give an increased voltage range (10K should be fine).

A2 will then read the voltage across DC2, and A1 will read the voltage across battery DC2+DC1, so to get the DC1 voltage, subtract the DC2 voltage from the A1 reading.

The MCU will have at least one GND pin, and they should all be connected to GND3, only the one oscillator is needed, and indeed only one is possible - a PIC would be fine for the project.
 

ronsimpson

Well-Known Member
Most Helpful Member
This picture: all grounds are the same.
One node is names 12V and another is named 24V.
To measure the bottom battery you simple read A2, knowing 4V is really 12V.
To measure the top battery you first read A1, knowing 4V is really 24V. This tells you the voltage of DC1+DC2. Now substrate out DC2.
upload_2017-9-11_14-38-38.png
 

BM8

New Member
Thank you Nigel and ronsimpson for your responses.
Your solution is understood, but is there any other way to protect the MCU in a standardized manner, because I will eventually have more than 2 batteries on the circuit and it would be perfect to somehow prepare a unique setting for all the batteries regardless of their position on the circuit.
 
Last edited:

Les Jones

Well-Known Member
Most Helpful Member
If you are now planning to use one MCU per battery you could use HC-12 RF modules to transmit the data back to a base station. I use this method. Each sensor module has an HC-12 and the base station has an HC-12. The communication is in the form of serial data. The base sends an address out to select a particular sensor (I use a # character followed by an upper case letter but you could use any addressing system.) The sensor then transmits the data back as an ASCII string of characters.
This is the schematic of the voltage sensor.
Monitor_V01.png
I now use a PIC12F1840. (I used a PIC12F1822 in the first one I built) The potentiometer is to fine tune the calibration

I also have sensors that also monitor current as well as voltage (Using an INA219) and some that measure temperature and humidity. (Using a DHT22) At the moment I just use a terminal emulator program running on a PC with an HC-12 connected to a serial port. I just type #and a letter and the sensor sends the data as a readable ASCII string of characters. I have been trying to get a Raspberry Pi to read data every few minutes and log it but I am having difficulty at getting the receive code to deal with errors in the transmission. Let me know if you would like a copy of the code that runs on the PIC12F1840

Les.
 

BM8

New Member
Les Jones, thank you for your response. Your solution is very interesting and very close to what I'm trying to achieve. Yes I'm interested in the PIC code please.
I'm just wondering if in your application the batteries (or DC sources) are in series or in parallel. If in series, how do you protect each module? Do you need to change the resistors for different modules?
FYI, I edited my last post and removed the schematic with many MCUs because I realized that I was getting off topic, so I concentrated on a single MCU.
 

MikeMl

Well-Known Member
Most Helpful Member
Here is a (conceptual) approach where I would start:

57.png
 

Les Jones

Well-Known Member
Most Helpful Member
Hi BM8,
I use the voltage monitor (And will be using the voltage and current monitor.) to monitor the voltage of a 12 volt battery for a low voltage lighting system in the garage when it is on charge. It is powered from the battery/charger while on charge so it's "ground" reference is the battery negative.This way you could have one of these sensors connected to any number of batteries in series as it's ground would be referenced to the negative of that 12 volt battery. I use the internal 4.096 volts reference on the PIC so that the input of the ADC reads 0 to 4.096 volts. This means that you hve to have a potential divider on the input to scale the voltage to this range. (The unit that also measures current using the INA219 can only read a maximum voltage of about 27 volts as the current sense resistor is in the positive line so this limit is due to the ratings of the INA219.)
This is the source code for the PIC12F1840
Code:
;   This is for reading voltge from ADC input and transmitting as ASCII string of characters
;    Started 19/02/2017
;   Modified to disable USART receive after recognising station ID (18/03/17)
;******************************************************************************************

;

;******************************************************************************************
;
;        OSC      : Internal OSC 4 MHz
;
;******************************************************************************************
        LIST    P=PIC12F1840,ST=OFF,R=DEC
        INCLUDE  "P12F1840.inc"

        __CONFIG _CONFIG1,  _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF &  _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF


        __CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_HI & _LVP_OFF

;*******************************************************************************
; Constants
;*******************************************************************************

Station_ID   equ   'D'   ;Station identification character for this station   


RAM_START       equ     0x20


; The periods are timed with timer1 which is set to run at the internal clock
; rate (4 Mhz) of Fosc/4 or 1.0MHz which is equal to a 1 uS period.




;DELAY_6MS   equ   D'2993'    ;Value to give delay of 6 mS
RX_Timer   equ   D'200'

;*******************************************************************************
; Pin Assignments
;*******************************************************************************
;
;   PIC signals
;
   #DEFINE       TX_Data       PORTA, 0       ;
   #DEFINE       RX_Data       PORTA, 1       ;
;
   #DEFINE       AN2       PORTA, 2       ; Analogue input
;   #DEFINE       Button       PORTA, 3       ; Push button to trigger  read (Not used)


; I/O pin use
;   RA0   TX                       (Pin 7)         (To pin 4 on programmer connection)
;   RA1   RX               (Pin 6)         (To pin 5 on programmer connection)
;   RA2   Analogue input         (Pin 5)
;   RA3   Trigger button       (Pin 4)   NOTE THIS PIN CAN ONLY BE USED AS AN INPUT.   
;                    (This is VPP for programming This could be connected to pin 3 or 5 of the header to program the chip on header.)
;   RA4 (AN3)   Not used         (Pin 3)
;   RA5                       (Pin 2)




;  Define GENERAL PURPOSE RAM AREA (80 bytes maximum)

;*******************************************************************************
; File Register Variables
;*******************************************************************************
        cblock  RAM_START

param1:   1          ; parameter 1    (Used in delay cycles routine)
param2:   1          ; parameter 2    (Used in delay cycles routine)
Temp_1:   1       ;Used in 2 second delay

tmpData:   1       ; Used in output_hexbyte routine

; delay counters
Del_Count:     1
       
ADC_Temp_L:   1
ADC_Temp_H:   1


; byte counter
    bytcnt:     1

ESEVN:   1       ; 10 000,000 (digit 8)
ESIX:   1       ; 1000,000s (digit 7 )
EFIVE:   1       ; 100,000s digit 6 store
EFOUR:   1       ; 10,000s digit 5 store
ETHREE:   1       ; 1000s digit 4 store
ETWO:   1       ; 100s digit 3 store
EONE:   1       ; 10s digit 2 store
EZERO:   1       ; 1's digit 1 store



COUNT:   1

BCD1:   1       ; overrange
BCD2:   1       ; MS decimal value
BCD3:   1       ;
BCD4:   1       ; decimal value
BCD5:   1       ; ls decimal value
   
BIN1:   1       ; LS binary value
BIN2:   1       ;
BIN3:   1       ;
BIN4:   1       ; MS binary value

TEMP:   1

RX_Count:   1


        endc
;*******************************************************************************
; Common RAM (0x70 to 0x7F)
;*******************************************************************************

TX_temp       EQU 0x70       ;Temporary storage for character to be transmitted.
RX_Temp       EQU 0x71       ;Temporary storage for received character.
BS_Temp       EQU 0x72       ;Temporary storage for "W" during bank select
;*******************************************************************************
;  Define Macro     Takes 2 uS
;*******************************************************************************
SELBANK MACRO   #BANK_NO
        MOVLB   #BANK_NO   ;1 cycle - 1 uS   
        ENDM


; *****************************

        ORG     0h
        GOTO    START
        ORG     4h
        GOTO    START
START
; *****************************
;   Initialized Segment
; *****************************
; Initialise OSC (4MHz, IntOSC)
        SELBANK 1               ; SET BANK1
        MOVLW   B'11101010'       
        MOVWF   OSCCON

   BCF   INTCON,GIE

; Initialise I/O port
        SELBANK 1               ; SET BANK1
        MOVLW   B'00001110'       ; Bits 0, 4, 5 output Bit 1, 2,  3 input.
        MOVWF   TRISA

;Set pullup on PORTA,3

   BCF   OPTION_REG,NOT_WPUEN

           SELBANK 4               ; SET BANK 4
   MOVLW   B'00001000'
   MOVWF   WPUA

           SELBANK 3               ; SET BANK 3
   MOVLW   B'00000100'
   MOVWF   ANSELA       ; Set bit 2 as analogue, set the rest to digital

         SELBANK 2               ; SET BANK2

; Initialized Comparitor.
           MOVLW   B'00000000'       ; Comparator disabled.
   MOVWF   CM1CON0           ;In bank 2
           SELBANK 0               ; SET BANK0

   CLRF   PORTA       ;Set all outputs low



; Initialized EUSART
           SELBANK 3               ; SET BANK 3
   MOVLW   B'00000000'
   MOVWF   BAUDCON

   BSF   TXSTA,BRGH   ; Set baud rate high bit
       BSF     TXSTA,TXEN    ;enable transmission  

   MOVLW   0x19       ; Decimal 25 for 9600 baud rate
   MOVWF   SPBRGL
   MOVLW   0x00
   MOVWF   SPBRGH

     BSF     RCSTA,SPEN    ;enable serial port
   BSF     RCSTA,CREN    ;enable receive

         SELBANK 0               ; SET BANK0

; Initialized Timer 1
         SELBANK 0               ; SET BANK0

   MOVLW   B'00010100'   ;Clock source FOSC, 1:2 prescale, Dedicated Timer1 oscillator circuit disabled,
                                ;Do not synchronize external clock input, Timer off

   MOVWF   T1CON       ;With 4 Mhz clock timer will increment every 500 nS

   MOVLW   0x00       ;All bits clear (Gate control not used.)
   MOVWF   T1GCON

; Initialized ADC

         SELBANK 1               ; SET BANK1
   MOVLW   0x09       ; Channel 2, ADON (Bit 0)
   MOVWF   ADCON0

   MOVLW   B'11010011'   ; Bit 7 Result right justified. ADCS 101 /16 (Bits 6 & 4 set ).  ADPREF bits 0 & 1 set (FVR module.)
   MOVWF   ADCON1

         SELBANK 2               ; SET BANK2
   MOVLW   B'10000011'   ;Bit 7 enable. Bits 0,1 4.096V ref
   MOVWF   FVRCON

         SELBANK 0               ; SET BANK0

; *****************************
;   Program main
; *****************************
; Main loop

;   CALL Delay_2_Sec

MAIN:

Wait_Rx_Chr:           ;Wait for a received character

   SELBANK 3
   BSF     RCSTA,CREN    ;enable receive
   SELBANK 0

   bcf   PORTA,5       ;   TEST switch off LED
   BCF   PORTA,4           ;TEST switch off LED
   CALL   SerialReceive
;   CALL    SerialTransmit ;Echo character as a test
;   MOVF   RX_Temp,W
;   BCF   PORTA,5           ;TEST switch off LED

   XORLW   '#'       ;Wait for # character.
   BTFSS   STATUS,Z
   GOTO   Wait_Rx_Chr   ; Not # character

   MOVLW   RX_Timer   ; Number of 10 mS to wait for station ID character
   MOVWF   RX_Count


Test_DAV:
       btfsc   PIR1,RCIF    ;check if data received
   GOTO   Read_Data
   CALL   Delay_10mS
   DECFSZ   RX_Count
   GOTO   Test_DAV
   GOTO   Wait_Rx_Chr



Read_Data:
;   bsf   PORTA, 4       ; led ON  test ONLY

     SELBANK 3               ; SET BANK 3
       movf    RCREG,W           ;get received data into W
   BCF     RCSTA,CREN       ;disable receive
;   MOVWF   RX_Temp
       SELBANK 0               ; SET BANK 0
;   MOVF   RX_Temp,W
;   CALL    SerialTransmit ;Echo character as a test
   XORLW   Station_ID
   BTFSS   STATUS,Z
   GOTO   Wait_Rx_Chr   ; Not this station

   CALL   Read_ADC

   CALL   Output_Decimal       ;Output in decimal

   MOVLW   0x0D       ;C/R
   CALL   SerialTransmit

   MOVLW   0x0A       ;L/F
   CALL   SerialTransmit

;   CALL Delay_2_Sec
   CALL Delay_100mS
   CALL Delay_100mS
   CALL Delay_100mS

   GOTO   Wait_Rx_Chr
;       --------------------------------------------------------------------------------------


; *****************************
;   Subroutines
; *****************************
                   
;*****************************************************************************       
;
;   Function :  SerialTransmit
;               This function sends the byte in W over the RS232 port. The
;               function will wait until previous data has been sent
;
;   Input:      Byte in W
;
;   Output:     
;
;*****************************************************************************       
SerialTransmit:
;   MOVWF   TX_temp
       btfss   PIR1,TXIF    ;check that buffer is empty
       goto    $-1
       SELBANK 3               ; SET BANK 3
;   MOVF   TX_temp,W   
       movwf   TXREG        ;transmit byte
       SELBANK 0               ; SET BANK 0
       return
;*****************************************************************************
     
SerialReceive:

;Start of test code
;   CALL   Toggle_bit5

;    SELBANK 3               ; SET BANK 3
;   BTFSC   RCSTA,1       ;test for OVERRUN error
;   BSF   PORTA,4
;       SELBANK 0               ; SET BANK 0
;End of test code

       btfss   PIR1,RCIF    ;check if data received
       goto    SerialReceive           ;wait until new data
     SELBANK 3               ; SET BANK 3
       movf    RCREG,W        ;get received data into W
;   MOVWF   RX_Temp
       SELBANK 0               ; SET BANK 0
;   MOVF   RX_Temp,W
       return
;*****************************************************************************  
       
;*Read ADC
;* Result in ADC_TempL & ADC_Temp_H
;*
;*****************************************************************************  
;
     
Read_ADC:
         SELBANK 1               ; SET BANK 1
   MOVLW   0x09       ; Channel 2, ADON (Bit 0)
   MOVWF   ADCON0
   
   NOP       ;For short delay
   NOP
   NOP
   NOP
   NOP

   BSF   ADCON0,ADGO   ;Start conversion
ADC_Tst:   
   BTFSC   ADCON0,ADGO   
   GOTO   ADC_Tst
   MOVFW   ADRESH
       SELBANK 0               ; SET BANK 0
   MOVWF   ADC_Temp_H
         SELBANK 1               ; SET BANK 1
   MOVFW   ADRESL
       SELBANK 0               ; SET BANK 0
   MOVWF   ADC_Temp_L



; Rotate left 4 bits
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   RLF   ADC_Temp_L
   RLF   ADC_Temp_H
   MOVLW   0xF0
   ANDWF   ADC_Temp_L   ; Clear bits 0 to 3 that could have been set by uncleared carry bit
   RETURN

;       -----------------------------------------------------------------------------



Delay_10mS:           ;Need to set param to (10000 - 14)/2  = 9986/2 = 4993 = 0x1381
   MOVLW   0x13
   MOVWF   param2
   MOVLW   0x81
   MOVWF   param1
   GOTO   delay_cycles




Delay_100mS:
   MOVLW   0xC3
   MOVWF   param2
   MOVLW   0x49
   MOVWF   param1
   GOTO   delay_cycles
   
Delay_2_Sec:
   MOVLW   D'20'
   MOVWF   Temp_1
D2_Loop:
   CALL   Delay_100mS
   DECFSZ   Temp_1
   GOTO   D2_Loop
   RETURN

;*******************************************************************************
; Function:    delay_cycles
; Description: Delay a specified number of instruction cycles including
;              interrupt cycles.  The function call overhead adds between
;              13 and 16 cycles of delay on top of the specified value.
; With 4 Mhz system clock and 1:2 prescale
;Delay will be  param * 2 uS + (13 * 1uS)  + 0 to 3 uS
;       = param * 2 uS + (13 to16.0 uS)  (Use 14 uS for calculation.)
;   So param = No. of uS/2 - 7
; Parameters:  param1 - least significant byte of 16 bit cycle delay
;              param2 - most significant byte of 16 bit cycle delay
; Returns:     None
;*******************************************************************************
delay_cycles:
        comf    param1,F                ; negate the delay by complementing the       (1 uS)           (1 cycle)
        comf    param2,F                ; low and high bytes               (1 uS)           (1 cycle)
        bcf     T1CON,TMR1ON            ; stop timer 1                   (1 uS)           (1 cycle)
        movf    param1,W                ; move the low byte of the delay into       (1 uS)           (1 cycle)
        movwf   TMR1L                   ; timer 1                   (1 uS)           (1 cycle)
        movf    param2,W                ; move the high byte of the delay into       (1 uS)           (1 cycle)
        movwf   TMR1H                   ; timer 1                   (1 uS)           (1 cycle)
        bcf     PIR1,TMR1IF             ; clear the timer 1 rollover flag       (1 uS)           (1 cycle)
        bsf     T1CON,TMR1ON            ; turn on timer 1               (1 uS)           (1 cycle)
       
tmr1_check:     
        btfss   PIR1,TMR1IF             ; wait for the timer 1 rollover flag to       1 uS while looping    (2 uS) (2 cycle) on exit
        goto    tmr1_check              ; trigger
        return               ;                       (2 uS)   (2 cycle)

;**********************************************************************************
; Subroutine BCD (to convert 28-bit binary to 8-digit BCD)
; Binary value is in BIN1, BIN2, BIN3 & BIN4. BIN1 is LSB, BIN4 is MSB
; Result in BCD is in BCD1, BCD2, BCD3, BCD4 & BCD5. BCD1 is for overrange,
; BCD2 is MSB, BCD5 is LSB
;**********************************************************************************

BIN_BCD:   
   bcf   STATUS,C   ; clear carry bit
   movlw   D'32'
   movwf   COUNT       ; 32 in count
   clrf   BCD1       ; set BCD registers to 0
   clrf   BCD2
   clrf   BCD3
   clrf   BCD4
   clrf   BCD5

LOOPBCD:
   rlf   BIN1,f       ; LSB shift left binary registers
   rlf   BIN2,f
   rlf    BIN3,f
   rlf   BIN4,f       ; MSB
   rlf   BCD5,f       ; LSB shift left BCD registers
   rlf   BCD4,f
   rlf   BCD3,f
   rlf   BCD2,f
   rlf   BCD1,f       ; MSB

   decfsz   COUNT,f       ; reduce count value return when 0
   goto   DECADJ       ; continue decimal adjust
   
; result in BCD1-5. (BCD1 overrange, BCD2 MS byte)

   swapf   BCD2,w       ; get ms nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   ESEVN       ; ms digit
   movf   BCD2,w       ; get 2nd ms nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   ESIX

   swapf   BCD3,w       ; get next nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   EFIVE       ; ms digit
   movf   BCD3,w       ; get next nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   EFOUR
   
   swapf   BCD4,w       ; get ms nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   ETHREE       ; ms digit
   movf   BCD4,w       ; get 2nd ms nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   ETWO

   swapf   BCD5,w       ; get ms nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   EONE       ; ms digit
   movf   BCD5,w       ; get 2nd ms nibble
   andlw   0x0F
   iorlw   0x30       ; convert to ASCII
   movwf   EZERO
   return           ; completed decimal to BCD operation

; subroutine decimal adjust

DECADJ   movlw   BCD5       ; BCD LSB address
   movwf   FSR1L       ; pointer for BCD5
   CLRF   FSR1H
   call   ADJBCD       ; subroutine to adjust BCD
   movlw   BCD4
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   movlw   BCD3
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   movlw   BCD2
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   movlw   BCD1
   movwf   FSR1L
   CLRF   FSR1H
   call    ADJBCD
   goto   LOOPBCD

; subroutine adjust BCD

ADJBCD   movlw   0x03       ; w has 03
   addwf   INDF1,w       ; add 03 to BCDx register (x is 1-5)
   movwf   TEMP       ; store w
   btfsc   TEMP,3       ; test if >7
   movwf   INDF1       ; save as LS digit
   movlw   0x30       ; 3 for MSbyte
   addwf   INDF1,w       ; add 30 to BCDx register
   movwf   TEMP       ; store w
   btfsc   TEMP,7       ; test if >7
   movwf   INDF1       ; save as MS digit
   return           ; end subroutine



;**********************************************************************************
;
;
Output_Decimal:

   CLRF   BIN4       ;Clear top 3 bytes (Not used.)
   CLRF   BIN3
   CLRF   BIN2
   
   MOVF   ADC_Temp_H,W   ;ADC high byte
   MOVWF   BIN2   

   MOVF   ADC_Temp_L,W   ;ADC low byte
   MOVWF   BIN1   

   CALL   BIN_BCD

Output_Reading:

   MOVF   EFOUR, W
       CALL    SerialTransmit   

   MOVF   ETHREE, W
       CALL    SerialTransmit   

   MOVLW   '.'
       CALL    SerialTransmit

   MOVF   ETWO, W
       CALL    SerialTransmit

   MOVF   EONE, W
       CALL    SerialTransmit


   MOVF   EZERO, W
       CALL    SerialTransmit


   MOVLW   ' '
       CALL    SerialTransmit



   MOVLW   'V'
       CALL    SerialTransmit
   MOVLW   'o'
       CALL    SerialTransmit
   MOVLW   'l'
       CALL    SerialTransmit
   MOVLW   't'
       CALL    SerialTransmit
   MOVLW   's'
       CALL    SerialTransmit
   MOVLW   ' '
       CALL    SerialTransmit



   RETURN


;Toggle_bit5:           ;Used for testing
;   MOVWF BS_Temp
;   MOVLW   b'00100000'
;   XORWF   PORTA,f       ;Toggle bit 5
;   MOVFW   BS_Temp
;   RETURN


   end
The station identification letter is the first entry in the list of constants.
You will have to make sure that none of the ststions send a message back to the master that could contain text that could be seen by another station and interpreted as it's station address.
The code could be tidied up by using RETLW instructions so the text messages could be in a table. Note there ar still some bits of code that I used for testing that are commented out. Let me know if you would like the code for the sensors using the INA219 and the DHT22.

Les.
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top