Translate

lunes, 5 de enero de 2009

Sensando Temperatura con LM35DZ

Ahora que podemos mostrar datos analógicos en un LCD y enviarlos vía serie, podemos usar un sensor y cual otro podría ser si no el famosísimo LM35DZ. Este sensor se encuentra por todas partes, debido a su bajo costo y su funcionalidad.

Este es el circuito básico de conexión de LM35:


Como podemos apreciar en el circuito la salida analógica del sensor es de 10mV por cada grado centígrado que registra; por otra parte la resolución de nuestro ADC es de 10 bits y como nuestras fuentes de referencia son las misma que alimentan al PIC,  tenemos que:


  5V / 1024 = .0048 V 


De este modo nosotros tenemos una resolución de @5mV en cada paso de nuestro canal analógico. Esta resolución es más que perfecta para usar ese sensor, de tal modo que usando la lógica cada grado centígrado equivaldrá a 2 pasos de nuestro ADC.


Con los datos anteriores podemos realizar el siguiente programa:




Como podemos apreciar el programa es muy sencillo, aunque C18 soporta el uso de flotantes, la conversión a cadena de texto no se puede, y en la parte serial no soporta %f  por esta razón se trabaja directamente con enteros y se logra una conversión similar. No es exacta falla por unas decimas pero para este programa no viene ningún problema en ello.


El circuito es el mismo del programa anterior solo habrá que sustituir el potenciómetro por el sensor.


Y por último la simulación:


domingo, 4 de enero de 2009

Usando un LCD

Al programa anterior le vendría bien mostrar el valor del ADC sin usar un PC, para ello podemos usar un LCD de 16x2.


Para este fin será necesario usar la libreria xlcd.h,  esta se encarga de mostrar los textos por nosotros. Esta librería posee varias funciones, pero nosotros nos enfocaremos especifcamente en 3 de ellas: openXLCD(); putrsXLCD(); y putsXLCD;


Antes de empezar a usar esta función es necesario hacer unos cambios a la librería, pero antes debemos pasarla a nuestra carpeta de proyecto.


Lo primero sera copiar la librería que se encuentra en Directorio_de_instalación\MCC18\h\xlcd.h a nuestra carpeta en mi caso será E:\Microchip Solutions\Proyectoblog\h\xlcd.h.


Después de eso copiaremos los archivos fuente de la libreria que se encuentran en Directorio_de_instalación\MCC18\src\extended\pmc\XLCD\*.c  a la carpeta de nuestro proyecto en nuestro caso será E:\Microchip Solutions\Proyectoblog\src\XLCD\ Nos aseguramos de copiar todos los archivos *.c a nuestra carpeta de proyecto.


Esto se hace para respaldar la libreria original y realizar modificaciones sin moderación alguna en nuestro proyecto, así evitamos daños a la librería original.


Posteriormente debemos configurar nuestro proyecto para que compile desde nuestra ruta la librería recien copiada. Para ello accedemos a Menú Project--->Build Option-->Project y se abrira la siguiente ventana:



Daremos click en la opción Show directories for  y posteriormente en Include search path:



Eliminaremos el path default y añadimos uno nuevo con nuestra ruta.

Posteriormente vamos añadir las fuentes de la librería, esto lo hacemos dando click derecho sobre la carpeta de y click en add:



Y seleccionaremos todos los archivos en ..\src\XLCD\  Por último añadimos el Header xlcd.h con el mismo procedimiento.


Ahora que ya tenemos preparado el entorno de trabajo lo primero que vamos a hacer es modificar la libreria para poder usar el LCD en el PORTD a 4 bits para eso abrimos el archivo xlcd.h  dando doble click. Una vez abierto es muy sencillo ya que esta completamente comentado los modos de trabajo asi que para nuestro ejemplo el archivo queda de la siguiente forma. Ubicamos esta porcion de codigo en la librería:



En este caso la librería ya fue editada para que trabaje como deseamos, pero es tan transparente que es bastante sencillo poder modificarla para llevarla a otro puerto del PIC.


