News:

Let's find out together what makes a PIC Tick!

Main Menu

Oversampling techniques

Started by Amod, Sep 24, 2022, 09:14 PM

Previous topic - Next topic

Amod

Is there any way of getting 12bit result by simple 10bit adc in Proton compiler.I found oversampling techniques in Picbasic Pro.


trastikata

Quote from: Amod on Sep 24, 2022, 09:14 PMIs there any way of getting 12bit result by simple 10bit adc in Proton compiler.I found oversampling techniques in Picbasic Pro.

Brief answer is to accumulate in a variable 4^n samples and then right shift it n-places, where n is the oversampling bits you want to achieve.

If you have spare processing time, I'd recommend using some averaging to filter out the noise. This would become accumulating in a variable k*(4^n) samples and right shifting it n+k bits.

tumbleweed

Just one word of caution...

Numerically, you'll always get an answer using this method. The question is whether the result is actually valid.

People often ignore the criteria under which this technique produces usable results (sampling freq, noise distribution, etc).
 

trastikata

Quote from: tumbleweed on Sep 25, 2022, 11:37 AMJust one word of caution...

Numerically, you'll always get an answer using this method. The question is whether the result is actually valid.

People often ignore the criteria under which this technique produces usable results (sampling freq, noise distribution, etc).

tumbleweed, this discussion pops-up every time when the topic of oversampling appears  ;D .

In practical terms - with the tests I've done, the PIC's internal ADC has enough normalized noise to comply with the requirements for the oversampling theorem.

Yasin

I think this is self-deception. Forget the software side. Let's take the hardware. If we want to measure 0~5 volts with a 10 bit adc. When the resolution is 12 bits, it becomes 5/4096 ~ 1.22 millivolts. When the resolution is 10 bits, it becomes 5/1024 ~ 4.88 millivolts. No matter how many samples are taken, this fact does not change. Multisampling works well to get a true-to-life average.

Yves

Is there higher than 10 bit resolution ADC in the 16 or 18 series? I wish microchip was integrating their 18bit with 4x programable amp ADC like the MCP3422 into their pics. That will simplify my life.

Yves
Yves

trastikata

@Yves
QuoteIs there higher than 10 bit resolution ADC in the 16 or 18 series?

There are quite few 8bit PICs with  12b ADCs - usually the one ending with xxx3. For example 18F2553/18F4553 are the 12b ADC versions of 18F2550/18F4550, 18F26J53/18F46J53 are those of 18F26J50/18F46J50, there are of course some 12b that do not have the 10b ADC versions.

@Yasin
QuoteI think this is self-deception. Forget the software side. Let's take the hardware.

Actually it works quite well, an example is almost any +16b ADC using delta sigma modulation  ;)

John Drew

I use averaging in a number of projects with 10bit ADC. Because there is always some jitter the average (using floats) exceeds 10 bits. There's probably some maths to prove this.
Trastikata is on track with his suggestion.
I see steady readings to give me an additional level of accuracy. It definitely works.
John

Yasin

Quote from: trastikata on Sep 25, 2022, 01:17 PMActually it works quite well, an example is almost any +16b ADC using delta sigma modulation  ;)

I didn't mean it worked badly. On the contrary, it's true. I just mean that with the multiplicity of sampling the actual resolution does not rise. For example when measuring with 10 bit adc. Let adc give X value at a constant voltage value. Then, when adc produces X+1 or X-1 value when there are ±2mV fluctuations, we see this change as Vref/1024 volts. Let's say Vref=5V, the resolution will be 4.88mV. Our changes of 0~±2mV will not actually be measured. The average will be much less volatile just because so many references are made. This is a good thing. If this was done with oversampling only, it would be possible to get 24-bit resolution from an adc that is actually 4 bits by increasing the sampling rate much more.

top204

OverSampling an ADC is one of those things that has a host of calculations and theories with it and a few different methods that all look staggeringly complex when written as mathematical calculations, but the principle is very straigtforward. :-) However, it is one of those principles that people, sometimes, get over complex with and rarely agree with each other about :-) But if it works for a project being built, it works... Regardless of its over-complex principle theories! :-)

