Analog Input

The Basics

To get analog input into the PIC you need to convert the analog to digital, w hich is converting a fluid stream of voltage to discrete samples ranging between 1 and 0. We use an ADC (Analog to Digital Converter) to do this, and the PIC18F 452 has one built in. The PIC's ADC functions just like your computer soundcard' s microphone input, but with (probably) a lower sampling rate and bit-depth. The PIC ADC's bitdepth is a max of 10b, whereas a generic crappy sound card has 16b quality. The PIC's sampling rate is determined by its clock speed. To get make sure the ADC is working, you'll use the serial framework you built in the previo us tutorial to transmit the ADC numbers to your computer's terminal.

Connectitidos!

Connection for this is very simple, I actually got it on my first try without any instructions. Any variable resistor will do, but a potentiometer is the mos t convenient for testing. Any electronic handbook or Physical Computing c an tell you how to wire the potentiometer. Basically the pot has three poles, on e goes to +5, another goes to GND, and the output goes to your ADC pin, in this case AN0.

First Up: ADC Configuration

Let's write an ADC_Init subroutine that will initialize your Analog to Digita l Converter all happy like. We'll do our ADC on port A, because, well, that's th e PIC's ADC port. We'll use pin AN0, which is the top left pin, so we'll first s et that pin to input. Then we need to set the A/D Control Register, ADCON0, so t hat port A is an analog input, since it can also be used as a digital input. We' ll just set the last bit which powers up the ADC unit. If you want more details on ADCON0 look in the datasheet on p. 183.

                ; Set port A to AN0 input
                bsf     TRISA, AN0
                ; Set port A to be an analog input see pp.183 and 184 in the dat asheet
                movlw   B'00000001'

Then we need to set the second A/D Control Register, ADCON1, which is a bit t rickier. When the ADC makes a conversion, since it' a 10 bit ADC, it can't hold all the bits in one register, so it uses two: ADRESH and ADRESL. Of course, the 10 bits don't fill up both registers, so you need to select whether they'll be r ight or left justified, with either the 6 most significant bits of ADRESH set to 0, or the 6 least significant of ADRESL set to 0.

                movlw   B'11001110'
                movwf   ADCON1

Here's the whole subroutine:

; Initialize analog input
ADC_Init
                ; Set port A to AN0 input
                bsf     TRISA, AN0
                ; Set port A to be an analog input see pp.183 and 184 in the dat asheet
                movlw   B'00000001'
                movwf   ADCON0
                movlw   B'11001110'
                movwf   ADCON1
                return

We'll just call this when we call our UART initialization routine.

Converting

Now we'll write a subroutine to trigger the getting and get the conversion. I t's actually pretty simple:

ADC_Get
                bsf     ADCON0, GO      ; start conversion
adc_wait                                ; don't move on until the conversion is done.
                btfsc   ADCON0, DONE
                goto    adc_wait
                movf    ADRESL, W
                return

So here's what's happening. To start the conversion you set the GO bit. The G O/DONE bit is bit 2 of ADCON0. To trigger a conversion you set it, and when the conversion is over the PIC will clear it. So we set it, then loop using btfsc un til the conversion is done. We're just moving ADRESL to W for now, which means w e only have 8 bit resolution. This is because to XMIT the full 10 bits we need t o write some tricky code, which I'll get into in the next tutorial. For now we d o 8 bits.

Communication Makes it Happen

All we need to do next is take the converted ADC data from W where our sub le ft it and send it to the computer using all the code we wrote in the last tutori al. Here's all the code glued together in a way that should work, sans header ju nk. This is not the end of the road however, so stay tuned. Note that I've put s ome simple sub calls into the initialization section and the main loop. I've lef t the LED blink in for time being, since it allows you to tell whether the main loop is running. When you're sure it's working you should remove the LED blinks so that the conversion runs at the maximum speed.

; Define Constants
COUNT1  equ     0x00
COUNT2  equ     0x01

; Define variables
char    equ 0x04

; Set port D to all output
        clrf    TRISD

; initialize variables
        call    Reset_char              ; sets char to 'A'

        call    UART_Init
        call    ADC_Init
Main
                bsf     PORTD,RD1       ; turn off the LED
                call    Delay           ; wait

                call    ADC_Get         ; converts, put ADRESL in W
                call    UART_Put

                bcf     PORTD,RD1       ; turn off the LED
                call    Delay           ; wait
                goto    Main

;; Subroutines
Reset_char
                movlw   'A'
                movwf   char
                return

Delay
                setf    COUNT1
                setf    COUNT2
Loop1
                decfsz  COUNT1, F
                goto    Loop1
                setf    COUNT1
                decfsz  COUNT2, F
                goto    Loop1
                return

UART_Init
                movlw   B'00100110'     ; See datasheet p 168
                movwf   TXSTA
                movlw   B'10000000'     ; Turn serial port on.
                movwf   RCSTA
                movlw   D'25'           ; Set baud rate to 9600 w/ 4 MHz clock. +.16% error
                movwf   SPBRG

                bcf     TRISC, TX       ; Set TX to output
                bcf     PIE1, TXIE      ; clear transmit interrupt

                return

UART_Put
                btfss   PIR1, TXIF      ; Wait for TXREG to be empty
                goto    UART_Put
                movwf   TXREG           ; Transmit byte in W
                return

; Initialize analog input
ADC_Init
                ; Set port A to AN0 input
                bsf     TRISA, AN0
                ; Set port A to be an analog input see pp.183 and 184 in the dat asheet
                movlw   B'00000001'
                movwf   ADCON0
                movlw   B'11001110'
                movwf   ADCON1
                return

ADC_Get
                bsf     ADCON0, GO      ; start conversion
adc_wait                                ; don't move on until the conversion is done.
                btfsc   ADCON0, DONE
                goto    adc_wait
                movf    ADRESL, W
                return

        end


Now, once you get it working and take out the LED delay, it should be spewing garble onto your hyperterminal. It's spewing garble because the PIC is sending the raw numbers from ADRESL and Hyperterminal is trying to interpret these numbe rs as ASCII codes, which they are not. In the next tutorial we'll see how to con vert it into ASCII, but for the time being let's just build a basic PD patch to print the raw output so you can see the value of ADRESL.

The key PD object is the comport external, which is available here. It's very easy to play with, here 's the basic patch, not much to it:

PD patch

To use this, open your comport with the open message, set the baud rate and l et it go. Comport will spew out numbers between 0 and 255, which you can pump in to a print object or an osc~ as I've done here.