El conversor análodo digital (A/D), sirve, como su nombre lo indica, para convertir una señal análoga (un voltaje, por ejemplo) en un valor digital (un conjunto de bits con valor definido). Los conversores poseen distintos rangos de trabajo y resoluciones.
En el caso del PIC16F876A, se cuenta con 5 conversores A/D. Si vemos el esquemático del PIC-MT, el único pin que efectivamente puede ser usado para realizar una conversión A/D es RA0/AN0. Dicho pin, puede ser conectado a una señal análoga en el rango 0-5V utilizando el pin 2 y el pin 3 (GND) de ADC. También es posible utilizar una señal análoga en el rango 0-10V, utilizando el pin 1 y el pin 3 (GND) de ADC.

La configuración del conversor A/D parte con los bits PCFG3:PCFG0. En el datasheet del PIC se puede encontrar que la configuración en que la única entrada análoga es AN0, y los voltajes de referencia son VDD y VSS, es 1110.
Por lo tanto,
PCFG0 = 0; PCFG1 = 1; PCFG2 = 1; PCFG3 = 1;
Los siguientes bits a configurar son CHS2:CHS0, con los que seleccionamos qué canal seleccionaremos para leer. Obviamente, como el único canal disponible (en el caso del PIC-MT, en otros casos puede variar) es AN0, entonces buscamos en el datasheet el valor con el cual se selecciona este canal. Este valor es 000.
Entonces,
CHS0 = 0; CHS1 = 0; CHS2 = 0;
Debemos escoger también el tiempo de conversión por bit. Según el datasheet del PIC, dependiendo del reloj al que esté funcionando el PIC, se deberá garantizar que haya un tiempo mínimo de 1.6 microsegundos por bit leído. En el mismo documento, existe una tabla de configuraciones de los bits que controlan el tiempo de conversión por bit (ADCS2, ADCS1 y ADCS0) y la frecuencia del reloj.
Lo anterior también dependerá de lo que estemos muestreando. En general la regla es que debemos tener al menos el doble de resolución de lo que estemos midiendo (si es una señal continua como la de una pila, da lo mismo). En este caso usaremos la configuración recomendada en el datasheet para una frecuencia de reloj de 20 MHz,
ADCS2 = 0; ADCS1 = 1; ADCS0 = 0;
El conversor A/D tiene una resolución de 10 bits. Debido a esto (y que el tamaño de los registros es de 8 bits), el resultado de la conversión se entrega en dos registros, ADRESH y ADRESL. Dado que necesariamente en el resultado sobrarán 6 bits (pues de los 16 bits de ADRESH y ADRESL sólo usaremos 10), debemos escoger como vendrá alineado este resultado. Hay dos posibilidades de alineamiento, la primera con los 6 bits más significativos de ADRESH en 0 (resultado alineado a la derecha), y la segunda con los 6 bits menos significativos de ADRESL en 0 (resultado alineado a la izquierda), que corresponden a los valores 1 y 0 del bit ADFM, respectivamente.
En nuestro caso, utilizaremos la alineación a la derecha, ADFM = 1. El siguiente paso es encender el conversor A/D, lo cual se realiza poniendo en 1 el bit ADON.
Para comenzar a leer se debe poner en 1 el bit GO (bit número 2 del registro ADCON0), lo que inicia el proceso de conversión. Luego de iniciar el proceso de conversión, el bit GO indica si es que dicho proceso ha terminado tomando el valor 0. Así que mientras GO sea 1, es necesario esperar a que la conversión termine. Por lo tanto, el código que inicializa la conversión y espera a que esta termine es,
//Empezaraleer GO = 1; //Esperarquelaconversióntermine while(GO);
Una vez terminada la conversión, se puede leer el resultado.
high_result = ADRESH; low_result = ADRESL;
Probaremos el conversor A/D con el medidor de distancia ultrasónico MaxSonar -EZ1, el cual posee una salida análoga en el rango 0-2.55V. El primer paso es alimentar el sonar, conectando su línea de 5V y su GND a los pines 2 y 3 del ICSP del PIC-MT.

