News:

;) This forum is the property of Proton software developers

Main Menu

Accuracy on 12 bit ADC 16f1788

Started by geoff_c, Jul 01, 2026, 07:29 PM

Previous topic - Next topic

geoff_c

So im taking a differential signal from iso224 and feeding it to AN0 and AN1 on the pic I am using a external reference and have it enabled.The external ref is 4.096 v measured on a reliable meter (4.095) I measure the differential input going to the pic and compare it to the reading displayed on a lcd and its always about 4 % Low even at different levels and even different boards. Isnt that to high of error, i was expecting less than 1 %. Ive tried different clock rates and really no different. I can get the board to function by appling a cal factor but wanted to make sure eventhing was good first. TIA.
Geoff

trastikata

What is the source and reference impedance? For higher accuracy you may need to buffer the Vref and analog inputs.

Also the did you use dedicated analog ground trace or just the digital ground plane/traces? Another thing to consider is capacitor charging time, did you set it properly?

geoff_c

The differential signal comes from the ISO224 through 100 ohms in each leg and a 10 nf cap across for noise. the A side of the ISO224 is a isolated suppy and the b side is the same 5 v that goes to the pic, so i thought i could join both pin 8 Vss & pin 19 Vss together.


    DelayUS 200

    ADCON0.1 = 1

    While ADCON0.1 = 1
        KickWatchdog()
    Wend

    AdcU = 0
    AdcU = AdcU + (ADRESH << 8)
    AdcU = AdcU + ADRESL

I have 200 us set for precharge , itried higher but it didnt change. Im not buffering the v ref theres a 1 uf cap on the output of v ref and then right into the pic. I use a ref 3040eaidbzr reference and clock is set to Fosc/16

Thanks

trastikata

Hi,

I was talking about the Tad and Fosc relationship. Check in the datasheet ADC CLOCK PERIOD (TAD) VS. DEVICE OPERATING FREQUENCIES and ADC Acquisition Requirements paragraphs.

geoff_c

Thanks. Fosc is 16 Mhz and im using Fosc /16 so its in the recommended range. What accuracy would you expect.

top204

#5
I have found the ADC peripheral on the 8-bit devices to be rather inaccurate, in the years I have used them, and that is mainly because of the ADC type used in them.

However, they are not too inaccurate for most applications, and the PIC16F1877 device is now quite an old type (2015/2016), without some of the extras in the ADC peripherals now available in the more recent devices, especially the 18F types.

If better accuracy is required, you will need to use an ADC peripheral chip. Or try one of the newer devices with the ADCC peripheral, because they are more accurate.

Try different clock settings for the ADC, and also alter the time to wait between ADC readings, to give the capacitor time to stabilise, and also remember, too high a resistance on the ADC's channel input will not allow the internal capacitor to charge/discharge correctly, this giving an inaccurate reading.

I created the code template below so that the ADC clock can be changed easily when it is initialised, and the time between samples can be altered in the ADC_Read12 procedure.

It is 'untested', because I do not have a PIC16F1788 device, but it looks OK based upon the datasheet specs:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A template code listing to operate a PIC16F1788 device with its internal oscillator at 16MHz.
' Reads the ADC in 12-bit mode and transmits the results to a serial terminal.
'
' **** Untested because a PIC16F1788 device is not available, so created from the datasheet specs. ****
'
' Written for the Positron8 compiler by Les Johnson.
' https://sites.google.com/view/rosetta-tech/positron-compilers-experimenters-notebook.
'
    Device = 16F1788                                                ' Tell the compiler what device to compile for
    Declare Xtal = 16                                               ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare Auto_Heap_Arrays = On                                   ' Tell the compiler to create arrays above standard variables, so assembler code is more efficient
    Declare Auto_Heap_Strings = On                                  ' Tell the compiler to create Strings above standard variables, so assembler code is more efficient
    Declare Auto_Variable_Bank_Cross = On                           ' Tell the compiler to create any multi-byte variables in the same RAM bank. For more efficiency
'
' Setup USART1
'
    Declare Hserial_Baud = 9600                                     ' Set the Baud rate for USART1
    Declare HSerout1_Pin = PORTC.6                                  ' Tell the compiler the pin for USART1 Tx
