compilador picc hitech

Upload: andres-raul-bruno-saravia

Post on 12-Oct-2015

289 views

Category:

Documents


14 download

TRANSCRIPT

  • 1

    Programacin de Microcontroladores PIC con PICC

    de HITECH-Microchip

    Por Andrs Ral Bruno Saravia Certified Trainer

  • 2

    Introduccin general Desde el ao 2009 Microchip compr a la compaa HITECH, la cual desarrollaba compiladores de lenguaje C para microcontroladores PIC. Como consecuencia, actualmente Microchip ofrece los compiladores de Hitech en versin gratuita instalable junto con el MPLAB. El compilador de Hitech ha sido modificado por Microchip para que est totalmente integrado a su plataforma del MPLAB, y tiene todos los recursos y formato que el resto de los compiladores de la familia de Microchip (MPLAB C18 y MPLAB C30). El compilador se caracteriza por ser ANSI C, y si bien no es poderoso en cuanto a funciones pre-hechas para que el usuario deba trabajar menos, como el PCW de CCS, el PICC, le permite una programacin totalmente transparente, permitindole al programador estndar de computadoras poder migrar fcilmente a programar microcontroladores PIC, o al programador Assembler, poder seguir trabajando como lo hacia pero de forma mucho mas simple. La versin que uno instala, al instalar el MPLAB, es la versin LITE, la cual es la versin gratuita sin limitaciones ofrecida por Microchip para el desarrollador. Microchip ofrece sus compiladores en 3 modos:

    Versin Standar: dura 45 das y tiene una perfomance media, la relacin de compresin es aceptable (paga licencia luego de los 45 das)

    Versin PRO: dura 45 das y tiene una altsima perfomance con la mayor relacin de compresin de cdigo (paga licencia luego de los 45 das)

    Versin Lite: duracin ilimitada, sin restricciones de ningn tipo, sin embargo su relacin de compresin es la mitad que la versin PRO, es decir que el mismo cdigo compilado en la versin Lite ocupa, en la memoria de programa, el doble de lo que ocupa la versin PRO.

    Otra caracterstica importante es que al ser de Microchip, la actualizacin es constante, y la documentacin es muy completa. El compilador C de HI-TECH se caracteriza por su sencillez, es muy sencillo migrar del lenguaje Assembler al Lenguaje C. El programador puede sentir una gran comodidad al programar cdigo encontrando la simplificacin de muchas estructuras que antes deban realizarse por medio de extensas rutinas de Assembler, y en ningn momento siente la falta de control sobre el hardware ya que puede acceder a cualquier BIT y registro SFR por medio de su etiqueta como figura en el DATA SHEET. Son todas estas las razones que nos llevan a recomendar ste compilador y no otro, a la hora de querer migrar a un lenguaje de alto nivel. Andrs Ral Bruno Saravia RTC Argentina Director

  • 3

    Comenzando desde el Principio El Lenguaje de Programacin C, es un lenguaje que rene todas las caractersticas que presentan los lenguajes de alto nivel y los de bajo nivel, uno puede tener control de su hardware a nivel BIT y al mismo tiempo tambin puede realizar cualquier tipo de operacin matemtica como si estuviera delante de una calculadora cientfica. Esto hace que hoy en da se lo considere un lenguaje de nivel intermedio. El compilador PICC de HITECH-Microchip (que en adelante simplemente lo llamaremos PICC), utiliza todas las etiquetas originales de los DATA SHEET, para hacer referencia a los BITs y los SFRs de los MCU. De esta forma no debe uno estar recordando formas especiales para configurar los puertos I/O o para enviar datos a los mismos. Tampoco tiene implementadas funciones para controlar su hardware, lo cual tambin simplifica lo que debe recordar el programador. Simplemente, el programador debe tener presente las palabras claves del lenguaje C y conocer el funcionamiento del HARDWARE del MCU. El compilador trae una extensa librera de funciones ANSI C, adaptadas a la realidad del MCU, que le permite al programador realizar cualquier conversin de datos que requiera, o procesamiento de funciones matemticas. Es como sentarse a programar delante de una computadora con el compilador Borland C. Por ello es tan sencillo para los programadores ANSI C utilizar el compilador PICC. Vamos a comenzar por tanto a crear nuestro primer programa en Lenguaje C para PICC. Lo primero que haremos ser ejecutar el MPLAB, ya que el compilador PICC esta integrado al mismo y se instala en el proceso de instalacin del MPLAB. Una vez ejecutado el MPLAB aparecer la siguiente pantalla:

  • 4

    En la parte superior de la pantalla se encuentra la barra de menes desde la cual podremos acceder a crear nuestro proyecto haciendo clic con el Mouse sobre el men Project:

    Se desplegar un men mediante el cual podemos crear nuestro nuevo proyecto. Si bien es posible crearlo manualmente, ejecutando la opcin New, es mejor realizarlo mediante la opcin Project Wizard, ya que mediante la misma la creacin del proyecto se realiza de forma guiada por el asistente de proyectos. Por lo tanto haremos click con el Mouse sobre la opcin Project Wizard La primer ventana que nos aparecer ser la de bienvenida:

    Ahora haremos click con el Mouse sobre el botn Siguiente y nos aparecer la ventana del primer paso, el cual consiste en seleccionar el tipo de MCU PIC que

  • 5

    queremos usar en nuestra aplicacin. Para ello bastar con hacer click con el Mouse sobre el men desplegable:

    Para nuestro primer proyecto seleccionaremos el PIC16F887, y Lugo haremos click con el Mouse sobre el botn siguiente. A continuacin, aparecer la ventana del segundo paso, el cual consiste en seleccionar el Toolsuite o herramienta de programacin:

  • 6

    El Toolsuite que usaremos ser el HI-TECH Universal ToolSuite , el cual es seleccionado haciendo click con el Mouse sobre el men desplegable.

    Debe verificarse que en la ventana Toolsuite Contents, el nombre del compilador no aparezca con una cruz roja, ya que si esto es as, nos estar indicando que no ha encontrado al compilador. En ese caso habr que marcar la ruta de acceso manualmente. Si todo anduvo bien esta es la ventana que tendra que aparecerles:

    Normalmente la Location es la que les aparece en la figura, si han instalado el MPLAB en el disco C, lo cual es recomendable, para evitar problemas con el compilador ya que a veces, dependiendo la versin de Windows que se tenga instalado (tipo SP2, o SP3) , no encuentra el camino para acceder al compilador o a las libreras. Continuando con la generacin de nuestro primer proyecto, haremos click con el Mouse sobre el botn Siguiente. Y llegamos al paso 3, donde el asistente nos pide crear un nuevo archivo de proyecto:

    Cargaremos en la ventana el nombre de nuestro proyecto, dentro de la carpeta que generemos para contener el mismo. Es importante que el camino de acceso hacia el archivo del proyecto sea corto, menor a 62 caracteres, de otra forma se

    Nota: Normalmente el ToolSuite que aparecer en el segundo paso ser el

    ltimo que se haya usado. Cuando recin se ha instalado el MPLAB, el

    ToolSuite ser el Microchip MPASM

  • 7

    generar un error en el proceso de compilacin. Para evitar el inconveniente, es aconsejable generar la carpeta que contendr nuestro proyecto, en el directorio raz, que en nuestro caso es C:\. Por una cuestin de orden, aconsejo crear una carpeta denominada Proyectos_HITECH, dentro de la cual crearemos otra carpeta dentro de la cual residir nuestro proyecto y que llamaremos Proyecto1. De esta forma escribiremos en la ventana la ruta completa mas el nombre de nuestro proyecto que en este caso se llamar LED_Titila: C:\Proyectos_HITECH\Proyecto1\LED_Titila Finalmente la pantalla de su mquina quedar de la siguiente forma:

    A continuacin hacemos click con el Mouse sobre el Botn Siguiente y se desplegar una ventana con el mensaje que nos advierte que esas carpetas no existen, y si queremos crearlas, pulsaremos YES, con lo cual pasar a deplegarse la ventana del cuarto paso:

  • 8

    En este paso, el asistente de generacin de proyecto, nos pregunta si queremos aadir algn archivo existente a nuestro proyecto, lo cual puede ser til para copiar librerias que tengamos armadas para otros proyectos, o incluir un archivo .C que queramos adjuntar al proyecto. En este ejemplo, no usaremos este paso y por tanto lo saltaremos, simplemente haciendo click con el Mouse sobre el Botn Siguiente. Ingresamos as al paso final, donde el asistente de proyecto nos muestra en la ventana final un Sumario de todo nuestro proyecto:

    Podemos ver dentro del marco Project Parameters los parmetros principales del proyecto: Tipo de dispositivo, el Toolsuite seleccionado y el lugar donde reside todo el proyecto. Si todo es correcto, simplemente hacemos click con el Mouse en el botn Finalizar. A continuacin se cierra el asistente de proyectos y se volvemos al ambiente de trabajo del MPLAB, donde podemos ver dentro del espacio de trabajo (workspace) las carpetas que formaran parte del proyecto que hemos creado. Estas carpetas contienen a su vez todos los archivos que se generan en el proceso de compilacin de todo el proyecto. La generacin se realiza de forma automtica, el programador simplemente debe pulsar el botn para la compilacin de todo el proyecto. Es posible compilar varios archivos de C de esta forma podemos crear varias libreras para ser compiladas con cualquier aplicacin que realicemos. Esta caracterstica no existe en todos los compiladores, solo en los que son de

  • 9

    Microchip. A esto se lo denomina soporte multi-fuente, ya que es posible compilar varios archivos fuentes.

    Crearemos ahora nuestro programa C o archivo fuente, denominado as porque ser el que abastece del cdigo que luego se compilar. Para crear este archivo pulsaremos sobre el cono New File: Esta accin abrir otra ventana sobre el espacio de trabajo:

    Ahora escribiremos nuestro primer cdigo de C. la idea es crear un programa sencillo para habituarnos al manejo del MPLAB, y a compilar el mismo:

  • 10

    Una vez escrito nuestro primer programa, lo compilaremos para generar el archivo que luego se grabar en la memoria de programa del microcontrolador. Para compilar pulsaremos el cono de Build:

    El programa accionar el proceso de compilado y si todo funcion bien se desplegar la ventana de salida con el siguiente resultado:

  • 11

    En la figura podemos apreciar el resultado de la compilacin, donde se nos indica la cantidad de memoria de programa disponible y usada, la cantidad de memoria de datos disponible y usada, la memoria de datos EEPROM, bits de configuracin y el cdigo de identificacin. De todo esto lo mas importante es la memoria de programa y de datos usada por nuestra aplicacin: 46 palabras de programa y 5 bytes de datos. Hasta aqu hemos visto como crear nuestro primer proyecto, y como compilarlo, es decir traducirlo de nuestro lenguaje C al cdigo de Mquina, el cual finalmente ser grabado dentro del microcontrolador. El proceso de compilado consiste en transformar nuestro cdigo escrito en lenguaje C y generar el cdigo de mquina. En este proceso el compilador genera los siguientes archivos:

    De todos estos archivos los 3 ms importantes son: Led_Titla.HEX, el cul contiene el programa traducido en cdigo Hexadecimal (.HEX) y que ser utilizado en el proceso de programacin del microcontrolador; el archivo Led_Titila.COF, el cual ser usado en el proceso de simulacin (por los programas MPSIM y ISIS de Proteus), y nuestro programa fuente Led_Titila.C el cual contiene nuestro programa .C . El resto de los archivos son resultado del proceso de creacin del proyecto y del proceso de compilado del mismo. Todos estos pasos que dimos, se realizarn cada vez que queramos crear un nuevo programa, y por tanto se repetirn en todas las aplicaciones que veremos en nuestro libro. Ahora deberemos concentrarnos en el Lenguaje C, para luego ver como programar nuestro microcontrolador (MCU).

  • 12

    Anlisis de un programa en C con PICC Elementos de un programa en C: Bueno ha llegado el momento de comenzar a ver el lenguaje, lo primero que veremos ser que elementos forman parte de un programa en lenguaje C hecho para el compilador PICC, con un ejemplo de cdigo:

    Encontramos: #include : esta lnea nos permite incluir dentro de nuestro cdigo el archivo htc.h. La instruccin #include es una instruccin dirigida al compilador, y por tanto se las denominan directivas. En este caso la directiva permite incluir un archivo dentro del cdigo que hemos creado. En el proceso de compilado, el compilador al encontrar esta directiva, buscar el archivo que se especifique y lo incluir dentro del proceso de compilacin. El archivo a buscar se coloca entre (parntesis angulares) cuando este se encuentra dentro de la carpeta include, de la carpeta 9.81 dentro de la carpeta PICC, dentro de la carpeta Archivos de Programa. El archivo htc.h es esencial pues dentro de l se encuentran las llamadas a todos los archivos de cabecera de MCU PIC, y que tienen en su interior todas las definiciones de los registros SFRs y BITs. Sin este archivo no podemos crear un programa que funcione, pues el compilador no tiene la referencia de las etiquetas usadas para mencionar los SFRs y los BITs. #include : esta lnea nos permite incluir dentro de nuestro cdigo el archivo stdio.h, el que permite definir todas las estructuras del lenguaje para el manejo de las I/O. #define _XTAL_FREQ 4000000: esta directiva le especifica al compilador que la frecuencia de clock del cristal externo es de 4Mhz, esto es necesario para que luego el compilador pueda calcular las funciones de delay. __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF): esta directiva le dice al compilador como se setean los fusibles de configuracin; en este caso se a

  • 13

    configurado que el PIC funciona con un oscilador a cristal del tipo estandar, y que tanto el watchdog como la proteccin de cdigo estn desactivadas. El seteo de los fusibles de configuracin en la versin 9.81 han sido modificados por Microchip, para que esta sea similar a la del C18. Hasta la versin 9.80 las etiquetas para el seteo eran diferentes, por ejemplo para configurar los fusibles del ejemplo anterior: __CONFIG(XT & UNPROTECT & WDTDIS) Las etiquetas deben ser consultadas en los archivos de cabecera correspondientes a cada microcontrolador y que se encuentran en la carpeta incluye que se encuentra en la siguiente ruta de acceso: C:\Archivos de programa\HI-TECH Software\PICC\9.81 Luego de los fusibles de configuracin encontramos la cabecera o comienzo del programa: void main (void) a esta cabecera se la conoce como declaracin de funcin, en este caso es la declaracin de la funcin principal. Todos los programas en lenguaje C estn formados por funciones, o subrutinas; en el lenguaje C a las rutinas, o conjuntos de instrucciones, se las denomina funciones. En todo programa existe una rutina o funcin principal, la cual puede a su vez llamar a otras funciones o rutinas que auxilian en su funcionamiento. La funcin principal en C se denomina main. Mientras que las funciones auxiliares pueden tener cualquier nombre, la principal debe llamarse main, y debe estar acompaada por las palabras void. Por ahora es todo lo que necesitamos saber sobre esta. Luego profundizaremos especialmente sobre el conocimiento de las funciones. Toda funcin o rutina, es decir programa, esta constituido por ordenes o instrucciones, en lenguaje C limitamos donde comienza y termina cada funcin, a esto se lo denomina bloque de instrucciones. Para delimitar a las funciones usamos los siguientes smbolos: con { abrimos la funcin y la cerramos con }. TRISB=0 : esta sentencia nos permite cargar el registro de configuracin del PORTB del microcontrolador, en este caso al cargarlo con cero, todos los bits que conforman al puerto funcionarn como puertos de salida (RB0 a RB7). While(1) : esta sentencia crea un bloque en el cual la lista de instrucciones se repetir de forma infinita, ejecutndose secuencialmente, a esta setencia se la conoce como bucle condicional. La condicin se encierra entre parntesis. Si la condicin es (1) , el bucle se ejecutar por siempre, crendose as un bucle infinito.

  • 14

    El bloque de sentencias que se repetir mientras lo determine la condicin, est limitado por las llaves { }. RB0=1 : pone un estado lgico 1 en el puerto RB0 __delay_ms(500): es una llamada a una funcin auxiliar, llamada __delay_ms la cul est implcita en el propio compilador, esto significa que no debemos cargar ninguna librera extra al principio del programa. El nmero 500, el cual se encuentra entre parntesis es el valor que le pasamos a la fundn para que la misma lo procese; de esta forma la funcin __delay_ms(500), crear un tiempo de espera de unos 500 ms con una tolerancia del 1% de error. Este tiempo le permite al usuario ver el puerto encendido por 500 milisegundos RB0=0 : pone un estado lgico 0 en el puerto RB0 __delay_ms(500): nuevamente se realiza la llama a la funcin de tiempo, pero ahora para que el usuario pueda ver el puerto apagado por 500 milisegundos. De esta forma si hemos colocado un LED en el puerto RB0, lo veremos titilar con lapsos de 500 milisegundos. Este programita sencillo, nos permite aprender de forma prctica cuales son los elementos que uno encuentra en un programa en C.

  • 15

    Fundamentos del Lenguaje C orientado a los MCU PIC Es particularmente importante comprender los diferentes tipos de datos y las reglas que rigen su operacin. La idea directriz de C es la definicin de procedimientos (funciones), que en principio devuelven un valor. Lo que para nosotros es -conceptualmente- el programa principal, tambin es en C una funcin (la ms externa). El Lenguaje C est diseado con vistas a la compatibilidad. En este sentido, todas las definiciones que puedan hacerse no sern concretas, pues son adaptables de acuerdo con la implementacin. Un entero, por ejemplo, es una entidad con ciertas caractersticas generales, pero su implementacin diferir en distintos microcontroladores. El lenguaje C maneja los datos en forma de variables y constantes. Las variables, simbolizadas mediante alfanumricos (cuyas reglas de construccin veremos ms adelante), presentan caractersticas que ser muy importante considerar: - Tipo de dato: cada variable (tambin las constantes) est caracterizada por el tipo de dato que representa. - Visibilidad: en un programa C, cada variable tiene un rango de visibilidad (procedimientos en los que es reconocida), que depende de cmo se la haya declarado. - Existencia: relacionado con la anterior caracterstica, es posible que el contenido de una variable perdure, o que se pierda, por ejemplo, al terminarse un procedimiento.

    Set de caracteres C emplea dos sets (conjuntos) de caracteres:

    El primero de ellos incluye todos los caracteres que tienen algn significado para el compilador.

    El segundo incluye todos los caracteres representables. C acepta slo ciertos caracteres como significativos. Sin embargo, otros caracteres pueden formar parte de expresiones literales (constantes literales, nombres de archivo, etc.) que no sern analizadas por C. Caracteres interpretados por C Los caracteres a los que C asigna especial significado se pueden clasificar en alfanumricos y signos especiales. Los caracteres alfanumricos incluyen las letras (alfabeto ingls, de A a Z), maysculas y minsculas, los dgitos, y el guin bajo (underscore: _).

  • 16

    En todos los casos, las maysculas son consideradas distintas de las minsculas. Toda cadena alfanumrica con significacin en C est compuesta exclusivamente por estos caracteres. Los signos especiales son los listados en la siguiente figura . Ellos se emplean como delimitadores, operadores, o signos especiales.

    Archivos De Cabecera Los archivos de cabecera son archivos cuya extensin es .h, (ejemplo stdio.h), y en principio uno incluye en su programa aquellos archivos necesario. Un archivo de cabecera contiene declaraciones de variables y constantes, prototipos de funciones, macros, etc. El lenguaje C ofrece una cantidad de importante de estos archivos para que uno pueda escribir los programas y hacer uso de diversas funciones que permitan, por ejemplo, ingresar datos por la USART, utilizar funciones matemticas, utilizar funciones para manipular cadenas, funciones para convertir datos y muchos etc. El programa ms sencillo de escribir en C necesita la inclusin de este archivo, ya que aqu se encuentran las funciones bsicas de entrada/ salida, (en el futuro E/S), (stdio significa Standar Input Output):

    #include Tambin es esencial incluir un archivo fundamental conocido como archivo de cabecera del procesador:

    #include Este archivo se encarga de incluir el archivo de definiciones de etiquetas de los registros de funciones especiales (los que se encargan de manejar el hardware del microcontrolador) segn el procesador que definimos al crear el proyecto. En estos archivos no se encuentran implementadas las funciones, sino solo sus cabeceras, es decir se definen los prototipos de dichas funciones.

  • 17

    Los archivos de cabecera se incluyen de la siguiente forma:

    #include Se utilizan los smbolos < > cuando el archivo de cabecera se encuentra en el directorio por defecto del lenguaje C instalado, el directorio por defecto para HITECH es:

    C:\Archivos de programa\HI-TECH Software\PICC\9.81\include

    Ahora si usted creo el archivo .h, (uno puede crear sus propios archivos de cabecera) lo incluye de la siguiente forma:

    #include miarchivo.h Comentarios En C tradicional los comentarios se colocan entre /* y */, este generalmente es usado para comentar un bloque o varias lneas de cdigo, pero tambin se puede usar para comentarios // que permiten comentarios en una sola lnea.

    /* Esto es un comentario Usado para comentar varias

    Lineas de cdigo como en este Ejemplo */

    // y esto tambin, pero se usa para una sola lnea

    Ejemplo de Aplicacin:

    Puntos Y Comas / Llaves Las sentencias ejecutables en C terminan en punto y coma ; Las llaves agrupan un conjunto de sentencias ejecutables en una misma funcin o en alguna estructura de iteracin o comparacin (ver ms adelante).

  • 18

    Los Identificadores Los identificaremos se utilizan para identificar, (valga la redundancia): variables, constantes, funciones, etc, bsicamente se trata de los nombres Deben comenzar con una letra y tener una longitud mxima de 32 caracteres. Slo pueden contener letras y nmeros, pero no caracteres especiales, salvo el guin bajo, (underscore). Tipos De Datos Bsicos El estndar Ansi define un conjunto de tipos bsicos y su tamao mnimo. En distintas implementaciones del lenguaje C estos tamaos puede cambiar, as por ejemplo: el int se define en Ansi C como un tipo de dato que almacena enteros en un rango de 32767 hasta +32767, En ciertas implementaciones de C para entornos de 32 bits el tipo int posee un rango de -2147483648 a 2147483647. En el caso de los microcontroladores la implementacin de los datos depende del tipo de procesador; as para Hitech en MCU PIC 16F la implementacin se define como indicamos a continuacin:

    Tipos de datos enteros y decimales Tipo Tamao (bits) Tipo Aritmtico bit 1 Entero sin signo signed char 8 Entero con signo unsigned char 8 Entero sin signo signed short 16 Entero con signo unsigned short 16 Entero sin signo signed int 16 Entero con signo unsigned int 16 Entero sin signo signed short long 24 Entero con signo unsigned short long 24 Entero sin signo signed long 32 Enteron con signo unsigned long 32 Entero sin signo float 24 or 32 Decimal double 24 or 32 Decimal long double igual al doble Decimal El compilador C de HI-TECH soporta tipos decimales, tambin conocidos como variables en punto flotante (pues la posicin del punto decimal se puede correr) soporta 24 y 32 bits. El punto flotante se implementa usando el formato IEEE 754 que usa un formato de 32 bits o implementando un formato modificado en 24 bits. Operadores Aritmticos Los operadores aritmticos son los siguientes:

  • 19

    Los operadores de decremento e incremento equivalen a:

    a=a + 1 ! a++ a=a - 1 ! a - -

    En caso de presentarse a con el operador delante: a= 8;

    b = ++a; b toma el valor de 9. Pero de plantearse lo siguiente:

    a = 8; b = a++;

    b toma el valor de 8. O sea que en este ltimo caso, primero ocurre la asignacin y luego el incremento en a. Operadores Relacionales Los operadores relacionales se usan normalmente en las estructuras comparativas usadas en los programas: OPERADOR ACCIN

    > Mayor que >= Mayor igual que < Menor que

  • 20

    En C, cualquier valor distinto de 0 es VERDADERO. FALSO es 0 (cero).

    Declaracin De Variables: En C siempre se deben declarar las variables. La declaracin consiste en un tipo de dato, seguido por el nombre de la variable y el punto y coma:

    int a; int b,c,d; int a = 10;

    Los tres casos son definiciones correctas de variables, en el ltimo adems de declarar la variable se le asigna un valor inicial. En caso de existir una expresin con variables de diferentes tipos, el resultado obtenido es del tipo de operando de mayor precisin. Todos los char se convierten a int. Todos los float se convierten a double. Hay que tener en cuenta que el tipo char es en realidad un int de menor precisin.

    Conversin De Tipos (Cast): A veces es til, o necesario, realizar conversiones explcitas para obligar que una expresin sea de un cierto tipo. La forma general de esta conversin en C es:

    (tipo) expresin; siendo tipo, el tipo de datos al que se convertir la expresin. NOTA: Esta conversin de tipos, (tambin llamada CAST), permite convertir expresiones no variables, esto es, si tengo una variable x de tipo int y le aplico (float)x lo que se convierte es el resultado, en caso que yo asigne esta operacin a otra variable, pero no se convierte la variable x a float. Supongamos que hacemos un programa que divide 10 por 3, uno sabe que el resultado ser flotante: 3.333, y como 10 y 3 son enteros uno escribira:

    int a=10, b=3; float r; r=a/b; printf(El resultado es %f, r);

    pero se encontrara que el resultado no es el deseado, esto ocurre porque en C la divisin entre enteros da como resultado un entero y en la realidad no siempre es as, (slo en el caso que b sea divisor de a). Pero cambiando el clculo de la divisin por:

    r=(float)a/b; as se garantiza que el resultado ser flotante.

  • 21

    La Funciones printf() Es una funcin que estn incluidas en el archivo de cabecera stdio.h y se utiliza para sacar informacin por la USART, y los datos son vistos por el software Hyperterminal de Windows. El nmero de parmetro pasados puede variar, dependiendo de la cantidad de variables a mostrar. El primer parmetro indica, por un lado, los caracteres que se enviarn por la USART y adems los formatos que definen como se vern los argumentos, el resto de los parmetros son las variables enviar. Ejemplo: int a=100, b=50; printf(%i es mayor que %i, a, b); se enviar por la USART:

    100 es mayor que 50 (sin las comillas). Los formatos ms utilizados con printf() son: CODIGO FORMATO %c Un solo carcter %d Decimal (un entero) %i Un entero %f Punto decimal flotante %e Notacin cientfica %o Octal %x Hexadecimal %u Entero sin signo %s Cadena de caracteres %% Imprime un signo % %p Direccin de un puntero Los formatos pueden tener modificadores para especificar el ancho del campo, el nmero de lugares decimales y el indicador de alineacin a la izquierda. Ejemplos: %05d, un entero de 5 dgitos de ancho; rellenar con ceros. Alineado a la derecha. %10.4f, un real de 10 dgitos de ancho, con 4 decimales. Alineado a la derecha. %-10.2f, un real de 10 dgitos de ancho, con 2 decimales. Alineado a la izquierda. En la funcin printf() tambin se pueden encontrar caracteres de escape que permiten intercalar algn carcter especial en la cadena. Ejemplo:

    printf(\nHola mundo.\n);

  • 22

    Aqu antes de imprimir el texto Hola mundo, \n obliga a un salto de lnea - retorno de carro, (ENTER) y . Y luego de imprimir el texto, hace otro salto de lnea - retorno de carro. Caracteres de escape: CDIGO DESCRIPCIN \n Salto de lnea retorno de carro (ENTER) \t Tabulado horizontal \v Tabulado vertical \r Retorno de carro. \b Backspace. \f Alimentacin de pgina. \ Imprime una comilla simple. \ Imprime una comilla doble. \\ Imprime una barra invertida, (\). \xnnn Notacin hexadecimal \nnn Notacin octal Importante: Antes de poder usar la sentencia printf, se tiene que haber configurado la USART cargando los registros de control que permiten su operacin. El PICC trae un archivo denominado usart.h el cual debe ser adjuntado en nuestra carpeta de proyecto y cdigo como se indica en el siguiente ejemplo: #include #include #include "usart.h"

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & LVP_OFF & CP_OFF & CPD_OFF & BOREN_ON);

    void main(void) {

    unsigned char input; init_comms();//setea la USART con usart.h

    // Enviamos un mensaje

    while(1) {

    printf("\n Hola Mundo!!!\n");

    } }

  • 23

    Estructuras de Control Y Comparacin: Estas estructuras le permiten al programa tomar una decisin a partir de la evaluacin de una condicin, si dicha condicin luego de ser evaluada se cumple, se ejecutar todas las sentencias que se encuentren debajo de la sentencia if, sino se cumplen se ejecutarn las que se encuentren debajo del else. Sentencia if (forma general): if (condicin)

    sentencia1; else

    sentencia2;

    Si la condicin es verdadera se ejecuta la sentencia1, de lo contrario la sentencia2. Ejemplo: Este es un programa que enciende un led si se ha pulsado un pulsador. El Led se encuentra colocado en el puerto RB0 y el pulsador en el puerto RA0. El programa ha sido implementado para un PIC16F1939: #include #include #define _XTAL_FREQ 4000000

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_ON); __CONFIG(WRT_OFF & VCAPEN_OFF & PLLEN_OFF & STVREN_OFF & LVP_OFF);

    void main(void) //funcin principal { //abrimos la funcin

    ANSELB=0; //desactivamos los puertos analgicos ANSELA=0; //y los configuramos como digitales TRISA=255; //configuramos todo el PORTA como entrada TRISB=0; //PORTB como salida while(1) //bucle iterativo infinito { //abrimos el bucle if (RA0==1) //testeamos si se pulso RA0 LATB0=1; //encendemos el BIT 0 del PORTB else LATB0=0; //apagamos el BIT 0 del PORTB } //cerramos bucle iterativo } //cerramos la funcin principal

    La condicin en un if siempre debe ir entre parntesis.

  • 24

    Si alguna de las ramas tiene ms de una sentencia estas deben ir encerradas entre { }.

    if (b) {

    r = a/b; printf(\nEl resultado es: %f, r);

    } else

    printf(\nNo se puede dividir por cero);

    No siempre se aplica la forma completa if-else, en ocaciones solo se una la forma simple del if y si esta se cumple, se colocan debajo las operaciones que se desean realizar. En el siguiente segmento de cdigo vemos esta forma: //contador binario para PIC16F877A #include #include #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & CP_OFF & CPD_OFF & BOREN_ON);

    //declaramos nuestras variables unsigned char contador=0; //creamos la variable contador

    //Programa principal void main(void) //funcin principal { //abrimos la funcin ADCON1=6; //desactivamos puertos analgicos TRISB=0; //PORTB como salida TRISA=255; //PORTA como entrada while(1) //bucle iterativo infinito { //abrimos el bucle if(RA0==1)//se pulso el pulsador??? { contador=contador+1;//contamos PORTB=contador;//mostramos la cuenta __delay_ms(2);//anti-rebotes de entrada while(RA0==1);//solt pulsador??? __delay_ms(2);//anti-rebotes de salida } PORTB=contador;//mostramos la cuenta

    } //cerramos bucle iterativo } //cerramos la funcin principal

  • 25

    Tambin se pueden escalonar las sentencias if para crear estructuras de toma de decisin mas complejas: if (condicin)

    Sentencia1; else if (condicin)

    Sentencia2; else if (condicin)

    Sentencia3; else

    Sentencia4 Y anidar como lo mostramos en el siguiente ejemplo de cdigo: if (TMR1IF==1) { Segundo++; if (Segundo==60) { Segundo=0; Minuto++; if(Minuto==60) { Minuto=0; Hora++; if(Hora==24) Hora=0; } } TMR1IF=0; TMR1L=0b00000000; TMR1H=0b10000000; } Sentencia switch: La sentencia switch permite evaluar diferentes valores para una misma variable: Su forma general es: switch(variable) {

    case valor1: sentencias; break;

    case valor2: sentencias;

  • 26

    break; case valor3:

    sentencias; break;

    .

    .

    . default:

    sentencias; } El switch evalua cada caso, cuando coincide uno de ellos con el contenido de la variable, ejecuta las sentencias del caso y termina el switch. En caso de no encontrar ningn case que corresponda, en igualdad, con el contenido de la variable, ejecuta las sentencias de la clusula default, si esta ha sido especificada, sino termina el switch. El siguiente programa pide el ingreso de un valor y luego ofrece un men de opciones para elegir que operacin se desea relizar: averiguar el cuadrado del nmero, el cubo y si es par o no. El programa ha sido realizado para PIC16F877A.

    #include #include #include #include "usart.h" #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & CP_OFF & CPD_OFF & BOREN_ON);

    void main(void) { int opcion, valor, res; char buf[80]; printf(Introduzca un valor entero mayor que 0:\n); printf(\n\n); gets(buf); valor= atoi(buf); printf(*** MENU DE OPCIONES ***\n\n); printf(1 - Averiguar el cuadrado:\n); printf(2 Averiguar el cubo:\n); printf(3 Averiguar si es par o no:\n); printf(\nIngrese opcin: ); printf(\n\n); gets(buf); opcion= atoi(buf); switch(opcion)

    { case 1:

    re = valor*valor;

  • 27

    printf(El cuadrado de %i es %i\n, valor, res);

    break; case 2:

    re = valor*valor*valor; printf(El cubo de %i es %i\n, valor, res); break;

    case 3: res = valor % 2; if (res)

    prinf(El nmero %i es impar\n, valor); else

    printf(El nmero %i es par\n, valor); break;

    default: printf(Opcin erronea);

    } } Bucles iterativos: Son estructuras que permiten ejecutar una cantidad de instrucciones un nmero de veces o mientas se cumpla una condicin determinada. Existen 3 formas para crear estos bucles: El bucle for Esta instruccin nos permite crear un bucle iterativo el cual se repetir una cantidad de veces, la cual esta determinada dentro de ciertas condiciones Forma general:

    for(inicializacin; condicin; incremento) sentencia;

    for(inicializacin; condicin; incremento) {

    sentencias; } El siguiente ejemplo muestra los primeros 100 nmeros enteros:

    #include #include #include #include "usart.h" #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & CP_OFF & CPD_OFF & BOREN_ON);

  • 28

    #include void main(void) { int i; for(i=1; i=1; i--) printf(%d, i); Se pueden evaluar ms de una variable: int i, j; for(i=1, j=100; i0; i++, j - -) printf(i = %d, j= %d\n, i, j); El siguiente bucle no termina nunca, (bucle infinito):

    for( ; ; ) Si la cantidad de sentencias, (equivalencias, operaciones aritmticas, llamadas a funciones, etc.), pertenecientes al bucle son ms de una, estas deben ir entre { }. El bucle while El bucle while es otra forma de bucle, la sentencia se ejecutar mientras la condicin se cumpla. Si la condicin se cumple, entonces se ejecuta la sentencia, caso contrario, no se ejecutar while (condicin)

    sentencia; si son varias las sentencias que deben ejecutarse, estas deben colocarse entre llaves usando la siguien while (condicin) {

    sentencias; }

  • 29

    Ejemplo: En el siguiente ejemplo aplicamos el while para crear un bucle iterativo permanente (while(1)) y luego para crear un enclavamiento para el pulsador. Mientras no se suelte, el sistema quedar en esa instruccin (while(RA0==1)). El cdigo crea un pulsador con retencin, invirtiendo (toggleando) el estado del PORT RB0, el cual tiene conectado un LED. El cdigo ha sido realizado para un PIC16F1939. #include #include #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_ON); __CONFIG(WRT_OFF & VCAPEN_OFF & PLLEN_OFF & STVREN_OFF & LVP_OFF); void main(void) //funcin principal { //abrimos la funcin ANSELB=0; //todo el PORTB como digital ANSELA=0; //todo el PORTA como digital TRISA=255; //todo el PORTA como entrada TRISB=0; //todo el PORTB como salida LATB=0; //inicializamos el LATB completo while(1) //bucle iterativo infinito { //abrimos el bucle if (RA0==1) //esta pulsado el pulsador??? { //si esta pulsado; LATB0=!LATB0; //toggleamos RB0 __delay_ms(2); //anti-rebote de entrada while(RA0==1); //solt el pulsador??? __delay_ms(2); //antirebote de salida } } //cerramos bucle iterativo } //cerramos la funcin principal

    El bucle do while do {

    sentencia; } while (condicin); La diferencia con el while es que en do while por lo menos el flujo del programa entra una vez al bucle y luego, (al llegar a la clusula while), decide si contina iterando.

  • 30

    Operaciones A Nivel De Bits, (Bitwise): El lenguaje c proporciona la posibilidad de manipular los bits de un byte, realizando operaciones lgicas y de desplazamiento. Operadores a nivel de bits: OPERADOR ACCIN & And entre bits | Or entre bits ^ Xor entre bits, (or exclusivo). ~ Not , (si es 1 pasa a ser 0 y viceversa) > Desplazamiento a derecha var > n Se desplaza n bits a la derecha. Los Operadores lgicos trabajan de la siguiente manera: 1 & 1 = 1 1 | 0 = 1 0 | 1 = 1 1 | 1 = 1 1 ^ 0 = 1 0 ^ 1 = 1 ~1 = 0 ~0 = 1 De forma general conviene tener siempre presente estos resultados X & 1 = X X & 0 = 0 X | 1 = 1 X | 0 = X X ^ 1 = ~X X ^ 0 = X Ejemplo: En el siguiente ejemplo usamos el operador de desplazamiento para desplazar un BIT por el PORTB. Este desplazamiento puede ser observado si tenemos 8 LEDS conectados al PORTB.El programa esta realizado para un PIC16F877A: #include #include #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF );

    unsigned char contador=0;

  • 31

    void main(void) { ADCON1=6; //todos los puertos como digitales TRISB=0b00000000;//todo el PORTB como salida TRISA=0b11111111;//todo el PORTA como entrada PORTA=0; //inicializamos el PORTA PORTB=0; //inicializamos el PORTB

    while(1) //Bucle repetitivo eterno { // abrimos el bucle RB0=1; // encendemos el RB0 (bit0 del PORTB) __delay_ms(500); //esperamos 500ms for(i=0,i

  • 32

    void main(void) { init_comms();//setea la USART con usart.h

    While(1) {

    printf(Ingrese cadena, de hasta 100 caracteres\n); gets(cadena); printf(Usted ingres: %s, cadena); }

    } FUNCIONES DE CADENAS Estas funciones estan incluidas en las librerias fundamentales del compilador y son usadas para el tratamiento de los strings. Para poder usarlas, se debe incluir el archivo de cabecera string.h, el cual se encuentra dentro de la carpeta include Se toma por convencin que los strings terminan con el carcter nulo para estas funciones. strcpy(): Se utiliza para copiar sobre una cadena:

    strcpy(cadena, Hola);

    Guarda la constante Hola en la variable cadena. ATENCIN: En C no se puede realizar entre cadenas la asignacin cadena = Hola ya que recordemos que son arrays de caracteres. strlen(): Devuelve la cantidad de caracteres que posee la cadena, sin contar el carcter nulo.

    a = strlen(cadena); strcat(): Concatena dos cadenas.

    strcat(cadena1, cadena2); Resultado: cadena1 es la suma de cadena1 + cadena2 strcmp(): Compara los contenidos de dos cadenas.

    strcmp(string1, cstring2);

  • 33

    Si son iguales devuelve 0. Si cadena1 es mayor que cadena2: devuelve un valor mayor a 0. Si cadena1 es menor que cadena2: devuelve un valor menor a 0. Funciones: Las funciones son porciones de cdigo que facilitan la claridad de desarrollo del programa. Todas las funciones retornan un valor y pueden recibir parmetros. La estructura general de un funcin en C es la siguiente: Tipo_de_retorno nombre_funcin (tipo param1,...,) {

    sentencias return(valor_de_retorno);

    }

    Los posibles tipos de retorno son los tipos de datos ya vistos: (int, float, void, char,etc). Para crear una funcin en C, primero hay que declarar el prototipo de la misma antes de la funcin main() y luego de la llave final del programa se define la funcin. Ejemplo: Programa con una funcin que recibe 2 parmetros enteros y retorna la suma de los mismos: #include #include #include #include "usart.h"

    #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF ); int suma(int x, int y); //prototipo de la funcin

    void main(void) {

    int a, b; unsigned char buf[80]; init_comms();//setea la USART con usart.h while(1) {

    printf(Ingrese valor de a: ); buf=gets();

  • 34

    a= atoi(buf); printf(\nIngrese valor de b: ); buf=gets(); b= atoi(buf); printf(\nLa suma de a y b es: %i, suma(a,b));

    } }

    //Ahora viene la definicin de la funcin int suma(int x, int y) {

    return x+y; }

    Se retorna de una funcin cuando se llega a la sentencia return o cuando se encuentra la llave de cierre de la funcin. Cuando lo que se desea escribir es un procedimiento que, por ejemplo, realice un dibujo o muestre un texto por pantalla o cargue una arreglo, o sea, que no devuelva ningn valor se escribe como tipo de retorno void, (tipo vaco). El siguiente programa consta de una funcin que se encarga de cargar un arreglo de caracteres:

    #include #include #include #include "usart.h"

    #define _XTAL_FREQ 4000000 __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF );

    void carga(void); char v[10]; void main(void) {

    int i; init_comms();//setea la USART con usart.h carga(); //llamo a la funcin que carga el arreglo for(i=0; i

  • 35

    En este caso la funcin se comporta como un procedimiento, por eso carece de la sentencia return, que estara de ms pues el retorno es void. mbito de las variables: Variable global: Conocida por todas las funciones. Se puede utilizar en cualquier punto del programa. Se declara fuera del main. Variable local: Se declara apenas abrir una llave en el cdigo, cuando la llave se cierra esta variable desaparece. Variable declarada en los parmetros formales de una funcin: Tiene el mismo comportamiento de las variables locales. Paso De Parmetros: Paso por valor: Cuando se pasa un parmetro por valor a una funcin, (ver ejemplo de la funcin que suma), la funcin hace copias de las variables y utiliza las copias para hacer las operaciones. No se alteran los valores originales, ya que cualquier cambio ocurre sobre las copias que desaparecen al terminar la funcin. Paso por referencia: Cuando el objetivo de la funcin es modificar el contenido de la variable pasada como parmetro, debe conocer la direccin de memoria de la misma. Necesita que se le anteponga a la variable el operador &, puesto que se le est pasando la direccin de memoria de la variable, ya que el objetivo es guardar all un valor ingresado por teclado. El siguiente programa tiene una funcin que intercambia los valores de dos variables de tipo char. #include #include #include #include "usart.h"

    #define _XTAL_FREQ 4000000

  • 36

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF );

    void cambia(char* x, char* y); //prototipo

    void main(void) {

    char a, b; init_comms();//setea la USART con usart.h a='@'; b='#'; printf("\n**** Antes de la funcin ****\n"); printf("Contenido de a = %c\n", a); printf("Contenido de b = %c\n", b); cambia(&a,&b); (*) printf("\n**** Despus de la funcin ****\n"); printf("Contenido de a = %c\n", a); printf("Contenido de b = %c\n", b); getc();

    }

    void cambia(char* x, char*y) (**) {

    char aux; aux=*x; *x=*y; *y=aux;

    } En la lnea (*) se llama a la funcin cambia() pasndole las direcciones de memoria de las variables, puesto que precisamente el objetivo es modificar los contenidos. La funcin en (**), recibe los parmetros como punteros, puesto que son los nicos capaces de entender direcciones de memoria como tales. Dentro de la funcin tenemos entonces x e y que son punteros que apuntan a la variable a y a la variable b; utilizando luego el operador * sobre los punteros hacemos el intercambio. ATENCIN: Los arrays, (entindase tambin cadenas), siempre se pasan por referencia y no hace falta anteponerle el smbolo &, pues como habamos dicho el nombre de un array es un puntero al primer elemento del mismo.

  • 37

    APENDICE: En este apndice encontrar el texto de los archivos de librera de las funciones para el manejo de la USART. Archivo USART.h: Librera para USART.C

    #ifndef _SERIAL_H_ #define _SERIAL_H_

    #define BAUD 9600 #define FOSC 4000000L #define NINE 0 /*poner 1 si usa 9 BITS */

    #define DIVIDER ((int)(FOSC/(16UL * BAUD) -1)) #define HIGH_SPEED 1

    #if NINE == 1 #define NINE_BITS 0x40 #else #define NINE_BITS 0 #endif

    #if HIGH_SPEED == 1 #define SPEED 0x4 #else #define SPEED 0 #endif

    //definicin de PINES de la USART segn el MCU

    #if defined(_16F87) || defined(_16F88) #define RX_PIN TRISB2 #define TX_PIN TRISB5 #else #define RX_PIN TRISC7 #define TX_PIN TRISC6 #endif

    /* Inicializacin de la USART */ #define init_comms()\ RX_PIN = 1; \ TX_PIN = 1; \ SPBRG = DIVIDER; \ RCSTA = (NINE_BITS|0x90); \

  • 38

    TXSTA = (SPEED|NINE_BITS|0x20)

    void putch(unsigned char); unsigned char getch(void); unsigned char getche(void);

    #endif

    Archivo USART.C: Librera para manejar la USART #include #include #include "usart.h"

    void putch(unsigned char byte) { /* enva un byte */ while(!TXIF) /* finaliz la transmisin?? */ continue; TXREG = byte; }

    unsigned char getch() { /* recibe un byte */ while(!RCIF) /* recibi un dato?? */ continue; return RCREG; }

    unsigned char getche(void) { unsigned char c; putch(c = getch()); return c; }

  • 39

    Aprendiendo a usar el XC8 de forma Prctica Hasta ahora hemos aprendido a realizar un proyecto con MPLABX y los fundamentos de la programacin en lenguaje C, la cul hemos ilustrado mediante ejemplos aplicados, para familiarizarlo con la sintaxis de los cdigos en Lenguaje C aplicado a los microcontroladores. Para aplicar los conocimientos adquiridos usaremos una placa de entrenamiento desarrollada por la firma MCElectronics, partner de Microchip en Argentina. La placa de Aplicaciones se denomina Starter Kit Student:

    La placa contiene todos los elementos necesarios para realizar todas las prcticas bsicas para el testeo de la mayora de los perifricos del MCU:

    Control de puertos I/O

    Manejo de display 7 segmentos de forma multiplexada

    Manejo de display LCD

    Puertos analgicos para leer Tensin y Temperatura

    Control de Timers e Implementacin de un RTC

    Lectura de Menoria EEPROM

    Control del PWM

    Envo y recepcin de datos por USART

  • 40

    Para programar dicha placa tambin usamos la herramienta de programacin PDX desarrollado por la misma firma y el cual es compatible con el PICKIT2 de Microchip, mediante el cual se pueden programar una extensa gama de microcontroladores de Microchip desde PIC10F hasta PIC32, va ICSP

    Sin embargo usted puede adaptar los ejemplos a cualquier circuito de aplicaciones que incluso haya construido, al igual que para programar el microcontrolador podr usar cualquiera que tenga, compre o construya.

  • 41

    Programa N1: Led-Pulsador En esta primera prctica aplicada introduciremos el concepto bsico de control, a partir de la lectura del estado de 3 pulsadores y el accionamiento de 3 Leds. Los pulsadores en la Placa Starter Kit Student se encuentran colocados en los puertos RA0, RA1 y RA2. Los led se encuentran en los Puertos RB0 al RB7. De estos solo usaremos RB0,RB1 y RB2. #include #include #define _XTAL_FREQ 4000000

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF );

    void main(void) //funcion principal { ANSEL=0; //todos los puertos como digitales ANSELH=0; TRISB= 0;//todo el PORTB como salidas TRISA=255;//todo el PORTA como entradas PORTA=0; //inicializamos el PORTA PORTB=0; //inicializamos el PORTB

    while(1) //Bucle repetitivo eterno { // abrimos el bucle if(RA0==1) //esta pulsado RA0?? RB0=1; //si s, encendemos RB0 else //sino RB0=0; //apagamos RB0

    if(RA1==1) //esta pulsado RA1?? RB1=1; //si s, encendemos RB1 else //sino RB1=0; //apagamos RB1

    if(RA2==1) //esta pulsado RA2?? RB2=1; //si s, encendemos RB2 else //sino RB2=0; //apagamos RB2

  • 42

    }

    } //cerramos la funcin principal

    #include :Esta lnea nos permite incluir un archivo de cabecera genrico para que el mismo incluya en el proceso de compilado el archivo de cabecera del microcontrolador que tengamos configurado en el proyecto. Dentro del archivo de cabecera del microcontrolador, el cual se identifica por el nombre del MCU por ejemplo PIC16F887.h. Dichos archivos se encuentran almacenados en la carpeta include del compilador. Para facilitar la migracin entre microcontroladores, existe el archivo xc.h el cual busca el archivo de cabecera del MCU que hayamos seteado en nuestro proyecto.

    #define _XTAL_FREQ 4000000:Esta lnea le indica al compilador cual es la frecuencia de entrada al microcontrolador, con ella el copilador calcula el tiempo necesario para crear los bucles de las macros delays

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF ): Esta lnea le permite al usuario setear los fusibles de configuracin que define las caractersticas de funcionamiento del ncleo (core) del microcontrolador. Dichas caractersticas

    while(1) : En esta lnea creamos un bucle infinito, el cual contiene al programa que se va ejecutar constantemente. Este programa nos muestra los elementos bsicos que encontramos en todo programa de lenguaje C.

  • 43

    Programa N2: Pulsador Touch En esta segunda prctica le vamos a ensear como leer sin rebotes el estado de 3 pulsadores el cual accionar 3 Leds. Los pulsadores tendrn memoria y la misma cambiar de estado cada vez que se accione el pulsador Los pulsadores en la Placa Starter Kit Student se encuentran colocados en los puertos RA0, RA1 y RA2. Los led se encuentran en los Puertos RB0 al RB7. De estos solo usaremos RB0,RB1 y RB2. #include #include #define _XTAL_FREQ 4000000

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & LVP_OFF );

    void main(void) //funcion principal { ANSEL=0; ANSELH=0; TRISB=0; TRISA=0b11111111; PORTA=0; //inicializamos el PORTA PORTB=0; //inicializamos el PORTB

    while(1) //Bucle repetitivo eterno { // abrimos el bucle if(RA0==1)//accionado el pulsador?? { RB0=!RB0;//invierte el estado de RB0 __delay_ms(2);//antirebote de entrada while(RA0==1);//esperamos hasta RA0=0 __delay_ms(2);//antirrobote de salida }

    if(RA1==1)//accionado el pulsador?? { RB1=!RB1;//invierte el estado de RB1 __delay_ms(2);//antirebote de entrada while(RA0==1);//esperamos hasta RA1=0 __delay_ms(2);//antirrobote de salida }

  • 44

    if(RA2==1)//accionado el pulsador?? { RB2=!RB2;//invierte el estado de RB0 __delay_ms(2);//antirebote de entrada while(RA2==1);//esperamos hasta RA2=0 __delay_ms(2);//antirrobote de salida }

    }

    } //cerramos la funcin principal

    Para evitar los rebotes que se producen en el accionamiento de un pulsador se

    han colocado las lneas __delay_ms(2). Existe un rebote tanto al accionar el pulsador, como al soltarlo. Para evitar leer los falsos estados que se producen en ese momento de inestabilidad mecnica del pulsador, mantenemos al microcontrolador dentro del delay.

    La sentencia __delay_ms(x) es en realidad una funcin embebida dentro del compilador y que se la conoce como macro. El compilador xc tiene varias macros de este tipo. En este caso la funcin introduce un tiempo, definido por x en milisegundos.

    while(RA2==1);: En esta lnea el programa queda a la espera que el usuario deje de accionar el pulsador. Mientras el pulsador se este accionando, el programa leer en el puerto de entrada 1 y por tanto al cumplirse la condicin del while, el programa quedar en este punto, sin hacer nada ya que la lnea se cierra con un punto y coma. Esto es lo mismo que haber creado un bucle vaco.

  • 45

    Programa N3: Contador 7 Segmentos En este tercer programa realizaremos un contador de 6 dgitos con cuenta ascendente y descendente. El siguiente es el circuito de aplicacin:

  • 46

    Para este circuito decidimos usar una implementacin independiente de la placa student en un circuito propio que le diera una mayor capacidad de cuenta. El circuito se realiz en PROTEUS.

  • 47

    Los displays estn multiplexados, y son controlados por el PORTD, el cual se encuentra conectado a un ULN2803, mediante el cual se amplia la capacidad de corriente de cada puerto a unos 500ma. Los segmentos, son comunes a todos los displays o dgitos del contador, y se encuentran conectados al PORTB. Con el bit de menor peso, RB0 excitamos el segmento a; y con el bit RB7 excitamos el segmento del punto decimal (dp). El microcontrolador usado es el PIC16F887, y usamos su reloj interno seteado en 4 Mhz. El contador nos permite contar de forma ascendente o descendente, el modo se controla mediante el pulsador BTN3. Por otra parte podemos habilitar o no el estado de cuenta, accionando el pulsador BTN2. Los pulsos a contar son simulados por el pulsador BTN1, y aplicados a RA0.

    #include #include #define _XTAL_FREQ 4000000

    __CONFIG(FOSC_INTRC_NOCLKOUT & WDTE_OFF & CP_OFF & LVP_OFF );

    //declaracion de funciones void LeerTeclado(void); void MostrarDisplay(void);

    //etiquetas del pines //entradas #define BTN1 RA0 #define BTN2 RA1 #define BTN3 RA2

    //salidas #define LED_3 RE0 #define LED_2 RE1 #define LED_1 RE2

    #define DIGITO1 RD0 #define DIGITO2 RD1 #define DIGITO3 RD2 #define DIGITO4 RD3 #define DIGITO5 RD4 #define DIGITO6 RD5

  • 48

    //constantes globales const unsigned char Conver7SEG[10]={0b00111111, 0b00000110,0b01011011,0b01001111,0b01100110,0b01101101,0b01111101,0b00000111,0b01111111,0b01100111};

    //variables globales unsigned long contador=0; unsigned char buffer[10]; bit ESTADO=0,MODO=0;

    //main void main(void) //funcion principal {

    IRCF0=0; //CONFIGURO OSCILADOR interno a 4MHz IRCF1=1; IRCF2=1; SCS=1;

    ANSEL=0; ANSELH=0;

    TRISA=255;

    TRISC=0; TRISD=0; TRISE=0;

    PORTD=0; PORTE=0; PORTC=0; //inicializamos los PORT

    while(1) //Bucle repetitivo eterno { // abrimos el bucle LeerTeclado(); MostrarDisplay(); }

    } //cerramos la funcin principal

    void LeerTeclado(void) { if(BTN1==1)

  • 49

    { if(ESTADO==1) { if(MODO==1) { contador=contador+1; if(contador==1000000) contador=0; } else { contador=contador-1; if(contador>999999) contador=999999; }

    }

    do { MostrarDisplay(); } while(BTN1==1); }

    if(BTN2==1) { ESTADO=!ESTADO; do { MostrarDisplay(); } while(BTN2==1); }

    if(BTN3==1) { MODO=!MODO; //invierte Bandera do { MostrarDisplay(); } while(BTN3==1); }

  • 50

    }

    void MostrarDisplay(void) { if(ESTADO==1) LED_1=1; else LED_1=0;

    sprintf(buffer,"%06lu",contador);

    PORTC=Conver7SEG[(buffer[0]-0x30)]; DIGITO1=1; __delay_ms(1); DIGITO1=0; PORTC=0;

    PORTC=Conver7SEG[(buffer[1]-0x30)]; DIGITO2=1; __delay_ms(1); DIGITO2=0; PORTC=0;

    PORTC=Conver7SEG[(buffer[2]-0x30)]; DIGITO3=1; __delay_ms(1); DIGITO3=0; PORTC=0;

    PORTC=Conver7SEG[(buffer[3]-0x30)]; DIGITO4=1; __delay_ms(1); DIGITO4=0; PORTC=0;

    PORTC=Conver7SEG[(buffer[4]-0x30)]; DIGITO5=1; __delay_ms(1); DIGITO5=0; PORTC=0;

  • 51

    PORTC=Conver7SEG[(buffer[5]-0x30)]; DIGITO6=1; __delay_ms(1); DIGITO6=0; PORTC=0; } El programa: Para poder trabajar con el oscilador interno hemos seteado el fusible de configuracin interno correspondiente en los bits de configuracin:

    FOSC_INTRC_NOCLKOUT Sin embargo tambin se hace necesario que agreguemos en el cdigo el seteo de la frecuencia a la cual vamos a trabajar pues el oscilador interno desemboca en un POSCALER (divisor de frecuencia) que nos permite obtener distintas frecuencias de trabajo:

    IRCF0=0; //CONFIGURO OSCILADOR interno a 4MHz IRCF1=1; IRCF2=1; SCS=1; El bucle principal se ha simplificado creando 2 funciones, una muestra la informacin en display y la otra lee si ha ingresado un pulso o si se accion algn

  • 52

    pulsador de funciones. Esto nos permite crear una estructura de mantenimiento del cdigo ms dinmica y prolija. En adelante, para la mayora de los ejemplos que vamos a ver hemos repetido el mismo esquema; un bucle simple desde el cual se invocan a las funciones mas complejas: while(1) //Bucle repetitivo eterno { // abrimos el bucle LeerTeclado(); MostrarDisplay(); }

    La funcin LeerTeclado no presenta mayores inconvenientes ya que el abc de su operacin la vimos en el Programa N2, sin embargo aqu existe el agregado del MostrarDisplay, al pulsarse el boton: if(BTN2==1) { ESTADO=!ESTADO; do { MostrarDisplay(); } while(BTN2==1); } Y mantenemos el MostrarDisplay()mientras se este pulsando el mismo, ya que el muestreo del display lo realiza el microcontrolador, y por lo tanto no puede no realizar ninguna tarea. Por ello mientras el usuario mantenga el pulsador accionado debemos continuar refrescando la informacin sobre el display. La estructura de la entrada RA0, es diferente a las otras ya que es la que debe llevar el estado de cuenta: if(ESTADO==1) { if(MODO==1) { contador=contador+1; if(contador==1000000) contador=0; } else { contador=contador-1; if(contador>999999) contador=999999;

  • 53

    }

    }

    Tanto ESTADO como MODO son dos banderas (FLAGS) en memoria de 1 bit (observe que as han sido declaradas), las cuales cambian de estado al ser accionados los pulsadores correspondientes.

    En el cdigo para poder incrementar o decrementar al contador debemos testear si ESTADO=1 ya que esta condicin habilita el conteo del contador, luego en funcin de MODO el contador se incrementa o decrementa. Para mostrar el estado de cuenta en el display es donde tenemos el mayor trabajo, ya que la rutina debe primero decodificar la cuenta a 7 segentos luego realizar el barrido de los display. La cuenta poda realizarse de dos formas, la mas sencilla, pero que insuma 3 mucho ms cdigo, consista en crear 6 contadores para contener cada dgito e incrementarlos en cascada, copiando el viejo modelo de contadores en cascada de la lgica discreta. La otra forma era mas simplificada, y mostraba un lado poco recurrente pero excelente del uso de las funciones estndar de C. nosotros optamos para este libro, esta ltima tcnica, ya que les permitir aprender como emplear de forma menos convencional las funciones estndar de C.

    El contador se implement como una variable del tipo unsigned long contador=0; para incrementarla o decrementarla sencillamente. Luego para decodificarla el primer paso es convertir la cuenta en un string o conjunto de caracteres ASCII, los cuales terminarn con un \0. Para hacer esta conversin usamos la siguiente funcin del C estndar:

    sprintf(buffer,"%06lu",contador);

    la misma es una variante del printf con la diferencia de que sprintf escribe con formato dentro de una variable tipo string, que en nuestro caso se

    denomina buffer y al cual le hemos asignado el tamao de [10]caracteres. La funcin lee el contenido del contador y lo almacena en el array buffer con el formato de 6 caracteres sin signo en formato ASCII.

    El formato se lo hemos establecido con "%06lu" de esta manera no importa cuantos dgitos tenga la cuenta actual, el especificador de formato, los completar con 0 si estos no alcanzan los 6 dgitos. Esto permite simplificar la rutina de muestreo. Una vez convertido a string, pasamos a leer cada elemento del buffer, tomando en cuenta que los array tienen almacenado su primer elemento con el ndice cero:

    buffer[0]-0x30

  • 54

    Esta forma nos permite, que leido l elemento del buffer, restarle el hexadecimal 0X30 para poder obtener el valor BCD del nmero almacenado (ya que los nmeros 0 al 9, en ASCII se almacenan como 0x30 a 0x39. De esta forma obtenemos el nmero de cada dgito en BCD para luego usarlo como puntero de una tabla de conversin BCD a 7 segmentos realizado con un array de constantes, el cual hemos declarado al principio del cdigo: const unsigned char Conver7SEG[10]={0b00111111, 0b00000110,0b01011011,0b01001111,0b01100110,0b01101101,0b01111101,0b00000111,0b01111111,0b01100111}; De esta forma obtenemos el dgito convertido a 7 segmentos para luego enviarlo al display:

    PORTC=Conver7SEG[(buffer[0]-0x30)];

    Luego para hacer el muestreo activamos el dgito correspondiente, durante un tiempo determinado, y posteriormente lo apagamos, borrando adems el puerto para evitar arrastrar visualmente la lectura a los dgitos siguientes:

    DIGITO1=1; __delay_ms(1); DIGITO1=0; PORTC=0;

    El tiempo de muestreo (__delay_ms(1)) determina el nivel de intensidad lumnica de los segmentos del display, ya que a mayor tiempo, mayor energa elctrica es convertida en energa fotnica en el display. Sin embargo si el tiempo de muestreo es demasiado largo, caemos dentro del lmite de la persistencia retiniana del ojo (unos 20ms) y tenemos efecto parpadeo. Para evitar dicho fenmeno, el muestreo de todos los dgitos debe realizarse antes de ese tiempo. La prctica demuestra que si se realiza el muestreo 100 veces por segundo, es imperceptible el multiplexado, y el ojo integra toda la muestra.

  • 55

    Programa N4: Mensaje en un display LCD En este nuevo ejemplo vamos a realizar la escritura de un mensaje en un display del tipo LCD inteligente alfanumrico de 2 lneas por 16 caracteres que tiene la placa de entrenamiento Starter Kit Student. Para manejar el LCD se modifiqu la librera original de microchip, denominada XLCD y que viene para PIC18, la cual originalmente se diseo para el compilador MPLAB C18. Esta librera esta adaptada al compilador XC8 y a la familia PIC16F. En el siguiente listado se describe la misma: Librera lcd_pic16.c: #define _XTAL_FREQ 4000000 /* * * Notes: * - Esta librera ha sido escrita para soportar el controlador HD44780 * y similares * - El usuario debe definer los siguientes items: * - tipo de interfaz con el LCD (4- or 8-bits) * - Si esta en 4-bit mode * - definir si se trabaja con nibble alto o bajo * - El Puerto de datos * - El registro TRIS para el Puerto de datos * - Los pines que manejan el puerto de control * - Los registros TRIS que controlan las lineas de control */ /* Interfaz 4 u 8 bits * Para 8 bits descomente el #define BIT8 */ /* #define BIT8 */ /* Si trabaja en 4 bits y usa el nibble bajo comente la siguiente lnea */ #define UPPER /* Si no usa el pin R/W para chequear el estado BUSY del LCD comente la * siguiente lnea y conecte el pin R/W del LCD a GND. */ //#define BUSY_LCD /* DATA_PORT define a que Puerto estan conectadas las lineas de dato */ #define DATA_PORT PORTD #define TRIS_DATA_PORT TRISD /* CTRL_PORT define donde estan conectadas las lineas del PORT de control. */ #define RW_PIN PORTEbits.RE0 /* PORT para RW */ #define TRIS_RW TRISEbits.TRISE0 /* TRIS para RW */ #define RS_PIN PORTEbits.RE1 /* PORT para RS */ #define TRIS_RS TRISEbits.TRISE1 /* TRIS para RS */ #define E_PIN PORTEbits.RE2 /* PORT para D */ #define TRIS_E TRISEbits.TRISE2 /* TRIS para E */

  • 56

    /* Display ON/OFF Control defines */ #define DON 0b00001111 /* Display on */ #define DOFF 0b00001011 /* Display off */ #define CURSOR_ON 0b00001111 /* Cursor on */ #define CURSOR_OFF 0b00001101 /* Cursor off */ #define BLINK_ON 0b00001111 /* Cursor Blink */ #define BLINK_OFF 0b00001110 /* Cursor No Blink */ /* Cursor or Display Shift defines */ #define SHIFT_CUR_LEFT 0b00000100 /* Desplaza Cursor a la Izquierda */ #define SHIFT_CUR_RIGHT 0b00000101 /* Desplaza Cursor a la Derecha */ #define SHIFT_DISP_LEFT 0b00000110 /* Desplaza Display a la Izquierda */ #define SHIFT_DISP_RIGHT 0b00000111 /* Desplaza Display a la Derecha */ /* Function Set defines */ #define FOUR_BIT 0b00101100 /* Interfaz 4-bit */ #define EIGHT_BIT 0b00111100 /* Interfaz 8-bit */ #define LINE_5X7 0b00110000 /* 5x7 characteres, una linea */ #define LINE_5X10 0b00110100 /* 5x10 characteres */ #define LINES_5X7 0b00111000 /* 5x7 characteres, multiples lineas */ #ifdef _OMNI_CODE_ #define PARAM_SCLASS #else #define PARAM_SCLASS auto #endif /* OpenXLCD * Configura Interfaz y tipo de display */ void OpenXLCD(PARAM_SCLASS unsigned char); /* SetCGRamAddr * Setea el generador de caracteres */ void SetCGRamAddr(PARAM_SCLASS unsigned char); /* SetDDRamAddr * Setea la direccin de la memoria de datos */ void SetDDRamAddr(PARAM_SCLASS unsigned char); /* BusyXLCD * Retorna el estado BUSY del LCD */ unsigned char BusyXLCD(void); /* ReadAddrXLCD * Lee la direccion de la DDRAM actual del LCD */ unsigned char ReadAddrXLCD(void); /* ReadDataXLCD * Lee un byte del LCD */ char ReadDataXLCD(void); /* WriteCmdXLCD * Escribe un commando al LCD */

  • 57

    void WriteCmdXLCD(PARAM_SCLASS unsigned char); /* WriteDataXLCD * Escribe un character imprimible al LCD */ void WriteDataXLCD(PARAM_SCLASS char); /* putcXLCD * normaliza el WriteDataXLCD para asemejarlo al C */ #define putcXLCD WriteDataXLCD /* putsXLCD * escribe un string de caracteres ubicado en la RAM del MCU al LCD */ void putsXLCD(PARAM_SCLASS char *); /* putrsXLCD * Escribe un string de caracteres ubicado en la ROM del MCU al LCD */ void putrsXLCD(const char *); // Rutinas de tiempo auxiliares para la libreria XLCD void DelayFor18TCY(void) { __delay_us(18); } void DelayPORXLCD(void) { __delay_ms(20); //Delay de 15 ms } void DelayXLCD(void) { __delay_ms(20); //Delay de 20 ms } /******************************************************************** * Nombre de la funcion: OpenXLCD * * Valor que retorna: void * ********************************************************************/ void OpenXLCD(unsigned char lcdtype) { #ifdef BIT8 DATA_PORT = 0; TRIS_DATA_PORT = 0x00; #else #ifdef UPPER DATA_PORT &= 0x0f; TRIS_DATA_PORT &= 0x0F; #else DATA_PORT &= 0xf0; TRIS_DATA_PORT &= 0xF0; #endif #endif TRIS_RW = 0; TRIS_RS = 0; TRIS_E = 0; RW_PIN = 0; RS_PIN = 0;

  • 58

    E_PIN = 0; // Delay 15ms Power on reset DelayPORXLCD(); //-------------------reset por medio del software---------------------- WriteCmdXLCD(0x30); __delay_ms(5); WriteCmdXLCD(0x30); __delay_ms(1); WriteCmdXLCD(0x32); while( BusyXLCD() ); //------------------------------------------------------------------------ // Set data interface width, # lineas, tipo de font while(BusyXLCD()); // espera si LCD esta ocupado WriteCmdXLCD(lcdtype); // Funcion para escribir un comando // enciende el LCD while(BusyXLCD()); // Espera si LCD esta ocupado WriteCmdXLCD(DOFF&CURSOR_OFF&BLINK_OFF); // Display OFF/Blink OFF while(BusyXLCD()); // Espera si LCD esta ocupado WriteCmdXLCD(DON&CURSOR_ON&BLINK_ON); // Display ON/Blink ON // Limpia display while(BusyXLCD()); // Espera si LCD esta ocupado WriteCmdXLCD(0x01); // Limpia display // Set entry mode inc, no shift while(BusyXLCD()); // Espera si LCD esta ocupado WriteCmdXLCD(SHIFT_CUR_RIGHT); // Entra el Modo while(BusyXLCD()); // Espera si LCD esta ocupado WriteCmdXLCD(0x06); // Modo Auto Incremento while(BusyXLCD()); // Espera si LCD esta ocupado SetDDRamAddr(0x80); // Setea el cursor en posicin 0 while(BusyXLCD()); // espera si LCD esta ocupado WriteCmdXLCD(CURSOR_OFF); // Cursor OFF return; } /******************************************************************** * Nombre de la funcin: WriteDataXLCD * * Valor que retorna: void * ********************************************************************/ void WriteDataXLCD(char data) { #ifdef BIT8 // interface 8-bit TRIS_DATA_PORT = 0; // Configura el Puerto como salida DATA_PORT = data; // Escribe el dato en el puerto RS_PIN = 1; // Setea bit RS RW_PIN = 0; DelayFor18TCY(); E_PIN = 1; // Genera Enable DelayFor18TCY();

  • 59

    E_PIN = 0; RS_PIN = 0; // Pone a cero RS TRIS_DATA_PORT = 0xff; // Configura el Puerto como entrada #else // Interface 4-bit #ifdef UPPER // Transfiere Nibble Alto TRIS_DATA_PORT &= 0x0f; DATA_PORT &= 0x0f; DATA_PORT |= data&0xf0; #else // transfiere Nibble Bajo TRIS_DATA_PORT &= 0xf0; DATA_PORT &= 0xf0; DATA_PORT |= ((data>>4)&0x0f); #endif RS_PIN = 1; // Setea bit RS RW_PIN = 0; DelayFor18TCY(); E_PIN = 1; // Genera el ENABLE DelayFor18TCY(); E_PIN = 0; #ifdef UPPER // Transfiere Nibble Alto DATA_PORT &= 0x0f; DATA_PORT |= ((data

  • 60

    DATA_PORT |= cmd&0xf0; #else TRIS_DATA_PORT &= 0xf0; DATA_PORT &= 0xf0; DATA_PORT |= (cmd>>4)&0x0f; #endif RW_PIN = 0; RS_PIN = 0; DelayFor18TCY(); E_PIN = 1; DelayFor18TCY(); E_PIN = 0; #ifdef UPPER DATA_PORT &= 0x0f; DATA_PORT |= (cmd4) & 0x0f); #endif RW_PIN = 0;

  • 61

    RS_PIN = 0; DelayFor18TCY(); E_PIN = 1; DelayFor18TCY(); E_PIN = 0; #ifdef UPPER DATA_PORT &= 0x0f; DATA_PORT |= ((DDaddr4) & 0x0f); #endif RW_PIN = 0; // Set control signals RS_PIN = 0; DelayFor18TCY(); E_PIN = 1; // Clock cmd and address in DelayFor18TCY(); E_PIN = 0; #ifdef UPPER // Upper nibble interface DATA_PORT &= 0x0f; // Write lower nibble

  • 62

    DATA_PORT |= ((CGaddr

  • 63

    return(data); // Return the data byte } /********************************************************************* * Function Name: ReadAddrXLCD * * Return Value: char: address from LCD controller * * Parameters: void * *********************************************************************/ unsigned char ReadAddrXLCD(void) { char data; // Holds the data retrieved from the LCD #ifdef BIT8 // 8-bit interface RW_PIN = 1; // Set control bits for the read RS_PIN = 0; DelayFor18TCY(); E_PIN = 1; // Clock data out of the LCD controller DelayFor18TCY(); data = DATA_PORT; // Save the data in the register E_PIN = 0; RW_PIN = 0; // Reset the control bits #else // 4-bit interface RW_PIN = 1; // Set control bits for the read RS_PIN = 0; DelayFor18TCY(); E_PIN = 1; // Clock data out of the LCD controller DelayFor18TCY(); #ifdef UPPER // Upper nibble interface data = DATA_PORT&0xf0; // Read the nibble into the upper nibble of data #else // Lower nibble interface data = (DATA_PORT4)&0x0f; // Read the nibble into the lower nibble of data #else // Lower nibble interface data |= DATA_PORT&0x0f; // Read the nibble into the lower nibble of data #endif E_PIN = 0; RW_PIN = 0; // Reset the control lines #endif return (data&0x7f); // Return the address, Mask off the busy bit } /******************************************************************** * Function Name: putsXLCD * Return Value: void * Parameters: buffer: pointer to string ********************************************************************/ void putsXLCD(char *buffer) { while(*buffer) // Write data to LCD up to null { while(BusyXLCD()); // Wait while LCD is busy

  • 64

    WriteDataXLCD(*buffer); // Write character to LCD buffer++; // Increment buffer } return; } /******************************************************************** * Function Name: putrsXLCD * Return Value: void * Parameters: buffer: pointer to string ********************************************************************/ void putrsXLCD(const char *buffer) { while(*buffer) // Write data to LCD up to null { while(BusyXLCD()); // Wait while LCD is busy WriteDataXLCD(*buffer); // Write character to LCD buffer++; // Increment buffer } return; } /******************************************************************** * Function Name: BusyXLCD * * Return Value: char: busy status of LCD controller * * Parameters: void * ********************************************************************/ unsigned char BusyXLCD(void) { #ifdef BUSY_LCD RW_PIN = 1; // Set the control bits for read RS_PIN = 0; DelayFor18TCY(); E_PIN = 1; // Clock in the command DelayFor18TCY(); #ifdef BIT8 // 8-bit interface if(DATA_PORT&0x80) // Read bit 7 (busy bit) { // If high E_PIN = 0; // Reset clock line RW_PIN = 0; // Reset control line return 1; // Return TRUE } else // Bit 7 low { E_PIN = 0; // Reset clock line RW_PIN = 0; // Reset control line return 0; // Return FALSE } #else // 4-bit interface #ifdef UPPER // Upper nibble interface if(DATA_PORT&0x80) #else // Lower nibble interface if(DATA_PORT&0x08) #endif { E_PIN = 0; // Reset clock line

  • 65

    DelayFor18TCY(); E_PIN = 1; // Clock out other nibble DelayFor18TCY(); E_PIN = 0; RW_PIN = 0; // Reset control line return 1; // Return TRUE } else // Busy bit is low { E_PIN = 0; // Reset clock line DelayFor18TCY(); E_PIN = 1; // Clock out other nibble DelayFor18TCY(); E_PIN = 0; RW_PIN = 0; // Reset control line return 0; // Return FALSE } #endif #else __delay_ms(5); return 0; #endif }

    Como puede observar en la primera parte de la librera se deben definir los pines fsicos donde se ha conectado el LCD . En la placa Student, el LCD esta conectado mediante una interfaz de 6 conexiones: LCD MCU ==================== RSRE1 RW..GND E..RE2 D4RD4 D5RD5 D6RD6 D7RD7

    Es por esta razn que hemos configurado la librera de la siguiente forma: /* Interfaz 4 u 8 bits * Para 8 bits descomente el #define BIT8 */ /* #define BIT8 */ /* Si trabaja en 4 bits y usa el nibble bajo comente la siguiente lnea */ #define UPPER /* Si no usa el pin R/W para chequear el estado BUSY del LCD comente la * siguiente lnea y conecte el pin R/W del LCD a GND. */ //#define BUSY_LCD

  • 66

    /* DATA_PORT define a que Puerto estan conectadas las lineas de dato */ #define DATA_PORT PORTD #define TRIS_DATA_PORT TRISD /* CTRL_PORT define donde estan conectadas las lineas del PORT de control. */ #define RW_PIN PORTEbits.RE0 /* PORT para RW */ #define TRIS_RW TRISEbits.TRISE0 /* TRIS para RW */ #define RS_PIN PORTEbits.RE1 /* PORT para RS */ #define TRIS_RS TRISEbits.TRISE1 /* TRIS para RS */ #define E_PIN PORTEbits.RE2 /* PORT para D */ #define TRIS_E TRISEbits.TRISE2 /* TRIS para E */

    En nuetros proyecto deberemos colocar dentro de la carpeta del proyecto la librera, la cual ser invocada por el programa principal. Nuestro programa simplemente ahora escribir un mensaje en la primera y segunda lnea del LCD. A continuacin listamos el cdigo de nuestro programa: #include #define _XTAL_FREQ 4000000 #include "lcd_pic16.c" __CONFIG( FOSC_INTRC_NOCLKOUT & WDTE_OFF & PWRTE_OFF & MCLRE_ON & CP_OFF & CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF);

    void SendCmdLCD(unsigned char CMD);

    void main(void) { ANSEL=0; ANSELH=0; OpenXLCD(FOUR_BIT & LINES_5X7 ); SendCmdLCD(CURSOR_OFF); while(1) { SetDDRamAddr(0x00); putrsXLCD("Microchip"); SetDDRamAddr(0x40); putrsXLCD("Compilador XC8"); }

    }

    void SendCmdLCD(unsigned char CMD)//escribe un comando { while(BusyXLCD()); WriteCmdXLCD(CMD);

  • 67

    } En la siguiente figura podemos ver el esquemtico de la placa Student de MCElectronics

  • 68

    Programa N5: Contador con display LCD En este nuevo programa veremos como implementar un contador ascendente / descendente usando un LCD inteligente para mostrar el estado de cuenta. Como en el ejemplo anterior usaremos la placa Starter Kit Student. Por lo tanto usaremos los pulsadores ubicados en RA0 y RA1 para manejar el contador. En la carpeta de nuestro proyecto colocaremos la librera del LCD que usamos en el ejemplo anterior. El cdigo es muy sencillo, lo listamos a continuacin: #include #include #include #include "lcd_pic16.c"

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & LVP_OFF & CP_OFF & CPD_OFF & BOREN_ON);

    //variables globales unsigned int contador=0; unsigned char buffer[20]; bit flag_modo=0;

    void init(void) { ANSEL=0; ANSELH=0; TRISB=0xFF; TRISA=0XFF; TRISD=0; TRISE=0; OpenXLCD(FOUR_BIT & LINES_5X7 ); }

    void muestra_LCD (void) { SetDDRamAddr(0x03); putrsXLCD("modo:"); if (flag_modo==0) putrsXLCD("UP "); else putrsXLCD("DOWN"); SetDDRamAddr(0x43); sprintf(buffer,"cuenta:%05u",contador);

  • 69

    putsXLCD(buffer); }

    void main(void) {

    init(); while(1) { muestra_LCD(); if (RA0==1) { if(flag_modo==0) contador=contador+1; else contador=contador-1; muestra_LCD(); __delay_ms(2); while(RA0==1); __delay_ms(2); } if (RA1==1) { flag_modo=!flag_modo; muestra_LCD(); __delay_ms(2); while(RA1==1); __delay_ms(2); } } } En este caso el cdigo del contador se simplifica ya que el MCU no tiene que encargarse del barrido del LCD, ya que de ello se encarga el controlador propio del LCD. Sin embargo para mostrar la informacin del conteo, debemos traducir a formato ASCII dicha cuenta. Esto lo hacemos mediante la sentencia: sprintf(buffer,"cuenta:%05u",contador); Como lo hicimos en el caso del contador 7 segmentos. Observe que los mensajes fijos son almacenados en la ROM y desplegados en

    pantalla mediante la funcin putrsXLCD(). Mientras que los strings

  • 70

    almacenados en la RAM son enviados al LCD mediante la funcin

    putsXLCD(). Por otra parte antes de enviar cada mensaje primero posicionamos el cursor del

    display sobre el lugar que queremos, usando SetDDRamAddr().

    El flag_modo le indica al programa si el contador, funciona en modo ascendente o descendente. Observe que cada vez que se modifica tanto el estado de cuenta, como el estado

    del flag_modo esto se refleja en el LCD pues se dispara el refresco del mismo mediante la rutina muestra_LCD();

  • 71

    Programa N6: Contador con Timer 0 En este ejemplo vamos a ensearle a programar el TIMER 0 conocido en el MCU como TMR0. Los microcontroladores PIC incorporan serie de contadores integrados conocidos como Timers/Counters, ya que cuando el contador cuenta pulsos provenientes del exterior funcionan como verdaderos contadores de eventos. Sin embargo, el contador tambin puede contar pulsos internos cuya fuente es el ciclo de mquina interno, es decir Fosc/4, en este caso se denomina Timer, pues en este caso cunta tiempo. En los PIC16F solo podemos encontrar hasta 3 Timers, mientras que en los PIC18F podemos encontrar hasta 5 Timers, dependiendo el modelo. En este ejercicio practicaremos con el Timer 0. Como lo haremos funcionar en la laca STUDENT, y esta no tiene destinada ninguna fuente externa para excitar a este contador, lo conectaremos a la fuente interna de clock Fosc/4 y activaremos un divisor de frecuencia interno que trae , en un factor de divisin 256. De esta forma retrazaremos el estado de cuenta para poder ver la misma en el LCD. En la siguiente figura podemos ver el esquema del TMR0:

    El TMR0 es un contador de 8 bits. Este contador esta controlado por un registro de control denominado OPTION_REG. Cada bit de dicho registro controla una funcin del TMR0. La fuente que excita el TMR0 puede ser interna o externa segn como se programe el bit T0CS. Cuando T0CS=1, la fuente de clock es externa, caso contrario, es interna, la salida de la unidad interna de Temporizacin la cual excita el ncleo de la CPU, a la frecuencia Fosc/4. Cuando la fuente de clock es externa, la seal debe aplicarse al Terminal T0CKI, y en este caso es posible seleccionar el flanco al cual se incrementar el contador, mediante la programacin del bit T0SE. Cuando T0SE=0, el contador se

  • 72

    incrementa por flanco ascendente, cuando T0SE=1, se incrementa por flanco descendente. Si la frecuencia de la seal de entrada es muy alta , la misma pede dividirse por un factor N. Para esto existe un Divisor de frecuencia programable , conocido como PRESCALER, ya que el mismo se encuentra a la entrada del TMR0. Dicho PRESCALER se comparte con el Watch Dog Timer, como consecuencia existe un bit el cual permite asignar el PRESCALER a la salida del Watch Dog Timer, para retardar su tiempo de accionamiento, o asignarlo a la entrada del TMR0. De esta forma si PSA=0, el PRESCALER queda conectado al TMR0, si PSA=1, el mismo queda conectado a la salida del WDT. Cuando se usa el PRESCALER, el factor de divisin se programa mediante tres bits denominados PS0, PS1, PS2. En el programa que se ha elaborado, se configur el TMR0 para que funcione excitado por la fuente interna T0CKI. Se asigno el PRESCALER con un factor de divisin mximo, 256. La cuenta podr observarse en el display. #include #include #include #include "lcd_pic16.c"

    __CONFIG(FOSC_XT & WDTE_OFF & CP_OFF & PWRTE_ON & LVP_OFF & CP_OFF & CPD_OFF & BOREN_ON);

    //variables globales char buffer[20];

    //funciones prototipo void init(void); void muestra_LCD(void); void Open_IO(void); void Open_TMR0(void);

    //funcin principal void main(void) {

    init(); //inicializa los puertos I/O, LCD y Timer0 while(1) { muestra_LCD(); } } //Configura los puertos I/O void Open_IO(void) { ANSEL=0;

  • 73

    ANSELH=0; TRISB=0xFF; TRISA=0XFF; TRISD=0; TRISE=0;

    }

    //Configura el Timer0 void Open_TMR0(void) { T0CS=0;// fuente de excitacin del TMR0 Fosc/4 PS0=1;// Prescaler en 256 PS1=1; PS2=1; PSA=0; //TMR0 con prescaler TMR0=0;//borramos el Timer }

    void init(void) { Open_IO(); Open_TMR0(); OpenXLCD(FOUR_BIT & LINES_5X7 ); }

    void muestra_LCD (void) { SetDDRamAddr(0x03); putrsXLCD("Contador TMR0"); SetDDRamAddr(0x43); sprintf(buffer,"cuenta:%03u",TMR0); putsXLCD(buffer); }

    La arquitectura del programa es totalmente modular; existen una serie de funciones que configuran y muestran el funcionamiento del TMR0.

    As Open_IO(), configura los puertos I/O; Open_TMR0(), configura el funcionamiento del TMR0 y muestra_LCD(), lee el contenido del TMR0 y lo muestra en el LCD. Es interesante destacar que apresar de que su estado de cuenta mximo es pequeo, ya que solo puede contar desde 0 a 255 por ser TMR0 de 8 bits, este timer es el nico que tiene la posibilidad de programar el flanco de excitacin de la seal de clock. Esta caracterstica es muy til en los automatismos industriales.

  • 74

    Programa N7: Contador con Timer 1 El Timer 1 es un Contador que al igual que lo hace el TMR0, puede contar tanto pulsos internos como externos, y en este ltimo caso, dichos pulsos pueden ser generados externamente o a partir de un oscilador propio que incorpora el Timer. En la siguiente figura podemos ver el diagrama interno del Timer 1:

    En los terminales T1OSO y T1OSI se puede colocar un cristal, por ejemplo de la frecuencia de 32768Hz para crear una base de tiempo para un reloj. En este caso se deber poner en uno el bit T1OSCEN, lo cual habilita una compuerta inversora Con su correspondiente resistor de realimentacin, mediante la cual se crea un oscilador interno, conocido como oscilador secundar