subtitle "adc_lib.asm" ;;; ********************************************************************* ;;; Library Name: adc_lib.asm ;;; Purpose: Routines for built-in A/D configuration and use. ;;; Revision: 0.2 ;;; Date: 03 October 2005 ;;; Author: L. Wyard-Scott ;;; Copyright: Public Domain ;;; Device: PIC16F873, PIC16F874, PIC16F876, PIC16F877. ;;; PIC16F873A, PIC16F874A, PIC16F876A, PIC16F877A. ;;; ********************************************************************* ;;; Special Directions: ;;; To use these routines, include "adc_lib.h" in the source code ;;; calling the routines. This file contains information about the ;;; routines and other equates that the calling code requires. ;;; ********************************************************************* ;;; Revision History: ;;; 0.2 - 03 October 2005 ;;; - Added support for 16F87XA devices. ;;; 0.1 - 11 February 2004 ;;; - Fixed a bug in manipulation of the TRISA buffer ;;; relating to the fact that RA4 does not have an ANx ;;; channel. ;;; 0.0 - 10 February 2004 ;;; - Creation. ;;; ********************************************************************* ;;; Ideas for future work: ;;; ;;; ********************************************************************* ;;; ********************************************************************* list ; Turn on list output. Note that device ; type needs to be specified to the ; assembler (on the command line, or through ; the MPLAB IDE). ;; Include register, bit, and other info specific to ;; the specified device. ifdef __16F873 #include messg "Assembling adc_lib.asm for PIC16F873." endif ifdef __16F874 #include #define HASPORTE ; Indicate the device has 8 ADC pins. messg "Assembling adc_lib.asm for PIC16F874." endif ifdef __16F876 #include messg "Assembling adc_lib.asm for PIC16F876." endif ifdef __16F877 #include #define HASPORTE ; Indicate the device has 8 ADC pins. messg "Assembling adc_lib.asm for PIC16F877." endif ifdef __16F873A #define A_Device #include messg "Assembling adc_lib.asm for PIC16F873A." endif ifdef __16F874A #define A_Device #include messg "Assembling adc_lib.asm for PIC16F874A." #define HASPORTE ; Indicate the device has 8 ADC pins. endif ifdef __16F876A #define A_Device #include messg "Assembling adc_lib.asm for PIC16F876A." endif ifdef __16F877A #define A_Device #include messg "Assembling adc_lib.asm for PIC16F877A." #define HASPORTE ; Indicate the device has 8 ADC pins. endif ;;; -------------------------------------------------------------------- ;;; Assembler Equates Section. Define assembly-time constants here ;;; using the EQU assembler directive. ;;; -------------------------------------------------------------------- ;;; --------------------------------------------------------------------- ;;; Variable Address Assignments. ;;; --------------------------------------------------------------------- UDATA ; Start of the uninitialized data section. ; The following statements reserve memory, ; the address of which is allocated by the ; linker. UDATA_OVR ; Overlayed memory locations. ChannelNumber: res 1 ; A/D channel number. TRISxValue: res 1 ; A/D channel number in TRISx form. Counter: res 1 ; A temporary counter value to help ; determine TRISxValue ;;; -------------------------------------------------------------------- ;;; Subroutines. ;;; -------------------------------------------------------------------- CODE ; Start of the code section. ;;; ******************************************************************** ;;; Subroutine Name: ADC_Sample ;;; Description: Obtains a 10-bit sample from a specified ;;; channel. Chip power supply rails (AVdd and ;;; AVss) are used as the reference voltage. ;;; This routine: ;;; 1. Configures the specified channel ;;; as an input (via TRISx, x=A, or E). ;;; 2. Selects the A/D channel. ;;; 3. Uses the FOSC/8 clock (suitable for ;;; systems with a 4MHz timing element). ;;; 4. Turns on the A/D. ;;; 5. Waits for an "acquisition time": the time ;;; required for the AMUX to switch and for ;;; the analog voltage to be held on a ;;; sampling capacitor. ;;; 6. Starts the conversion. ;;; 7. Waits for the conversion complete flag ;;; to be set. ;;; 8. Turns off the A/D. ;;; 9. Returns with the results as specified, ;;; below. ;;; Requires: W contains the A/D channel number: ANx ;;; For the PIC16F873,6 this must be 0-4. ;;; For the PIC16F874,7 this must be 0-7. ;;; No error-checking on this parameter is performed. ;;; Returns: W contains the 8 MSBits of the resulting sample, ;;; ADRESH,ADRESL contain the full 10-bit value ;;; in right-justified format (ADRESH b7-b2 are 0). ;;; Locations Affected: ChannelNumber, TRISxValue, Counter variables. ;;; ******************************************************************** ADC_Sample: global ADC_Sample ; Make this a callable module. ;; Save a copy of the A/D conversion channel. banksel ChannelNumber movwf ChannelNumber movwf Counter ;; Convert the binary-weighted value into a form suitable ;; for writing to a TRISx register. movlw 1 movwf TRISxValue movf Counter,F ; Sets STATUS,Z btfss STATUS,Z goto ADC_SampleNextChannel goto ADC_SampleDoneShiftTRISx ADC_SampleNextChannel: bcf STATUS,C ; Shift in a 0. rlf TRISxValue,F decfsz Counter,F goto ADC_SampleNextChannel ADC_SampleDoneShiftTRISx: ;; If this is a PIC16F874 or PIC16F877 determine if this ;; is PORTE or PORTA. ifdef HASPORTE ; { movf TRISxValue,W andlw 0x1F btfss STATUS,Z goto ADC_SampleIsPortA ADC_SampleIsPortE: ;; The channel is in Port E. Establish TRISE by masking ;; in the "1" in the position determined, above. swapf TRISxValue,F bcf STATUS,C rrf TRISxValue,W banksel TRISE iorwf TRISE,F goto ADC_SampleDoneTrisX endif ; } ADC_SampleIsPortA: ;; RA4 is not an analog input channel. Thus, if ;; TRISxValue = 0x10 or 0x20, then the value needs to ;; shifted left one more time. movlw 0x30 andwf TRISxValue,W btfsc STATUS,Z goto ADC_SampleSetTRISA rlf TRISxValue,F ADC_SampleSetTRISA: movf TRISxValue,W banksel TRISA iorwf TRISA,F ADC_SampleDoneTrisX: ;; Make all analog channels input with reference voltage ;; internal and results right-justified. Note that doing this ;; is permissable even if some of the pins are digital I/O. ;; No bank select is necessary: currently in Bank 1. ;; Note that the "Midrange Microcontroller Manual" ;; mistakenly specifies the A/D format bit in the ;; incorrect position (it says b5, the data sheet says b7). movlw 0x80 movwf ADCON1 ;; Construct the ADCON0 value. ;; First, insert the channel number. banksel ChannelNumber swapf ChannelNumber,F bcf STATUS,C rrf ChannelNumber,W ;; Select the FOSC/8 clock and turn the A/D on. iorlw 0x41 ;; Write the value. banksel ADCON0 movwf ADCON0 ;; Wait for the designated acquisition time. This is 2 uS ;; for a 4 MHz device frequency and FOSC/8 selected. ;; This exceeds the minimum value of 1.6 us. ;; HOWEVER! There seems to be cross-talk between the ;; channels if there isn't more delay than the minimum ;; specified. The following code delays for 10 uS. ;; NOP instructions could be used, but 10 would be ;; required. The GOTO instructions take 2uS to execute ;; each and therefore the code ends up being ;; a little smaller. goto $+1 ; 10us delay at fxtal = 4MHz. goto $+1 goto $+1 goto $+1 goto $+1 ;; Start the A/D conversion. bsf ADCON0, GO ;; Spin-wait until the GO/DONE* bit goes low. ADC_SampleWaitConversion: btfsc ADCON0, GO goto ADC_SampleWaitConversion ;; Falling through to here means the conversion is complete. ;; Turn off the A/D bcf ADCON0, ADON ;; Result is right-justified in ADRESH:ADRESL. ;; Construct a return parameter that provides easy-access ;; to the 8 MSBits of a left-justified value. banksel ADRESL movf ADRESL,W banksel ChannelNumber movwf ChannelNumber bcf STATUS,C rrf ChannelNumber,F bcf STATUS,C rrf ChannelNumber,F movlw 0x3F andwf ChannelNumber,F banksel ADRESH movf ADRESH,W banksel Counter movwf Counter swapf Counter,F bcf STATUS,C rlf Counter,F rlf Counter,F movlw 0xC0 andwf Counter,W addwf ChannelNumber,W return END ; End of source code.