Una vez que hemos modificado nuestra librería a nuestro gusto podemos empezar a programar. La idea es mostrar un mensaje incial, despues presionar el boton y mostrar el valor del ADC tanto serialmente como en el lcd, podemos basarnos en el siguiente codigo:



Como podemos apreciar en el código las funciones de la librería xlcd.h  que mas usamos fueron: openXLCD(); putrsXLCD(); y putsXLCD;


openXLCD(); Sirve para incializar el LCD con los argumentos de 4bits y el tamaño de cada caracter. Una vez que se llama esta función es necesario añadir 3 pausas para inicializar el LCD, estas pausas estan debajo del alias de nuestro botón.


putrsXLCD(); Sirve para poner una cadena de texto desde la memoria de nuestro programa.


putsXLCD; Sirve para poner una cadena de texto desde la memoria de datos, estas dos funciones se implementaron en una funciçon que cree para poder usar el mismo comando sin atascar la memoria del PIC con la función writeLCD.


El circuito propuesto para este programa es el siguiente:



Y el video de la simulación:


sábado, 3 de enero de 2009

Leyendo un Canal AD y enviando el valor vía Serie

Es momento de hacer algo un poco mas elaborado que prender simples LEDS, así que vamos a leer una entrada analógica y el valor lo mandaremos vía serie al terminal.


Hay 3 librerías nuevas que vamos a utilizar: stdio.h, adc.h y usart.h. La librería stdio.h  nos va a servir para enviar los datos al terminal de la PC haciendo uso del comando printf


printf:

Este comando es util para enviar texto y/o numero formateados vía el hardware serial, la sintaxis es:

printf(sentencias/formato,valor de formato)


Las sentencias son las palabras o cadenas que enviaremos vía serie, el formato es opcional, pero a la ves muy útil, para utilizar el formato es necesario usar el símbolo %  para poder anexar un valor numérico después.

Por ejemplo podemos enviar la palabra hola usando el comando printf:

 

printf("hola");  


Como podrán ver el texto debe ir entrecomillado para poder enviarse, en este ejemplo solo enviamos la palabra hola, no usamos ningún formato ni nada. Para enviar un texto con un formato podemos usar el siguiente ejemplo:

  i=10;

  printf("el valor de i es: %d",i);


Como se puede apreciar, esta vez usamos un formato el cual fue %d  con esto estamos diciendo que ira un numero decimal en el lugar que corresponde a %d. Este numero decimal pertenece a i  y es declarado seguido y dentro de la instrucción printf. Tenemos que notar que a diferencia del ansi C no se usa el operado &i, ya que seria un puntero solamente se pone la variable deseada y también es necesario decir que printf no soporta el formateo de flotantes con %f como el ansi C.

La librería adc.h

Esta librería nos permitira configurar los registros del ADC sin acceder a ellos directamente, es decir, en lugar de asiganr valores individuales a los tres registros que componen la función de ADC del PIC usaremos una sola instrucción para ello.


OpenADC ();

Será quién nos permita configurar los 3 registros de nuestro de nuestro PIC, tenemos que tomar en cuenta que los argumentos de la función openadc(); cambien con respecto al PIC que usamos, es por ello que debemos revisar con calma la ayuda de esta libreria.


Para nuestro caso usamos un PIC18F4520, por lo que la función openadc(); llevará 8 argumentos. de este forma podemos construir la función de la siguiente manera:


 OpenADC ( ADC_FOSC_8 &             // Configuramos el FOSC a 8 
                      ADC_RIGHT_JUST &  // Sera 10 bits de 0-1024
                      ADC_4_TAD,                 // TAD en 4
                      ADC_CH0 &                   // Solo Canal0, RA0
                      ADC_INT_OFF &         // Sin interrupciones AD
                      ADC_VREFPLUS_VDD &   // Sin VREF+
                      ADC_VREFMINUS_VSS,   // Sin Vref- 
                      14 );                                        // Bits digitales de ADCON1