Una vez alimentado, se debe conectar la salida análoga del sonar (AN), a la entrada análoga del PIC-MT en ADC. Dado el rango en el que entrega los datos el sonar, conectaremos su salida análoga al pin 2 de ADC. Es importante estar consciente de que dicho pin está pensado para recibir una señal en el rango 0-5V, y dado que la señal entregada por el sonar tiene un valor máximo de 2.55V, no estaremos utilizando casi la mitad de la resolución. Además, la resolución que queda para las mediciones no es la mejor. Aún así, el ejemplo aporta bastante desde el punto de vista educativo y ese es precisamente el objetivo de este tutorial. De todas formas se puede hacer una medición más precisa utilizando un voltaje de referencia de 2.55V o simplemente conectando el sonar por medio del puerto serial.
Una vez leídos los datos, procederemos a enviar el resultado (10 bits) por el puerto serial (convirtiendo el valor en bits a una cadena de caracteres 0 y 1). Luego, mostraremos en la primera línea del LCD, el valor decimal de la lectura y en la segunda línea el valor en centímetros de la distancia reportada por el sonar.
La primera parte correspondiente al envío del valor en binario del resultado por el puerto serial, es
uns8 high_result, low_result, mask, i, j, buf; uns16 result, range; for(;;) { //Empezaraleer GO = 1; RB4 = 0; //Prendemoselledcadavezqueempezamosaleer while(GO); //Esperarquelaconversióntermine //Leer high_result = ADRESH; low_result = ADRESL; //Verificarsielbit1dehigh_resultes1ó0 if(high_result & 2) buf = '1'; else buf = '0'; //Enviarelcaractercorrespondientealbit while(TXIF == 0); TXREG = buf; //Verificarsielbit0dehigh_resultes1ó0 if(high_result & 1) { buf = '1'; } else { buf = '0'; } //Enviarelcaractercorrespondientealbit while(TXIF == 0); TXREG = buf; //Enviarunespacioparasepararlos //dosbitsdehig_resultdelosbitsdelow_result while(TXIF == 0); TXREG = ''; //Paracadaunodelosbitsdelow_result for(i = 0; i < 8; ++i) { j = 7 - i; //Creamosunamáscaraparaprobarlosbits //Desdeelmássignificativo(bit7) //almenossignificativo(bit0) mask = 1 << j; //Probamossielbites1ó0 if(mask & low_result) buf = '1'; else buf = '0'; //Enviamosporelpuertoserialel //caracterquerepresentaalbit while(TXIF == 0); TXREG = buf; } //Saltodelínea while(TXIF == 0); TXREG = '\n'; //Retornodecarroparaseguiralineado //alaizquierdaenhyperterminal while(TXIF == 0); TXREG = '\r'; }
Si nos conectamos por hyperterminal al puerto serial conectado al PIC-MT deberíamos ver lecturas parecidas a esta (dependerá por supuesto de lo que en ese momento esté leyendo el sonar),

El siguiente paso es unir los 2 bits de ADRESH y los 8 bits de ADRESL en una variable, de manera de poder realizar operaciones aritméticas sobre ella.
//Copiamoslos2bitsdeADRESH //(los6mássignificativosson0) result = high_result; //Losllevamosasuposicióncorrecta result = result << 8; //Copiamoslos8bitsdeADRESL result += low_result;
Ahora mostraremos en la primera línea del LCD el valor decimal de la lectura. No entraremos en detalles acerca del funcionamiento del LCD, pues será visto más adelante en un tutorial exclusivo del manejo del LCD.
El código que escribe el valor decimal de la lectura es,
//BorramoselLCD send_cmd(CLR_DISP); //Calculamoslacifradelosmiles. //Elvalordecimalnoesmásgrandeque1023 //(10bits,valormáximoes2^10-1). i = result/1000; //EnviamosalLCDlacifradelosmiles send_char('0' + i); //Calculamoslascentenas j = result/100; //Restamossiesnecesario10centenas //queyaescribimosenlosmiles if(i) j -= 10; //Escribimoslascentenas send_char('0' + j); //Dejamosenjlasdecenasyunidades j = result%100; //Calculamoslasdecenas j /= 10; //Enviamoslasdecenas send_char('0' + j); //Calculamoslasunidades j = result % 10; //Enviamoslasunidades send_char('0' + j);
El programa hasta este punto se encuentra en el archivo tut_atd0.c (también necesitará Delay.c).
Ahora procederemos a calcular la cantidad de centímetros reportada por el sonar. El primer paso es interpretar el número decimal. Si el rango del conversor A/D es de 0-5V y contamos con 10 bits, entonces dada una lectura (como decimal), tenemos que el voltaje correspondiente es 5*result/1023. Pero como esta operación es entera, es mejor multiplicar por 100 y simular dos decimales del voltaje, quedando, Voltaje leído = (5*result*100)/1023. No debemos olvidar que hemos multiplicado por 100.
Mirando el datasheet del sonar, vemos que los valores correspondientes al rango 0-2.55V se mapean en distancias en el rango 0-255”, y como en el paso anterior multiplicamos el voljate por 100, tenemos que la expresión (5*result*100)/1023 representa el número de pulgadas que el sonar está reportando.
//Distanciareportadaporelsonarenpulgadas range = result*500; range /= 1023;
Finalmente, sabemos que 1” = 2.54 cm, por lo que para realizar la conversión debemos multiplicar por 254 (y no olvidar que hay un factor 100 oculto).
//Obtenermedidaencentímetros(hayunfactorde100). range = range*254; //Obtenemosloscentímetrosfinales range /= 100;
Dado que vamos a enviar el número de centímetros reportados por el conversor A/D al LCD y el procedimiento es el mismo que cuando enviamos el valor decimal de la lectura, es mejor modificar el programa y crear una función que envíe un número decimal de hasta 10 bits al LCD.
La porción relevante de código es,
//BorramoselLCD send_cmd(CLR_DISP); //Enviamoselvalordecimal to_lcd(result); //Distanciareportadaporelsonarenpulgadas range = result*49; //500/1023=0.4887 range /= 100; //Obtenermedidaencentímetros(hayunfactorde100). range = range*254; //Obtenemosloscentímetrosfinales range /= 100; send_cmd(DD_RAM_ADDR2); //PasaralasegundalíneadelLCD to_lcd(range); RB4 = 1; //Apagamoselled delay_ms(1000); //Generamosundelay
Si bien hay bastantes aproximaciones en el proceso (y por lo tanto un error acumulado), no deja de ser interesante observar los centímetros reportados en el lcd y ver que son totalmente razonable (fue probado con distancia a paredes, techos y otros objetos).
El ejemplo completo se encuentra en el archivo tut_atd1.c, con todos los comentarios necesarios para su comprensión y posterior modificación.