'
' ADC Clock values for the ADC_Init procedure
'
$define cADC_FRC       %00000011                                    ' Use the FRC clock, which is supplied from a dedicated FRC oscillator
$define cADC_FOscDiv2  %00000000                                    ' Use the FOSC/2
$define cADC_FOscDiv4  %00000100                                    ' Use the FOSC/4
$define cADC_FOscDiv8  %00000001                                    ' Use the FOSC/8
$define cADC_FOscDiv16 %00000101                                    ' Use the FOSC/16
$define cADC_FOscDiv32 %00000010                                    ' Use the FOSC/32
$define cADC_FOscDiv64 %00000110                                    ' Use the FOSC/64
'
' Create any global variables, constants and aliases here
'
    Dim wADC_Value As Word                                          ' Holds the value read from the ADC
   
'------------------------------------------------------------------------------------------
' The main program starts here
' Read the ADC peripheral and transmit the results to a serial terminal
'
Main:
    Setup()                                                         ' Setup the program and any peripherals

    Do                                                              ' Create a loop
        wADC_Value = ADC_Read12(0)                                  ' Read the 12-bit ADC peripheral
        HRSOut1Ln "wADC_Value = ", Dec wADC_Value                   ' Transmit the result to a serial terminal
        DelayMS 500                                                 ' Delay, so that the values do not swamp the terminal
    Loop                                                            ' Do it forever

'------------------------------------------------------------------------------------------
' Read the 12-bit ADC
' Input     : pChan holds the ADC channel to read
' Output    : Returns the 12-bit ADC reading value
' Notes     : None
'
Proc ADC_Read12(pChan As Byte), Word
    Dim wADRES_SFR As ADRESL.Word
 
    pChan = pChan << 2                                              ' Shift the channel bits into the correct position
    ADCON0 = %00000001                                              ' Set for 12-bit mode, and clear the channel bits of ADCON0, and turn on the ADC
    ADCON0 = ADCON0 | pChan                                         ' Or in the channel bits of ADCON0
    DelayUS 20                                                      ' Acquisition time delay (change as required)
    ADCON0bits_GO_DONE = 1                                          ' Start the conversion
    Repeat: Until ADCON0bits_GO_DONE = 0                            ' Wait for the conversion to finish 
    Result = wADRES_SFR                                             ' Return the result
EndProc

'------------------------------------------------------------------------------------------
' Initialise the ADC peripheral
' Input     : pClk sets the ADC clock to use:
'              6 = FOSC/64 (or use cADC_FOscDiv64)
'              5 = FOSC/16 (or use cADC_FOscDiv16
'              4 = FOSC/4  (or use cADC_FOscDiv4
'              3 = FRC     (or use cADC_FRC)
'              2 = FOSC/32 (or use cADC_FOscDiv32
'              1 = FOSC/8  (or use cADC_FOscDiv8
'              0 = FOSC/2  (or use cADC_FOscDiv2
'           : pFVR holds 1 if the FVR is used for +Vref. 0 for VDD used for +Vref
' Output    : None
' Notes     : For a PIC16F1788
'
Proc ADC_Init(pClk As Byte, pFVR As Bit)
    ADCON0 = %00000001                                              ' ADRMD is set for 12-bit mode. ADC enabled. CHS is defaulted to AN0
    If pFVR = 1 Then
        ADCON1 = %10000001                                          ' ADFM set for 2s complement. -VREF is VSS. +VREF is FVR
    Else
        ADCON1 = %10000000                                          ' ADFM set for 2s complement. -VREF is VSS. +VREF is VDD
    EndIf
    pClk = pClk << 4                                                ' Move the clock bits into the correct position
    ADCON1 = ADCON1 | pClk                                          ' Or in the Clock bits (bits 4 to 6)
    ADCON2 = $00
    ADRESL = 0
    ADRESH = 0
EndProc

'------------------------------------------------------------------------------------------
' Set the ADC FVR for 4.096 Volts on a PIC16F1788
'
Proc FVR_4096()
    FVRCONbits_ADFVR1 = 1                                           ' \ ADC Fixed Voltage Reference Peripheral Output is 4x (4.096V)
    FVRCONbits_ADFVR0 = 1                                           ' /
EndProc