Como se puede apreciar en el codigo anterior, con los 8 argumento configuramos los 3 registros y los argumentos los podemos cambiar conforme nos convenga. Cabe destacar que el ultimo numero el 14  corresponde a los bits0-3 del registro ADCON1 donde seleccionamos el tipo de I/O de los pines para este caso solo RA0 es analógico los demás son digitales.



La Librería usart.h

Con esta librería vamos a configurar el modo de transmision serie de nuestro PIC, usaremos la función OpenUSART (); esta función esta dada por 7 argumentos, que corresponden a la configuración de nuestra transmisión, para este ejemplo buscamos una transmisión asyncrona de 8 bits sin paridad y con una velocidad de 9600KBps. De esta forma tenemos que:

OpenUSART ( USART_TX_INT_OFF &   // Sin interrupcion de TX  
                          USART_RX_INT_OFF &   // Sin interrupcion de RX  
                          USART_ASYNCH_MODE & // Modo Asincrono
                          USART_EIGHT_BIT &      // Solo 8 bits de transmision
                          USART_CONT_RX &         // Recepcion continua
                          USART_BRGH_HIGH,      // BRG HIGH
                          129 );                                     // 9600Kbps @ 20MHZ


Dependiendo de la frecuencia de nuestro crystal es como asiganaremos el ultimo valor decimal, que en este caso es 129 ya que segun la hoja del PIC como esta activado BRGH, solo es cuestion de buscar en la tabla correspondiente el valor de FOSC.

Una vez que tenemos el resumen de esas librerias podemos crear el programa. el cual cada que presionemos un boton conectado a RB0 enviara la lectura del ADC con una pausa de medio segundo. Para ello podemos basarnos en el siguiente programa:

El diagrama de conexion que vamos a usar:



La salida serie pueden usar un max232 o algunos componentes discretos lo que les convenga y funcione.

Y por útimo la simulacion del programa:

lunes, 22 de diciembre de 2008

Una secuencia para LED's

Ahora que ya sabemos hacer blink en las salidas del PIC, podemos jugar con el puerto para hacer una secuencia de luces. La idea es correr el encendido de los LED's desde RB0 a RB7 y de RB7 a RB0.

Asi que vamos a usar los operadores corriemiento a la derecha(>>) y corrimiento a la izquierda(<<), para cambiar el sentido del corrimiento vamos a utilizar un botón, por lo que necesitaremos que un PIN actúe como entrada. Cuando este activado nos entregara un 1 lógico y la secuencia recorrerá de Rb0 a RB7 y cuando este desactivado nos entregara un 0  lógico y la secuencia recorrerá de RB7 a RB0. 

Para este fin usaremos el siguiente programa:

Como podemos ver el if..else es el que hace toda la tarea dependiendo del estado del botón, dentro del if..else hay un for que nos ayuda a recorrer el BIT 8 veces que son los pines del PORTB y la pausa que nos ayuda a visualizar el corrimiento.

Tenemos que notar que para cambiar el sentido del corrimiento tendremos que esperar a que se termine la secuencia de cada for, ya que no para de forma inmediata, para que se salga de forma inmediata se tendrá que hacer con una condición y la sentencia break, pero ojala eso lo puedan implementar ustedes.

Adjunto el video de la simulación:

sábado, 20 de diciembre de 2008

Accediendo a los Bits Individuales de los Registros

Hasta ahora hemos trabajado con los registros del PIC de manera directa, asignando un valor que determina el estado de todos los bits que están dentro del él, pero también podemos asignar valores individuales a todos los registros del PIC, por ejemplo, si en vez de hacer blink en todo el PORTB solo queremos hacerlo en uno de sus bits como por ejemplo en RB3, podríamos asignar el siguiente valor:
 
 PORTB=8;
 Delay10KTCYx(250);
 PORTB=0;
 Delay10KTCYx(250);
 
