/*
This file is linked with the documentation ! */
//---------------------------------------------------------------------------
// File: C:\pic\GPSDO\ADC_PIC16F1783.c
// Author: Wolfgang Buescher, DL4YHF
// Date: 2016-02-12
// Purpose: ADC initialisation for PIC17(L)F178x.
// Development System : Microchip MPLAB IDE v8.85,
// XC8 C Compiler ("Free Mode") V1.35 .
//
//
//---------------------------------------------------------------------------
#include "switches.h" // project specific 'compiler' switches & options
// Include an awful lot of compiler-specific junk ... see gpsdo_pic_main.c
#ifdef __BORLANDC__ // compiling with Borland C ? Use WB's "PIC emulator" ..
# include "pic_emulator/xc.h"
#elif (defined __XC8) // compiling for PIC, using Microchip's "XC8" compiler ?
# include "xc.h"
# include "stdint.h"
#else // neither Borland nor XC8 (forget about CC5X) ...
# error Your compiler is not supported here yet. Please add support yourself.
#endif // using BORLAND C, Microchip's "XC8" ?
#include "adc_pic.h" // header for THIS module ("ADC functions for PIC" by DL4YHF)
//---------------------------------------------------------------------------
void ADC_Init(void) // Initialize the A/D converter.
{ // Details about the ADC in the PIC16F1782/3 datasheet, DS40001579E,
// pages 141.., Ch. 17.1, "ADC Configuration" .
//
// Note: ADC-associated port pins have already been initialized, e.g. RA0/AN0,
// including the TRIS and ANSEL settings (see gpsdo_pic_main.c) .
#if(0) // removed, because with the 'FVR' as reference, the ADC was far too noisy !
FVRCON = 0b10001010; // "Fixed Voltage Reference Control Register"...
// ||||||\|_ b1..0 = A/D-CONV. voltage reference selection : 10bin = 2.048 V
// ||||\|___ b3..2 = COMPARATOR voltage reference selection : 10bin = 2.048 V
// ||||_____ b4 = "TSRNG" (temperature sensor range) not used
// |||______ b5 = "TSEN" (temperature sensor enable)
// ||_______ b6 = "FVRRDY" ('ready flag', ignored here)
// |________ b7 = "FVREN" : 1 = fixed voltage reference enabled
#endif // ("FVR" not used for the ADC so don't initialise it here)
// Channel selection : see DS40001579E page 149 (ADCON2) + 147 (ADCON0)
// and page 140 (shows what the 'CHS' and 'CHSN'-bits are for)
ADCON2 = 0b00001111;
// ||||\__|_ b3..b0 = "CHSN" : channel selection for the NEGATIVE input :
// |||| 1111 = "ADC Negative Reference, selected by ADNREF" (in ADCON1)
// |||| 0000 = AN0 (would be wrong because AN0 is the *POSITIVE* input)
// \__|_____ b7..b4 = auto-conversion trigger select:
// 1001 = PSMC2 Falling Edge Event (*)
// 1000 = PSMC2 Rising Edge Event
// 0111 = PSMC2 Period Match Event
// 0110 = PSMC1 Falling Edge Event (*)
// 0101 = PSMC1 Rising Edge Event
// 0100 = PSMC1 Period Match Event
// 0010 = CCP2, Auto-conversion Trigger (**)
// 0001 = CCP1, Auto-conversion Trigger
// 0000 = disabled (software trigger, single conversion)
// other: illegal / 'reserved'
// (*) In the GPSDO, both PSMCs are already occupied.
// (**) CCP2 *and* CCP1 *both* use Timer1.
// Timer1+CCP1 is already occupied to measure the GPS sync pulse,
// so the only chance for a jitter-free HARDWARE ADC trigger
// is CCP2 . Timer1 overflows 10 MHz / 65536 = 152.xxx times per second.
// But for the GPS, Timer1 *must* run over the entire 16 bit range.
// Beware of another pitfall .. DS40001579E, page 179 :
// > 22.10 CCP Auto-Conversion Trigger
// > When any of the CCP’s are configured to trigger a
// > auto-conversion, the trigger will clear the
// > TMR1H:TMR1L register pair. This auto-conversion
// > does not cause a Timer1 interrupt. The CCP module
// > may still be configured to generate a CCP interrupt.
// > In this mode of operation, the CCPR1H:CCPR1L
// > register pair becomes the period register for Timer1 .
// Thus "quickly reprogramming CCPR2 in the interrupt
// to prepare the next jitter-free A/D conversion"
// simply won't work. Eeeek.
// The only timer left (for a timer-triggered conversion)
// would be the old 8-bit "Timer2" ("Timer/Counter"),
// with its antique 2^n pre- and postscaler.
// Unfortunately, despite FOUR BITS TO SELECT THE ADC TRIGGER SOURCE,
// Timer2 can NOT trigger the ADC via hardware -> need the ISR for this.
// But code to initialize Timer2 doesn't belong here.. it's in gpsdo_pic_main.c .
#if( SWI_ADC_BITS_PER_SAMPLE==10 )
ADCON0 = 0b10000001; // ADCON0 : DS40001579E page 147 ...
#else // |||||||| // not 10 but 12 bits per sample (slightly faster) :
ADCON0 = 0b00000001;
#endif // ||||||||
// |\___|||_ b0 = "ADON" : 1=ADC enabled, 0=ADC disabled
// | ||__ b1 = "GO/!DONE" : 1=start/conversion in progress,
// | | 0=conversion complete / not in progress
// | |___ b6..2 : "CHS4..0" = "Positive Differential Input Channel Select"
// | 00000 = AN0 (first analog input)
// |________ b7 = "ADRMD" : 0 "ADRES formatted for 12-bit result"
// 1 "ADRES formatted for 10-bit result" (does this also mean FASTER CONVERSION) ?
// Reference voltage selection and conversion clock : see DS40001579E page 148
#if(1) // (0)=use the PIC's internal 'FVR' (fixed voltage reference), (1)=use 'VDD' (supply voltage) as reference
ADCON1 = 0b10100000; // using 'Vdd' as Vref : With Vdd=3.6 V, much LESS noise than with 'FVR'
#else // ||||||||
ADCON1 = 0b10100011; // using 'FVR' (2.048 V) as Vref : 20 dB more noise than with 'Vdd' !
#endif // ||||||||
// ||||||\|_ b1..0 = "ADPREF" : ADC Positive Voltage Reference configuration
// |||||| 00 = VREF+ on VDD, 01 = VREF+ on VREF+ pin,
// |||||| 11 = internal 'Fixed Voltage Reference'
// ||||||___ b2 = "ADNREF" : ADC "Negative" Voltage Reference configuration
// ||||| 0 = VREF- on VSS (ground), 1 = VREF- on VREF- pin
// ||||| (never "negative" in the sense of a negative voltage!)
// |||||____ n.c.
// |\_|_____ b6..4 = "ADCS" : 010 = Fosc/32, e.g. T_AD = 32/40MHz = 0.8 us
// |________ b7 = "ADFM" : 1 = 2's complement, 0 = something exotic
// -> movlw 0xA3
// movwf 0x1E ; actually 0x9E which is ADCON1, with BSR=1 (?)
// return
// For some reason, the 'movwf 0x1E' also modified the content of PORTA ?!
// Why does ADCON1 (value doesn't seem to matter)
// modify PORTA (clear bit 3 = RA3) ?
//
// With 15 * T_AD = 15 * 0.8 us = 1 / 83.3333 kHz, 80 kSamples / second
// appered to be possible (when 'slightly violating the spec'),
// but (at so often) that's a false assumption. See speed test further below.
// At a 'planned' single-channel sampling rate of 20 kHz, this would
// allow 4-fold oversampling, and if the PIC16 (*and the C compiler*)
// turns out good enough, we could even provide some anti-aliasing
// (more than a stupid integrate-and-dump filter permits.. maybe an IIR..)
// To validate the above assumptions, there's a TEST for the ADC speed
// in GPSDO_PIC_MAIN.C, right after calling ADC_Init() .
//
#if(0) // (0)=normal compilation, (1)=TEST for the ADC speed...
// and the unpredictable sample & hold jitter seen at f_sample=80 kHz !
# asm
adc_test_loop:
BANKSEL ( PORTA );
BSF BANKMASK(PORTA),4 // ~ IOP_ADCCLK_PIN_HI
BANKSEL ( ADCON0 );
BSF BANKMASK(ADCON0), 1 // start next conversion as early as possible (bit 1 = "GO")
// (the current drawn by the sample and hold input caused
// a 400 ns long 'spike' on the active analog input,
// which should appear a constant time after the rising edge
// from 'IOP_ADCCLK_PIN_HI'. But the time was NOT constant ?!)
adc_test_wconv: // wait for analog/digitial conversion
BTFSC BANKMASK(ADCON0), 1 // ADCON0 bit 1 = "GO" / "not DONE"
GOTO adc_test_wconv // not "done" -> continue waiting
BANKSEL ( PORTA );
BCF BANKMASK(PORTA),4 // ~ IOP_ADCCLK_PIN_LO
GOTO adc_test_loop
//
// On a PIC16F1783, got here with the following waveforms on an oscilloscope:
// __ ____________________________________ ________________
// | | | |
// PORTA.4 |___| Sample & Hold switch closes |___|
// . . . S & H cap .
// . . . 'fully charged' .
// _____________ ___________________________
// AN0 . . | ___------- . | ___---
// (X mVpp) . . \ / . . \ /
// . . .\/ . . \/
// . . . . . . (short dip caused by ADC
// .<->.<---->. .<--------->. . inrush current,
// t [ns] .400. 520 200 ~~1400 ns . with a deliberately
// . . . high impedance source,
// For 12bit/ |<------------ loop time: 14.5 us ------>| to find out when the
// sample: |<---"ADC busy" : ca. 13.8 us ------>| ADC *really* 'samples').
// For 10bit/ |<------------ loop time: 12.7 us ------>|
// sample: |<---"ADC busy" : ca. 12.3 us ------>|
//
// With T_AD = 32 / 40 MHz = 0.8 us, these are slightly more than
// (13.8/0.8=) 17 A/D conversion cycles !
// The overhead of a few CPU instructions (with 0.1 us/instruction)
// cannot be responsible for this. So where is the bug ?
// (DS40001579E page 142, FIGURE 17-2, ANALOG-TO-DIGITAL CONVERSION TAD CYCLES) :
// > T_AD cycle #15, T_AD cycle #17 :
// > Holding cap. discharge
// (WB: But we DON'T WANT to discharge the cap when there is only ONE input!)
// > On the following cycle, GO bit is cleared, ADIF bit is set,
// > holding capacitor is connected to the analog input.
//
// (DS40001579E page 141):
// > One full 12-bit conversion requires 15 T_AD periods.
// (DS40001579E page 1 ):
// > - Fully differential 12-bit converter
// > - Up to 75 ksps conversion rate
// (Guess that's the typical marketing guerilla on 'page one'.
// 1 / ( 15 T_AD cycles * 1 us ) would be 66.666 kHz .
// 1 / ( 13 T_AD cycles * 1 us ) would be 76.923 kHz .
// Talking about a TWELVE BIT A/D converter on page 1, but bragging
// with the speed when running the converter at TEN BIT resolution.)
//
//(DS40001579E page 377, table 30-14, "ADC Conversion Requirements"):
// > Param No. | Sym. | Characteristic | Min | Typ | Max | Units | Conditions
// > ----------+-------+-------------------+------+-------------+-----+-------+---------------
// > AD130 | T_AD | ADC Clock Period | 1.0 | - | 0.9 | us | T_osc based
// > AD131 | T_CNV | Conversion Time, | - | 15 (12 bit) | - | T_AD | Set GO/!DONE bit
// > | | not including | - | 13 (10 bit) | - | | to conversion complete.
// > | | acquisition time) | | | | | ADRES may be read on the
// > | | | | | | | following T_CY(!) cycle.
// > AD131 | T_ACQ | Acquisition Time | - | 5.0 | - | us |
//
//
// 17 T_AD cycles required instead of 15 ? Is 'T_ACQ' = 5.0 us carved in stone ?
// According to DS40001579E page 142, the ADC "inputs" the signal
// in T_AD cycle #1, then disconnects the S & H capacitor from the input
// (between from cycle #1 and #2), then (in T_AD cycle #2) "samples", whatever that means,
// and digitizes the 'sampled value' in T_AD cycles #3 to #15.
//
# endasm
#endif // ADC test ?
} // end ADC_Init()
/*
EOF ( ADC_PIC16F1783.c )
*/