The simple, and functional, method is... Take a multitude of ADC readings and accumulate them. i.e. Add them all to each other. Then divide the accumulated value by a value based upon the amount of accumulations taken.

The one important thing that comes into play with over sampling is the Nyquist theory, that states the sampling must be faster than the signal being sampled, because multiple samples do take longer than a single ADC reading.

The demo program below shows a simple method of oversampling using averaging. It has several procedures to oversample a 10-bit ADC at different resolutions:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A demonstration of simple oversampling of an ADC for higher resolutions
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F26K22                           ' Tell the compiler what device to compile for
    Declare Xtal = 64                           ' Tell the compiler what frequency the device will be operating at (in MHz)
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600                ' Set the Baud rate to 9600
    Declare HRSOut1_Pin = PORTC.6
'
' Create a variable for the demo
'
    Dim wADC_RawValue As Word                   ' Holds the oversampled 12-bit ADC value

'-------------------------------------------------------------------------------------
' The main program starts here
' Read the oversampled ADC value and display it on a serial terminal
'
Main:
    Setup()                                     ' Setup the program

    Do                                          ' Create a loop
        wADC_RawValue = ADC_Read12()            ' Read the oversampled 12-bit ADC
        HRSOutLn Dec wADC_RawValue              ' Display the value on a serial terminal
        DelayMS 100                             ' A small delay between samples, so they can be seen changing
    Loop                                        ' Do it forever

'-------------------------------------------------------------------------------------
' Oversample a 10-bit ADC to return a 12-bit result
' Input     : None
' Output    : Returns the oversampled 12-bit ADC value
' Notes     : None
'
Proc ADC_Read12(), Word
    Dim wAccum As Word = 0
    Dim bSamples As Result.Byte0

    For bSamples = 15 DownTo 0                  ' Create an over sampling loop
        wAccum = wAccum + ADIn 0                ' Accumulate the ADC readings
    Next
    Result = wAccum >> 2                        ' Divide the accumulator
EndProc

'-------------------------------------------------------------------------------------
' Oversample a 10-bit ADC to return a 13-bit result
' Input     : None
' Output    : Returns the oversampled 13-bit ADC value
' Notes     : None
'
Proc ADC_Read13(), Word
    Dim dAccum As Dword = 0
    Dim bSamples As Result.Byte0

    For bSamples = 63 DownTo 0                  ' Create an over sampling loop
        dAccum = dAccum + ADIn 0                ' Accumulate the ADC readings
    Next
    Result = dAccum >> 3                        ' Divide the accumulator
EndProc

'-------------------------------------------------------------------------------------
' Oversample a 10-bit ADC to return a 14-bit result
' Input     : None
' Output    : Returns the oversampled 14-bit ADC value
' Notes     : None
'
Proc ADC_Read14(), Word
    Dim dAccum As Dword = 0
    Dim bSamples As Result.Byte0

    For bSamples = 255 DownTo 0                 ' Create an over sampling loop
        dAccum = dAccum + ADIn 0                ' Accumulate the ADC readings
    Next
    Result = dAccum / 16                        ' Divide the accumulator
EndProc

'-------------------------------------------------------------------------------------
' Oversample a 10-bit ADC to return a 15-bit result
' Input     : None
' Output    : Returns the oversampled 15-bit ADC value
' Notes     : None
'
Proc ADC_Read15(), Word
    Dim dAccum As Dword = 0
    Dim wSamples As Result

    For wSamples = 1023 DownTo 0                ' Create an over sampling loop
        dAccum = dAccum + ADIn 0                ' Accumulate the ADC readings
    Next
    Result = dAccum / 32                        ' Divide the accumulator
EndProc