Seria valido, ya que solo asignamos un 1 a RB3 y posteriormente  asignamos 0 a todo el PORTB, pero sería mas sencillo si accediéramos al bit del registro directamente, lo cual es posible. Para acceder a los bits individuales de los registros tenemos que usar la siguiente sentencia:
 
 nombre_del_registrobits.nombre_del_bit 


De tal forma que para encender el bit RB3 de PORTB tendremos que asignar la siguiente sentencia: PORTBbits.RB3, 
recordemos que C18 es sensible a las mayúsculas, así que escribir portbbits.rb3 nos generará un error en la sintaxis. Esta forma de acceder a los bits es valida para todos los registros del PIC. pero siempre hay que especificar el nombre correcto del bit del registro, para ello podemos acceder a la librería del PIC por ejemplo a la librería p18f4520.h. 


Ahora hagamos un blink en el bit RB3 del PORTB con el siguiente programa

:


Y aqui la simulación del programa anterior:




El acceder a los bits individuales es práctico, ya que nos perrmite leer el estado de un solo bit, o cambiar el estado de un solo bit sin modificar el estado del registro.

viernes, 19 de diciembre de 2008

Añadiendo librerías

El siguiente paso es añadir librerías a nuestro primer programa, como había comentado la sentencia for  del ejemplo "hola mundo" sirve para crear una pausa entre poner el PORTB a cero y poner PORTB a 1, la pausa sera lo que tarda i en recorrer 500; sin embargo no manejamos con exactitud el lapso que tarda en hacer eso.

Para poder llevar una pausa mas real, podemos hacer uso de las librerías de C18, las librerías se encuentran en el directorio de instalación del compilador y en le subdirectorio h.  Para saber de cuales librerías disponemos basta con revisar el PDF llamado MPLAB C18 C COMPILER LIBRARIES  ubicado en la subcarpeta doc  del directorio de instalación.

Para nuestro programa vamos a usar la librería delays.h  la cual posee las siguientes funciones:

Delay1TCY                       Demora 1 ciclo.
Delay10TCYx                  Demora en multiplos de 10 ciclos.
Delay100TCYx               Demora en multiplos de 100 ciclos.
Delay1KTCYx                 Demora en multiplos de 1000 ciclos.
Delay10KTCYx              Demora en multiplos de 10000 ciclos.

Exceptuando Delay1TCY, las demás funciones necesitan un argumento de tamaño byte para poder generar la pausa. En general un ciclo esta dado por 4 instrucciones del reloj, de tal forma que para sacar una pausa en términos de segundos se necesita usar la formula:

 No de ciclos = ( Pausa deseada * FOSC) / 4

Donde No de ciclos  es le argumento que necesitamos para las funciones,  Pausa deseada  es el tiempo que queremos que demore la función, FOSC  es el acrónimo a Frecuency Oscilator que esta dada por el cristal que usemos, final mente 4 son los ciclos por instrucción.

De esta manera al programa de hola mundo agreguemos un pausa de medio segundo entre los cambios de estado del PORTB.

Pausa deseada = 0.5 segundos

FOSC = 20 MHz

  No de ciclos = ( 0.5 * 20,000,000 ) / 4

 No de ciclos =  2, 500,000

Ya que los argumentos de las funciones no deben sobrepasar un byte (255) el valor máximo que podemos meter para nuestra pausa es 250 así que 250 * 10,000 = 2,500,000. De esta forma la función que nos permitirá obtener una pausa de medio segundo será Delay10KTCYx. Solo tenemos que modificar el código anterior para obtener un  blink  en todo el PORTB cada medio segundo. 

Y aquí la simulación del programa

Optimizando

Se dice optimizar cuando se trata de hacer las mismas cosas, con los menores recursos posibles, para nuestro caso sería con el menor código posible. El código anterior lo podemos optimizar de la siguiente manera:

 

Como podemos ver en el código anterior eliminamos las sentencias:

   PORTB=0;

   Delay10KTCYx(250);

Y cambiamos la sentencia:

  PORTB=255;  por la sentencia

  PORTB ^= 255;