'------------------------------------------------------------------------------------------
' Set the ADC FVR for 2.048 Volts on a PIC16F1788
'
Proc FVR_2048()     
    FVRCONbits_ADFVR1 = 1                                           ' \ ADC Fixed Voltage Reference Peripheral Output is 2x (2.048V)
    FVRCONbits_ADFVR0 = 0                                           ' /
EndProc

'------------------------------------------------------------------------------------------
' Set the ADC FVR for 1.024 Volts on a PIC16F1788
'
Proc FVR_1024()    
    FVRCONbits_ADFVR1 = 0                                           ' \ ADC Fixed Voltage Reference Peripheral Output is 1x (1.024V)
    FVRCONbits_ADFVR0 = 1                                           ' /
EndProc

'------------------------------------------------------------------------------------------
' Disable the ADC FVR on a PIC16F1788
'
Proc FVR_Off()    
    FVRCONbits_ADFVR1 = 0                                           ' \ ADC Fixed Voltage Reference Peripheral Output is off
    FVRCONbits_ADFVR0 = 0                                           ' /
EndProc   
 
'------------------------------------------------------------------------------------------
' Enable the FVR on a PIC16F1788
' Input     : None
' Output    : None
' Notes     : Sets the ADC FVR for 4.096 Volts
'  
Proc FVR_Init()  
    FVRCON = %10100011                                              ' CDAFVR off. FVREN enabled. TSRNG Lo_range. ADFVR 4x. TSEN enabled
    Repeat: Until FVRCONbits_FVRRDY = 1                             ' Wait for the FVR to become stable
EndProc

'------------------------------------------------------------------------------------------
' Initialise the oscillator
' Input     : None
' Output    : None
' Notes     : For a PIC16F1788
'
Proc Osc_Init()   
    OSCCON  = $78                                                   ' SCS is FOSC. SPLLEN is disabled. IRCF is 16MHz
    OSCSTAT = $00
    OSCTUNE = $00
    BORCON  = $00
    DelayMS 100                                                     ' A delay to wait for the oscillator to stabilise
EndProc

'------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup() 
    Osc_Init()                                                      ' Initialise the oscillator
    FVR_Init()                                                      ' Setup the FVR for the default 4.096 volts
    PinInput PORTA.0                                                ' Set the ADC channel to input
    ANSELA.0 = 1                                                    ' Set pin AN0 as analogue input   
    ADC_Init(cADC_FOscDiv64, 0)                                     ' Setup the ADC to use the FOsc/64 clock, and VDD as +Vref
EndProc

'------------------------------------------------------------------------------------------
' Setup the config fuses for an internal oscillator on a PIC16F1788 device.
' OSC pins are general purpose I/O lines.
'
    Config1 FOSC_INTOSC,_                                           ' INTOSC oscillator. I/O function on CLKIN pin
            WDTE_OFF,_                                              ' Watchdog Timer disabled
            PWRTE_OFF,_                                             ' Power-up Timer disabled
            MCLRE_ON,_                                              ' MCLR/VPP pin function is MCLR
            CP_OFF,_                                                ' Program memory code protection is disabled
            CPD_OFF,_                                               ' EEPROM code protection is disabled
            BOREN_ON,_                                              ' Brown-out Reset enabled
            CLKOUTEN_OFF,_                                          ' Clock Out function is disabled. I/O or oscillator function on the CLKOUT pin
            IESO_ON,_                                               ' Internal/External Switchover mode is enabled
            FCMEN_ON                                                ' Fail-Safe Clock Monitor is enabled

    Config2 WRT_OFF,_                                               ' Flash Memory Self-Write protection off
            VCAPEN_OFF,_                                            ' Voltage Regulator Capacitor functionality is disabled on RA6
            PLLEN_OFF,_                                             ' PLL disabled
            STVREN_ON,_                                             ' Stack Overflow or Underflow will cause a reset
            BORV_LO,_                                               ' Brown-out Reset Voltage (Vbor). Low trip point selected
            LPBOR_OFF,_                                             ' Low Power Brown-Out Reset is disabled
            LVP_ON                                                  ' Low-voltage programming enabled

If the accuracy keeps being too low, you could try averaging multiple ADC samples, in a moving average filter, or a median filter using a bubble-sort mechanism.

Regards
Les


trastikata

Hi,

what is the output drive current for the external reference, is it buffered, this is important too.