'-------------------------------------------------------------------------------------
' Oversample a 10-bit ADC to return a 16-bit result
' Input     : None
' Output    : Returns the oversampled 16-bit ADC value
' Notes     : None
'
Proc ADC_Read16(), Word
    Dim dAccum As Dword = 0
    Dim wSamples As Result

    For wSamples = 4095 DownTo 0                ' Create an over sampling loop
        dAccum = dAccum + ADIn 0                ' Accumulate the ADC readings
    Next
    Result = dAccum / 64                        ' Divide the accumulator
EndProc

'-------------------------------------------------------------------------------------
' Setup the program
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    IntOsc_64Mhz()                              ' Set the device to operate at 64MHz with its internal oscillator
    ADC_Init()                                  ' Setup the ADC
EndProc

'-----------------------------------------------------------------------------------------
' Setup the ADC
' Input     : None
' Output    : None
' Notes     : Set for 10-bit operation
'           : Uses the Gnd and VDD as vrefs
'           : Uses the FRC oscillator
'           : Sets AN0 as an analogue pin
'
Proc ADC_Init()
    ADCON1 = $00
    ADCON2 = 0b10001111
    ADRESL = $00
    ADRESH = $00
    ADCON0 = $00
    ANSELAbits_ANSA0 = 1            ' Setup AN0 as an analogue pin
EndProc

'-----------------------------------------------------------------------------------------
' Setup the device to use its internal oscillator at 64MHz
' Input     : None
' Output    : None
' Notes     : For use with PIC18Fx6K22 devices
'
Proc IntOsc_64Mhz()
    OSCCON  = $70
    OSCCON2 = $04
    OSCTUNE = $40
    Repeat : Until OSCCON2bits_PLLRDY = 1                   ' Wait for the PLL to stabilise
EndProc

'-----------------------------------------------------------------------------------------
' Configure the fuses to operate at 64MHz using the internal oscillator, on a PIC18F26K22 device
'
Config_Start
    FOSC     = INTIO67          ' Use the internal oscillator for the device's oscillator
    PLLCFG   = Off              ' Disable PLL
    PRICLKEN = On               ' Primary clock enabled
    FCMEN    = Off              ' Fail-Safe Clock Monitor disabled
    IESO     = Off              ' Internal/External Oscillator Switchover mode disabled
    PWRTEN   = On               ' Power up timer enabled
    BOREN    = SBORDIS          ' Brown-out Reset enabled in hardware only (SBOREN is disabled)
    BORV     = 190              ' Brown Out Reset Voltage set to 1.90 V nominal
    WDTEN    = Off              ' Watch dog timer is always disabled. SWDTEN has no effect.
    WDTPS    = 128              ' Watchdog Timer Postscale 1:128
    CCP2MX   = PORTC1           ' CCP2 input/output is multiplexed with RC1
    PBADEN   = Off              ' PORTB<5:0> pins are configured as digital I/O on Reset
    CCP3MX   = PORTB5           ' P3A/CCP3 input/output is multiplexed with RB5
    HFOFST   = On               ' HFINTOSC output and ready status are not delayed by the oscillator stable status
    T3CMX    = PORTC0           ' Timer3 Clock Input (T3CKI) is on RC0
    P2BMX    = PORTB5           ' ECCP2 B (P2B) is on RB5
    MCLRE    = EXTMCLR          ' MCLR pin enabled, RE3 input pin disabled
    STVREN   = Off              ' Stack full/underflow will not cause Reset
    LVP      = Off              ' Single-Supply ICSP disabled
    XINST    = Off              ' Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
    Debug    = Off              ' Debug Disabled
    Cp0      = Off              ' Block 0 (000800-001FFF) not code-protected
    CP1      = Off              ' Block 1 (002000-003FFF) not code-protected
    CP2      = Off              ' Block 2 (004000-005FFF) not code-protected
    CP3      = Off              ' Block 3 (006000-007FFF) not code-protected
    CPB      = Off              ' Boot block (000000-0007FF) not code-protected
    CPD      = Off              ' Data EEPROM not code-protected
    WRT0     = Off              ' Block 0 (000800-001FFF) not write-protected
    WRT1     = Off              ' Block 1 (002000-003FFF) not write-protected
    WRT2     = Off              ' Block 2 (004000-005FFF) not write-protected
    WRT3     = Off              ' Block 3 (006000-007FFF) not write-protected
    WRTC     = Off              ' Configuration registers (300000-3000FF) not write-protected
    WRTB     = Off              ' Boot Block (000000-0007FF) not write-protected
    WRTD     = Off              ' Data EEPROM not write-protected
    EBTR0    = Off              ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1    = Off              ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTR2    = Off              ' Block 2 (004000-005FFF) not protected from table reads executed in other blocks
    EBTR3    = Off              ' Block 3 (006000-007FFF) not protected from table reads executed in other blocks
    EBTRB    = Off              ' Boot Block (000000-0007FF) not protected from table reads executed in other blocks
