NU32v2: Analog Input
*** UNDER CONSTRUCTION ***
Our PIC32 has a single analog-to-digital converter (ADC) that, through the use of multiplexers, can be used to sample the analog voltage at up to 16 different pins (Port B). The ADC has 10-bit resolution, which means it can distinguish 2^10 = 1024 different voltage values, usually in the range 0 to 3.3 V, the voltage used to power the PIC32. This yields 3.3 V/1024 = 3 mV resolution. The ADC is typically used in conjunction with sensors that produce analog voltage values.
Overview
A single sample of an analog input consists of two steps: sample acquisition and sample conversion.
- During the acquisition period, the input pin is connected to a sample and hold amplifier. This acquisition period must be of sufficient duration to allow the sample and hold amplifier (SHA) output voltage to settle to the same voltage as the input pin. This takes finite time, considering the 4.4 pF internal holding capacitor.
- Once the acquisition period has ended, the SHA is disconnected from the input pin. This allows the output of the SHA to be constant during the conversion process, even if the voltage on the input is changing. Conversion to a 10-bit digital value takes 12 ADC clock cycles: one for each of the 10 bits, plus two more.
Thus the total time for a sample is the acquisition time plus the conversion time.
The conversion time can be understood by the fact that the ADC uses successive approximation to find the digital representation of the voltage. In this method, the SHA voltage is compared to the voltage produced by an internal digital-to-analog converter (DAC). The DAC takes a 10-bit number and produces a voltage proportional to the value (0x000 = minimum voltage, typically 0 V, and 0x3FF = maximum voltage, typically 3.3 V). In the first step of the conversion process, the DAC produces a voltage in the middle of the range, corresponding to the most significant bit = 1 and all others zero, i.e., 0x200 = 0b1000000000. If the SHA voltage is greater than the DAC voltage, the first bit of the ADC result is 1, otherwise it is zero. On the next cycle, the most significant bit is set to the result from the first test, and the second most significant bit is set to 1. The process continues until all 10 bits of the result are determined. This process is a binary search.
According to Table 31-37 of the Data Sheet (Electrical Characteristics section), the ADC clock period () must be at least 65 ns. Furthermore, the sampling time must be at least 132 ns. With this information, we find that the minimum sample time is (65 ns x 12) + 132 ns = 912 ns, indicating it is at least theoretically possible to take one million samples per second. However, the ADC clock period can only be 2*k*(PBCLK period), where k is an integer from 1 to 256. For us the peripheral bus clock period is 1/(80 MHz) = 12.5 ns, so the smallest k we can choose is k=3, giving an ADC clock period of 2*3*12.5 ns = 75 ns. Thus our theoretical minimum sample time is (75 ns x 12) + 132 ns = 1.032 us.
When a conversion completes, the result is stored in a buffer of ADC results called ADC1BUF, which consists of 16 4-byte words. Your program can read from this buffer.
Options The ADC peripheral provides a plethora of options, some of which are described here:
- Data format: The result of the conversion is stored in a 32-bit word, and it can be represented as a signed integer, integer, fractional value, etc. Typically we would use a16-bit or 32-bit integer.
- Sampling and conversion initiation events: Sampling can be initiated by a software command, or immediately after the previous conversion has completed (auto sample). Conversion can be initiated by (1) a software command, (2) the expiration of a specified sampling period, (3) a period match with Timer 3, or (4) a signal change on the INT0 pin. Typically only the first two are relevant to us. If sampling and conversion are being done automatically (not through software commands), the conversion results will be placed in the ADC1BUF in successively higher addresses, before returning to the first address in ADC1BUF after an AD1CON2-specified number of conversions.
- Voltage reference: The input range of the ADC is typically 0 to 3.3 V (the power rails of the PIC). If you are interested in voltages in a different range, say 1.0 V to 2.0 V, for example, you can set up the ADC so 0x000 corresponds to 1.0 V and 0x3FF corresponds to 2.2 V, to get better resolution in this smaller range: (2 V - 1 V)/1024 = 1 mv resolution. These reference voltage limits are VREF+ and VREF- and must be provided to the PIC externally.
- Interrupts: An interrupt may be generated after a specified number of conversions.
- ADC clock period: The ADC clock period can range from 2 times the PB clock period up to 512 times the PB clock period, in powers of two.
- Analog input scan: You can set up the analog input to automatically scan through a sequence of input pins, storing the results in a result buffer.
- Dual buffer mode for reading and writing conversion results: When an ADC conversion is complete, it is written into an output buffer. After a series of one or more conversions is complete, as specified by the AD1CON2 SFR, an interrupt flag is set, indicating that the program can read the results in. If the program is too slow to respond, however, the next set of conversions may begin to overwrite the previous results. To make this less likely, we can divide the 16-word ADC1BUF into two buffers, each consisting of 8 words: one in which the current conversions are being written, and one from which the program should read the results.
In the sample code below, we focus on just a few of the many possible configurations.
Details
The operation of the ADC peripheral is determined by the following SFRs:
- AD1PCFG: Only the least significant 16 bits are relevant. If a bit is 0, the associated pin is configured as an analog input. If a bit is 1, it is digital I/O.
- AD1CON1, AD1CON2, AD1CON3: These configuration registers determine the ADC operating options listed above, among other things.
- AD1CHS: This SFR determines which pins will be sampled (the "positive" inputs) and what they will be compared to (i.e., VREF- or analog input 1). When in scan mode, the sample pins specified in this SFR are ignored.
- AD1CSSL: This SFR indicates which analog inputs will be sampled in scan mode (if AD1CON2 has configured the ADC for scan mode). Inputs will be scanned from lower number inputs to higher numbers.
Apart from these SFRs, the ADC module has associated bits in the interrupt flag register IFS1, the interrupt enable control register IEC1, and the interrupt priority control register IPC6.
For more details, see the Reference Manual.
Library Functions
Library functions can be found in pic32-libs/include/peripheral/adc10.h.
- SetChanADC10(config): Sets the AD1CHS SFR to config (i.e., which pins are sampled).
- OpenADC10(config1, config2, config3, configport, configscan): The bits set as 1 in configport correspond to pins configured as analog inputs. The bits set as 0 in configscan are inputs that are included in an autoscan (AD1CSSL is the bitwise NOT of configscan). AD1CON3, AD1CON2, and AD1CON1 are set as config3, config2, and config1, respectively.
- EnableADC10(): Sets the bit in AD1CON1 that indicates that the ADC is activated.
- CloseADC10(): Turns off the ADC (clears the bit in ADC1CON1 that indicates that the ADC is activated) and disables the interrupt.
- ReadADC10(bufindex): Reads the conversion result stored in the bufindex'th element of the ADC1BUF.
- ReadActiveBufferADC10(): Returns 0 if conversions are currently being written into words 0-7 of ADC1BUF, and 1 if they are being written into words 8-15. Only relevant in the dual buffer mode.
Sample Code
Manual (Software) Conversion with SFRs This sample code works with the SFRs directly. It simply does a conversion when requested in software. No automatic sampling or scanning of inputs. This code is modified from Example 17-1 in the Reference Manual. (untested)
int i,ADCValue; AD1PCFG = 0xFFFB; // PORTB = Digital; RB2 = analog AD1CON1 = 0x0000; // SAMP bit = 0 ends sampling, starts converting AD1CHS = 0x00020000; // Connect RB2/AN2 as CH0 input AD1CSSL = 0; // no input scan, so don't specify inputs to scan AD1CON3 = 0x0002; // ADC clk period = 6 PB clk periods = 75 ns AD1CON2 = 0; // no input scan // don't turn on the ADC until all other configuration is finished! AD1CON1SET = 0x8000; // turn ADC ON, defaults to software sampling while (1) { AD1CON1SET = 0x0002; // set SAMP bit. clears the DONE bit, starts sampling for (i=0; i<10; i++); // give it enough time to settle // a real timing operation would be better! this would fail if optimized! AD1CON1CLR = 0x0002; // conversion starts when SAMP bit is cleared while (!(AD1CON1 & 0x0001)); // check the DONE bit ADCValue = ADC1BUF0; // when done, you can copy the value from ADC1BUF0 }
Auto-Convert Two Channels and Dual Buffer with the Peripheral Library The following code uses the peripheral function library, and is modified from Microchip sample code in examples/plib_examples/adc10 to use the peripheral bus clock for Tad. (untested)
#include <plib.h> #define SYS_FREQ 80000000 unsigned int channel4; // conversion result as read from result buffer unsigned int channel5; // conversion result as read from result buffer unsigned int offset; // buffer offset to point to the base of the idle buffer int main(void) { SYSTEMConfig(SYS_FREQ, SYS_CFG_ALL); CloseADC10(); // ensure the ADC is off before setting the configuration // define setup parameters for OpenADC10 // Turn module on | ouput in integer | trigger mode auto | enable autosample #define PARAM1 ADC_FORMAT_INTG | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON // define setup parameters for OpenADC10 // ADC ref external | disable offset test | disable scan mode | perform 2 samples | use dual buffers | use alternate mode #define PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_2 | ADC_ALT_BUF_ON | ADC_ALT_INPUT_ON // define setup parameters for OpenADC10 // use PB clock | Tad = 6 PB clk periods = 75 ns | use 10 Tad for sample time #define PARAM3 ADC_CONV_CLK_PB | ADC_CONV_CLK_3Tcy2 | ADC_SAMPLE_TIME_10 // define setup parameters for OpenADC10 // set AN4 and AN5 as analog inputs #define PARAM4 ENABLE_AN4_ANA | ENABLE_AN5_ANA // define setup parameters for OpenADC10 // do not assign channels to scan #define PARAM5 SKIP_SCAN_ALL // use ground as neg ref for A | use AN4 for input A | use ground as neg ref for A | use AN5 for input B // configure to sample AN4 & AN5 SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN4 | ADC_CH0_NEG_SAMPLEB_NVREF | ADC_CH0_POS_SAMPLEB_AN5); // configure to sample AN4 & AN5 OpenADC10(PARAM1, PARAM2, PARAM3, PARAM4, PARAM5); // configure ADC using the parameters defined above EnableADC10(); // Enable the ADC while (1) { while (!mAD1GetIntFlag()); // wait for conversion to complete offset = 8 * ((~ReadActiveBufferADC10() & 0x01)); // determine which buffer is idle and create an offset channel4 = ReadADC10(offset); // read result of channel 4 from idle buffer channel5 = ReadADC10(offset + 1); // read result of channel 5 from idle buffer mAD1ClearIntFlag(); } return 0; }