Documentos de Académico
Documentos de Profesional
Documentos de Cultura
En otro tutorial vimos como el mdulo PWM funcionaba como una especie de
conversor digital -> analgico (DAC). El conversor analgico-digital (ADC) que equipa
a muchos microcontroladores hace justo lo contrario, convirtiendo un voltaje
analgico externo en un nmero, con el que podremos operar.
Como siempre empezaremos con una introduccin sobre los fundamentos de un
conversor AD y los registros usados para su manejo en los PIC. Tras entender el
proceso, presentaremos las rutinas de C18 para el manejo del ADC y escribiremos
nuestras propias funciones, que nos permitirn optimizar el rendimiento del ADC y no
depender de un compilador en particular.
En la siguiente entrada veremos como al igual que pasaba con el puerto serie, usando
interrupciones podemos optimizar el rendimiento del PIC mientras estamos usando el
conversor AD.
Cdigo asociado a esta entrada: adc1.c
Conceptos bsicos de un ADC:
Normalmente Vref- suele ser Vss=GND=0V y Vref+ = Vcc = 5V, pero pueden
usarse otros voltajes de referencia. Si por ejemplo queremos medir una
seal que sabemos que oscila entre 2 y 3 voltios usaramos Vref-=2 y
Vref+=3. As aprovecharamos mejor el rango dinmico del conversor.
se ve, voltajes por debajo de Vref- o por encima de Vref+ son posibles y se
cuantifican como nivel mnimo 0 o mximo, 1023. Niveles por debajo de 0V
o por encima de la tensin de alimentacin (normalmente 5V) pueden ser
daar el PIC.
Nivel
0
1
2
1023
Voltaje
<r
[r,2r]
[2r,3r]
>1023
r
Aunque un PIC puede tener del orden de 8-12 posibles canales (pines) de
entrada analgica, solo tiene normalmente un nico mdulo ADC, lo que
significa que no podemos tomar medidas simultneas de varios canales. Si
es necesario, lo que podemos hacer es ir conectando (seleccionando) los
sucesivos canales al ADC para ir midiendo sus voltajes.
Un comentario final: aunque es muy conveniente tener un ADC integrado dentro del
microcontrolador, si lo pensamos en trminos de nivel de ruido, etc. las cercanas de
un microcontrolador con la lneas de reloj, rpidos cambios en las lneas digitales,
comunicaciones, etc. no es el mejor sitio para ubicar un ADC. Esto quiere decir que si
estamos muy interesados en la precisin, tal vez sera conveniente considerar un ADC
aparte, que nos comunicara los datos adquiridos a travs de SPI o similares. Una vez
dicha la advertencia nos centraremos en como usar de la forma ms eficiente el ADC
de un microcontrolador (en este caso un PIC).
Bit 7
X
No
usado
bit 6
X
No
usado
bit 5
CHS3
bit 4
CHS2
bit 3
CHS1
bit 2
CHS
0
Seleccin de canal:
desde 0 (0000) a 12 (1100)
bit 1
GO
bit 0
ADON
Lanza
ADC
ON/OF
F
bit 7
X
bit 6
x
bit 5
VCFG1
bit 4
VCFG
0
No
usado
No
usado
Uso (1) de
Vref (+),Vref(-)
bit 3
bit 2
bit 1
PCFG PCFG2 PCFG0
3
Reparto canales
digitales / analgicos
bit 0
PCFG0
VCFG1: por defecto (0) usamos Vss=Gnd=0V como Vref-. Si es 1 se tomara como
Vref- el voltaje presente en AN2.
VCFG0: Lo mismo que el anterior pero para Vref+. Por defecto usaremos Vdd
(alimentacin, tpicamente 5 o 3.3V) como Vref+. Si es 1 se usa el voltaje presente
en AN3 como Vref+.
PCFG: Posiblemente no necesitaremos todos los posibles canales analgicos. Estos
bits permiten decidir cuales de los pines se dedican a canales analgicos y cuales
permanecen como canales digitales. Los valores posibles van desde 0000 (todos
analgicos) a 1111 (todos digitales).
Los canales analgicos se denotan en la documentacin como AN0, AN1, AN2, etc.
Los dispositivos con 8 canales (252/452) los reparten entre el puerto A y el E. Los
que tienen 13 canales usan tambin parte del puerto B (2520/4520). Mirar la
documentacin, porque el orden no es consecutivo. Por ejemplo, para el 4520
Can
al
Pin
AN
0
RA
0
AN
1
RA
1
AN
2
RA
2
AN
3
RA
3
AN
4
RA
5
AN
5
RE
0
AN
6
RE
1
AN
7
RE
2
AN
8
RB
2
AN
9
RB
3
AN1
0
RB1
AN1
1
RB4
AN1
2
RB0
bit 7
ADFM
bit 6
x
Format
o
No
usado
bit 5
bit 4
bit 3
ACQT
ACQT
ACQT
2
2
2
Programacin T
adquisicin
bit 2
bit 1
ADCS ADCS1
2
Reloj del ADC
bit 0
ADCS0
El caso ADFM=0 se suele usar si slo queremos los 8 bits ms significativos. En ese
caso basta hacer: res = ADRESH;
ACQT: bits para la programacin del T de adquisicin. Recordar que el tiempo de
adquisicin es el que debemos esperar despus de seleccionar (conectar) un canal al
ADC mientras se carga el condensador cuyo voltaje posteriormente mediremos. Si
escogemos 000 el usuario es responsable de la espera (de unos pocos usec,
especificada en la documentacin) entre la seleccin del canal (poner los bits CHS
adecuados en ADCON0) y el inicio del proceso de conversin (poner a 1 el bit GO de
ADCON0).
El modo manual (000) era la norma entre familias anteriores, que de hecho no
contaban con estos bits de configuracin. En la familia 2520/4520 es posible
programar dicho tiempo de espera. De esta forma tras seleccionar el canal podemos
inmediatamente lanzar la conversin (ADCON0.GO=1) y el mdulo esperara el tiempo
programado antes de iniciar la conversin. Esto permite optimizar el aprovechamiento
del ADC como veremos.
El tiempo de adquisicin es la suma de varios factores, siendo los principales la
capacidad del condensador a cargar y el tiempo que tarda el amplificador del ADC en
estabilizarse. Es importante consultar el datasheet si queremos optimizar el muestreo
porque podemos encontrarnos bastantes diferencias entre diversos modelos.
ADCS: seleccionan la velocidad del reloj del ADC como una fraccin (div = 2, 4, 8,
16, 32, 64) del reloj principal (aunque tambin es posible asociarlo a un oscilador RC
independiente). El inverso de la frecuencia del ADC determina el llamado Tad, que
viene a ser el tiempo dedicado a obtener cada bit del resultado:
F_ad = Fosc / div
Tad = 1/F_ad = div / Fosc (en microsec si Fosc est en MHz)
Obviamente deberamos escoger un divisor bajo para obtener la frecuencia ms alta
(Tad ms pequeo) posible para tardar lo menos posible en cada conversin. Sin
embargo hay una limitacin: el Tad no puede ser menor que un cierto tiempo mnimo
dependiente del modelo. Por ejemplo para la familia 252/452 se recomienda que Tad
sea como mnimo 1.6 usec. En cambio para la 2520/4520 podemos bajar a 0.75 usec.
Eso significa que si tenemos un cristal de 20 Mhz, el divisor escogido sera:
1:32 si tenemos un 452, ya que Tad = div / Fosc = 32/20 Mhz = 1.6 usec =
recomendado
1:16 si tenemos un 4520 ya que Tad = div / Fosc = 16/20 MHz = 0.8 usec > 0.75
usec recomendados
Nuestro primer programa con el ADC
El proceso a seguir para una conversin (segn se describe en la documentacin de
Microchip) es el siguiente:
1. Configurar el ADC con la asignacin de canales (PCFG), programar el reloj
del ADC (bits ADCS), seleccionar o no voltajes de referencia (VCFG), etc.
Vemos que C18 tiene casi una funcin para cada paso del proceso. Por ejemplo, el
compilador MikroC Pro es mucho ms monoltico, lo que lo hace ms simple pero
menos adaptable. En MikroC Pro, todo lo anterior se resume en una sola llamada:
res = ADCRead(0); que configura, selecciona canal 0, espera, lanza conversin y
cuando termina devuelve res.
Los parmetros que recibe OpenADC son esencialmente los valores de ADCON0,
ADCON1 y ADCON3. Como siempre la ventaja es que damos valores a dichos
registros combinando diversas banderas ms fciles de recordar. Para aquellos
dispositivos con slo disponen de ADCON0 y ADCON1 la funcin slo usa dos
parmetros en vez de tres. Con SetChanADC seleccionamos (ADC_CH0) AN0=RA0
como el pin cuyo voltaje vamos a medir.
Normalmente la configuracin del ADC se hace una sola vez y luego se hacen
sucesivas conversiones (posiblemente de diferentes canales). Podemos agrupar el
cdigo anterior en una funcin que reciba como argumento el canal a muestrear y
haga todas las etapas (aunque como veremos ms adelante, en ciertas circunstancias
es ms eficiente "romper" la funcin para no tener una funcin bloqueante):
uint16 ADC_read(uint8 ADC_channel)
{
uint16 ad_res;
SetChanADC(ADC_channel);
Delay10TCYx(5);
ConvertADC();
while(BusyADC());
ad_res = ReadADC();
return ad_res;
}
Una llamada al cdigo anterior mide el voltaje del canal deseado una sola vez. Si el
voltaje en el canal analgico est cambiando rpidamente, querremos muestrearlo
muchas veces por segundo para seguir sus cambios. Supongamos que queremos
tomar 2000 muestras por segundo. A esto es a lo que se suele denominar una
frecuencia de muestreo de 2000 Hz. Con este ritmo, deberemos lanzar tomar una
muestra cada 1/2000 seg = 0.2 msec = 200 usec/muestra.
El cdigo quedara:
TRISB=0; TRISC=0; TRISAbits.TRISA0=1;
while(1)
{
PORTCbits.RC0=1; res=ADC_read(ADC_CH0); PORTCbits.RC0=0;
PORTB=res>>2;
Delay10TCYx(250);
}
Declaramos PORTB y PORTC como salidas y RA0 como entrada. Dentro del bucle, tras
el cdigo de la conversin, simplemente esperamos 500 usec (2500 ciclos @ 20 MHz)
antes de una nueva conversin. Hemos dejado fuera la configuracin del ADC (misma
lnea de antes) y el apagado del mdulo (porque lo estamos usando continuamente).
Para ver si est funcionando hemos conectado un potencimetro en RA0 y una tira de
LEDS en PORTB, donde mostramos los 8 bits ms significativos del resultado.
Moviendo el potencimetro veremos cambiar los LEDs.
La orden de poner RC0 a 1 al arrancar la conversin y de bajarlo al terminar es para
controlar el tiempo que tardamos. Con un osciloscopio podramos ver claramente el
tiempo dedicado a la conversin como la parte en la que este alta la seal del pin
RC0. Como ya hemos hecho en otras ocasiones, incluso con un simple voltmetro
podemos estimar dicho tiempo de forma bastante aproximada. En particular para este
programa he medido un voltaje en RC0 de 0.56V. El voltaje que el mismo voltmetro
me da para Vcc es de 4.86V. El voltaje medido en RC0 dividido por 4.86 representa el
porcentaje en el que RC0 est en alto del total del periodo. Por lo tanto podemos
escribir que si t es el tiempo dedicado a la conversin entonces:
t / (t + 500)
ADCON0 |= (ch<<2); }
//SetChanADC(ADC_channel);
//ConvertADC();
//while(BusyADC());
//ad_res = ReadADC();
Si repetimos el bucle anterior con esta funcin vemos que el tiempo baja a unos 45
usec, una ganancia del 15%.
Podemos mejorar un poco ms si nos ajustamos a las especificaciones del PIC usado
(18F2520/4520). La documentacin indica que para esta familia basta un tiempo de
adquisicin (entre seleccionar canal y lanzar conversin) de unos 3 usec, lo que a 20
MHz equivale a unos 15 ciclos . Claramente los 50 ciclos (10 usec) de antes son
excesivos. Nos bastara con especificar un retrado de p.e. 20 ciclos:
uint16 ADC_read2b(uint8 ch)
{
uint16 ad_res;
select_ADC(ch);
//SetChanADC(ADC_channel);
Delay10TCYx(2);
ADCON0bits.GO=1;
while(ADCON0bits.GO);
ad_res =ADRESH; ad_res<<=8; ad_res+=ADRESL;
return ad_res;
//ConvertADC();
//while(BusyADC());
//ad_res = ReadADC();
Con esto bajamos a unos 40 usec, otro 10% adicional. Notad que dependiendo del
PIC usado este cdigo tendra que ser modificado.
Finalmente podemos usar una caracterstica adicional de la familia 18F2520/4520. El
tiempo de adquisicin puede ser preprogramado, lo que hace que podamos lanzar la
conversin nada ms seleccionar el canal. Tras poner a 1 el bit GO, el mdulo ADC no
lanza la conversin inmediatamente sino que espera el tiempo programado. El tiempo
se especifica en unidades de Tad y hay que programarlo en la configuracin
(OpenADC) del mdulo:
OpenADC(ADC_FOSC_16 & ADC_RIGHT_JUST & ADC_4_TAD,
ADC_INT_OFF & ADC_VREFPLUS_VDD & ADC_VREFMINUS_VSS, 7);
En este caso preprogramamos un delay de 4 x Tad. Como Tad = 16/20 = 0.8 usec eso
equivale a 3.2 usec, que es superior al tiempo de adquisicin mnimo recomendado
para estos PIC. La funcin de conversin queda ahora:
uint16 ADC_read2b(uint8 ch)
{
uint16 ad_res;
select_ADC(ch);
ADCON0bits.GO=1;
while(ADCON0bits.GO);
ad_res =ADRESH; ad_res<<=8; ad_res+=ADRESL;
return ad_res;
//SetChanADC(ADC_channel);
// Launch conversion without delay
//while(BusyADC());
//ad_res = ReadADC();
Como vemos no tenemos que preocuparnos del delay, ya que el mdulo lo respetar.
Con este nuevo cdigo la duracin de la llamada a la funcin es de unos 24 usec. Las
ganancias se pueden detectar con la bajada del voltaje del pin RC0 o ms
grficamente, monitorizando RC0 con el osciloscopio. En la figura se adjuntan las
capturas de la duracin de la funcin para el original y las tres modificaciones
propuestas (de arriba abajo y de izquierda a derecha). Vemos que hemos conseguido
reducir a la mitad el tiempo, pudiendo llegar hasta unas 40000 muestras/segundo (24
usec por conversion).
En otra entrada veremos como combinar el uso del ADC con interrupciones. En primer
lugar veremos como usar una interrupcin de un temporizador para lanzar la
conversin AD a intervalos exactos. En segundo lugar veremos como usar la
interrupcin del propio mdulo ADC para evitar que la funcin anterior sea
bloqueante. El uso de la interrupcin AD nos permite no tener que esperar (lnea
while(ADCON0bits.GO)) hasta que el mdulo termine: la interrupcin nos avisar
cuando el resultado este listo en ADRESH:ADRESL.
Antes de explorar esta combinacin de ADC + INTS, vamos a ver un pequeo
proyecto (brjula electrnica ) en el que usaremos lo que ya sabemos del ADC.
Consiste en leer el voltaje de dos sensores magnticos (en dos ejes perpendiculares)
y combinar su informacin para obtener una desviacin en grados respecto al norte
magntico.