/*
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 ) */