Config_End

tumbleweed

As I said, you'll always get an answer, and it may even seem to be good.

Here are just some of the criteria for oversampling and decimation to actually work:
Quote1. During the sampling interval required to perform the desired number of ADC conversions, the
signal of interest must not change more than 0.5 effective LSB of the end result
. For example, if
you are using this technique to increase the effective resolution of a 12-bit measurement to 16 bits,
the signal of interest must not vary during the sampling interval by more than 1/32 LSB of the ADC.

2. During the sample interval, the ADC must convert the signal 4^n times, where n is the number of
virtual bits desired in the result.

3. There must be some noise on the input signal. This noise must have an amplitude greater than 1
LSB of the ADC; it must have an mean value of zero, and it must be randomly distributed (white).

4. The result is truncated by shifting right 2^n places to yield the desired resolution.

John Drew

Here's my thinking on why averaging works.
1) there's always some jitter on my conversions
2) if e.g. there is a reading of 100 one quarter of the time and a reading of 101 three quarters of the time then it is probable that for enough iterations that the actual value is about 100.75
3) an averaging process over many reads (assuming no movement) can therefore reveal better than expected from most encoders because of the jitter.
Am I way off in my thoughts?
I'm well aware of not carrying the process too far with regard to significant figures, especially as floats also have their limitations. I also understand that fast changes on the input voltage negate my reasoning to a large degree.

My satellite tracking system uses a pot for azimuth and these are quite jittery using A/D, averaging makes a big difference and 1 degree resolution and accuracy is possible. Due to the speed of satellites directional antennas with 15 degree beam widths (3dB point) perform very well using a pot for azimuth and an accelerometer for elevation.

In one of my projects that does NOT use A/D I detect if the value is changing (motor drive present) in which case I reduce the averaging loop, if it is stationary I increase the averaging. The project tracks the moon with a 10m dish and typically re-adjusts every 15 secs or so. As the moon is only 1/2 degree wide it rapidly moves out of the antenna's beam. The main lobe just over illuminates the moon at 1296MHz. Every dB gained is important for moon bounce operation so accuracy in calculating position and movement is critical. This project uses 12 bit SPI encoders and the averaging provides some improved read out resolution and accuracy.

John

tumbleweed

There's a decent discussion of when this technique might be appropriate in the Silicon Labs app note
Improving ADC Resolution by Oversampling and Averaging AN118. Look at Appendix B.

Basically, take a series of ADC measurements and create a histogram plot of them.
If you get a Gaussian distribution (such as shown in figure 5) then the technique should help. If you don't then you're likely fooling yourself.

Everyone tends to skip over all the discussions involving sampling rate, noise density and distribution, ADC errors, dithering requirements, etc, and just implements the method thinking they get an accurate result.


top204

#14
Because of the rather noisy ADCs that most microcontrollers have, the over sampling works well for lower frequencies. However, SDR and mobile phones use a similar principle of over sampling and resolution increase to build the radio data and the constellation data for bit phases, so the principle works well.

I've used the methods above with audio in and audio out and they work very well. I needed 12-bit audio for a 12-bit DAC from a 10-bit ADC input, so I used the over sampling for that and the audio quality was very good to listen to and on a scope and audio spectrum analyser. I also use a "similar" technique to convert a 12-bit audio stream to 16-bit for a DAC output and, again, the audio quality was very good. It is never going to be the same accurate resolution as an actual ADC or DAC for the required resolution, but it produces a good approximation.

