DT_Analog.pbp Download (Save the file to your PBP folder.)
View in Browser
You've probably noticed that when
using the Analog to Digital converter, the numbers are NEVER
stable. At best, the result will always be bouncing back and forth
between two numbers.
While it's extremely annoying, there is a reason for it, and the number
of times it bounces between those two numbers is actually indicating the
values of more bits of resolution that are not included in the original
10-bit result.
This effect can be exploited to increase the effective number of bits in the result.
By taking a number of consecutive samples and averaging the results
with a lower divisor, you can get up to 16-bit accuracy from that poor
little 10-bit converter.
An explanation of the Oversampling technique can be found on Wikipedia.
http://en.wikipedia.org/wiki/Oversampling
Samples required:
A/D
BITSSamples
requiredMAX result
Conv. Time(1)
10
1
1023
24.2us
11
4
2046
96.8us
12
16
4092
387us
13
64
8184
1.54ms
14
256
16368
6.2ms
15
1024
32736
24.7ms
16
4096
65472
99.1ms
(1) Conversion time based on 5us acquisition with 1.6us Tad.
For each additional bit of resolution, the number of samples required is multiplied times 4 (22(n-10)). The samples are accumulated in a 32-bit variable, then divided by a number that is doubled for each additional bit (2(n-10)). [This is all handled by the module.]
Maximum results:
Since the highest value provided by the converter is 1023, it can't bounce between 1023-1024.
This limits the maximum values to slightly lower than expected, which is (1023 << (bits-10))
For 16-bits, the maximum is (1023 << 6) or 65472. This value is returned in the ADmax variable after the conversion for easy voltage calculations.
OSC | DEFINE ADC_CLOCK | Clk source | Tad (µs) | 12 Tad (µs) |
---|---|---|---|---|
4 | 1 | Fosc/8 | 2 | 24 |
8 | 5 | Fosc/16 | 2 | 24 |
10 | 5 | Fosc/16 | 1.6 | 19.2 |
12 | 2 | Fosc/32 | 2.6 | 31.2 |
16 | 2 | Fosc/32 | 2 | 24 |
20 | 2 | Fosc/32 | 1.6 | 19.2 |
24 | 6 | Fosc/64 | 2.6 | 31.2 |
32 | 6 | Fosc/64 | 2 | 24 |
40 | 6 | Fosc/64 | 1.6 | 19.2 |
48 | 3 | FRC | 4 | 48us |
Here's a simple example that displays 12-bit A/D values on an LCD. Of course, by changing one number it will display 16-bit values too. |
|
;--- Set your __configs here as needed ---
DEFINE OSC 20
INCLUDE "DT_Analog.pbp" ; DT's 16-bit Analog Module
DEFINE ADC_BITS 10 ; Set-up ADC for fast 10-bit results
DEFINE ADC_CLOCK 2
DEFINE ADC_SAMPLEUS 5
;----[Change these to match your hardware]----------------------------------
DEFINE LCD_DREG PORTB ; LCD Data Port
DEFINE LCD_DBIT 0 ; Starting Data Bit
DEFINE LCD_RSREG PORTB ; Register Select Port
DEFINE LCD_RSBIT 4 ; Register Select Bit
DEFINE LCD_EREG PORTB ; Enable Port
DEFINE LCD_EBIT 5 ; Enable Bit
DEFINE LCD_BITS 4 ; Data Bus Size
DEFINE LCD_LINES 2 ; Number of Lines on LCD
DEFINE LCD_COMMANDUS 2000 ; Command Delay time in uS
DEFINE LCD_DATAUS 50 ; Data Delay time in uS
ADCON1 = %10001101 ; right justify, AN0 & AN1 analog
PAUSE 250 : LCDOUT $FE,1 ; Initialize LCD
PAUSE 100 : LCDOUT $FE,1 ; not all LCDs need both lines
ADbits = 12 ; set to 12-bit resolution
;---------------------------------------------------------------------------
Main:
FOR ADchan = 0 to 1 ; Do both AN0 and AN1
GOSUB GetADC ; Get A/D value
IF ADchan = 0 THEN
LCDOUT $FE, $80 ; LCD line 1
ELSE
LCDOUT $FE, $C0 ; LCD line 2
ENDIF
LCDOUT "CH-",DEC1 ADchan," = ",DEC ADvalue," "
NEXT ADchan
GOTO Main:
Here's another example that
shows all the resolutions from 1-16 bit from a single A/D
channel via the USART with HyperTerminal or other ANSI terminal program. |
|
;--- Set your __configs here as needed ---
DEFINE OSC 20
INCLUDE "DT_Analog.pbp" ; DT's 16-bit Analog Module
DEFINE ADC_BITS 10
DEFINE ADC_CLOCK 2
DEFINE ADC_SAMPLEUS 5
DEFINE HSER_SPBRG 10 ; 115200 @ 20 Mhz
DEFINE HSER_RCSTA 90h ; Hser receive status init
DEFINE HSER_TXSTA 24h ; Hser transmit status init
DEFINE HSER_CLROERR 1 ; Hser clear overflow automatically
Volts VAR WORD ; Volts calculated from the A/D reading
ADCON1 = %10001101 ; right justify, AN0 & AN1 analog
HSEROUT [27,"[2J"] ; Clear screen
;---------------------------------------------------------------------------
Main:
HSEROUT [27,"[H"] ; home cursor
HSEROUT [27,"[34m",27,"[1m"] ; bold blue text
HSEROUT ["ADbits DEC HEX BIN16 ADmax Volts"]
HSEROUT [27,"[37m",13,10] ; black text
ADchan = 1 ; Select channel AN1
FOR ADbits = 1 to 16 ; cycle thru all bit resolutions
GOSUB GetADC ; get the A/D value
GOSUB ShowAD ; show the A/D results
NEXT ADbits ; do next resolution
GOTO Main
;---------------------------------------------------------------------------
ShowAD:
IF ADbits < 10 THEN HSEROUT [" "]
HSEROUT [DEC ADbits,"-bit = ",DEC ADvalue," "]
HSEROUT [27,"[",DEC ADbits+1,";16H",HEX4 ADvalue," ",BIN16 ADvalue, _
" ",DEC ADmax]
IF ADbits < 16 THEN ; calculate the Voltage with 4 decimals
Volts = 50000 * ADvalue
Volts = DIV32 ADmax
ELSE ; with 16-bit, ADmax is too big for DIV32
ADmax = ADmax >> 1 ; cut it in half
Volts = 25000 * ADvalue ; and scale the multiplier accordingly
Volts = DIV32 ADmax ; PBPL & Longs, doesn't have that problem
ENDIF
HSEROUT [27,"[",DEC ADbits+1,";49H", _
DEC Volts/10000,".",DEC4 Volts//10000 ,13,10]
RETURN