domingo, 26 de agosto de 2012

Uso de entrada analógica y manejo de display alfanumérico

En esta entrega enseñaré 2 cosas:
1. Leer un valor por alguna entrada analógica del PIC.
2. Manejo de un display alfanumérico con PIC.

Lectura de un valor analogico
Esta característica es muy importante a la hora de trabajar con sensores, debido a que estos entregan valores de tensión para reflejar la variable física que esta midiendo.
En estas entradas se pueden sensar valores de 0 a 5 V y se pueden representar(según el PIC) con diferentes resoluciones, como por ejemplo 256 valores, 1024(revisar datasheet).
Así si trabajamos con una resolución de 256 tendremos para 0V una salida de 0 y para los 5V veremos el numero 255, lo mismo con otras resoluciones y valores intermedios.

Para configurar las entradas analógicas debemos ir a Analog en el wizard y seleccionar que entradas ocuparemos para nuestros propósitos, por ejemplo yo configurare A0, A1 y A3.

 setup_adc_ports(AN0_AN1_AN3);

Y al lado en Units una resolucion de 0-1023(1024 valores).

Display alfanumerico
Los displays son muy usados para permitir al usuario leer de una forma facil alguna variable, mensaje, dato, etc.
Los hay en varios modelos y caracteristicas donde las principales son el numero de filas y columnas que tienen disponible para desplegar mensajes. Estos se expresan como una matriz donde por ejemplo si el display puede desplegar 16 caracteres en dos filas se dice que es un display alfanumerico de 16x2.
Configurarlo en CCS es muy fácil y para eso en el mismo wizard nos dirigimos a Drivers y seleccionamos LCD driver. Las conexiones las podemos ver en I/O Pins pero yo las expondré en los ejemplos descargables.


Ya con el código hecho por el wizard nos queda leer el valor analógico y desplegar caracteres en el display. Expondré el código en el cual están comentado todas las lineas importantes.

______________________________________________________

#include <16F873A.h>
#device adc=10 //resolucion de 1024 valores

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES HS                       //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES RESERVED                 //Used to set the reserved FUSE bits

#use delay(clock=20000000) //oscilador externo de 20MHz
#include <LCD.C>
float a=0; //definimos la variable "a"

void main()
{

   setup_adc_ports(AN0_AN1_AN3); //especifica que canales son configurados como analogicos
   setup_adc(ADC_CLOCK_DIV_2);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   lcd_init(); //se inicializa el display

   while(true){
   set_adc_channel(0); //especifica de que canal se leera el valor analogico
   a=read_adc(); //se lee el valor analogico y se guarda digitalmente en la variable "a"
   printf(lcd_putc,"\f"); //limpiamos el display alfanumerico
   printf(lcd_putc,"Valor Digital"); //en la primera linea aparece el mensaje "Valor Digital"
   lcd_gotoxy(1,2); //saltamos al primer cuadro de la segunda linea del display
   printf(lcd_putc,"%f",a); //imprimimos el valor digital "a" en la segunda linea
   delay_ms(100); //esperamos un tiempo de 100ms para esperar a la siguiente conversion

   }   

}


______________________________________________________________


sábado, 25 de agosto de 2012

Interrupción Timer 2

El Timer 2 es un reloj interno del PIC de 8 bits bastante preciso y con el cual podemos temporizar con mas o menos exactitud rutinas que necesitan de tiempo. Las bases teóricas están expuesta en los datasheet o fácilmente en google. Como el objetivo de este blog es ser practico expondré como configurar correctamente esta interrupción.

El wizard del C compiler hace bastante del trabajo de codigo y se expone a continuacion:
Primero destacar que ahora trabajaremos con un PIC mas poderoso, el cual es el modelo 16F873A así que deben seleccionarlo en Device en las configuraciones generales. Lo haremos funcionar con un oscilador externo de 4Mhz el cual mostrare como conectar con el esquema en Proteus.
En Communications desactivamos Use RS-232 y en Timers dejamos Enabled Timer2, no se preocupen de las otras opciones que aparecen en Timer2 porque lo configuraremos mediante una formula.
Luego en Interrupts activamos Timer 2 Overflow .
El codigo quedará asi(con los FUSES incluidos en el archivo .c):
______________________________________________________________________

#include <16F873A.h>

#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES XT                     //Crystal osc <= 4mhz for PCM/PCH , 3mhz to 10 mhz for PCD
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES RESERVED                 //Used to set the reserved FUSE bits

#use delay(clock=4000000)
//------------------------Aqui esta la interrupción del timer 2-------------------
#int_TIMER2
void  TIMER2_isr(void) 
{

}
//---------------------------------------------------------------------------------------


void main()
{

   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_CLOCK_DIV_2);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_1,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);

   // TODO: USER CODE!!

}
____________________________________________________________________

Configurando el tiempo de desborde(Overflow) para que se ejecute el codigo de la interrupcion:

Nos interesa esta parte del codigo para configurarlo:
setup_timer_2(T2_DIV_BY_1,0,1)