The theories are important sometimes, but for real applications in the real world, you cannot beat practical methods of trying it to see if it works first. Then if it does not quite work, look at some of the theories behind it. But the mathematical calculations that are printed in most articles and books just make my eyes glaze over with confusion. Yet if I see the same calculations in pseudo programatic format, they usually make sense. :-)

I've actually seen articles that have a random noise generator attached to the ADC input via a mixer, but I have yet to find an ADC that is absolutely silent in its lower bits in the real world, even when operating with a DC input, without the device operating at sub zero temperatures, so it makes no practical sense to have one for standard applications, but in the theory world it is essential. :-)

John Drew

Amod, thanks for starting the topic. It's resulted in some good information. Always great to learn something new. On my part it's off to learn more about the ideas raised by our learned friends.
Cheers
John
VK5DJ

Yves

Isn't by amplifying a signal or using a lower reference voltage either internally or externally increases the resolution?
if you use a 2.048V as VREF. your resolution will be 2048/1024 = 2mV or VREF of 1024 you get 1mV. As far I'm concerned large signal don't really requires high resolution in real world.
 
Yves
Yves

trastikata

Quote from: Yves on Sep 27, 2022, 06:22 AMIsn't by amplifying a signal or using a lower reference voltage either internally or externally increases the resolution?
if you use a 2.048V as VREF. your resolution will be 2048/1024 = 2mV or VREF of 1024 you get 1mV. As far I'm concerned large signal don't really requires high resolution in real world.

Not really, if you need to measure a signal with a swing from 0 to 5 volts with a 13b accuracy but you have a 12b ADC (assuming 5v reference), the only way is oversampling ... or buying 13b ADC.

If your signal of interest has a swing of 3.3v, but your ADC is 5v referenced, then you can use some signal conditioning, but you will have other problems to deal with.




top204

As an experiment, and so I could simulate the oversampling program, I created a procedure to add a pseudo random value to the ADC's raw reading. The demonstration program also calculates the voltage received via the ADC's input so that it can be matched with what the actual voltage is. This will show, to a certain extent, the ADC oversampling working. The voltage calculation also works in a similar way to a simple low pass filter for the voltage displayed, because of the divisions required within it, so it is stable to two decimal places even though the ADC values vary slightly.

The program is listed below, and see the ADC_Read10 procedure that is used instead of the compiler's ADin command and adds the pseudo random value to bit-0 of the ADC's raw reading. This can be changed to add more random noise by changing the "Anding" that masks the required bits of the pseudo random value for further experiments:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A demonstration of simple oversampling of an ADC for approximated higher resolutions
' Displays the ADC value and a voltage on the serial terminal
'
' This experiment seeds the raw ADC's reading with a pseudo random value
'
' Written for the Positron8 compiler by Les Johnson.
'
    Device = 18F26K22                           ' Tell the compiler what device to compile for
    Declare Xtal = 64                           ' Tell the compiler what frequency the device will be operating at (in MHz)
    Declare Float_Display_Type = Fast           ' Tell the compiler to use the faster and more accurate Float to ASCII converter
'
' Setup USART1
'
    Declare Hserial1_Baud = 9600                ' Set the Baud rate to 9600
    Declare HRSOut1_Pin = PORTC.6

    Symbol cQuanta = (5.0 / 4092)               ' Holds the quantasisation value for oversampled 12-bits at 5 Volts +Vref
'
' Create some variables for the demo
'
    Dim wADC_Value As Word                      ' Holds the oversampled 12-bit ADC value
    Dim fVolts As Float                         ' Holds the voltage
  
