A ds18S20/B20 one-wire temperature control

A series of ds18S20 and ds18B20 of digital temperature sensor uses 1-wire bi-directional protocol to request and receive temperature data. DS18S20 has 9-bit resolution while ds18B20 has 12-bit resolution.

In this project I create a system that follow these operations:

  • The CPU reads temperature data and display it on a MUX SSD.
  • A two-switch input uses to set the temperature for turning relay on/off.
  • Each time I set the temperature, the CPU save that setting to the EEPROM
  • Anytime the current temperature exceeds the setting, the relay works.

The design is controlled by a PIC16F628A/PIC16F84A CPU. The inputs are: ds18X20 and input tactile switches. The outputs are a SPDT relay and a four-digit MUX SSD that I posses. I use an extra 4511 BCD to SSD decoder that I left. The system is powered by a +12V maximum AC/DC input to a regulated +5V/100mA 78L05 voltage stabilizer.

The firmware is written using MikroC pro for 8-bit PIC. The schematic capture and simulation is made using Proteus VSM 8.

I made my own PCB with in-house components. For more high quality PCB, choosing a PCB supply service could be a good option.

Ds18S20
Schematic: 7404 is use for simulation only and 2N5551 is use for PCB design only

Project galleries:

Project archive:

https://github.com/BongPeav/MikroC-8-bit-PIC/raw/master/ds18S20_SSD_CON.rar

MikroC source code for this project:

#define INC     PORTA.RA0
#define DEC     PORTA.RA1
#define RLY     PORTA.RA3

unsigned char T0Cnt,setTemp,setRead;
//  Set TEMP_RESOLUTION to the corresponding resolution of used DS18x20 sensor:
//  18S20: 9  (default setting; can be 9,10,11,or 12)
//  18B20: 12
const unsigned short TEMP_RESOLUTION = 12;
unsigned Cnt1,Cnt2,Cnt3,Cnt4;
bit minus,ssdOn;

void interrupt(void){
     asm{
         CLRWDT
         }

     if(INTCON.T0IF==1){
       T0Cnt++;
       INTCON.T0IF=0;
       }
}

void ioInit(void);
void ssdOut(unsigned int temp);
void tempRead(void);
unsigned int gettemp();
void tempSet(void);
void dataRead(void);

void main() {
     unsigned tempData,oldData;
     ioInit();
     dataRead();
     tempData=getTemp();
     tempData=0;
     delay_ms(20);
     oldData=0;
     while(1){
              if(ssdOn==1){
              if(Cnt1>=75){
                 tempData=getTemp();
                 tempData=getTemp();

                 Cnt1=0;
                 }

       ssdOut(tempData);
       }
       else
       ssdOut(setTemp);
       tempSet();
       if(Cnt4>=50){
       if(tempData>=setTemp)  {
                           RLY=1;
                           Cnt4=0;
                           }
       else                {
                           RLY=0;
                           Cnt4=0;
                           }

                           }
       }
}

