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.
- ADFM: 1/0: right/left justify the result in ADRESH and ADRESL.
- ADSC2: I can't remember what this is exactly, but I set it to 1 and it wor ked ;) Sorry.
- The next two bits are unimplemented, so set them to 0.
- The last four bits configure which pins are analog and digital, what is us ed for the reference voltage (we're not using a voltage reference at this point) and the number of analog input channels and reference channels. We just need AN 0 to be analog, so we'll set these bits to '1110'.
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:
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.