'-------------------------------------------------------------------------------------
' The main program starts here
' Read the oversampled ADC value and display its results on a serial terminal
'
Main:
    Setup()                                     ' Setup the program
    Seed $0345                                  ' Seed the pseudo random generator
   
    Do                                          ' Create a loop
        wADC_Value = ADC_Read12(0)              ' Read the oversampled 12-bit ADC
        fVolts = wADC_Value * cQuanta           ' Calculate the voltage from the oversampled, 12-bit ADC conversion
        HRSOutLn "ADC=", Dec wADC_Value, " : ", ' Display the 12-bit ADC value on a serial terminal
                 Dec2 fVolts, " Volts"          ' Display the voltage on a serial terminal
        DelayMS 100                             ' A small delay between samples, so they can be seen changing
    Loop                                        ' Do it forever

'-------------------------------------------------------------------------------------
' Oversample a 10-bit ADC to return an approximised 12-bit result
' Input     : pChan holds the ADC channel to read
' Output    : Returns the oversampled 12-bit ADC value
' Notes     : None
'
Proc ADC_Read12(pChan As Byte), Word
    Dim wAccum As Word = 0
    Dim bSamples As Result.Byte0
   
    For bSamples = 15 DownTo 0                  ' Create an over sampling loop
        wAccum = wAccum + ADC_Read10(pChan)     ' Accumulate the ADC readings
    Next
    Result = wAccum >> 2                        ' Divide the accumulator
EndProc

'-------------------------------------------------------------------------------------
' Read the 10-bit ADC directly on a PIC18F26K22 device
' Input     : pChan holds the ADC channel to read
' Output    : Returns the 10-bit ADC value
' Notes     : Adds a pseudo random value into bit-0 of the ADC's result
'           : This is for the oversampling
'
Proc ADC_Read10(pChan As Byte), Word
    Dim wADRES As ADRESL.Word                   ' Create a 16-bit SFR from ADRESL and ADRESH
    Dim bRand As Byte                           ' Holds the pseudo random number
   
    ADCON0 = 0b00000001                         ' Clear the channel bits and enable the ADC peripheral
    pChan = pChan << 2                          ' Move the channel bits into the correct position for ADCON0               
    ADCON0 = ADCON0 | pChan                     ' Or the channel bits into ADCON0
    bRand = Random                              ' Get a pseudo random value
    bRand = bRand & 0b00000001                  ' Mask out the unwanted bits of the pseudo random value
    ADCON0bits_GO_DONE = 1                      ' \ Wait for the ADC to complete its reading
    Repeat : Until ADCON0bits_GO_DONE = 0       ' /
    Result = wADRES                             ' Load the result with the ADC value  
    Result = Result | bRand                     ' Or in the pseudo random value bit
EndProc

'-------------------------------------------------------------------------------------
' Setup the program
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    IntOsc_64Mhz()                              ' Set the device to operate at 64MHz with its internal oscillator
    ADC_Init()                                  ' Setup the ADC
EndProc

'-----------------------------------------------------------------------------------------
' Setup the ADC
' Input     : None
' Output    : None
' Notes     : Set for 10-bit operation
'           : Uses the Gnd and VDD as vrefs
'           : Uses the FRC oscillator
'           : Sets AN0 as an analogue pin
'
Proc ADC_Init()
    ADCON1 = $00
    ADCON2 = 0b10001111
    ADRESL = $00
    ADRESH = $00
    ADCON0 = $00
    ANSELAbits_ANSA0 = 1            ' Setup AN0 as an analogue pin
EndProc

'-----------------------------------------------------------------------------------------
' Setup the device to use its internal oscillator at 64MHz
' Input     : None
' Output    : None
' Notes     : For use with PIC18Fx6K22 devices
'
Proc IntOsc_64Mhz()
    OSCCON  = $70
    OSCCON2 = $04
    OSCTUNE = $40
    Repeat : Until OSCCON2bits_PLLRDY = 1                   ' Wait for the PLL to stabilise
EndProc