void ssdOut(unsigned int temp){
     char ssd[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
     char d1,d2,d3;
     d1=temp/100;
     d2=(temp%100)/10;
     d3=temp%10;
     
     switch(T0Cnt){

                   case 2 :
                        PORTB=0x00;
                        PORTB=d1&0x0F;
                        PORTB|=0x20;
                        break;
                        
                   case 3 :
                        PORTB=0x00;
                        PORTB=d2&0x0F;
                        PORTB|=0x40;
                        break;
                        
                   case 4 :
                        PORTB=0x00;
                        PORTB=d3&0x0F;
                        PORTB|=0x80;
                        break;
                        
                   case 5 :
                        Cnt1++;
                        Cnt2++;
                        CNt3++;
                        Cnt4++;
                        T0Cnt=0;
                        break;
                   }
}

void ioInit(void){
     PCON.OSCF=1;
     TRISB=0x00;
    TRISA=0x03;
    CMCON=0x07;
     PORTA=0x00;
     OPTION_REG=0x03; // 1:16
     INTCON.GIE=1;
     INTCON.T0IE=1;
     INTCON.T0IF=0;
     PORTB=0x00;
     T0Cnt=0;
     Cnt1=0;
     Cnt2=0;
     Cnt3=0;
     Cnt4=0;
     minus=0;
     ssdOn=1;
}

unsigned int gettemp(){
       unsigned int temp;
       const unsigned short RES_SHIFT = TEMP_RESOLUTION - 8;

       Ow_Reset(&PORTA, 4);                         // Onewire reset signal
       Ow_Write(&PORTA, 4, 0xCC);                   // Issue command SKIP_ROM
       Ow_Write(&PORTA, 4, 0x44);                   // Issue command CONVERT_T
       Delay_us(120);

       Ow_Reset(&PORTA, 4);
       Ow_Write(&PORTA, 4, 0xCC);                   // Issue command SKIP_ROM
       Ow_Write(&PORTA, 4, 0xBE);                   // Issue command READ_SCRATCHPAD

       temp =  Ow_Read(&PORTA, 4);
       temp = (Ow_Read(&PORTA, 4) << 8) + temp;
       if(temp&0x8000){
                             minus=1;
                             temp=~temp;
                             }
       else
           minus=0;
           
       temp = temp >> RES_SHIFT ;
       return temp;
}

void tempSet(void){
     if(DEC==0&&Cnt2>=10){
                ssdOn=0;
                if(setTemp<120) setTemp++;
                EEPROM_Write(0x00,setTemp);
                Cnt3=0;
                Cnt2=0;
                }
                
     if(INC==0&&Cnt2>=10){
     ssdOn=0;
                if(setTemp<120) setTemp--;
                EEPROM_Write(0x00,setTemp);
                Cnt3=0;
                Cnt2=0;
                }
     setRead=setTemp;
     if(Cnt3>=150) {
       ssdOn=1;
       Cnt3=0;
     }
     
}

void dataRead(void){
     setTemp=EEPROM_Read(0x00);
}

For any custom design relates to this project, you can contact the author.

PIC16F876A RTC with seven segments display

I made a seven segments clock display with ds1307 RTC. It feature an 8-digit hh-mm-ss with adjustment and alarm setting. Due to a design and PCB fabrication difficulties, I decided to divide two distinct board, one’s for the main controller board and another one is a display board that stack on the main controller board. The schematic below is the main controller board powered by PIC16F876A.

SSD_CLOCK_MAINBOARD
Schematic Circuit 7404 is just use for simulation only not for PCB design. ULN2804 is not include in simulation but for PCB design.

copper side
copper soldering side

component side
component side

A 7-segments display board stack above the main board.

Multiplexed display board
Schematic of multiplexed seven segments display board

7-segment display board
7-segment display board copper side

component side
components legend side

Completed project
I have assembled and test this project with error-free.

Making the PCB for this project by hands could spend a lot times etching, drilling and soldering. The user could select a PCB service from a PCB supplier.

PIC16F876A is an 8-bit PIC microcontroller with 28-pin package. its package is also come with a DIP version suitable for hobbyist projects and experiment.

This device is fairly fit to this project referring to the numbers of I/O pins requirement, RAM and ROM space.

An I2C peripheral is also require because it needs to read/write timing data between the DS1307 real time clock chip.

The CPU uses its timer0 timer ticks to multiplex an 8-digits 7-segments display. The display made from eight individual 0.8 inch hi-read common cathode display.

I put some buttons to adjust the HH:MM:SS and also the alarm setting. An output active buzzer beeps any time the alarm time is reached. Buttons and buzzer active time is implemented using the timer0 timer ticks method.

MPLABX XC8 is a compiler of choice for this project. Using this free compiler, the coder can access all the registers definition of the SFR just like one’s the the assembly language programming.

Source code and completed PCB project could be get from GitHub.

https://github.com/BongPeav/xc8/raw/master/SSD_CLOCK.X.rar

MPLABX XC8 source code lists below:

#include <xc.h>
#include "config.h"
#include "defination.h"
#include "i2c.h"

unsigned char T0Cnt;
unsigned char T1Cnt;
unsigned char ssdCnt;
unsigned char _time[3];
unsigned char _timeAlarm[3];
unsigned char modeSel;
unsigned char buzTimer;
unsigned char _PORTB;
unsigned int ssdBlink;
bit             blink;
bit             swOn;

void T0Init(void);
void timersTick(void);
void T1Init(void);
void ioInit(void);
void varInit(void);
void clockSetup(void);
void clockAdj(void);
void alarmSet(void);
void alarmCheck(void);
void SSDout(unsigned char hrs,unsigned char mns,unsigned char scs);

unsigned char getDate(unsigned char add);
unsigned char bcd2dec(unsigned char bcd);
unsigned char dec2bcd(unsigned char dec);
void    rtcInit(void);

unsigned char eeRead(unsigned char add);
void eeCommand(void);
void eeWrite(unsigned char add,unsigned char dat);
void alarmLoad(void);

__EEPROM_DATA(12,30,10,0,0,0,0,0);

void interrupt ISR(void){
    if(TMR0IF&&TMR0IE){
        T0Cnt++;
        TMR0=-78;
        TMR0IF=0;
    }

    if(TMR1IF&&TMR1IE){
        T1Cnt++;
        TMR1IF=0;
    }
}

void main(){
    
    ioInit();
    T0Init();
    i2cMasterInit(100000);
    varInit();
    alarmLoad();
    rtcInit();
    T1Init();
    while(1){
        timersTick();
        SSDout(_time[2],_time[1],_time[0]);
        if(modeSel==0)  clockAdj();
        clockSetup();
        alarmCheck();
    }
}

void T0Init(void){
    T0CS=0;
    PSA=0;
    OPTION_REGbits.PS=0x05; // 1:64
    GIE=1;
    TMR0IE=1;
    TMR0IF=0;
}

void timersTick(void){

    if(T0Cnt>=2){
        ssdCnt++;
        ssdBlink++;
        T0Cnt=0;
    }

    if(ssdBlink>=blinkRate){
        blink^=1;
        if(modeSel==0)
        for(int i=0;i<3;i++){
            _time[i]=getDate(i);
            _time[i]=bcd2dec(_time[i]);
        }
        buzTimer++;
        ssdBlink=0;
    }

    if(T1Cnt>=7){
        swOn^=1;
        T1Cnt=0;
    }
}

void ioInit(void){
    TRISA=0xFC;
    TRISB=0x00;
    TRISC=0x00;
    PORTB=0;
    ADCON1bits.PCFG=0x06;   // all digital
    CMCONbits.CM=0x07;      // comparator off
}

void varInit(void){
    T0Cnt=0;
    ssdCnt=0;
    ssdBlink=0;
    modeSel=0;
    T1Cnt=0;
    buzTimer=0;
}

void SSDout(unsigned char hrs,unsigned char mns,unsigned char scs){
    // COMMON CATHODE
    const unsigned char SEG[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
    unsigned char hrs10,hrs1,mns10,mns1,scs10,scs1;
/*
    hrs10=hrs>>4;
    hrs1=hrs&0x0F;
    mns10=mns>>4;
    mns1=mns&0x0F;
    scs10=scs>>4;
    scs1=scs&0x0F;
*/

    hrs10=hrs/10;
    hrs1=hrs%10;
    mns10=mns/10;
    mns1=mns%10;
    scs10=scs/10;
    scs1=scs%10;
    
    switch(ssdCnt){
        case 1 :
            PORTA=0x00;
            PORTC=0x00;
            PORTB=SEG[hrs10]|_PORTB;
            D1=1;
            break;

        case 2 :
            PORTA=0x00;
            PORTC=0x00;
            PORTB=SEG[hrs1]|_PORTB;
            D2=1;
            break;

        case 3 :
            PORTA=0x00;
            PORTC=0x00;
            if(blink){
                PORTB=64|_PORTB;
                D3=1;
            }
            break;

        case 4 :
            PORTA=0x00;
            PORTC=0x00;
            PORTB=SEG[mns10]|_PORTB;
            D4=1;
            break;

        case 5 :
            PORTA=0x00;
            PORTC=0x00;
            PORTB=SEG[mns1]|_PORTB;
            D5=1;
            break;

       case 6 :
            PORTA=0x00;
            PORTC=0x00;
            if(blink){
                PORTB=64|_PORTB;
                D6=1;
            }
            break;

      case 7 :
            PORTA=0x00;
            PORTC=0x00;
            PORTB=SEG[scs10]|_PORTB;
            D7=1;
            break;

      case 8 :
            PORTA=0x00;
            PORTC=0x00;
            PORTB=SEG[scs1]|_PORTB;
            D8=1;
            break;

      case 9 :   
            PORTA=0x00;
            PORTC=0x00;
            ssdCnt=0;
            break;
    }
}

unsigned char getDate(unsigned char add){
    unsigned char _time;
    i2cMasterStart();
    i2cMasterWrite(ds1307_W);
    i2cMasterWrite(add);
    i2cMasterRepeatedStart();
    i2cMasterWrite(ds1307_R);
    _time=i2cMasterRead(0);
    i2cMasterStop();
    return _time;
}

unsigned char bcd2dec(unsigned char bcd){
    unsigned char dec;
    dec=10*(bcd>>4)+(bcd&0x0F);
    return dec;
}

unsigned char dec2bcd(unsigned char dec){
    unsigned char bcd,bcd10,bcd1;
    bcd10=dec/10;
    bcd1=dec%10;
    bcd=(bcd10<<4)+bcd1;
    return bcd;
}

void clockSetup(void){
    
    if(mSel==0&&swOn==1){
        modeSel++;
        swOn=0;
    }

    switch(modeSel){
        case 1 :
            alarmSet();
            break;
        case 2 :
            modeSel=0;
            break;
       
    }
}

void    rtcInit(void){
        i2cMasterStart();
        i2cMasterWrite(ds1307_W);
        i2cMasterWrite(0x07);       // Select Control Register
        i2cMasterWrite(0x10);       // 1Hz
        i2cMasterStop();
}

void clockAdj(void){
    unsigned char temp;

    if(sAdj==0&&swOn==1){
        _time[0]++;
        if(_time[0]==60) _time[0]=0;
        temp=dec2bcd(_time[0]);
        i2cMasterStart();
        i2cMasterWrite(ds1307_W);
        i2cMasterWrite(0x00);
        i2cMasterWrite(temp);
        i2cMasterStop();
        swOn=0;
    }

    if(mAdj==0&&swOn==1){
        _time[1]++;
        if(_time[1]==60) _time[1]=0;
        temp=dec2bcd(_time[1]);
        i2cMasterStart();
        i2cMasterWrite(ds1307_W);
        i2cMasterWrite(0x01);
        i2cMasterWrite(temp);
        i2cMasterStop();
        swOn=0;
    }

    if(hAdj==0&&swOn==1){
        _time[2]++;
        if(_time[2]==24) _time[2]=0;
        temp=dec2bcd(_time[2]);
        i2cMasterStart();
        i2cMasterWrite(ds1307_W);
        i2cMasterWrite(0x02);
        i2cMasterWrite(temp);
        i2cMasterStop();
        swOn=0;
    }
}

void eeWrite(unsigned char add,unsigned char dat){
    // write address and data to eeprom
    while(EECON1bits.WR);
    EEADR=add;
    EEDATA=dat;
    EECON1bits.EEPGD=0; // point to data memory
    EECON1bits.WREN=1;  // enable write
}

unsigned char eeRead(unsigned char add){
    unsigned char edata;
    EEADR=add;
    EECON1bits.EEPGD=0; // select data space
    EECON1bits.RD=1;    // read
    edata=EEDATA;
    return edata;
}

void eeCommand(void){
    // Special Command
    GIE=0;
    EECON2=0x55;
    EECON2=0xAA;
    EECON1bits.WR=1;
    GIE=1;
    TMR0IE=1;
    TMR0IF=0;
    EECON1bits.WREN=0;
}

void alarmLoad(void){
    for(int i=0;i<3;i++){
        _timeAlarm[i]=eeRead(i);
    }
}

void alarmSet(void){

    _PORTB=0x00;
    PORTB|=_PORTB;
    for(int i=0;i<3;i++){
        _time[i]=_timeAlarm[i];
    }

    if(sAdj==0&&swOn==1){
        _timeAlarm[0]++;
        if(_timeAlarm[0]==60)   _timeAlarm[0]=0;
        eeWrite(sAlarm,_timeAlarm[0]);
        eeCommand();
        swOn=0;
    }

    if(mAdj==0&&swOn==1){
        _timeAlarm[1]++;
        if(_timeAlarm[1]==60)   _timeAlarm[1]=0;
        eeWrite(mAlarm,_timeAlarm[1]);
        eeCommand();
        swOn=0;
    }

    if(hAdj==0&&swOn==1){
        _timeAlarm[2]++;
        if(_timeAlarm[2]==24)   _timeAlarm[2]=0;
        eeWrite(hAlarm,_timeAlarm[2]);
        eeCommand();
        swOn=0;
    }

     alarmLoad();
     
}

void T1Init(void){
    T1CONbits.T1CKPS=0x02;  // 1:4
    T1CONbits.T1OSCEN=0;
    T1CONbits.TMR1CS=0;     //FOSC/4
    T1CONbits.TMR1ON=1;
    TMR1H=0;
    TMR1L=0;
    INTCONbits.PEIE=1;
    PIE1bits.TMR1IE=1;
    PIR1bits.TMR1IF=0;
}

void alarmCheck(void){
    unsigned short long alarmSet;
    unsigned short long timeRun;
    alarmSet=(3600*_timeAlarm[2])+(60*_timeAlarm[1])+_timeAlarm[0];
    timeRun=(3600*_time[2])+(60*_time[1])+_time[0];

    if((timeRun<=alarmSet+10)&&(timeRun>=alarmSet)){
        _PORTB=128;
    }
    else
        _PORTB=0;
    
}

Bill of Material:

Category	References	Value	Stock Code	Unit Cost
Capacitors	C1	10p	Maplin WX44X	
Capacitors	C2	10p	Maplin WX44X	
Capacitors	C3	1000U	Digikey P5142-ND	
Capacitors	C4	100n	Maplin BX03D	
Resistors	R1	220R	M220R	
Resistors	R2	220R	M220R	
Resistors	R3	220R	M220R	
Resistors	R4	220R	M220R	
Resistors	R5	220R	M220R	
Resistors	R6	220R	M220R	
Resistors	R7	220R	M220R	
Resistors	R8	220R	M220R	
Resistors	R9	10k	M10K	
Resistors	R10	10k	M10K	
Resistors	R11	1k	M1k	
Resistors	R12	10k	M10K	
Resistors	R13	10k	M10K	
Resistors	R14	10k	M10K	
Resistors	R15	10k	M10K	
Resistors	R16	10k	M10K	
Resistors	R17	470R	M220R	
Integrated Circuits	U1	CONN-SIL8		
Integrated Circuits	U2	DD7SEG08		
Integrated Circuits	U3	7404		
Integrated Circuits	U4	7404		
Integrated Circuits	U5	ULN2804		
Transistors	Q1	2N3904		
Diodes	D1	CONN-SIL7		
Diodes	D2	LED		
Miscellaneous	BAT1	3V		
Miscellaneous	BUZ1	BUZZER03		
Miscellaneous	BUZ2	BUZZER03		
Miscellaneous	DP1	DD7SEG08		
Miscellaneous	DP2	DD7SEG08		
Miscellaneous	DP3	DD7SEG08		
Miscellaneous	DP4	DD7SEG08		
Miscellaneous	J1	DD7SEG08		
Miscellaneous	J2	CONN-SIL8		
Miscellaneous	J3	CONN-SIL2		
Miscellaneous	J4	ICSP		
Miscellaneous	J5	CONN-SIL7		
Miscellaneous	J6	CONN-SIL8		
Miscellaneous	SW1			
Miscellaneous	SW2			
Miscellaneous	SW3			
Miscellaneous	SW4			
Miscellaneous	SW5			
Miscellaneous	X1	DD7SEG08		
Miscellaneous	X2	CRYSTAL		

Click here to download full PCB project file.

PCB from PCBWay

Making a distance measurement using ultrasonic Sensor and PIC16F716

HC-SR04 is an ultrasonic range measurement. It can measure distance using sound wave up to 4m.

In typical application we usually use it with character LCD. But in this situation I use seven segments display instead.

The system is built using three main components: HC-SR04, PIC16F716 CPU and a 4-digit multiplexed seven segments display.

To measure the measured range I use a simple equation (time in microSecond/59) in centimeter. I use timer1 of PIC16F716 with a clock rate of 4MHz with no prescaler. So the instruction clock rate is 4MHz/4 = 1MHz giving a instruction time of 1uS. I use this clock as timer1 clock source.

Multiplexed seven segments display is driven by timer0 interrupt. each digit is turn on in the range of milliseconds.

sch
Schematic Circuit
pcb
PCB Layout

Source Code (MPLABX XC8) :

/*
PIC16F716 HC-SR04 Ultrasonic range measurement
*/

#include <xc.h>
#include "config.h"

#define _XTAL_FREQ 4000000

#define echoPin PORTAbits.RA1
#define trigPin PORTAbits.RA0

void systemInit(void);
void ssdDisplay(unsigned int dat);
unsigned int getDistance(void);

unsigned int cnt;
unsigned int hc04;
bit sensorOn;

// TIMER0 overflow at 32.7mS
void interrupt ISR(void){
if(T0IF==1) {
cnt++;
hc04++;
}

if(hc04>=1500) {
sensorOn=1;
hc04=0;
}
T0IF=0;
}

void main(){
unsigned int distance,old;
unsigned int dMax,dMin;
systemInit();
TMR1L=0;
TMR1H=0;
while(1){ 
if(sensorOn==1){
distance=getDistance();
sensorOn=0;
}
dMax=old+2;
dMin=old-2;
if((distance>=dMin)&&(distance<=dMax))
distance=old;
ssdDisplay(distance);
old=distance;
}
}

void systemInit(){
TRISA=0x02;
TRISB=0x00;
PORTA=0x00;
PORTB=0x00;
ADCON1bits.PCFG=0x07; // DIGITAL IO
// TMR0 Setup
OPTION_REGbits.T0CS=0; // INT CLK
OPTION_REGbits.PSA=1; // WDT PRESCALER
OPTION_REGbits.PS=0x07; // 1:128
// TMR1 Setup
T1CONbits.T1CKPS=0x00; //1:1
T1CONbits.TMR1CS=0; // fosc/4
T1CONbits.nT1SYNC=1 ; // do not sync
T1CONbits.TMR1ON=0;
INTCONbits.GIE=1;
INTCONbits.T0IE=1;
INTCONbits.T0IF=0;
cnt=0;
hc04=0;
sensorOn=0;
}

void ssdDisplay(unsigned int dat){
// commond anode
char ssd[10]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
char d0,d1,d2;
char mask=0x7F;

static unsigned char sch=0;
if(cnt>=10) {
sch++;
cnt=0;
}

d2=(dat%1000)/100;
d1=(dat%100)/10;
d0=dat%10;

switch(sch){
// 1th
case 1: PORTA=0x00;
mask=0xFF;
PORTB=0xA7&mask; 
break;

// 4th
case 2: PORTA=0x00;
if(dat>=100){
mask=0x7F;
PORTB=ssd[d2]&mask;
PORTA|=0x04;
}
break;
// 3th
case 3 : PORTA=0x00;
if(dat>=10){
mask=0x7F;
PORTB=ssd[d1]&mask;
PORTA|=0x08;
}
break;

// 2nd
case 4 :PORTA=0x00;
mask=0x7F;
PORTB=ssd[d0]&mask;
PORTA|=0x10;
break;

case 5 : sch=0;
mask=0x7F;
// sensorOn=1;
break;
}
}

unsigned int getDistance(void){
unsigned long duration;
unsigned int distance;
unsigned char T1L,T1H;
trigPin=1;
__delay_us(10);
trigPin=0;
while(echoPin==0);
T1CONbits.TMR1ON=1;
while(echoPin==1);
T1CONbits.TMR1ON=0;
T1L=TMR1L;
T1H=TMR1H;
duration=(T1H*256)+500+T1L;
if(duration<=25000)
distance=duration/58;
else if (duration<116)
distance=0;
else distance=0;
duration=0;
TMR1L=0;
TMR1H=0;
return distance;
}

 

download link:

https://github.com/BongPeav/xc8/raw/master/HC-SR04-SSD.X.rar

 

A simple DC Voltmeter with polarity

Using the built-in ADC module in PIC16 we can measure the DC voltage value with both polarities, negative to positive or positive to negative.

I use to opposite voltage references Vref- and Vref+.  Since the maximum reference voltage of ADC is 5V. I divide it into 2.5V equally. So I connect Vref- to -2.5V and Vref+ to +2.5V. So I can measure either positive and negative voltage source.

The ADC resolution of PIC16F876A is 10 bits. The numbers of step is 1024 (  0 to 1023 decimal). The lowest value is 0 when the measure voltage is -2.5V and the highest is 1023 when the measure voltage is +2.5V. The step size is 5V/1023 = 0.0048V per step.

Vref
Schematic Circuit

I use a pot to change the voltage range from -2.5V to +2.5V. The input to the ADC is direct with no use of voltage divider. So it can measure the voltage of 2.5Vpp.

C code written in MPLABX XC8

#include <xc.h>
#include "config.h"
#define _XTAL_FREQ 4000000

void ssdOut(int volt);
void adInit();
unsigned int getAd();

void main(){
int temp;
TRISA=0xFF;
TRISB=0x00;
TRISC=0x00;
adInit();

while(1){
temp=getAd();
ssdOut(temp);
}
}

void adInit(){
ADCON1bits.ADFM=1;
ADCON1bits.ADCS2=1;
ADCON1bits.PCFG=0x0F;
ADCON0bits.ADCS=0x03;
ADCON0bits.CHS=0;
ADCON0bits.ADON=1;
}
unsigned int getAd(){
ADCON0bits.GO_nDONE=1;
while(ADCON0bits.GO_nDONE);
return ((ADRESH<<8)+ADRESL);
}

void ssdOut(int volt){
// common cathod
const char sg[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F} ;
short long temp,d1,d2,d3;
short long voltage;
char minus;
temp=volt-512;

if(temp<0) {
temp=~temp;
minus=1;
}
else
minus=0;

voltage=(temp*501/1024);

d1=voltage/100;
d2=(voltage%100)/10;
d3=voltage%10;

if(minus){
PORTC=0x00;
PORTB=64;
PORTC=0x01;
__delay_ms(1);
}

PORTC=0x00;
PORTB=sg[d1]|128;
PORTC=0x02;
__delay_ms(1);

PORTC=0x00;
PORTB=sg[d2];
PORTC=0x04;
__delay_ms(1);

PORTC=0x00;
PORTB=sg[d3];
PORTC=0x08;
__delay_ms(1);
}

Here is the source code:

https://github.com/BongPeav/xc8/raw/master/Vref%2B-.X.rar