Recordemos que el operador XOR (^) regresa 1 si los bits son complementarios de lo contrario regresa cero y al hacer 255 xor 255 regresara cero, así podemos invertir el estado de 255 a 0 en una sola linea. y el resultado final será el mismo.

Analicemos el programa "hola mundo"

Bien,ya que hicimos nuestro primer programa vamos a describirlo linea a linea para que sea mas claro lo que hace cada comando.

Primero tenemos esta sentencia #include < p18f4520.h >.  Estamos haciendo uso de el operador de preprocesado (#) seguido de la palabra reservada include, con esto estamos indicando al compilador que durante la compilación incluya el archivo p18f4520.h, este archivo contiene las direcciones de todos los registros del PIC, así como sus bits individuales y su configuración. Es por esta razón que podemos usar directamente los nombres de los registros como PORTB, TRISB, ADCON0, etc. En dado caso que no insertaramos la librería del PIC, tendriamos que definir cada registro del PIC con el operador de preprocesado (#) y la palabra reservada define seguida del nombre y dirección del registro a usar.

Posteriormente tenemos las siguientes sentencias:

#pragma config OSC = HS, FCMEN = OFF, IESO = OFF 
#pragma config PWRT = OFF,BOREN = OFF,BORV = 0 
#pragma config WDT = OFF, WDTPS = 32768 
#pragma config MCLRE = ON, LPT1OSC = OFF,PBADEN = OFF, CCP2MX = PORTC

Como veran hacemos uso del operador  de preprocesado(#) seguido de la palabra reservada pragma y config  estas sentencias las usamos para configurar parte de configH y configL que es el registro del PIC donde declaramos el tipo de oscilador y otras funciones especiales, estos regisrtros varian dependiendo cada PIC, para este ejemplo configuramos un Oscilador externo en High Speed, con la sentecia OSC=HS, en las funciones especiales como Power on reset, brown out reset Bor, FCMEN IESO LPT, etc. Las declaramos como off,  es decir, que no las vamnos a usar. La función de Memory clear la activamos para poder hacer un reset fisico, MCLRE=ON.

Posteriormente tenemos la sentencia: unsigned int i; Con la cual estamos declarando una variable global, que pdemos usar entodo nuestro programa, de tipo entero sin signo que va de 0 a 65535.

Seguido de la declaracion de la variable i tenemos la siguiente sentecia:

void main(void)
{ 

Con la cual estamos dando inicio al programa, seguido del inicio del programa tenemos:

  TRISB = 0;          //  declaramos el puerto B como salida
 ADCON0 = 0;       //   sin AD canal 0 por defecto
 ADCON1 = 15;     //   Todas las salidas digitales sin Vrefs
 ADCON2 = 0;      //   8 bits no importa TAD ni FOSC
 PORTB = 0;        //  PORTB apagado

Estas sentencias fueron explicadas anteriormente y su comentario es muy claro, solo que hay que resaltar algo, PORTB en realidad es un alias de LATB en los PIC18F's los puertos son conocidos como LATx y escribir PORTx es una alias, asi que es igual escribir PORTB=0; que LATB=0;

Finalmente construimos el programa con:

    while(1)                                     // creamos un ciclo infinito
   {                                                    // abrimos el cilco infinito
     PORTB=255;                        // encendemos el Todo el PORTB
    for(i=0; i==500; i++);      // creamos una especie de pausa
    PORTB=0;                            // apagamos el puerto para hacer blink
   }                                                 // cerramos el ciclo infinito
 }                                                  // cerramos la funcion main
  

De igual manera es muy transparante esa porcion de código,  solo que me gustaria hacer énfasis en la parte del for, en este caso se uso la variable i dentro del for para ir de 0 a 500 como no tiene sentencias internas la estructuta for se le pone al final la terminación (;), ahora bien esta función lo que hace es un pausa muy arcaica es decir no es propiamente una pausa, si no un intervalo de ejecucion entre lo que termina de llegar i a 500 y la siguiente instrucción. Para usar una pausa formalmente necesitamos añadir una libreria adicional.