El primer parametro es el prescaler y puede ser 1,4 o 16 para este PIC y tiene como objetivo dividir la frecuencia del oscilador.
El segundo es el registro PR2 que indica el momento de desborde del timer, es un numero que varia de 0 a 255.
El tercer parametro es el postscaler que el cual indica cuantas veces se debe desbordar el timer para ejecutar la interrupcion y ejecutar el codigo correspondiente para posteriormente resetear la cuenta, es un numero de 1 a 16.

Resumiendo las configuraciones del Timer 2:
1. Se divide la frecuencia mediante el prescaler.
2. Se establece un desborde(overflow) cuando se llena la cuenta del registro PR2.
3. El postscaler nos indica cuantas veces debe suceder el desborde para que se ejecute la interrupción.

La formula para calcular el valor de uno de los parametros del Timer 2 es:

PR2 = [([Tiempodeseado/(4/fosc)])/(Prescaler*Postscaler)]-1

Recuerden que los valores maximos para prescaler es 16, para PR2 es 255 y para postscaler 16.

En nuestro caso con un oscilador de 4 Mhz el tiempo maximo que le podemos asignar al Timer 2 es de 65.536ms.
Si pusieramos un oscilador de 20Mhz nuestro tiempo maximo de temporizacion sería 13.107ms, etc.


Ejemplo:
Con las configuraciones del PIC dadas con anterioridad se desea configurar el Timer 2 para que ejecute una interrupción cada 50ms.

Solucion:

Como 50ms esta cerca de la temporizacion maxima que es 65.536ms ocupamos los valores maximos permitidos para prescaler y postscaler. Así mediante la formula calculamos PR2:

PR2 = [([50ms/(4/4E6)])/16*16)]-1

PR2=194.3125 ----> Como debe ser un valor entero entonces lo aproximamos a 195(mejor que sobre a que falte) lo que nos da una temporizacion exacta de 50.176ms(un valor muy muy cercano!)

Por lo tanto el Timer 2 nos queda:

setup_timer_2(T2_DIV_BY_16,195,16);

Aca dejo el codigo fuente y la simulacion en proteus.






viernes, 3 de agosto de 2012

Interrupcion en RB0

En la pantalla para crear nuestro proyecto configuramos tal cual nuestro PIC como en los ejemplos anteriores. En el ejemplo usare el oscilador RC del PIC16F628A de 4MHz, desactivare las comunicaciones por RS232(ya hablaremos de eso mas adelante), y setearé la interrupcion de RB0.
Luego le damos a aceptar y nos generará el siguiente codigo. Recuerden el truco de los FUSES para que no tengan problemas al compilar en otros computadores.

-----------------------------------------------------------------------------------------
#include <16F628A.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC                    //Internal RC Osc
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //No brownout reset
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES RESERVED                 //Used to set the reserved FUSE bits

#use delay(clock=4000000)

#int_EXT
void  EXT_isr(void) 
{
//Aqui va el codigo cuando se genere una pulsacion en RB0
}



void main()
{

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
//Setup_Oscillator parameter not selected from Intr Oscillator Config tab

   // TODO: USER CODE!!

}

--------------------------------------------------------------------------------

En este ejemplo pondremos un pulsador en RB0 y al pulsarlo sumara +1 al conteo, cuando llegue a 5 cambiará de estado un led conectado en RA0.

Aqui ocuparemos la condicion if la cual es igual que en C.

if(condicion){

}else{

}

Y la instrucción output_toggle(pin_xy) para cambiar el estado de algun pin y del puerto x.
Con esto completamos nuestro codigo así.

-----------------------------------------------------------------------------------------
#include <16F628A.h>

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES INTRC                    //Internal RC Osc
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NOBROWNOUT               //No brownout reset
#FUSES MCLR                     //Master Clear pin enabled
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES RESERVED                 //Used to set the reserved FUSE bits

#use delay(clock=4000000)

int conteo=0; //se declara conteo como variable entera
#int_EXT
void  EXT_isr(void) 
{
conteo++; //sumamos +1 a la variable conteo
delay_ms(200); //tiempo para que el operador suelte el pulsador
}



void main()
{

   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);
//Setup_Oscillator parameter not selected from Intr Oscillator Config tab

while(true){
if(conteo==5){  //preguntamos si conteo es 5
output_toggle(pin_a0); //cambiamos de estado el pin a0 si conteo es 5
conteo=0; //reseteamos la cuenta
}
}

}

--------------------------------------------------------------------------------

La implementacion en Proteus quedará asi:





Interrupciones

Las interrupciones consisten en rutinas que se ejecutan fuera del programa principal y que tiene por objetivo ser atendidos de forma inmediata interrumpiendo la rutina principal para después reanudarla.
Un ejemplo para entenderlo sería:
Estar almorzando(programa principal) y que en un momento indefinido suene el teléfono, la reacción será ir a contestar(interrupción) y luego seguir almorzando.
Acá veremos las siguientes interrupciones:
  • Interrupcion en RB0
  • Interrupcion del Timer 2 (es un timer que tiene bases de tiempo bastante precisas)
En la siguiente entrega se verá un ejemplo de interrupcion en RB0.