'-----------------------------------------------------------------------------------------
' Configure the fuses to operate at 64MHz using the internal oscillator, on a PIC18F26K22 device
'
Config_Start
    FOSC     = INTIO67          ' Use the internal oscillator for the device's oscillator
    PLLCFG   = Off              ' Disable PLL
    PRICLKEN = On               ' Primary clock enabled
    FCMEN    = Off              ' Fail-Safe Clock Monitor disabled
    IESO     = Off              ' Internal/External Oscillator Switchover mode disabled
    PWRTEN   = On               ' Power up timer enabled
    BOREN    = SBORDIS          ' Brown-out Reset enabled in hardware only (SBOREN is disabled)
    BORV     = 190              ' Brown Out Reset Voltage set to 1.90 V nominal
    WDTEN    = Off              ' Watch dog timer is always disabled. SWDTEN has no effect.
    WDTPS    = 128              ' Watchdog Timer Postscale 1:128
    CCP2MX   = PORTC1           ' CCP2 input/output is multiplexed with RC1
    PBADEN   = Off              ' PORTB<5:0> pins are configured as digital I/O on Reset
    CCP3MX   = PORTB5           ' P3A/CCP3 input/output is multiplexed with RB5
    HFOFST   = On               ' HFINTOSC output and ready status are not delayed by the oscillator stable status
    T3CMX    = PORTC0           ' Timer3 Clock Input (T3CKI) is on RC0
    P2BMX    = PORTB5           ' ECCP2 B (P2B) is on RB5
    MCLRE    = EXTMCLR          ' MCLR pin enabled, RE3 input pin disabled
    STVREN   = Off              ' Stack full/underflow will not cause Reset
    LVP      = Off              ' Single-Supply ICSP disabled
    XINST    = Off              ' Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
    Debug    = Off              ' Debug Disabled
    Cp0      = Off              ' Block 0 (000800-001FFF) not code-protected
    CP1      = Off              ' Block 1 (002000-003FFF) not code-protected
    CP2      = Off              ' Block 2 (004000-005FFF) not code-protected
    CP3      = Off              ' Block 3 (006000-007FFF) not code-protected
    CPB      = Off              ' Boot block (000000-0007FF) not code-protected
    CPD      = Off              ' Data EEPROM not code-protected
    WRT0     = Off              ' Block 0 (000800-001FFF) not write-protected
    WRT1     = Off              ' Block 1 (002000-003FFF) not write-protected
    WRT2     = Off              ' Block 2 (004000-005FFF) not write-protected
    WRT3     = Off              ' Block 3 (006000-007FFF) not write-protected
    WRTC     = Off              ' Configuration registers (300000-3000FF) not write-protected
    WRTB     = Off              ' Boot Block (000000-0007FF) not write-protected
    WRTD     = Off              ' Data EEPROM not write-protected
    EBTR0    = Off              ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1    = Off              ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTR2    = Off              ' Block 2 (004000-005FFF) not protected from table reads executed in other blocks
    EBTR3    = Off              ' Block 3 (006000-007FFF) not protected from table reads executed in other blocks
    EBTRB    = Off              ' Boot Block (000000-0007FF) not protected from table reads executed in other blocks
Config_End

The pseudo random bit does actually make a difference with some voltages, because without it, some voltages are slightly out by 0.01, but with it, they are more accurate. :-)

Below is a screenshot of the above demonstration program working in the Proteus simulator:

ADC - 12-bit OverSample Experiment.jpg

Yves

Hello all,

I have done a simulation on Proteus of Less oversampling code. I have just added some codes to send the data to PLX-DAQ to plot the results. As voltage source I use a very slow sinusoidal wave of 0 to 10 mV and shifted the wave to positive 10mV so it never goes to zero. I have added the Proteus COMPIM virtual port on the simulation  to send data to the PC.  On the spreadsheet I have calculated the mV/bit which end up to be 1.22 mV / bit. Is that the prove the oversampling works as 5000mV/1024 is only 4.882 for 10 bit resolution.
I think I should try this with a real chip and use my PICO oscilloscope to generate a slow tooth wave. I have attached the the zip file. Is my approach right? You can download the PLX-DAQ at https://www.parallax.com/package/plx-daq/
Regards,

Yves 
Yves