Assignment title: Information


72 9 Analogue to Digital Conversion Module 9.1 Aim This laboratory practical will investigate methods for configuring and using the analogue‐to‐digital convertor (ADC) module on the PIC16F877A to convert voltages from a potentiometer and light dependent resistor (LDR) located on the E‐block sensor board attached to PORTA. The relationship between converted digital values to physical quantities such as voltage will be investigated. 9.2 Learning Outcomes After this laboratory practical you will: x Understand how a physical quantity can be represented by a digital value through the use of an analogue transducer and the process of analogue to digital conversion. x Understand the capabilities and limitations of the 10 bit ADC module on the PIC16F877A. x Be able to configure the PIC16F877A ADC module correctly. x Be able to create useful code to monitor analogue voltages using polling and interrupts. x Implement a library file of useful ADC functions. 9.3 Background 9.3.1 Introduction An analogue‐to‐digital convertor (ADC) is a system that converts an analogue signal (voltage) that is continuous in both time and magnitude, into a digital representation. In order to do this, the signal is first sampled before being quantised. Sampling is the act of taking a ‘snapshot’ of the voltage at a point in time. Quantisation is the process of converting a sampled voltage into a discrete digital value. An ADC converts an analogue signal into a digital signal that is discrete in both time and amplitude. In most applications ADC conversion takes place at a regular rate defined by the sampling rate. The Nyquist‐Shannon sampling theorem stipulates that the sampling rate must be at least double the highest frequency contained in a band‐limited continuous‐time signal. A failure to adhere condition can lead to aliasing, where high frequency content appears incorrectly as lower frequency content in the digital signal. Aliasing can be avoided by band‐limiting the analogue signal using an anti‐aliasing filter. There are many different types of ADCs e.g. flash, sigma‐delta, successive approximation, pipelined. Each design has its own strengths and weaknesses in terms of key parameters such as speed, complexity, resolution, linearity etc. The type of ADC design chosen in any give applications will depend on the requirements of that application. Commercial ADCs generally come as dedicated ICs and may require other external components such as voltage references to operate. ADCs are also often found integrated into MCUs to allow the conversion of analogue signals from transducers in embedded systems applications. A transducer is a device that converts one form of physical quantity to another. Some examples of common transducers73 are microphones (sound), temperature sensors, light‐level sensors, potentiometers (linear/rotary position). 9.3.2 PIC16F877A ADC module The PIC16F877A has a single on board ADC module which has a resolution of 10 bits. This means that it can convert an analogue voltage into one of 2 1024 10 separate digital values ranging from 0‐1023. In front of the ADC is an 8‐channel multiplexer (MUX) which allows the selection of any one of 8 channels connected to external pins on PORT A (RA0 to RA5) and PORT E (RE0 to RE2). The MUX allows up to 8 separate analogue inputs to be converted using a single ADC unit, but only one input can be converted at any one time. This means that the maximum possible sampling rate is reduced in proportion to the number of multiplexed channels converted e.g. sampling 4 channels will reduce the maximum sampling rate by a factor of 4. Figure 9‐1 Schematic of the PIC16F877A ADC module The ADC can supply its own voltage reference internally, or if required external voltage references can be supplied. The reference voltages (VREF+ and VREF‐) are important because they define the voltage conversion range of the ADC. Generally speaking the voltage resolution of an ADC is defined by, ADC Resolution 2 REF REF N V V    We will be using the on board ADC module in conjunction with internally generated voltage references, which are sourced from the power supply rails. In the case of the Matrix Multimedia E‐ block development system the internal voltage references are thus, 5 V 0 V REF DD REF SS V V V V   So the ADC voltage resolution available is, 10 5 0 ADC Resolution 4.88 mV 2 2 REF REF N V V    74 In general, it is important to match the conversion range of an ADC with the anticipated operating range of the analogue signal you are monitoring. If the range is too small you will experience clipping of the converted digital signal as the analogue voltage goes out of range. If the range is too high you will reducing the effective resolution at which the ADC is operating. Note that other parameters can affect the effective resolution of a real‐world ADC including linearity, missing codes and non‐monotonicity. However, such considerations are outside of the scope of this laboratory practical. 9.3.3 ADC conversion clock requirements The PIC16F877A ADC module works on the principle of successive‐approximation. This means that each ADC conversion requires a number of conversion cycles to arrive at the final converted digital value. In total 12 conversion cycles are required (one for each bit of resolution plus two for overhead). The minimum conversion cycle time (TAD) is 1.6 µs if the ADC is to achieve its specified accuracy. The conversion cycle time is determined by the ADC conversion clock. The ADC conversion clock can be supplied internally from the crystal oscillator driving the MCU via a prescaler or via a dedicated RC oscillator (for low power applications). There are seven software selectable options in total: x 2 TOSC x 4 TOSC x 8 TOSC x 16 TOSC x 32 TOSC x 64 TOSC x Internal RC oscillator Since we are using a 3.2786 MHz crystal to drive the PIC16F877A in the laboratory, the crystal clock interval is, T f OSC OSC 1/ 305.2 ns . In order to maintain the criteria for a 1.6 µs conversion clock interval then we need to select the conversion clock source as 8 TOSC which yields a conversion clock interval, TAD 2.441 μs . The total conversion time per sample is then 12 29.3 TAD μs . 9.3.4 ADC acquisition time requirements The analogue voltage input to the ADC module is sampled before conversion by a sample‐and‐hold circuit, which comprises a capacitor and switch. It is necessary to allow enough time for the capacitor to charge and for the amplifier that buffers the signal to the ADC to settle if the sampled analogue voltage is to be accurate enough to provide true 10 bit resolution on conversion. This so called acquisition time (TACQ) also depends on ambient temperature and the source impedance of the analogue input being sampled. Detailed information on the calculation of acquisition time can be found in the PIC16F977A datasheet. A worst case value of TACQ = 19.72 µs can be calculated which assumes the maximum recommended source impedance of 2.5 kΩ and an ambient temperature of 50°C. In summary, ensuring a minimum period of 20 µs per conversion for signal acquisition will ensure operation of the ADC to the specified accuracy. It may however be possible to reduce this acquisition time further if one has detailed knowledge of the impedance of the analogue source.75 9.3.5 Configuring the PIC16F877A ADC module The ADC module is configured through two SFRs, ADCON1 and ADCON2. These registers contain bitfields to do the following, x Set the ADC conversion clock source. x Select which MUX channel to convert. x Start an ADC conversion. x Turn the ADC module on/off. x Set the justification of the conversion result stored in the in the ADCRESH and ADCRESL registers. x Assign which of the 8 analogue available input pins are assigned to the ADC module (some can be configured as GPIO instead). x Select internal/external voltage reference sources. The contents of the ADCON0 and ADCON1 SFRs are summarised in figure 9‐2 and figure 9‐3. This looks quite complicated at first, but luckily the ADC should be configured in a fixed manner for correct operation in these laboratory practicals. Only the MUX channel selection bitfield (CHS2:0) needs to be changed for your application. Figure 9‐2 ADCON0 SFR76 Figure 9‐3 ADCON1 SFR Table 9‐1 summarises the configuration requirements for the ADC. Table 9‐1 Summary of ADC configuration requirements Bit field Value Reason ADCS2:0 4 Need to set the conversion clock interval to 8 TOSC when the MCU is clocked at 3.2768 MHz (see section 9.3.3). This ensures TAD>1.6 µs. CHS2:0 0 Set AN0 as the default MUX channel for conversion. This is connected to the LDR on the E‐block sensor board. ADON 1 The ADC module should be turned on ready for use ADFM 1 Set results justification to right‐justified. This allows easy combination of ADRESH and ADRESL to yield a 10 bit result (see section 9.3.7) PCFG3:0 0 For the purposes of this lab, the analogue input pins are not used as GPIO so they can all be assigned as analogue inputs. The voltage refs, VREF+/VREF‐ need to be sourced from the power supply rails, VDD (5 V) and VSS (0 V) respectively. The required configuration for ADCON0 and ADCON1 is summarised in the tables below (X represents a ‘don’t care’ state). Note: The ADCS bitfield is split over both registers. The MSB (ADCS2) is contained in bit 6 of ADCON1, while the LSBs are located in bits 7 & 8 of ADCON0.77 Table 9‐2 ADCON0 Configuration ADCS1 ADCS0 CHS2 CHS1 CHS0 GO/DONE ‐ ADON 0 1 0 0 0 0 X 1 Table 9‐3 ADCON1 Configuration ADFM ADCS2 ‐ ‐ PCFG3 PCFG2 PCFG1 PCFG0 1 1 X X 0 0 0 0 This configuration can be achieved by writing the following values to the ADC configuration SFRs (Don’t care’ states are written as zeroes in this instance). ADCON0 = 0x41; ADCON1 = 0xC0; 9.3.6 Starting an ADC conversion Once the ADC module has been configured correctly and turned on, starting an ADC conversion requires the following two actions, x Wait the required minimum acquisition time (20 µs) has elapsed to allow the analogue voltage to stabilise in the sample and hold circuit. x Set the Go/Done bit in the ADCON0 SFR to command the conversion process to start. These actions can easily be implemented using the code below __delay_us(20); // Wait for acquisition time (worst case 20 us) ADCON0bits.GO = 1; // Set GO/DONE Bit to start conversion When the conversion is complete then the Go/Done bit is automatically reset by the MCU. This condition can be checked by polling. The following code sets up a do‐nothing while loop that polls the GO/DONE bit, and exits only when it has been reset. while(ADCON0bits.GO==1); // Wait for GO bit to clear (conversion complete) When the result is ready it is stored in the ADC results SFRs, ADRESH and ADRESL. 9.3.7 Retrieval and storage of ADC results It is necessary to use two SFRs to store the 10 bit result of a conversion by the ADC module. This is because of the maximum 8 bit register width set by the PIC16 family architecture. The ADC result registers are called ADRESH and ADRESL. Combining both of these 8 bit SFRs provides a 16 bit wide result register. The ADC result itself is only 10 bits wide so there is an option provided to set the justification of the result in the register. Results can be stored in either a left‐justified or right justified manner depending on the state of the ADFM bit in the ADCON1 SFR. Regardless of the selected justification type, the unused bits are padded with zeroes.78 Figure 9‐4 Justification of the ADC result The simplest way of retrieving the result of the 10 bit ADC conversion is to configure right‐justification (ADFM=1) and use a bitwise shift to combine the registers before assigning the result to a variable of type unsigned int, result = (ADRESH<<8) + ADRESL; // Combine to produce final 10 bit result Alternatively, if the full resolution of the ADC is not important then it is possible use left‐justification (ADFM=0) and simple retrieve the high byte as follows, result = ADRESH; // Retreive 8 bit resolution result directly This will provide an effective ADC resolution of 8 bits because the 2 LSBs of the result, stored in the ADRESL register, are ignored. 9.3.8 ADC conversion complete interrupt There is a peripheral interrupt available which is triggered whenever an ADC conversion is completed. You can interact with this by using the ADIE/ADIF bits in the PIE1/PIR1 SFRs. Don’t forget to enable peripheral interrupts by setting the PEIE bit in the INTCON SFR if you wish to use this facility. Using interrupts allows the MCU to get on with other useful tasks while an ADC conversion takes place. See the PIC16F877A datasheet for more information on using the ACD conversion complete interrupt. 9.4 Procedure 9.4.1 Exercise 1 – Configuring and using the ADC module In this exercise we will create some basic ADC functions and investigate their operation, utilising the LCD to provide output. Create a new project in the MPLAB X IDE, then create an empty source file and populate it with the code listing below. Note: You will need to place a copy of the LCD library files in the project directory in order to build the project successfully. // Filename: ADClib.c // Version: 1.0 // Date: // Author: // Purpose : Provides functions to easily use ACD to monitor Matrix // : Multimedia sensor boards #include 79 #include "LCDdrive.h" #define _XTAL_FREQ 3276800 // Required for the __delay_us and __delay_ms macros //Function Prototypes void ADC_initialise(void); unsigned int ADC_read(void); void ADC_channel_select(unsigned char channel); void main(void) { TRISB=0x00; LCD_initialise(); ADC_initialise(); while(1) { LCD_cursor(0,0); LCD_putsc("AN0 (LDR) "); ADC_channel_select(0); LCD_cursor(10,0); LCD_display_value(ADC_read()); LCD_cursor(0,1); ADC_channel_select(1); LCD_putsc("AN1 (POT) "); LCD_cursor(10,1); LCD_display_value(ADC_read()); __delay_ms(250); } } /* ADC_Config Configures the ADCON1 register for Fosc/8 and enables the ADC * function once before prior to using any of the LCD display functions * contained in this library. */ void ADC_initialise(void) { TRISA = 0x03; // Assign AN0 (RA0) and AN1 (RA1) as input ADCON1bits.ADCS2 = 0; // select ADC conversion clock select as Fosc/8 ADCON0bits.ADCS=0x01; // this provides the required minimum conversion time // with a 3.2768 MHz clock ADCON1bits.ADFM=0x01; // Set results to right justified ADCON1bits.PCFG=0x00; // Assign all ADC inputs as analogue ADCON0bits.ADON = 1; // Turn ADC On } /* ADC_Read reads the current analogue reading channel selected. It starts the * conversion by setting the Go/Done bit. Conversion is complete when the bit * is cleared by the MCU so a polling loop is set up detect this. After * conversion the ADRESH and ADCRESL are combined to provide a 10 bit result. */ unsigned int ADC_read(void) { unsigned int result; __delay_us(20); // Wait for acquisition time (worst case 20 us) ADCON0bits.GO = 1; // Set GO Bit to start conversion80 while(ADCON0bits.GO==1); // Wait for GO bit to clear (conversion complete) result =(ADRESH<<8)+ADRESL; // Combine to produce final 10 bit result return(result); } /* ADC_channel_select() selects the current channel for conversion. * On the E‐block sensor board: * Channel 0 (AN0) is connected to the LDR. * Channel 1 (AN1) is connected to the potentiometer. */ void ADC_channel_select(unsigned char channel) { ADCON0bits.CHS=channel; // This selects which analogue input // to use for the ADC conversion } Build the project and upload the generated HEX file onto the PIC16F877A. Observe the display on the LCD and monitor what happens when you: a) Turn the potentiometer on the E‐block sensor board. b) Place you hand over the top of the LDR on the E‐block sensor board. Study the code carefully and write a short summary of the purpose of each of the functions. Pay attention the parameter and return data types and justify provide a justification for the use of each one. Write down a flow chart describing the operation of the main() function using the subroutine call box (see figure 8‐5). Write another flowchart describing the operation of the ACD_read() function. MPLAB provides a useful feature, Call Graph for mapping the interconnectivity of functions in C code. This can be useful for generating hierarchy diagrams for software design reports. You can access the call graph by right clicking on a function in the editor and selecting Show Call Graph from the context menu. Figure 9‐5 Call Graph in MPLAB X Right click on the following line and select Show Call Graph, to produce a call graph of the main() function void main (void)81 You can right click on the boxes representing functions and select Expand Callees from the resulting context menu to see further levels of function calls. Boxes can also be dragged around to create a neat result. Arrange the boxes in the Call Graph output window until you have a hierarchical structure similar to that shown in figure 9‐5. Right click on whitespace in the window and select Export… to bring up a dialog window which allows you to save the result as a .png file. Insert this into your logbook. 9.4.2 Exercise 2 ‐ Building an ADC function library In this exercise we will build a library of useful ADC functions so that we can easily make use of the ADC module on board the PIC16F877A. Once created, this library can be used in later projects/assignments. Create a new project called ADClib in the MPLAB X IDE, create two empty source files called ADClib.c and main.c. Create a third empty file and name it ACDlib.h Restructure the code from the previous exercise so that the definitions for the three ADC functions ADC_initialise(), ADC_read() and ADC_channel_select() are placed in ADClib.c. Place the prototypes for the ADC functions in ADClib.h Place the main() function from the previous exercise into the main.c file. Be sure to put the required #include directive at the top so you can access the ADC functions that have now been moved out to the file ADClib.c. #include “ADClib.h” Build the project and ensure it is operating correctly. Remember that you will need to place a copy of the LCD library files in the project directory in order for it to build successfully. You have now created a library of useful ADC functions. To utilise them in other projects just place a copy of the ADClib.c and ADClib.h files in the project directory. Don’t forget that you must always run the ACD_initialise() function once prior to using the ADC in order to make sure the module is properly configured. 9.4.3 Exercise 3 – Converting ADC values to represent physical quantities In this exercise we will utilise our newly created ADC functions library to investigate the conversion of ADC values to represent actual physical quantities such as voltage. Create a new project in the MPLAB X IDE, then create an empty source file and populate it with the code listing below. Note: You will need to place a copy of the LCD and ADC library files in the project directory in order to build the project successfully. Create an empty source file and insert the following template code: // Filename: Lab9Ex3 // Version: 1.082 // Date: // Author: // Purpose : Provides functions to easily use ACD to monitor Matrix // : Multimedia sensor boards #include #include "LCDdrive.h" #include "ADClib.h" #define _XTAL_FREQ 3276800 // Required for the __delay_us and __delay_ms macros void main(void) { TRISB=0x00; LCD_initialise(); ADC_initialise(); // Insert your code solution here } Expand the provided template code so that the current potentiometer value in volts is displayed to three decimal places on the top line of the LCD display. The value should be appended with the correct units (V). The display should be updated every 200 ms using the timer 1 overflow interrupt. The background code executing in the main() function should deal with making the required ADC conversions. Ensure you coded solution is modular i.e. separate the tasks logically into individual functions. You will need to use the LCD_display_float() function provided in the LCD library. Review the LCD header file, LCDdrive.h for guidance in using this function. The calculation of the ADC resolution for one LSB is described in section 9.3.2. Simply multiply the ADC result by the resolution to determine the analogue voltage. You may view the correct operation for your developed code in this task by uploading the following HEX file: J:\Embedded Systems\MPLAB\Examples\ADCVoltage.hex Write down a high level flow chart of your main() function describing your code which shows function calls made. Write a high level flow chart representation of your ISR. Consider the combination of acquisition time and conversion time required to make an accurate ADC conversion. What limit does this does this place on the maximum sampling rate possible, assuming only a single ADC channel is converted? Refer to sections 9.3.3 and 9.3.4 and show any working. 9.4.4 Exercise 4 – Using the ADC conversion complete interrupt Create a new project in the MPLAB X IDE, then create an empty source file. Add copies of the the LCD and ADC library files to the project directory. Restructure the code from the previous exercise so that:83 x The LCD continues to be updated using the Timer1 overflow interrupt at 200 ms intervals. x The ADC conversion is driven using the ADC conversion complete interrupt (ADIE/ADIF). Every time the interrupt occurs, the ISR should store the conversion result and start a new conversion. Note: In your solution, the main() function should contain only the initial configuration code and a while(1) loop.