You are on page 1of 79

2011

INTRODUCCION A LA PROGRAMACION DE MICROCONTROLADORES PIC EN CCS C


Por John Caipa Roldan
Este material es una traduccin y actualizacin del libro original de Nigel Gardner titulado "An introduction to programming the Microchip PIC in CCS C", copyright Bluebird Electronics 2002.

Ing. John F. Caipa Roldan Institucin Educativa Politcnico "AGS" 18/02/2011

Tabla de contenido
INTRODUCCION .................................................................................................................................. 4 PORQUE USAR C? ........................................................................................................................... 4 TERMINOLOGIA .............................................................................................................................. 5 RECOMENDACIONES EN LA ESCRITURA DE CODIGO C ................................................................... 7 1. FUNDAMENTOS DE C .................................................................................................................. 8 ESTRUCTURA DEL CODIGO C ........................................................................................................... 8 COMPONENTES DEL CODIGO C ....................................................................................................... 9 #PRAGMA ...................................................................................................................................... 10 MAIN() ........................................................................................................................................... 10 #INCLUDE ...................................................................................................................................... 10 FUNCION PRINTF ........................................................................................................................... 11 VARIABLES ..................................................................................................................................... 12 CONSTANTES ................................................................................................................................. 13 COMENTARIOS .............................................................................................................................. 14 FUNCIONES.................................................................................................................................... 14 COMPATIBILIDAD CON HARDWARE.............................................................................................. 15 PALABRAS CLAVES DE C................................................................................................................. 15 2. VARIABLES ................................................................................................................................ 17 TIPOS DE DATOS ............................................................................................................................ 17 DECLARACION DE VARIABLES........................................................................................................ 18 ASIGNACION DE VALORES A VARIABLES ....................................................................................... 20 ENUMERACION ............................................................................................................................. 21 TYPEDEF......................................................................................................................................... 22 CONVERSION DE TIPO ................................................................................................................... 23 CLASES DE ALMACENAMIENTO PARA VARIABLES ........................................................................ 23 3. FUNCIONES ............................................................................................................................... 25 FUNCIONES.................................................................................................................................... 25 FUNCION PROTOTIPO ................................................................................................................... 25 VOID .............................................................................................................................................. 27 USANDO LOS ARGUMENTOS DE UNA FUNCION ........................................................................... 27 USANDO FUNCIONES QUE RETORNAN VALORES ......................................................................... 28 1

DECLARACION CLASICA Y MODERNA DE FUNCIONES .................................................................. 30 PASANDO CADENAS CONSTANTES A FUNCIONES ........................................................................ 31 4. OPERADORES ............................................................................................................................ 32 OPERADORES ARITMETICOS ......................................................................................................... 32 OPERADORES RELACIONALES ....................................................................................................... 33 OPERADORES LOGICOS ................................................................................................................. 34 OPERADORES DE BIT A BIT ............................................................................................................ 34 OPERADORES INCREMENTALES Y DECREMENTALES .................................................................... 36 PRECEDENCIA DE OPERADORES .................................................................................................... 37 5. DECLARACIONES DE CONTROL DEL PROGRAMA ..................................................................... 39 DECLARACION if .......................................................................................................................... 39 DECLARACION if-else .............................................................................................................. 40 OPERADOR ? ................................................................................................................................. 42 DECLARACION for ....................................................................................................................... 42 DECLARACION while................................................................................................................... 43 DECLARACION do-while ........................................................................................................... 44 DECLARACIONES DE CONTROL ANIDADAS.................................................................................... 45 DECLARACION break................................................................................................................... 46 DECLARACION continue ........................................................................................................... 46 DECLARACION switch ................................................................................................................ 47 DECLARACION null(;) .............................................................................................................. 49 DECLARACION return ................................................................................................................ 50 6. ARREGLOS Y CADENAS ............................................................................................................. 51 ARREGLOS UNIDIMENSIONAL ...................................................................................................... 51 CADENA DE CARACTERES .............................................................................................................. 53 ARREGLOS MULTIDIMENSIONALES ............................................................................................... 54 INICIALIZANDO ARREGLOS ............................................................................................................ 55 ARREGLOS DE CADENAS ................................................................................................................ 56 FUNCIONES PARA MANIPULAR CADENAS .................................................................................... 56 7. PUNTEROS................................................................................................................................. 58 INTRODUCCION A LOS PUNTEROS ................................................................................................ 58

RESTRICCIONES PARA PUNTEROS ................................................................................................. 59 PUNTEROS Y ARREGLOS ................................................................................................................ 61 PASANDO PUNTEROS A FUNCIONES ............................................................................................. 62 8. ESTRUCTURAS Y UNIONES........................................................................................................ 64 INTRODUCCION A LAS ESTRUCTURAS ........................................................................................... 64 PUNTEROS A ESTRUCTURAS.......................................................................................................... 67 INTRODUCCION A LAS UNIONES ................................................................................................... 69 9. LENGUAJE C ESPECIFICO PARA PIC ........................................................................................... 71 ENTRADAS Y SALIDAS .................................................................................................................... 71 MEZCLANDO C Y ASSEMBLER ........................................................................................................ 73 MANIPULACION AVANZADA DE BIT .............................................................................................. 75 TEMPORIZADORES ........................................................................................................................ 76

INTRODUCCION Un gran avance en la programacin de microcontroladores son las herramientas que utilizan el lenguaje de alto nivel C como es el caso del compilador C de CCS. Con dicha herramienta teniendo algo de conocimiento del lenguaje C facilita mucho el desarrollo de programas y aplicaciones con microcontroladores, en este caso el PIC de Microchip. El lector de este material aunque debe tener las bases de la programacin en el lenguaje Assembler y conocer las caractersticas principales de los microcontroladores PIC no debe preocuparse, ya que el objetivo de este material es presentar los fundamentos del lenguaje C apoyado en algunos ejemplos y ejercicios, con la finalidad de que el lector tenga las bases necesarias para empezar su camino en el desarrollo de aplicaciones con PIC utilizando el compilador C de CCS y el Simulador PROTEUS de Labcenter Electrnics. Para el desarrollo de los ejemplos y ejercicios que se muestran en el transcurso del libro se recomienda sean compilados en Microsoft Visual C++ Express, el cual puede descargarse gratuitamente de la pgina de Microsoft. El Microcontrolador empleado en este material es el PIC16F84A de Microchip, adems los programas ejemplo que utilizan este Microcontrolador se escribieron y compilaron en PIC C Compiler de CCS. PORQUE USAR C? El lenguaje C fue desarrollado en los laboratorios Bell a finales de los 60s por Dennis Ritchie y Brian Kernighan. Una de las primeras plataformas para su implementacin fue PDP-11 que corra bajo el ambiente UNIX. Desde su introduccin, este ha evolucionado, estandarizado y se ha establecido en toda la industria de software como el lenguaje de programacin ms empleado. C es un lenguaje portable desarrollado para tener mnimas modificaciones cuando se transfieran programas de un computador a otro. Esto est bien cuando se trabaja con PCs y capas principales. Pero los Microcontroladores y Microprocesadores son un caso diferente. El flujo del programa principal bsicamente permanece sin cambios, pero las diferentes configuraciones de puertos y control de perifricos son especficos para cada Microcontrolador. Un ejemplo de esto es que los registros de puertos sobre un PIC son configurados 1=Input (entrada) 0=Output (salida), mientras que en H8 son 0=Input (entrada) 1=Output (salida). El uso del lenguaje C en las aplicaciones con Microcontroladores es debido a que provee programas ms complejos con un desarrollo ms fcil y tiempo de diseo ms corto comparado con el lenguaje Assembler, pero con el inconveniente que no es tan eficiente en lo relacionado con memoria de programa.

TERMINOLOGIA Comencemos con la terminologa bsica empleada en este contexto. Microcontrolador Es un circuito integrado programable que contiene todos los componentes necesarios para controlar el funcionamiento de una tarea determinada. El Microcontrolador empleado en este libro es el PIC16F84A de Microchip (Fig. 1). I/O Pin de conexin con el mundo exterior que puede ser configurado como entrada o salida. I/O es necesario en la mayora de los casos para permitirle al Microcontrolador comunicarse, controlar y leer informacin. Software La informacin que el Microcontrolador necesita para operar o correr. Este tiene que estar libre errores para una aplicacin exitosa. Puede ser escrito en una variedad de lenguajes como C, Pascal o Assembler. Hardware El Microcontrolador, memorias, perifricos, fuentes de voltaje, y todos los dems componentes conectados a este para hacer que trabaje y se comunique con el mundo exterior. Simulador Aplicacin en la que puede probar y depurar sus diseos de manera interactiva y rpida evitando la programacin del dispositivo real, ejemplo: ISIS PROTEUS . Programador Unidad que permite al programa ser cargado dentro de la memoria del Microcontrolador. Estos vienen en diferentes formas, protocolos de comunicacin y precios, ejemplos: PICSTART PLUS, PICKIT de Microchip. Archivo Fuente Programa escrito en un lenguaje como Assembler o C que usted puede entender. Este archivo tiene que procesarse antes de que el Microcontrolador lo reciba. Compilador Paquete software que convierte al archivo fuente en un archivo objeto. El compilador C que se utiliza en este material es PIC C Compiler de CCS. Archivo Objeto Archivo que se produce despus de compilar el archivo fuente. La extensin es .OBJ o .HEX, y es el archivo que necesita el Simulador y el Microcontrolador para funcionar.

(a)

(b) Fig. 1. (a) Diagrama de pines PIC16F84A, (b) Diagrama de bloques PIC16F84A Fuente: http://ww1.microchip.com/downloads/en/devicedoc/35007b.pdf

RECOMENDACIONES EN LA ESCRITURA DE CODIGO C La escritura de un programa es como construir una casa, si la base es firme, el resto del cdigo permanecer solido. Pero si la base es dbil, el cdigo perder su firmeza en algn punto. Las siguientes recomendaciones tomadas del estndar C++ han sido adaptadas para el PIC. Etiquetas Defnalas para que identifiquen su funcin Los nombres de las etiquetas son el corazn de la programacin, as que defnalas apropiadamente a su funcin y su uso en el programa. Ejemplo: use ErrorCheck en vez de ERRORCHECK. Corchetes o llaves selas de manera ordenada, ejemplo:
if (condicin) { }

Tabulacin e Indentacin Use espaciado en lugar de tabulacin (8 espacios), recuerde que la configuracin de tabulacin de un editor no es la misma en otro, haga el cdigo portable, utilice Indentacin para que su cdigo luzca organizado. Longitud de lnea Mantenga una longitud de lnea de mximo 78 caracteres para mantener compatibilidad entre monitor e impresora. Formato de instruccin else if Se puede incluir una instruccin else extra para decidir cualquier condicin no cubierta por las instrucciones precedentes if, ejemplo:
if (condicion1) { } else if (condicin2) { } else { //decide cualquier condicin no cubierta anteriormente }

Inicialice todas las variables Coloque todas las variables en un valor conocido para prevenir condiciones aleatorias o flotantes. Comentarios Los comentarios permiten descifrar la historia que usted est escribiendo. Usted sabe como su programa est funcionando hoy pero en dos semanas o dos aos podr recordar, o puede alguien diferente entender su programa como lo tiene ahora? Use comentarios para marcar reas donde hay que hacer alguna modificacin, errores a depurar o complementos futuros de su cdigo. 7

1. FUNDAMENTOS DE C Este captulo presenta algunos de los aspecto clave del lenguaje de programacin C. Se entregar una vista rpida de cada aspecto. El objetivo es proporcionar al lector un conocimiento bsico de C tal que puede comprender los ejemplos de los siguientes captulos. ESTRUCTURA DEL CODIGO C Todos los programas escritos en C contienen: directivas del preprocesador, declaraciones, definiciones, expresiones, instrucciones y funciones. Directivas del preprocesador Una directiva es un comando del preprocesador de C (el cual es invocado automticamente como el primer paso en la compilacin de un programa). Las directivas ms comunes son la directiva #define, la cual substituye una etiqueta por un identificador especifico, y la directiva #include, la cual incluye funciones de un archivo externo al programa actual. Declaraciones Una declaracin establece el nombre y atributos de las variables, funciones y tipos usados en el programa. Las variables globales son declaradas fuera de las funciones y son visibles desde el final de la declaracin hasta el final del archivo. Una variable local es declarada dentro de una funcin y es visible desde el final de la declaracin hasta el final de la funcin. Definiciones Una definicin establece el contenido de una variable o funcin. Una definicin tambin asigna el espacio de almacenaje necesario para las variables y funciones. Expresiones Una expresin es una combinacin de operadores y operandos que producen un nico valor Instrucciones Controlan el flujo y orden en la ejecucin del programa en un cdigo C. Funciones Una funcin es una coleccin de declaraciones, definiciones, expresiones e instrucciones que desempean una tarea especfica. Los corchetes encierran el cuerpo de una funcin. Las funciones no se pueden anidar en C. Funcin main Todos los programas hechos en C deben incluir una funcin llamada main donde la ejecucin del programa comienza. Los corchetes que encierran la funcin main definen el comienzo y punto de finalizacin del programa. El siguiente ejemplo muestra la estructura general de un programa C escrito en Microsoft Visual C++ Express, el cual puede descargarlo desde la web de Microsoft.
#include <stdio.h> #define PI 3.142 /* directiva del preprocesador */ /* Incluye el encabezado estndar de C*/ /* define una constante simblica */

float area; int cuadrado (int r);

/* declaracin variable global */

void main() { /* Comienza la funcin main */ int radio_alCuadrado; /* declaracin variable local */ int radio = 3; /*declaracin e inicializacin*/ radio_alCuadrado = cuadrado(radio); /*funcin de usuario*/ area = PI * radio_alCuadrado; printf("El area es %6.4f unidades cuadradas\n",area); getchar(); /* funcin de terminacin*/ } /*fin de la funcin main */ int cuadrado(int r) /* encabezado de la funcin*/ { int r_alCuadrado; r_alCuadrado = r * r; return(r_alCuadrado); }

COMPONENTES DEL CODIGO C Todos los programas escritos en C contienen ciertos componentes esenciales tales como instruccin y funciones. Las instrucciones son la parte del programa que realiza las operaciones. Todos los programas en C contienen una o ms funciones. Las funciones son subrutinas, cada una de las cuales contienen una o ms instrucciones y pueden ser llamada por otras partes del programa. Cuando escribe programas con lneas en blanco, usar indentacin y comentarios mejora la visualizacin, no solo para usted en el futuro, sino tambin para aquellas personas interesadas en su trabajo. El siguiente ejemplo muestra algunas de las partes requeridas de un programa en C.
#include <stdio.h> /* mi 1er codigo en C */ void main() { printf("Hola Mundo!"); getchar(); /* termina el programa oprimiendo ENTER */ }

La sentencia #include <stdio.h> le dice al compilador que incluya el archivo fuente llamado stdio.h al programa. La extensin .h pertenece a archivos de encabezado. Un archivo de encabezado contiene informacin acerca de funciones estndar que est usando el programa como printf() y getchar(). El archivo stdio.h (Standard Input and Output), contiene muchas de las funciones de entrada y salida. /* mi 1er cdigo en C */ es un comentario en C. Todos los comentarios estn precedidos por un /* y terminan con un */. Los comentarios son ignorados por el compilador y por consiguiente no afectan la velocidad ni el tamao del cdigo compilado. Todos los programas escritos en C deben tener la funcin main(). Esta funcin es el punto de entrada del programa. Todas las funciones tienen el mismo formato, el cual es el siguiente:

TipoFuncion NombreFuncion() { Cdigo; }

Las instrucciones dentro de una funcin son ejecutadas secuencialmente, comenzando con el corchete abierto y terminando con el corchete cerrado. Los corchetes {} muestran el comienzo y el fin de bloques de cdigo en C. Finalmente, printf("Hola Mundo"); representa una instruccin tpica en C. Casi todas las instruccin en C terminan con punto y coma (;). El carcter final de lnea no es reconocido por C como un terminador de lnea. En consecuencia, no hay restricciones sobre la posicin de las instrucciones en una lnea ni del nmero de estas. Todas las lneas tienen un punto y coma (;) al final para informar al compilador que ha alcanzado el final de la instruccin. El error comn de no incluirlo es sealado como error en la lnea siguiente. La excepcin a esta regla es con el comando if donde el punto y coma necesita estar al final de la siguiente lnea, ejemplo:
if (EstoEsVerdad) HagaEstaFuncion();

#PRAGMA El comando pragma le informa al compilador que desempee una accin particular al momento de realizar la compilacin como el PIC especifico usado en la aplicacin. Ejemplo:
#pragma device PIC16F84A

En CCS C el comando pragma es opcional, el siguiente comando es el que ms se utiliza:


#device PIC16F84A

MAIN() Como ya se ha dicho anteriormente todo programa debe incluir una funcin main la cual puede aparecer solo una vez en el programa. Ningn parmetro es necesario dentro de los parntesis (). Como main est clasificada como funcin, todo el cdigo siguiente a esta funcin debe estar dentro de un par de corchetes {}.
void main() { Cuerpo del programa }

#INCLUDE El archivo de encabezado, (denotado por la extensin .h) contiene informacin acerca libreras de funciones tales como registros especiales de un PIC especfico, etc. Ejemplo:
#include <16F84A.h>

10

Esta informacin la utiliza el compilador para conectar detalles especficos de hardware y el programa fuente. El siguiente ejemplo es hecho en el compilador C de CCS.
#include <16F84A.h> //definicion del PIC especifico #include <ctype.h> #fuses XT,NOWDT, PUT, NOPROTECT //palabra de configuracin #use delay(clock=4000000) //frecuencia del oscilador #use rs232(baud=9600,xmit=PIN_B0,rcv=PIN_B1) void main() { Printf("Escriba caracteres:"); while(TRUE) putc(toupper(getc())); //toupper convierte a maysculas }

Las definiciones de PIN_B0 y PIN_B1 se encuentran en el archivo de encabezado 16F84A.h. La funcin toupper() se encuentra en el archivo de encabezado ctype.h. Ambos archivos deben estar en el programa para que el compilador tenga la informacin acerca de las funciones que usted est usando. Habr notado probablemente que la directiva #include no termina con punto y coma. La razn para esto es que esta directiva no es una palabra clave de C, pero en cambio es una instruccin del compilador. Todo el contenido de la librera incluida es insertado en el archivo fuente en la etapa de compilacin. FUNCION PRINTF Es una funcin estndar contenida en el archivo de encabezado stdio.h. printf() le permite al programa imprimir informacin en pantalla. El formato general para printf() es:
printf("Texto de control", Argumentos);

Texto de control es una cadena de caracteres entre comillas dobles. La cadena de caracteres puede ser una combinacin de letras, nmeros y smbolos. Algunos smbolos especiales de formato estn denotados con el smbolo %. Argumentos es una entrada opcional y puede estar compuesto por constantes y variables que utilice el programa y que se quieren imprimir. Los siguientes dos ejemplos muestran a printf() imprimiendo un mensaje e imprimiendo una constante del programa.
printf("Hola Mundo!"); printf("Microchip es el #%d!",1);

%d da el formato para la constante que se est imprimiendo. La Tabla 1 muestra todos los especificadores de formato de C y el tipo de datos que afectan.

11

Tabla 1. Especificadores de formato en C. Especificador de formato Tipo de dato que afecta %c Carcter sencillo %uc Carcter sin signo %s Cadena de caracteres %d Enteros decimales con signo %f Punto flotante (notacin decimal) %e Punto flotante (notacin exponencial o cientfica) %g Punto flotante (%f o %e, cualquiera en su formato corto) %u Enteros decimales sin signo %x, %X Enteros hexadecimales sin signo %p Apuntador %o Enteros octales sin signo l Prefijo usado con %d, %u, %x, %o para especificar entero largo

NOTA: Un 0 (cero) seguido del smbolo % dentro de un formato de cadena obliga a imprimir los ceros que estn al comienzo. El siguiente nmero especifica el tamao del campo a imprimir. Ejemplo:
printf("El Hex del decimal 12 es %03x\n",12);

Imprime en pantalla lo siguiente:


El Hex del decimal 12 es 00c Tabla 2. Caracteres especiales. \n \t \r \f \ \ \\ Smbolos Especiales Lnea nueva Tab horizontal Retorno de carro Formfeed Comilla sencilla Comilla doble Backslash \? \a \b \0 \v \xhhh %% Smbolo de interrogacin Beep Backspace Carcter nulo(null) Tab vertical Inserta cdigo hex hhh Signo de porcentaje

El formato de especificacin puede ser tambin as: %[flags][ancho][.presicion], entonces retomando un ejemplo anterior:
printf("El area es %6.4f unidades cuadradas\n",area);

Esta instruccin imprime en pantalla el valor de area en un espacio con ancho de 6 y precisin decimal de 4 posiciones. VARIABLES Una variable es un nombre que se le da a una localizacin especfica de memoria. Esta localizacin de memoria puede guardar varios valores dependiendo de cmo haya sido declarada la variable. En C, todas las variables deben declararse antes de ser usadas. Una declaracin de variables le

12

informa al compilador que tipo de variable est siendo usada. Todas las declaraciones de variables son instrucciones en C y por consiguiente deben finalizar con un punto y coma. Los cinco tipos bsicos de datos que soporta C son: char, int, float, double, void. El formato general para declarar una variable es el siguiente:
TipoVariable NombreVariable;

Un ejemplo de declaracin de un variables es: char ch;. El compilador interpreta esta instruccin como la definicin de la variable ch como tipo char (caracter o entero sin signo de 8 bits). CONSTANTES Una constante es un valor fijo que no puede ser alterado por el programa. Por ejemplo, 25 es una constante. Las constantes enteras son especificadas sin ninguna componente fraccional como -100 o 40. Constantes en punto flotante requieren punto decimal seguido por su componente fraccional. El nmero 456.75 es una constante en punto flotante. Los caracteres tambin son constantes y deben estar encerradas por comillas sencillas como 'A' o '&'. Cuando el compilador encuentra una constante en el programa, este decide qu tipo de constante es. El compilador C podr, por defecto, acomodar la constante dentro del tipo de dato ms pequeo que lo pueda contener. As que 15 es un tipo int, 64000 es un tipo long y 105020 es un tipo int32. Una constante es declarada usando la sentencia #define.
#define <etiqueta> valor

<etiqueta> define el nombre que usar a travs del programa, valor es el valor que se le asignara a <etiqueta>. Ejemplos:
#define VERDADERO 1 #define pi 3.14159265359

Usted puede especificar el tipo de constante usando los siguientes sufijos:


F U punto flotante unsigned FL L long double long

C le permite especificar constantes en los formatos hexadecimal y octal. Las constantes hexadecimales deben tener el prefijo '0x'. Por ejemplo 0xA4 es una constante hexadecimal valida. En adicin a las constantes numricas, C soporta constantes tipo string (cadena de caracteres). Las cuales son una serie de caracteres encerrados entre comillas dobles. Las constantes no son almacenadas en memoria, son resueltas en tiempo de compilacin. Para guardar constantes en la memoria ROM del chip, use la palabra clave const en la declaracin de una variable. Por ejemplo:
char const id[5]={"1234"};

13

Se estn usando cinco localizaciones de memoria para guardar la cadena porque esta debe terminar con el carcter null (\0) como se explicar ms adelante. COMENTARIOS Los comentarios son empleados para documentar el significado y operacin del cdigo fuente. Todos los comentarios son ignorados por el compilador. Un comentario puede ser puesto en cualquier parte del programa excepto en la mitad de alguna instruccin, nombre de funcin o nombre de variable. Los comentarios pueden abarcar muchas lneas y pueden ser usados para remover temporalmente una lnea de cdigo. Finalmente, los comentarios no se pueden anidar. Los comentarios tienen dos formatos. El primer formato es usado por todos los compiladores de C y es el siguiente:
/* Este es un comentario */

El segundo formato es soportado por la mayora de compiladores y es el siguiente:


// Este es un comentario

FUNCIONES Las funciones son los bloques de construccin bsicos de un programa en C. todos los programas en C contienen al menos una funcin, main(). La mayora de los programas que usted escribe contendr muchas funciones. El formato para un programa en C con muchas funciones es el siguiente:
void main() { } funcion1() { } funcion2() { }

main() es la primera funcin llamada cuando el programa se est ejecutando. Las otras funciones, funcion1() y funcion2(), pueden ser llamadas por cualquier otra funcin en el programa. Tradicionalmente main() no es llamada por ninguna otra funcin, sin embargo, no hay restricciones en C con respecto a esto. El siguiente es un ejemplo de dos funciones en C.
#include <stdio.h> void main() { printf("Me ");

14

funcion1(); printf("C."); } funcion1() { printf("gusta programar en"); }

Una cuestin que hay que tener en cuenta al escribe sus propias funciones es que cuando el corchete de cierre es alcanzado, el programa continuar ejecutndose una lnea despus del punto en el cual la funcin ha sido originalmente llamada. COMPATIBILIDAD CON HARDWARE El compilador necesita saber acerca del hardware para que el cdigo pueda ser compilado correctamente. Un programa tpico en el compilador C de CCS puede comenzar as:
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000)

La primera lnea incluye las definiciones especficas del dispositivo tales como nombre de pines, registros especiales, etc. La segunda lnea establece la palabra de configuracin del PIC, para este caso se deja al oscilador de cristal como oscilador del dispositivo, se desactiva el Watch Dog Timer, se habilita el Power-up Timer y se desactiva la proteccin de cdigo. La ltima lnea le informa al compilador la velocidad del oscilador. Las siguientes lneas son otro ejemplo:
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7) #use i2c(master,scl=PIN_B6,sda=PIN_B7) //config. RS232 //config. I2C

En adicin, las variables de C pueden ser creadas y mapeadas a registros del hardware. Estas variables pueden ser bits o bytes. Despus que son definidas, pueden ser utilizadas en un programa como cualquier otra variable. Ejemplo:
#bit carry=3.0 #byte portb=6 #byte intcon=11

PALABRAS CLAVES DE C El estndar C ANSI define 32 palabras clave para usar en el lenguaje C. En C, ciertas palabras son reservadas del compilador para definir tipos de datos y para su uso en loops (bucles o lazos). Todas las palabras claves de C deben escribirse en minscula para que el compilador las reconozca. Tpicamente, la mayora de los compiladores de C agregan varias palabras adicionales que toman ventaja de la arquitectura del procesador. La siguiente es una lista de las palabras claves de C:

15

auto break case char const continue default do

double else enum extern float for goto if

int long register return short signed sizeof static

struct switch typedef union unsigned void volatile while

Ejercicios 1. Escriba un programa en Microsoft Visual C++ Express que imprima en pantalla su nombre, su documento de identidad y su email e lneas diferentes. 2. Escriba un programa en Microsoft Visual C++ Express que declare una variable tipo entera llamada year. Esta variable debe entregar el valor del ao actual y luego, usando la funcin printf(), mostrar el valor del ao en pantalla. El resultado de su programa debe parecerse a lo siguiente: El ao es 2011.

16

2. VARIABLES Un aspecto importante del lenguaje C es como este almacena datos. Este captulo examinar ms en detalle cmo se usan las variables en C para almacenar datos. TIPOS DE DATOS El lenguaje de programacin C soporta cinco tipos de datos bsicos (como se haba comentado en el capitulo anterior) y cuatro tipos de modificadores. La Tabla 3 muestra la definicin de los tipos de datos bsicos y el tipo de modificador.
Tabla 3. Tipos de datos y modificadores en C. Tipo Significado Carcter Dato carcter Entero Nmeros enteros con signo Flotante Nmeros con punto flotante Doble precisin Nmeros con punto flotante de doble precisin Vacio (void) Sin valor Con signo Nmeros positivos o negativos Sin signo Solo nmeros positivos Largo Duplica el tamao de un nmero corto Mitad del tamao de un nmero

Palabra clave char int float double void signed unsigned long short

Cada tipo de dato representa un rango particular de nmeros, que pueden cambiar dependiendo del modificador usado. La Tabla 4 muestra los posibles rangos de valores para algunos tipos de datos bsicos y los modificadores en CCS C.
Tabla 4. Rangos de valores de los tipos de datos en CCS C. Tipo Tamao bit short (int1) 1 int (int8) 8 long (int16) 16 long long (int32) 32 float (float32) 32 char (unsigned int8) 8 void signed int8 8 signed int16 16 signed int32 32 Rango 0 a 1 0 a 255 0 a 65535 0 a 4294967295 -1.5E45 a 3.4E38 0 a 255 Sin valor -128 a 127 -32768 a 32767 -2147483648 a 2147483647

NOTA: revisar la documentacin del compilador C para tipos de datos y rangos numricos actualizados. Todos los tipos, excepto float, por defecto son unsigned. C permite una notacin ms corta para los tipos unsigned int, short int, y long int. Simplemente use las palabras unsigned, short o long sin la palabra int. Para realizar las operaciones aritmticas de una forma sencillas para la CPU, C representa todos los nmeros negativos en el formato complemento a 2. Para encontrar el complemento a 2 de un nmero simplemente invierta todos los bits y sume 1 al resultado. Por ejemplo, para convertir el nmero con signo +29 en complemento a 2 se procede as:

17

00011101= 11100010 + 1 ________ 11100011=

29 se invierten todos los bits se suma 1 -29

Ejercicios 1. Escriba la siguiente declaracin de otra manera. long int i; 2. Para entender la diferencia entre un nmero con signo y uno sin signo, escriba el siguiente programa en Microsoft Visual C++ Express. El entero sin signo 35000 es representado por -30536 en el formato entero con signo.
#include <stdio.h> void main() { short int i; // entero con signo equiv. a signed int16 en CCS unsigned short int u; // entero sin signo equiv. a int16 en CCS u = 35000; i = u; printf("%d %u\n",i,u); getchar();//detiene la ejecucin hasta oprimir ENTER }

DECLARACION DE VARIABLES Las variables pueden ser declaradas en dos lugares bsicos: dentro de una funcin o por fuera de todas las funciones. Las variables son llamadas locales o globales respectivamente. Las variables son declaradas de la siguiente forma:
TipoVariable NombreVariable;

Donde TipoVariable es uno de los tipos de datos validos en C y NombreVariable es el nombre asignado a la variable. Las variables locales (declaradas dentro de una funcin) solo pueden ser usadas por las instrucciones dentro de la funcin donde ha sido declarada. El valor de una variable local no puede ser accesado por otras funciones por fuera de la funcin de origen. La cosa ms importante a recordar acerca de las variables locales es que son creadas al momento de entrar a la funcin y destruidas cuando la funcin ha finalizado. Las variables locales deben declararse al comienzo de la funcin antes de las instrucciones. Es aceptable para variables locales que estas tengan nombres iguales en diferentes funciones. Considere el siguiente ejemplo.
#include <stdio.h> void f2() { int cont; for (cont=0; cont<10; cont++)

18

printf("%d \n",cont); } void f1() { int cont; for (cont=0; cont<10; cont++) f2(); } int main() { f1(); getchar(); return 0; }

Este programa imprime los nmeros del 0 al 9 en pantalla 10 veces. La operacin del programa no es afectada por la variable cont que est definida en ambas funciones. Las variables globales, por otra parte, pueden ser usadas por todas las funciones definidas en el programa. Las variables globales deben ser declaradas antes que cualquier funcin que las vaya a usar. Lo ms importante, las variables locales no son destruidas hasta que la ejecucin del programa haya sido completada. El siguiente ejemplo muestra como se emplean las variables globales.
#include <stdio.h> int max; void f1() { int i; for (i=0; i<max; i++) printf("%d ",i); } int main() { max = 10; f1(); return 0; }

En este ejemplo, ambas funciones main() y f1() hacen referencia a la variable max. La funcin main() asigna un valor a max y la funcin f1() usa el valor de max para controlar el bucle del for. Ejercicios 1. Cules son las diferencias principales entre variables locales y variables globales? 2. Ambos tipos de variables podran compartir el mismo nombre en C?. Escriba el siguiente programa.
#include <stdio.h> int cont; void f1()

19

{ int cont; cont = 100; printf("contador en f1(): %d\n ",cont); } int main() { cont = 10; f1(); printf("contador en main(): %d\n ",cont); return 0; }

En la funcin main() la referencia a cont es una variable global. En la funcin f1() la variable local cont domina sobre el uso de la variable global. ASIGNACION DE VALORES A VARIABLES Hasta el momento se ha discutido solo como declarar una variable en el programa y no realmente como se asignan valores a esta. La asignacin de valores a variables es sencilla, as:
NombreVariable = valor;

Debido a que la asignacin de valores a variables se hace con una instruccin, se debe incluir el punto y coma al final. Un ejemplo de cmo asignar el valor 100 a la variable tipo entera cont es:
cont = 100;

El valor 100 es una constante. Diferentes tipos de constantes existen en C. una constante tipo carcter esta especificado porque debe estar encerrada en comillas sencillas, como 'M'. Todos los nmeros enteros se emplean cuan se asignan valores a enteros. Nmeros en punto flotante deben usar valores con punto decimal. Por ejemplo, para decirle a C que el valor 100 est en punto flotante, use 100.0. A una variable tambin se le puede asignar el valor que tiene otra variable diferente. El siguiente programa ilustra esta asignacin.
void main() { int i; int j; i=0; j=i; }

Ejercicios 1. Escriba un programa en Microsoft Visual C++ Express que declare una variable tipo entero llamada cont. De un valor de 100 a cont y use la funcin printf() para imprimir el valor en pantalla. La salida debe lucir as:
100 es el valor de cont

20

2. Escriba un programa en Microsoft Visual C++ Express que declare tres variables, char, float, y double con los nombres ch, f, y d. Asignar el valor 'R' a ch, 50.5 a f, y 156.007 a d. Imprima el valor de todas las variables en pantalla. La salida debe lucir como la siguiente: ch esta asignada con r f esta asignada con 50.5 d esta asignada con 156.007 ENUMERACION En C, es posible crear una lista de constantes enteras. Este tipo de declaracin es llamada enumeracin. La lista de constantes creadas con enumeracin puede ser usada en cualquier lugar. La forma general para crear una enumeracin es la siguiente:
enum nombre {lista de enumeracion} variable(s);

La lista de variables es opcional en la enumeracin. Las variables de enumeracin pueden contener solo los valores que estn definidos en la lista de enumeracin. Por ejemplo, en la instruccin:
enum TipoColor {rojo,verde,amarillo} color;

A la variable color solo se le pueden asignar los valores rojo, verde o amarillo (esto es, color = rojo;). El compilador asignara valores enteros a la lista de enumeracin comenzando con 0 en la primera entrada. Cada entrada es una unidad ms grande que la anterior. Por consiguiente, en el ejemplo anterior rojo es 0, verde es 1 y amarillo es 2. Estos valores por defecto pueden cambiarse especificando un valor para la constante. El siguiente ejemplo ilustra la tcnica.
enum TipoColor {rojo,verde=9,amarillo} color;

Esta instruccin asigna 0 a rojo, 9 a verde y 10 a amarillo. Una vez la enumeracin es definida, el nombre puede usarse para crear variables adicionales en otros puntos del programa. Por ejemplo, la variable MiColor puede crearse con la enumeracin TipoColor asi:
enum TipoColor MiColor;

La variable tambin puede probarse contra otro variable diferente:


if (color==fruta) //haga esto

Esencialmente, enumeracin ayuda a documentar el cdigo. En lugar de asignar un valor a una variable, una enumeracin puede usarse para distinguir el significado del valor. 21

Ejercicios 1. Crear una enumeracin con la familia de microcontroladores PIC16F8X. 2. Crear una enumeracin con la denominacin de la ms baja a la ms alta de la moneda colombiana. 3. El siguiente fragmento de cdigo es correcto? Por qu?
enum {PIC16C51,PIC16C52,PIC16C53} dispositivo; dispositivo = PIC16C52; printf("el primer PIC es %s\n", dispositivo);

TYPEDEF La declaracin typedef es usada para definir nuevos tipos de datos por medio de tipos existentes. Su formato es el siguiente:
typedef NombreAnterior NombreNuevo

El nuevo nombre puede usarse para declarar variables. Por ejemplo, el siguiente programa usa el nombre smallint para el tipo signed char.
#include <stdio.h> typedef signed char smallint; void main() { smallint i; for(i=0;i<10;i++) printf("%d ",i); }

Cuando se usa typedef, usted debe recordar dos puntos clave: La declaracin typedef no desactiva el nombre original o el tipo, en el ejemplo anterior signed char todava es un tipo valido; varias declaraciones typedef pueden usarse para crear nuevos nombres para el mismo tipo original. Typedef es tpicamente usado por dos razones. La primera para crear un programa portable. Si el programa que se est escribiendo va a ser usando en maquinas con enteros de 16-bits y de 32bits, usted querra asegurar que solo los enteros de 16-bits van a ser empleados. La segunda razn para usar la declaracin typedef es para ayudarle a documentar su cdigo. Ejercicios 1. Crear un nuevo nombre para el tipo unsigned long que se llame UL. Use este typedef en un programa corto que declare una variable usando UL, asigne un valor a est y muestre el valor en pantalla. 2. El siguiente fragmento de cdigo es correcto?
typedef int altura; typedef altura largo; typedef largo profundo; profundo d;

22

CONVERSION DE TIPO C le permite combinar diferentes tipos de datos en una sola expresin. Por ejemplo, el siguiente ejemplo es un fragmento de cdigo valido:
char ch = '0'; int i = 15; float f = 25.6

La combinacin de tipos de datos es gobernado por un conjunto estricto de reglas de conversin que le informan al compilador como resolver las diferencias. La primera parte del conjunto de reglas es una promocin de tipo. El compilador C automticamente promover un dato char o short int en una expresin a un dato int cuando la expresin sea evaluada. Una promocin de tipo solo es vlida durante la evaluacin de la expresin; la variable por s misma no se hace fsicamente ms larga. Ahora que la promocin de tipo automtica ha sido completada, el compilador C convertir todas las variables en una expresin sobre el tipo de la variable ms larga. Esta tarea se completa sobre una operacin por medio de operaciones bsica. El siguiente fragmento de cdigo muestra como imprimir la porcin entera de un nmero en punto flotante:
float f; f = 100.2; printf("%d",(int)f);

El numero 100 se imprimir en pantalla al ejecutarse el cdigo. Si dos enteros de 8-bits son multiplicados, el resultado ser un valor de 8-bits. Si el resultado es asignado a un tipo long, el resultado todava seguir siendo un entero de 8-bits, debido a que la aritmtica es desarrollada antes de que el resultado sea asignado a la nueva variable.
int a = 250, b = 10; long c; c = a * b;

El resultado ser 196 el cual es errneo. Entonces si necesita un valor tipo long como respuesta, entonces al menos un valor necesita definirse inicialmente como long o escribir lo siguiente.
c = (long) a * b;

El resultado ser 2500 porque a primero ha sido convertido multiplicacin se haya completado. CLASES DE ALMACENAMIENTO PARA VARIABLES

a tipo long antes que la

Cada variable y funcin en C tiene dos atributos, el tipo y clase de almacenamiento. El tipo ya ha sido discutido como char, int, etc. Hay cuatro clases de almacenamiento: automtica, externa, esttica y registro. Estas clases tienen los siguientes nombres en C:
auto extern static register

23

Auto Las variables declaradas dentro de una funcin son por defecto auto, tal que.
{ char c; int a,b,e; }

Es lo mismo que:
{ auto char c; auto int a,b,e; }

Cuando un bloque de cdigo es introducido, el compilador asigna espacio RAM para las variables declaradas. Las localizaciones RAM son utilizadas en ese bloque local de cdigo y puede ser usado por otros bloques de cdigo. Extern La palabra clave extern declara una variable o una funcin y especfica que esta variable tiene conexin externa. Esto significa que su nombre es visible en otros archivos diferentes en donde fue definida. Extern no est definida en el compilador C de CCS. Static La clase static define variables globales activas que son inicializadas a cero, a menos que se defina lo contrario.
void test() { char x,y,z; static int cont = 4; printf("cont = %d\n",++cont); }

La variable cont es inicializada una vez, y despus se incrementa cada vez que la funcin test es llamada. Register La clase register se origina de aplicaciones de sistemas donde esta puede ser usada para reservar memoria de alta velocidad para variables usadas frecuentemente. Register no est definida en el compilador C de CCS.

24

3. FUNCIONES Las funciones son los bloques bsicos del lenguaje C. todas las instrucciones deben estar dentro de funciones. En este captulo se discutir como pasar argumentos a funciones y como recibir un argumento de una funcin. FUNCIONES En secciones anteriores, se estudiaron varios ejemplos de funciones siendo llamadas desde un programa principal, por ejemplo:
void main() { f1(); } int f1() { return 1; }

En realidad, este programa producir un error. La razn es que la funcin f1() debe declararse o definirse antes de usarse, tal como las variables. Si est empleando una funcin estndar de C, el archivo de encabezado que incluye en el comienzo del programa ya ha informado al compilador acerca de la funcin. Si est empleando una funcin que usted ha creado, hay dos caminos para corregir este error. Una forma es usar funcin prototipo, que sern explicadas en la siguiente seccin. La otra forma es reorganizar el programa de la siguiente manera:
int f1() { return 1; } void main() { f1(); }

El error ya no se genera porque la funcin f1() es definida antes que sea llamada en el main(). FUNCION PROTOTIPO Hay dos mtodos empleados para informarle al compilador que tipo de valor retorna una funcin. La forma general es la siguiente:
tipo NombreFuncion();

Por ejemplo, la declaracin en sum() podra decirle al compilador que la funcin sum() retorno un entero. La segunda forma para informarle al compilador acerca del valor que retorna una funcin es la funcin prototipo. Una funcin prototipo no solo entrega el valor a retornar de la funcin, sino que tambin declara el nmero y tipo de los argumentos que la funcin acepta. El prototipo debe corresponder con la declaracin de la funcin exactamente.

25

Los prototipos le ayudan a identificar errores en el programa reportando cualquier conversin ilegal de tipos entre los argumentos que pasan a una funcin y en su declaracin. Adems reporta si el nmero de argumentos enviados a una funcin no concuerdan con los especificados en la declaracin de la funcin. El formato general para una funcin prototipo es como se muestra a continuacin:
tipo NombreFuncion(tipo var1, tipo var2, tipo var3);

En el ejemplo anterior, el tipo de cada variable puede ser diferente. Un ejemplo de una funcin prototipo se entrega en el siguiente programa. La funcin calcula el volumen definido por largo, ancho y alto.
int volumen(int s1, int s2, int s3); void main() { int vol; vol = volumen(5,7,12); printf("El volumen es: %d\n",vol); } int volumen(int s1, int s2, int s3) { return s1*s2*s3; }

Note que return usa una expresin en lugar de una constante o variable. La importancia de los prototipos no es significativa en programas cortos, como los que se han realizado hasta ahora. Sin embargo, cuando el tamao de los programas crezca de unas pocas lneas a cientos de stas, la importancia de los prototipos en la depuracin de errores ser evidente. Ejercicios 1. Para mostrar como los errores son capturados por el compilador, cambie el programa anterior para mandar cuatro parmetros a la funcin volumen:
vol = volumen(5,7,12,15);

2. El siguiente programa es correcto? por qu?


double myfunc(void); void main() { printf("%f\n",myfunc(10.2)); } double myfunc(double num) { return num/2.0; }

26

VOID Se hace una excepcin cuando una funcin no tiene ningn parmetro de entrada ni de salida. Esta funcin podra declararse como sigue:
void NombreFuncion(void)

Un ejemplo de esto podra ser:


double pi(void) //definicion de la funcion { //sin parametros de entrada return 3.1415926536; //pero retorna el valor de pi } void main() { double pi_val; pi_val = pi(); //llama el valor de pi printf("pi= %f\n",pi_val); }

USANDO LOS ARGUMENTOS DE UNA FUNCION Un argumento de funcin es un valor que se le pasa a una funcin cuando se le hace un llamado. C permite de ningn a varios argumentos en la declaracin de una funcin. El nmero de argumentos que una funcin puede aceptar depende del compilador, pero el estndar C ANSI especifica que una funcin debe ser capaz de aceptar al menos 31 argumentos. Cuando una funcin es definida, deben ser declaradas variables especiales para recibir parmetros. Estas variables especiales son definidas como parmetros formales. Los parmetros son declarados entre los parntesis que siguen al nombre de la funcin. Por ejemplo, la funcin que sigue calcula e imprime la suma de dos enteros que son enviados a la funcin cuando esta es llamada.
void sum(int a, int b) { printf("%d\n",a+b); }

Un ejemplo de cmo la funcin podra ser llamada en un programa es el siguiente:


void sum(int a, int b); //esto es una funcion prototipo void main() { sum(1,10); sum(15,6); sum(100,25); } void sum(int a, int b) { printf("%d\n",a+b); }

27

Cuando sum() es llamada, el compilador copiar el valor de cada argumento dentro de las variables a y b. es importante recordar que los valores pasados a una funcin (1, 10, 15, 6, 100, 25) son llamadas argumentos y las variables a y b son los parmetros formales. Las funciones pueden pasar argumentos en dos formas. La primera forma es nombrada como llamado por valor (call by value). Este mtodo copia el valor de un argumento dentro de un parmetro formal en una funcin. Cualquier cambio hecho a un parmetro formal no afecta el valor original de la rutina de llamado. El segundo mtodo es nombrado como llamado por referencia (call by reference). En este mtodo, la direccin del argumento es copiada dentro del parmetro formal de una funcin. Dentro de esta funcin, el parmetro formal se usa para acceder a la variable actual en la rutina de llamado. Esto significa que se pueden realizar cambios a la variable usando el parmetro formal. Este mtodo se discutir en el captulo sobre apuntadores. Por ahora, solo se usar el primer mtodo cuando se pasen argumentos a una funcin. Ejercicios 1. Escriba una funcin que tome un argumento entero e imprima su valor en pantalla. 2. Cul es el error en el siguiente programa?
imprimirEsto(int num) { printf("%d\n",num); } void main() { imprimirEsto (156.7); }

USANDO FUNCIONES QUE RETORNAN VALORES Cualquier funcin en C puede retornar un valor a la rutina que realiza el llamado. Tpicamente, la funcin es puesta al lado derecho del signo igual (=). El valor de retorno no necesariamente necesita usarse en una instruccin asignada, pero puede usarse en una funcin printf(). El formato general para informarle al compilador que una funcin retorna un valor es el siguiente:
tipo NombreFuncion(parmetros formales) { <instrucciones> return valor; }

Donde tipo especifica el tipo de dato del valor que retorna la funcin. Una funcin puede retornar cualquier tipo de dato excepto un arreglo (array). Si no se especifica un tipo de dato, entonces el compilador C asume que la funcin retorna un entero (int). Si su funcin no retorna un valor, el estndar C ANSI especifica que la funcin debe retornar nada (void). Esto explcitamente le informa al compilador que la funcin no retorna un valor. El siguiente ejemplo muestra un uso tpico para una funcin que retorna un valor.
#include <stdio.h> #include <math.h>

28

void main() { double result; result = sqrt(16.0); printf("%f\n",result); }

Este programa hace un llamado a la funcin sqrt() la cual retorna un nmero en punto flotante. Este nmero es asignado a la variable result. Note que el archivo encabezado math.h es incluido debido a que contiene la informacin acerca sqrt() usada por el compilador. Es importante que el tipo de dato que retorna la funcin sea el mismo tipo de dato de la variable a la cual se le asignar el valor de retorno. Lo mismo se tiene que garantizar para los argumentos que se entregan a la funcin. Entonces, Cmo se debe retorna un valor desde una funcin? La forma general es la siguiente:
return NombreVariable;

Donde NombreVariable es una constante, variable o cualquier expresin valida en C que tenga el mismo tipo de dato que el valor de retorno. El siguiente ejemplo muestra los dos tipos de funciones.
int func(); int sum(int a, int b); void main() { int num; num = func(); printf("%d\n", num); num = sum(5,127); printf("%d\n",num); } int func() { return 6; } int sum(int a, int b) { int result; result = a + b; return result; }

Una cosa importante para resaltar, es cuando una declaracin return es encontrada, la funcin retorna inmediatamente a la rutina de llamado. Cualquier instruccin despus del return no ser ejecutada. El valor retornado de una funcin no requiere ser asignado a una variable o usado en alguna expresin, sin embargo, el valor se pierde de esta manera. Ejercicio 1. Cul es el error en la siguiente funcin?

29

void main() { double result; result = f1(); printf("%f\n",result); } int f1() { return 60; }

DECLARACION CLASICA Y MODERNA DE FUNCIONES La versin original de C usa un mtodo diferente para la declaracin de parmetros formales. Esta forma, llamada la forma clsica, y es la siguiente:
tipo tipo tipo tipo { } NombreFuncion(var1,var2,,varn) var1; var2; varn; <instrucciones>

Note que la declaracin est dividida en dos partes. Solo los nombres de los parmetros son incluidos dentro de los parntesis. Por fuera de los parntesis el tipo de datos y los nombres de los parmetros formales son especificados. La forma moderna, que se ha usado en ejemplos anteriores, es la siguiente:
tipo NombreFuncion(tipo var1,tipo var2,,tipo varn)

En este tipo de declaracin de funcin, tanto el tipo de dato como los nombres de los parmetros formales son especificados dentro de los parntesis. El estndar C ANSI permite ambos tipos de declaracin de funciones. El propsito es mantener la compatibilidad con programas C antiguos donde hay literalmente millones de lneas de cdigo. Si encuentra la forma clsica en un fragmento de cdigo, no tiene de que preocuparse; su compilador C debe ser capaz de manejarlo. Como consejo, usted debera usar la forma moderna cuando escriba su cdigo. Ejercicios 1. Qu es una funcin prototipo y cules son los beneficios de usarla? 2. Convierta el siguiente programa de la forma clsica de declaracin de funciones a la forma moderna.
void main(void) { printf("area = %d\n", area(10,15));

30

} area(l,w) int l,w; { return 1*w; }

PASANDO CADENAS CONSTANTES A FUNCIONES Debido a que los microcontroladores PIC tienen limitaciones en cuanto a acceso ROM, cadenas de caracteres constantes (constant strings) no pueden ser pasadas a funciones de la manera ordinaria. El compilador C de CCS maneja esta situacin de una manera no estndar. Si una cadena de caracteres es pasada a una funcin que permita solo un parmetro carcter, entones la funcin es llamada para cada carcter en la cadena de caracteres. Por ejemplo:
void lcd_putc(char c) { . . . . . } lcd_putc("abcd");

Es lo mismo que:
lcd_putc("a"); lcd_putc("b"); lcd_putc("c"); lcd_putc("d");

31

4. OPERADORES En C, las expresiones juegan un papel importante. La razn principal es que C define ms operadores que la mayora de lenguajes. Una expresin es una combinacin de operadores y operandos. En la mayora de los casos, los operadores C siguen las reglas del algebra y deben parecerle familiares. OPERADORES ARITMETICOS El lenguaje C define cinco operadores aritmticos para la suma, resta, multiplicacin, divisin y modulo.
+ * / % suma resta multiplicacin divisin modulo

Los operadores +,-,* y / pueden ser usados con cualquier tipo de dato. El operador modulo, %, puede usarse solo con nmeros enteros. El operador modulo entrega residuo de una divisin entre enteros. Por consiguiente, este operador no tiene significado cuando se aplica a nmeros en punto flotante. El operador puede usarse de dos formas, la primera siendo el operador de resta. La segunda forma es para invertir el signo de un nmero. El siguiente ejemplo ilustra las dos formas de manejar el signo menos:
a = a b; a = -a; //resta //inversin de signo

Los operadores aritmticos pueden usarse con cualquier combinacin de constantes y/o variables. Por ejemplo, la siguiente expresin es una instruccin valida en C.
result = cont 163;

C adems permite algunas abreviaturas cuando usa operadores aritmticos. Uno de los ejemplos anteriores, a = a b; puede escribirse a-=b;. Este mtodo puede usarse con los operadores +,-,* y /. El siguiente ejemplo muestra varias abreviaturas.
a*=b a/=b a+=b a-=b a%=b a<<=b a>>=b a&=b a|=b a^=b es lo mismo que a= a*b a=a/b a=a+b a=a-b a=a%b a=a<<b a=a>>b a=a&b a=a|b a=a^b

Tomando el cdigo C y comparndolo con su versin en Assembler se muestra a continuacin como es lograda la funcin aritmtica dentro del PIC. 32

int a,b,c,; a = b + c; Se convierte en: 0007: 0008: 0009: a = b - c; Se convierte en: 0007: 0008: 0009: 000A:

MOVF ADDWF MOVWF

b,W c,W a

;carga b ;suma c con b ;guarda en a

MOVF MOVWF MOVF SUBWF

b,W a c,W a,F

;carga b ;guarda en a ;carga c ;resta c de a

Ejercicios 1. Escriba un programa en Microsoft Visual C++ Express que encuentre el residuo de 5/5, 5/4, 5/3, 5/2, y 5/1. 2. Escriba un programa en Microsoft Visual C++ Express que calcule el nmero de segundos de un ao. OPERADORES RELACIONALES Los operadores relacionales en C comparan dos valores y devuelven un resultado verdadero o falso basado en la comparacin. Los operadores relacionales son los siguientes:
> >= < <= == != mayor que mayor o igual que menor que menor o igual que igual que diferente que

Una cosa a resaltar acerca de los operadores relacionales es que el resultado de la comparacin es siempre 0 o 1, aun cuando C defina verdad como cualquier valor diferente de cero. Falso siempre es definido como cero. Los siguientes ejemplos muestran algunas expresiones con operadores relacionales.
var > 15; //si var es menor o igual a 15, el resultado es 0 (falso) var != 15; //si var es mayor o menor a 15, el resultado es 1 (verdadero)

Ejercicios 1. Reescriba la siguiente expresin usando un operador relacional diferente.


cont != 0;

2. Cundo la siguiente expresin es verdadera o falsa?


cont >= 35;

33

OPERADORES LOGICOS Los operadores lgicos soportan las operaciones lgicas bsicas AND, OR, y NOT. De nuevo, estos operadores retornan 0 para falso o 1 para verdadero. Los operadores lgicos y la tabla de verdad para estas operaciones se muestran a continuacin:
p 0 0 1 1 q 0 1 0 1 AND p && q 0 0 0 1 OR p || q 0 1 1 1 NOT !p 1 1 0 0

Los operadores lgicos y relacionales estn estrechamente acoplados cuando se evala una expresin. Un ejemplo de juntar estos operadores es el siguiente:
cont>max || !(max==57) && var>=0

Otra parte de C que emplea los operadores relacionales y lgicos son las instrucciones de control de programa que se estudiarn en el siguiente captulo. Ejercicios 1. Reescriba la siguiente expresin usando cualquier combinacin de operadores relacionales y lgicos.
cont == 0; result <= 5;

2. Dado que C no entrega explcitamente una funcin OR exclusiva, escriba una funcin XOR basada en la siguiente tabla de verdad.
p 0 0 1 1 q 0 1 0 1 XOR 0 1 1 0

OPERADORES DE BIT A BIT C contiene seis operadores especiales que desarrollan operaciones bit a bit sobre nmeros. Estas operaciones de bit pueden usarse solo sobre tipos de datos enteros y caracteres. El resultado de usar cualquiera de estas operaciones es una operacin bit a bit de los operandos. Los operadores de bit a bit son los siguientes:
& | ^ ~ >> << AND bit a bit OR bit a bit XOR bit a bit complemento a uno desplazamiento hacia la derecha desplazamiento hacia la izquierda

El formato general para el uso de los operadores de desplazamiento es el siguiente:


variable << expresin

34

variable >> expresin

El valor de expresin determina cuantos lugares hacia la izquierda o derecha variable es desplazada. Cada desplazamiento hacia la izquierda causa que todos los bits de corran una posicin de bit a la izquierda, y un cero es insertado sobre el lado derecho. El bit que es desplazado fuera del final de variable se pierde. La nica situacin a resaltar acerca del uso de los desplazamientos es que un desplazamiento hacia la izquierda es equivalente a multiplicar un nmero por 2 y un desplazamiento hacia la derecha es equivalente a dividir un nmero por 2. Los operadores de desplazamiento son casi siempre ms rpidos que su equivalente operacin aritmtica debido a cmo trabaja la CPU. Un ejemplo de todos los operadores bit a bit se muestra a continuacin.
AND 00000101 (5) 00000110 (6) -----------00000100 (4) XOR 00000101 (5) 00000110 (6) -----------00000011 (3) DESP IZQ. 00000101 (5) << 2 -----------00010100 (20) OR 00000101 (5) 00000110 (6) -----------00000111 (7) NOT (complemento a uno) 00000101 (5) -----------11111010 (250) DESP DER. 00000101 (5) >> 2 -----------00000001 (1)

&

NOTA: no realice desplazamientos mas all de los bits que el operando posea, debido a que se obtiene un resultado indefinido.
a = b | c; Se convierte en: 0007: 0008: 0009: a = b & c; Se convierte en: 0007: 0008: 0009: a = b >> 3; Se convierte en: 0007: 0008: 0009: 000A: 000B: 000C: 000D: j = ~a;

MOVF IORWF MOVWF

b,W c,W a

;carga b ;opera b OR c ;guarda en a

MOVF ANDWF MOVWF

b,W c,W a

;carga b ;opera b AND c ;guarda en a

MOVF MOVWF RRF RRF RRF MOVLW ANDWF

b,W a a,F a,F a,F 1F a,F

;carga b ;guarda en a ;rota el contenido ;hacia la derecha ;tres veces ;enmascara el contenido ;del registro para a

35

Se convierte en: 0007: 0008: 0009:

MOVF MOVWF COMF

a,W j j,F

;carga a ;guarda en j ;complementa j

Ejercicios 1. Escriba un programa que invierta los bits ms significativos (MSB) de un dato tipo signed char. 2. Escriba un programa que imprima la representacin binaria de un nmero tipo char. OPERADORES INCREMENTALES Y DECREMENTALES Cmo podra usted incrementar o decrementar en una unidad una variable? Probablemente una de dos formas le vendra a la mente. Tal vez las siguientes:
a = a + 1; o a = a - 1;

Una vez ms, los creadores de C han llegado con una notacin abreviada para incrementar o decrementar un nmero. Los formatos generales son los siguientes:
a++; a--; o o ++a; --a; para incrementar para decrementar

Cuando los signos ++ o -- preceden la variable, la variable es incrementada y despus el valor es usado en una expresin. Cuando los signos ++ o -- van despus de la variable, el valor de la variable se usa en la expresin y luego se incrementa.
int j, a = 3; 0007: 0008: j = ++a; 0009: 000A: 000B: j = a++; 000C: 000D: 000E: MOVLW MOVWF INCF MOVF MOVWF MOVF INCF MOVWF 03 a a,F a,W j a,W a,F j

;registro asignado a a ;a = 4 ;carga a en w ;almacena w en j ;carga el valor de a en w ;a = 5 ;almacena w en j, j = 4

NOTA: no utilice la siguiente expresin.


a = a++;

Porque se genera el siguiente error en el cdigo generado:


MOVF INCF MOVWF a,W a,F a ;valor de a cargado en w ;valor de a incrementado ;el valor previo es recargado sobrescribiendo el ;valor incrementado

36

El siguiente ejemplo ilustra los dos usos.


#include <stdio.h> void main(void) { int i,j; i = 10; j = i++; printf("i = %d, j = %d\n",i,j); i = 10; j = ++i; printf("i = %d, j = %d\n",i,j); }

El primer printf() imprimir un 11 para i y un 10 para j. el segundo printf() imprimir un 11 para ambos i y j. Mezclando operadores
Escribiendo sum = a+b++ sum = a+b-sum = a+ ++b sum = a+ --b Se obtine sum = a+b sum = a+b b = b+1 b = b-1 b = b = sum sum b+1 b-1 = a+b = a+b

Ejercicios 1. Reescriba los operadores de asignacin en el siguiente programa para incrementar o decrementar las variables.
void main(void) { int a, b; a = 1; a = a+1; b = a; b = b-1; printf("a=%d, b=%d\n", a,b); }

2. Cules son los valores de a y b despus de que el siguiente segmento de cdigo se ha ejecutado?
a = 0; b = 0; a = ++a + b++; a++; b++; b = --a + ++b;

PRECEDENCIA DE OPERADORES La precedencia se refiere al orden en el cual los operadores son procesados por el compilador C. por ejemplo, si la expresin a+b*c se encontrar en su programa, Qu operacin ocurrira en primer lugar, la suma o la multiplicacin? El lenguaje C mantiene precedencia para todos los operadores. La siguiente lista muestra la precedencia desde la ms alta a la ms baja. 37

Prioridad 1 2 3 4 5 6 7 8 9 10 11 12 13

Operador () ++ -sizeof & * + - ~ ! ++ -* / % + << >> < > <= >= == != & ^ | && || = *= /= %= += -= <<= >>= $= ^= |=

Ejemplo (a+b)/c a=-b a%b a+b a=b>>c a>=b a==b a=b&c a=b^c a=b|c a=b&&c a=b||c a+=b

parentesis mas/menos/NOT/complemento incremento/decremento/sizeof multi/dividir/modulo suma/resta Desp izg. o derecho mayor/menor/igual que AND bit a bit XOR bit a bit OR bit a bit AND lgico OR lgico asignacin

Algunos de estos operadores todava no los hemos estudiado, pero no se preocupe, los estudiaremos ms adelante. Los parntesis pueden usarse para fijar el orden especfico en el cual las operaciones se lleven a cabo. Un par de ejemplos del uso del parntesis para clarificar o cambiar la precedencia de una declaracin son los siguientes:
10-2*5 = 0 (10-2)*5 = 40 cont*sum+88/val-19%cont (cont*sum) + (88/val) (19%cont)

38

5. DECLARACIONES DE CONTROL DEL PROGRAMA En este captulo aprender acerca de las instrucciones que C usa para controlar el flujo de ejecucin en un programa. Adems aprender como los operadores relacionales y lgicos se usan con estas declaraciones de control. DECLARACION if La declaracin if es una instruccin del tipo condicional. El bloque de cdigo asociado con la instruccin if es ejecutado basado en el resultado de una condicin. Nuevamente, cualquier valor diferente de cero es verdadero y cualquier valor cero es falso. El formato ms sencillo es el siguiente:
if (expresion) { . instruccones; . }

Los corchetes {} se usan para encerrar el boque de cdigo. Esto le informa al compilador que si la expresin es verdadera, ejecute el cdigo dentro de los corchetes. Ejemplo:
if (cont < 0) { Cont = 0; printf("cuenta descendente\n"); } if (TestMode == 1) { ... Haga esto }

Otros operadores de comparacin usados en la instruccin if son los siguientes:


x x x x x x x x == y != y > y < y <= y >= y && y || y x igual a y x es diferente a y x es mayor que y x es menor que y x es menor o igual que y x es mayor o igual que y AND lgica OR lgica

Un ejemplo de una de estas funciones convertida en Assembler es el siguiente:


int j, a = 3; 0007: 0008: if (j == 2) 0009: 000A: 000B: 000C: { j = a; MOVLW MOVWF MOVLW SUBWF BTFSS GOTO 03 a 02 j,W STATUS,Z 00F ;carga a con 3 ;carga w con 2 ;testeo entre w y j ;si es cero salta ;j no es igual a 2

39

000D: 000E: } 000F:

MOVF MOVWF

a,W j

;como es cero ;carga a en j

Ejercicios 1. Cules de las siguientes expresiones resultan en un valor verdadero?


a. b. c. d. e. 0 1 -1 5*5<25 1==1

2. Escriba una funcin que le informe cuando un nmero sea par o impar. La funcin retorna 0 cuando el nmero sea par y 1 cuando el nmero sea impar. DECLARACION if-else Qu hara si se tienen dos bloques de cdigo que se ejecutan basados en el resultado de una misma expresin? Si la expresin es verdadera, el primer bloque de cdigo se ejecuta, si la expresin es falsa el segundo bloque de cdigo se ejecuta. Entonces, probablemente usted empleara una declaracin if en combinacin con una declaracin else. El formato general para la instruccin if-else es el siguiente:
if (expresion) intruccion1; else intruccion2;

El formato para una declaracin if-else que use boques de cdigo (ms de una lnea) es el siguiente:
if (expresion) { . instruccones; . } else { . instruccones; . }

Tenga en cuenta que tanta la instruccin if como else pueden tener tantas instrucciones como necesiten. Adems los corchetes pueden descartarse cuando solo haya una instruccin en cualquiera de los dos casos. Un ejemplo de nica instruccin if-else es el siguiente:
if (num<0) printf("Nmero negativo.\n"); else printf("Nmero positivo.\n");

40

La adicin de la declaracin else proporciona dos opciones en la toma de decisiones. Pero qu pasa si quiere combinar varias declaraciones if y else para realizar ms decisiones? Coincidencia o no, C provee un mtodo con el cual usted puede combinar varios if con else para suministrar diversos niveles de decisin. El formato general es el siguiente:
if (expresion1) { . instruccones; . } else if (expresion2) { . instruccones; . } else { . instruccones; . }

Aqu se puede apreciar que se pueden evaluar las diferentes expresiones para elegir un bloque de cdigo a ejecutar. Antes de explica ms acerca de este mtodo, aqu hay un ejemplo sencillo.
if(num == 1) printf("lleva 1\n"); else if(num == 2) printf("lleva 2\n"); else if(num == 3) printf("lleva 3\n"); else printf("no lleva nada\n");

NOTA: dentro de la declaracin if, hay que asegurar el correcto uso de los comparadores sencillos y dobles, por ejemplo, los operadores sencillos &, = o | tiene el efecto de causar que la funcin actu sobre la variable, opuesto a los operadores dobles &&, == o || que actan como comparadores de la variable bajo prueba. Este es un error comn y no es fcil de encontrar en cdigos extensos, debido a que el compilador no lo toma como error. Ejercicios 1. El siguiente fragmento de cdigo es correcto?
if (cont>20) printf("cont es mayor de 20"); count-- ; }

2. Escriba un programa que imprima 1 peso, 5 pesos, 10 pesos, 20 pesos, 50 pesos o 100 pesos dependiendo del valor de una variable. Los nicos valores validos de la variable son 1, 5, 10, 20, 50 y 100.

41

OPERADOR ? El operador ? es actualmente una expresin de la declaracin if-else. El formato es el siguiente:


(expr1) ? (expr2) : (expr3);

Donde cada expr es una declaracin C vlida. Primero, se evala expr1. Si el resultado es VERDADERO (recuerde este es cualquier valor diferente de cero), entonces se evala expr2. Pero si el resultado es FALSO (cero), entonces se evala expr3. Lo siguiente es un ejemplo del operador ?
int i,j; i = j; i ? j=0 : j=1;

j=i ? 0 : 1;

Puesto que i es 1 o diferente de cero, ser evaluada la expresin j=0. Distinto a la declaracin if el operador ? retorna un valor. Por ejemplo:
i = (i<20) ? i+1 : 10;

Donde i se incrementar a menos que este sea igual o mayor a 20, entonces se le asignar el valor 10. DECLARACION for Uno de los tres bucles o lazos (loops) que suministra C es el lazo for. Si tiene una instruccin o un conjunto de instrucciones que necesitan repetirse, con un lazo for puede fcilmente implementar esto. El formato bsico para un lazo for es similar en todos los lenguajes, como BASIC o Pascal. La forma ms comn de un lazo for es la siguiente:
for (inicializacin ; condicin de finalizacin ; incremento) { instrucciones; }

La seccin inicializacin se emplea para entregarle un valor inicial a la variable de cuenta en el lazo. Note que esta variable de cuenta debe ser declarada antes que el lazo for puede usarla. Esta seccin del lazo for se ejecuta solo una vez. La seccin condicin de finalizacin es evaluada antes de cada ejecucin del lazo. Normalmente esta seccin testea la variable de cuenta del lazo en busca de una condicin VERDADERO o FALSO. Si la condicin de finalizacin es verdadera el lazo se ejecuta. Si la condicin de finalizacin es falsa el lazo termina su ejecucin y el programa sigue su flujo. La seccin incremento del lazo for normalmente incrementa la variable de cuenta del lazo. A continuacin se muestra un ejemplo de un lazo for.
void main(void) { int i; for(i=0; i<10; i++)

42

printf("%d ",i); printf("hecho"); }

Este programa imprimir los nmeros 0-9 en pantalla. El programa trabaja de la siguiente manera: primero la variable de cuenta del lazo, i, es puesta a cero; la siguiente expresin i<10 se evala. Si esta declaracin es verdadera la instruccin printf("%d ",i); es ejecutada. Cada vez despus que la instruccin printf("%d ",i); se ejecuta, la variable de cuenta del lazo es incrementada. Este proceso continua hasta que la expresin i<10 se convierte en falsa. En este punto, el lazo for termina su ejecucin y la instruccin printf("hecho"); es ejecutada dando fin al programa. Como se afirmo anteriormente, la condicin de finalizacin es ejecutada en el comienzo de cada iteracin del lazo. Por consiguiente, si el chequeo es falso desde el comienzo, el lazo for nunca llegara a ejecutarse. Usted no est restringido a solo incrementos en la variable de cuenta. A continuacin se muestran algunas variaciones sobre el lazo for.
for (num=100; num>0; num=num-1) for (cont=0; cont<50; cont+=5) for (cont=1; cont<10 && error==false; count++)

A continuacin se convierte un ejemplo en Assembler para verificar que sucede.


int h,a; for (h=0;h!=10;h++) 0007: 0008: 0009: 000A: 000B: a++; 000C: 000D: 000E: 000F:

CLRF MOVLW SUBWF BTFSC GOTO INCF INCF GOTO

h 0A h,W STATUS,Z 00F a,F h,F 008

;borra h ;carga 10 en w ;resta w de h ;y testeo por cero ;si i=10, sale del lazo ;incrementa a ;increment h ;regresa

Ejercicios 1. Qu funcin cumplen las siguientes declaraciones for()?


for(i=1; ;i++) for( ; ; ) for(num=1; num; num++)

2. Escriba un programa que imprima todos los mltiplos de un nmero. DECLARACION while Otro bucle en C es el lazo while. Cuando una expresin es verdadera, el lazo while repite una declaracin o un bloque de cdigo. El formato general es el siguiente:
while (expresion) instruccion; o

43

while (expresion) { instrucciones; }

Donde expresin es cualquier expresin valida en C. el valor de expresin es comprobado antes de cada iteracin de la declaracin o bloque de cdigo. Esto significa que si expresin es falsa la declaracin o bloque de cdigo no sern ejecutados. A continuacin se muestra un ejemplo (hecho en el compilador C de CCS) de un lazo while:
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { char ch; printf("Escribe la letra q\n"); ch=getch(); while(ch!='q') ch=getch(); printf("Escribiste la letra correcta!\n"); }

Habr notado que la primera declaracin pide un carcter desde el teclado. Luego la expresin es evaluada. Siempre y cuando el valor de ch no sea igual a q, el programa continuar solicitando otro carcter desde el teclado. Una vez una q se reciba, la funcin printf es ejecutado y el programa termina. Ejercicios 1. Qu funcin cumplen las siguientes declaraciones while?
a. while(i<10) { printf("%d ",i); i++; } b. while(1) printf("%d ",i++);

2. Escriba un programa que pida un carcter desde el teclado usando la declaracin ch=getch(); e imprmalo en pantalla. Cuando se encuentre retorno de carro (Enter), debe finalizar el programa. DECLARACION do-while El ultimo tipo de bucle en C es el lazo do. Este se puede combinar con la declaracin while de la siguiente manera:
do { instrucciones; } while(expresion)

44

En este caso las instrucciones siempre se ejecutan antes que expresin es evaluada (al menos una sola vez). El parmetro expresin puede ser cualquier expresin vlida en C. un ejemplo de un lazo do-while se muestra a continuacin:
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { char ch; do { ch = getch(); } while(ch != 'q'); printf("Escribiste la letra q!\n"); }

Este programa es equivalente al ejemplo estudiado en la seccin anterior. Ejercicios 1. Reescriba los puntos a y b del ejercicio 1 de la seccin anterior usando el lazo do-while. 2. Reescriba el ejercicio 2 de la seccin anterior usando el lazo do-while. DECLARACIONES DE CONTROL ANIDADAS Cuando el cuerpo de un lazo contiene a otro, el segundo lazo se dice que esta anidado dentro del primero. Cualquier bucle o lazo de C u otra declaracin de control pueden anidarse dentro de cualquier otra. El estndar C ANSI especifica que los compiladores debe tener al menos 15 niveles de anidado. Un ejemplo de un lazo for anidado se muestra a continuacin:
i = 0; while(i < 10) { for(j=0;j<10;j++) printf("%d ",i*10+j); i++; }

Esta rutina imprime los nmeros de 00-99 en pantalla. Ejercicio 1. Escriba un programa que obtenga un carcter desde el teclado (ch=getch();). Cada vez que un carcter sea ledo, use su valor ASCII para imprimir un igual nmero de puntos (.) en pantalla. Por ejemplo, si se lee la letra 'D' (con valor ASCII 68), su programa imprime 68 puntos en pantalla. Cuando se lea la letra 'Q', el programa debe finalizar.

45

DECLARACION break La declaracin break permite terminar cualquier lazo en cualquier punto dentro de su cuerpo. Esta declaracin se desva de la terminacin normal de una expresin. Cuando la declaracin break es encontrada en la ejecucin de un lazo, el programa salta a la siguiente lnea despus del lazo. Por ejemplo:
void main(void) { int i; for(i=0;i<50;i++) { printf("%d ",i); if(i==15) break; } }

Este programa imprime los nmeros 0-15 en pantalla. La declaracin break trabaja con todos los lazos de C. Ejercicios 1. Qu funcin cumple el siguiente lazo?
for(i=0;1;i++) { printf("Microchip es #1!"); if(getch()=='q') break; }

2. Escriba tres programas que usen los lazos de C, estos deben realizar una cuenta hasta que se oprima una tecla. Puede usar la funcin kbhit() para detectar cuando una tecla ha sido presionada. kbhit() retorna 1 cuando una tecla esta presionada y 0 en otro caso. kbhit() requiere el archivo de encabezado conio.h. DECLARACION continue Vamos a asumir que cuando una cierta condicin ocurre en un lazo, usted quiere saltar al final del lazo pero sin salir de este. C suministra la declaracin continue. Cuando el programa se encuentra con esta declaracin, este salta todas las otras instrucciones entre continue y la condicin de terminacin del lazo. Por ejemplo:
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { int i; for(i=0;i<100;i++) { continue; printf("%d ",i); } }

46

Este lazo nunca ejecutar la declaracin printf(). Cada vez que continue es alcanzado, el programa salta el printf() y evala la expresin i<100 despus de incrementar i. Una declaracin continue puede causar que el programa vaya directamente a la condicin de terminacin para los lazos while y do-while, un continue puede causar que se ejecute la parte incremental de un lazo y que se evale la condicin de terminacin. DECLARACION switch La declaracin if es buena para seleccionar entre un par de alternativas, pero se vuelve engorroso cuando existen muchas alternativas. De nuevo C cumple con la tarea al suministrarle la declaracin switch. Esta declaracin es equivalente a tener mltiples declaraciones if-else. La forma general para la declaracin switch es la siguiente:
switch (variable) { case constante1: instrucciones; break; case constante2: instrucciones; break; case constanteN: instrucciones; break; default: instrucciones; }

La entrada variable es sucesivamente testeada contra una lista de enteros o caracteres constantes. Cuando se encuentra una correspondencia, el cuerpo de la declaracin asociado con esa constante se ejecuta hasta que un break es encontrado. Si ninguna constante corresponde, las instrucciones asociadas con la declaracin default son ejecutadas. El default es opcional. Un ejemplo de una declaracin switch se muestra a continuacin:
#include <stdio.h> int main() { char ch; for(;;) { ch = getchar(); if(ch=='x') return 0; switch(ch) { case '0': printf("Domindo\n"); break; case '1': printf("Lunes\n"); break; case '2': printf("Martes\n"); break;

47

case '3': printf("Miercoles\n"); break; case '4': printf("Jueves\n"); break; case '5': printf("Viernes\n"); break; case '6': printf("Sbado\n"); break; default: printf("Entrada Invalida\n"); } } }

Este ejemplo lee un nmero entre 0 y 6. Si el nmero esta fuera de este rango, el mensaje Entrada Invalida se imprime. Valores dentro del rango se convierten en un da de la semana. El estndar ANSI declara que un compilador C debe soportar al menos 257 declaraciones case. Dos declaraciones case no pueden tener el mismo valor dentro del mismo switch. Tambin las declaraciones switch pueden anidarse, siempre y cuando los switch interiores no tengan conflicto con valores de switch exteriores. Un compilador ANSI debe suministrar al menos 15 niveles de anidamiento para las declaraciones switch. A continuacin se muestra un ejemplo de switches anidados.
switch (a) { case 1: switch (b) { case 0: printf("b es falso"); break; case 1: printf("b es verdadero"); break; } break; case 2: . .

La declaracin break dentro de un switch tambin es opcional. Esto significa que dos declaraciones case pueden compartir la mismo porcin de cdigo. Un ejemplo de esto se muestra a continuacin.
void main(void) { int a=6,b=3; char ch; printf("S = Suma\n"); printf("R = Resta\n"); printf("M = Multiplicacion\n"); printf("D = Division\n");

48

printf("Escoja una Opcion:\n"); ch=getch(); switch (ch) { case 'R': b=-b; case 'S': printf("\t\t%d",a+b); break; case 'M': printf("\t\t%d",a*b); break; case 'D': printf("\t\t%d",a/b); break; default: printf("Opcion Incorrecta"); } }

Ejercicios 1. Qu error tiene el siguiente segmento de cdigo?


float f; switch(f) { case 10.05: . .

2. Use una declaracin switch para imprimir el valor de una moneda. El valor de la moneda est contenido en la variable moneda. Las fases para describir las monedas son: de 20, de 50, de 100, de 200 y de 500. 3. Cules son las ventajas del uso de la declaracin switch sobre la declaracin ifelse? DECLARACION null(;) La declaracin null es una instruccin que contiene sola el smbolo punto y coma (;). Este puede aparecer dondequiera que una declaracin sea esperada. Nada pasa cuando la declaracin null es ejecutada, a diferencia de la instruccin NOP de Assembler, la cual introduce un ciclo de retardo. Declaraciones como do, for y while requieren que una instruccin ejecutable aparezca como el cuerpo de la declaracin. La declaracin null satisface la sintaxis para esos casos.
for (i=0;i<10;linea[i++]=0) ;

En este ejemplo, la expresin del lazo for linea[i++]=0 inicializa los 10 primeros elementos de linea a 0. La instruccin del cuerpo es un null, entonces no se requieren ms instrucciones.

49

DECLARACION return La declaracin return termina la ejecucin de una funcin y retorna el control a la rutina que hizo el llamado. Un valor puede retornarse a la funcin que realiza el llamado si es requerido pero si este es omitido, el valor retornado es indefinido. Si no se incluye un return en la funcin, el control es aun pasado a la funcin que hizo el llamado despus de ejecutar la ltima lnea de cdigo. Si no se necesita retornar un valor, declare la funcin para ser del tipo void.
GetValue(c) int c; { c++; return c; } void GetNothing(c) int c; { c++; return; } void main() { int x; x = GetValue(); GetNothing(); }

50

6. ARREGLOS Y CADENAS En este captulo se discutirn los arreglos (array) y cadenas de caracteres (strings). Un arreglo es simplemente una lista de variables relacionadas del mismo tipo de dato. Una cadena es definida como un arreglo de caracteres, y tambin es conocido como el arreglo ms comn de una sola dimensin (unidimensional). ARREGLOS UNIDIMENSIONAL Un arreglo es una lista de variables que son todas del mismo tipo de dato y que pueden referenciarse a travs del mismo nombre. Una variable individual en un arreglo es llamada un elemento del arreglo. Este es la manera sencilla de manejar grupos de datos relacionados. La forma general para declarar un arreglo unidimensional (vector) es la siguiente:
tipo NombreArreglo [tamao];

Donde tipo es un tipo de dato vlido en C, NombreArreglo es el nombre del arreglo, y tamao especifica cuantos elementos tiene el arreglo. Por ejemplo, si se quiere definir un arreglo de 50 elementos se emplea la siguiente declaracin.
int altura[50];

Cuando se declara un arreglo, C define el primer elemento con un ndice 0. Si el arreglo tiene 50 elementos, el ltimo elemento tiene ndice 49. Usando el ejemplo anterior, digamos que quiero indexar el elemento 25 del arreglo altura y asgnale un valor de 60. El siguiente ejemplo muestra cmo hacer esto.
altura[24] = 60;

C almacena el arreglo unidimensional en posiciones de memoria contiguas. El primer elemento est en la direccin ms baja. Si el siguiente segmento de cdigo es ejecutado.
int num[10]; int i; for(i=0;i<10;i++) 0007: 0008: 0009: 000A: 000B: num[i] = i; 000C: 000D: 000E: 000F: 0010: 0011: 0012: 0013:

CLRF MOVLW SUBWF BTFSC GOTO MOVLW ADDWF MOVWF MOVF MOVWF INCF GOTO

i 0A i,W STATUS,C 013 0E i,W 04 i,W 00 i,F 008

;borra i ;testea si i<10

;si no es as, deja la rutina ;carga el comienzo de num

El arreglo num se ver as en memoria: 51

elemento

1 0

2 1

3 2

4 3

5 4

6 5

7 6

8 7

9 8

10 9

Cualquier elemento del arreglo puede usarse en cualquier lugar donde usted quiera emplear una variable o una constante. A continuacin se muestra otro ejemplo hecho en el compilador C de CCS, en este simplemente se le asigna el cuadrado del ndice al elemento del arreglo, luego se imprimen todos los elementos.
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { int num[10]; int i; for(i=0;i<10;i++) num[i] = i * i; for(i=0;i<10;i++) printf("%d ",num[i]); }

Qu pasa si se tiene un arreglo con diez elementos y usted accidentalmente escribe en el onceavo elemento? C no tiene ninguna comprobacin de lmites de los ndices en arreglos. Por lo tanto, se podra leer o escribir en un elemento no declarado en el arreglo. Sin embargo, esto por lo general tendr resultados desastrosos. A menudo esto causar que el programa colapse y e incluso a veces que el ordenador se bloquee. C no le permite asignar los valores de un arreglo a otro simplemente usando una asignacin como la siguiente:
int a[10], b[10]; . . a=b;

El ejemplo anterior es incorrecto. Si usted quiere copiar el contenido de un arreglo dentro de otro, usted debe copiar cada elemento individual desde el primer arreglo dentro del segundo arreglo. El siguiente ejemplo muestra como copiar el arreglo a[] dentro del b[] asumiendo que cada arreglo tiene 20 elementos.
for(i=0;i<20;i++) b[i] = a[i];

Ejercicios 1. El siguiente fragmento de cdigo es correcto?


int i; char cont[10]; for(i=0;i<100;i++) cont[i]=getch();

2. Escriba un programa que lea 10 caracteres desde el teclado usando getch(). El programa reportar si cualquiera de estos caracteres concuerda con uno especfico. 52

CADENA DE CARACTERES El arreglo unidimensional ms comn es la cadena de caracteres. C no tiene incorporado un tipo de dato cadena (string). En lugar de esto, este soporta cadenas usando arreglos unidimensionales de caracteres. Una cadena es definida con un 0. Si cada cadena debe terminar con un vacio, entonces cuando se declare la cadena debe agregarse un elemento adicional. Este elemento adicional almacenara el cero. Todas las cadenas constantes son automticamente terminadas en cero por el compilador C. Puesto que estamos utilizando cadenas. Cmo se puede introducir una cadena en el programa mediante el teclado? La funcin gets(str) leer los caracteres desde el teclado hasta que un retorno de carro (Enter) es encontrado. La cadena de caracteres que ha sido leda ser almacenada en el arreglo str declarado. Usted debe asegurar que el tamao del arreglo sea ms grande o igual que el nmero de caracteres ledos desde el teclado incluyendo el cero (null = \0). A continuacin se muestra un ejemplo de cmo usar la funcin gets() en un programa.
#include <stdio.h> void main(void) { char str[20]; int i; printf("Escriba una cadena (<20 caracteres):\n"); gets(str); for(i=0;str[i];i++) printf("%c",str[i]); printf("\n%s",str); getchar();//termina el programa oprimiendo ENTER }

En el ejemplo anterior se observa que la cadena puede imprimirse de dos maneras: como un arreglo de caracteres usando %c o como una cadena usando %s. Ejercicios 1. Cul es el error en el siguiente programa? La funcin strcpy() copia el segundo argumento dentro del primero.
#include <string.h> void main(void) { char str[10]; strcpy(str, "Microcontrolador"); printf(str); }

2. Escriba un programa que lea una cadena de caracteres desde el teclado y los imprima en orden inverso en pantalla.

53

ARREGLOS MULTIDIMENSIONALES C no est limitado a los arreglos unidimensionales. Usted puede crear arreglos de dos o ms dimensiones. Por ejemplo, para crear un arreglo entero llamado numero de dimensin 5x5, puede hacerlo de la siguiente manera:
int numero[5][5]; //usa 25 posiciones de memoria

Dimensiones adicionales pueden hacerse simplemente aadiendo otro conjunto de parntesis cuadrados. Por simplicidad, solo se trataran los arreglos de dos dimensiones (Matrices). La mejor forma de representar un arreglo de dos dimensiones es por medio del formato fila/columna. Por lo tanto, los arreglos de dos dimensiones son accesado una fila a la vez, de izquierda a derecha. La Fig. 2 muestra una representacin grfica de un arreglo de 5x5.

Fig. 2. Arreglo de 5x5

Los arreglos de dos dimensiones se usan de la misma manera que se usan los arreglos unidimensionales. Por ejemplo, el siguiente programa carga un arreglo de 5x4 con el producto de los ndices, entonces imprime el contenido del arreglo en el formato fila/columna.
#include <stdio.h> void main(void) { int arreglo[5][4]; int i,j; for(i=0;i<5;i++) for(j=0;j<4;j++) arreglo[i][j]=i*j; for(i=0;i<5;i++) { for(j=0;j<4;j++) printf("%d ",arreglo[i][j]); printf("\n"); } getchar(); // termina el programa oprimiendo ENTER }

La salida que genera este programa debe lucir como la siguiente:


0 0 0 0 0 0 1 2 3 4 0 2 4 6 8 0 3 6 9 12

54

Como se puede dar cuenta, cuando se usa un arreglo multidimensional al nmero de variables necesarias para acceder a cada elemento individual del arreglo s incrementan. Debido a la capacidad de memoria del PIC16F84A, arreglos de 100 o 10x10 no son posibles. Sin embargo, un arreglo de 50 elementos podra implementarse o podra utilizar un Microcontrolador con ms capacidad de memoria como por ejemplo el PIC16F877A. Ejercicios 1. Escriba un programa que declare un arreglo de 3x3x3 y lo cargue con los nmeros del 1 al 27. Imprima el arreglo en pantalla. 2. Empleando el programa del ejercicio 1, imprima la suma de cada fila y de cada columna. INICIALIZANDO ARREGLOS Hasta el momento se ha estudiado como asignarle valores a elementos individuales de un arreglo. C suministra un mtodo con el cual usted puede asignarle un valor inicial a un arreglo tal como se hace para las variables. La forma general para un arreglo unidimensional se muestra a continuacin:
tipo NombreArreglo[tamao] = {ListaValores};

El parmetro ListaValores es una lista de constantes separadas por comas que son compatibles con el tipo de arreglo. La primera constante ser asignada al primer elemento del arreglo, la segunda constante al segundo elemento y as. El siguiente ejemplo muestra como se inicializa un arreglo tipo entero de 5 elementos.
int i[5] = {1,2,3,4,5};

El elemento i[0] tendr un valor de 1 y el elemento i[4] tendr un valor de 5. Una cadena (arreglo de caracteres) puede inicializarse de dos formas. Primero, usted puede hacer una lista de cada carcter individual como se muestra a continuacin:
char str[3] = {'a', 'b', 'c'};

El Segundo mtodo consiste en usar una cadena entre comillas, como se muestra a continuacin:
char nombre [5] = "John";

Abra notado que no se utilizan corchetes para encerar la cadena, debido a que no son necesarios en este tipo de inicializacin debido a que las cadenas en C deben terminar con un vacio. El compilador automticamente aade un vacio al final de "John". Los arreglos multidimensionales se inicializan de la misma manera que los unidimensionales. Es sencillo simular el formato fila/columna cuando define un arreglo de dos dimensiones. El siguiente ejemplo muestra como inicializar un arreglo de 3x3.
int num[3][3]={ 1,2,3, 4,5,6, 7,8,9};

55

Ejercicios 1. la siguiente declaracin es correcta?


int cont[3] = 10,0, 5.6, 15.7;

2. Escriba un programa que defina una tabla con el valor al cuadrado y al cubo de un rango de nmeros. Cada fila debe tener los nmeros, su valor al cuadrado y al cubo. Cree un arreglo de 9x3 que mantenga esta informacin para los nmeros del 1 al 9. Debe preguntarle al usuario por un nmero usando la funcin scanf("%d",&num);. entonces imprime al nmero y su valor al cuadrado y al cubo. ARREGLOS DE CADENAS Arreglos de cadenas de caracteres son comunes en C. pueden declararse e inicializarse como cualquier otro arreglo. Pero la manera en la cual usted usa el arreglo es un tanto diferente. Por ejemplo, Qu define la siguiente declaracin?
char nombres[10][40];

Esta instruccin especifica que el arreglo nombres contiene 10 nombres, de hasta 40 caracteres de largo cada uno (incluyendo el vacio). Para acceder a una cadena de esta tabla, hay que definir solo el primer ndice. Por ejemplo, para imprimir el quinto nombre de este arreglo, puede usar el siguiente comando.
printf("%s",nombres[4]);

Lo mismo pasa para arreglos con dimensiones mayores a dos. Por ejemplo, si el arreglo animales se declarara de la siguiente manera:
char animales[5][4][80];

Para acceder a una cadena especifica, debe usar las dos primeras dimensiones. Por ejemplo, para acceder a la segunda cadena de la tercera lista, se escribe animales[2][1]. Ejercicio 1. Escriba un programa que cree una tabla que contenga las palabras para los nmeros del 0 al 9. Permita que el usuario escriba un digito para que entonces su programa imprima en pantalla la palabra respectiva. Para obtener un ndice dentro de la tabla, reste cero al carcter tecleado. FUNCIONES PARA MANIPULAR CADENAS Las cadenas de caracteres pueden manipularse de diferentes maneras dentro de un programa. Un ejemplo es copiando una cadena desde una fuente a un destino por medio de la funcin strcpy(). Esta funcin permite a una cadena constante ser introducida en RAM.

56

#include <string.h> //la librera de funciones string char cadena[10]; //define un arreglo string . . strcpy (cadena, "Hi There");//disposicin de caracteres dentro del arreglo

Tenga en cuenta que apuntadores a ROM no son validos en las microcontroladores PIC por lo que usted no puede pasar una cadena constante a ninguna de estas funciones. strlen("Hola") no es vlida. A continuacin se presenta otro ejemplo.
#include <stdio.h> #include <string.h> void main() { char s1[10], s2[10]; strcpy(s1,"abc"); strcpy(s2,"def"); strcat(s1,s2); printf("%u\n",strlen(s1)); //imprime el nmero 6 printf(s1); //imprime abcdef if(strcmp(s1,s2)!=0) printf("\n No son Iguales"); getchar(); }

Algunas funciones que manipulan cadenas estn disponibles a continuacin:


strcat strchr strrchr strcmp strncmp stricmp strncpy strlen strlwr strpbrk strstr Aade una cadena al final de otra Localiza un caracter en una cadena, buscando desde el principio Localiza un caracter en una cadena, buscando desde el final Compara dos cadenas numricamente ('a'!='A') Compara los n primeros caracteres de dos cadenas numricamente Compara dos cadenas ignorando su forma (mayus./minus.) Copia los n primeros caracteres de una cadena en otra Devuelve la longitud de una cadena Reemplaza letras maysculas con minsculas Encuentra la primera ocurrencia de algn caracter en dos arreglos Busca una cadena dentro de otra

NOTA: Asegrese de que el tamao de los arreglos de cadenas sean acordes con el tamao de las cadenas que se manipulen.

57

7. PUNTEROS Este captulo cubre un de los ms importantes y ms problemticos rasgos de C, el puntero. Un puntero es bsicamente la direccin de un objeto en el programa. INTRODUCCION A LOS PUNTEROS Un puntero es una posicin de memoria (variable) que guarda la direccin de otra posicin de memoria. Por ejemplo, si una variable puntero a contiene la direccin de la variable b, entonces a apunta a b. si b es una variable que est en la posicin 100 de la memoria. Entonces a debera contener el valor 100. La forma general para declarar una variable puntero es la siguiente:
type *NombreVariable;

En un puntero type es uno de los tipos de datos validos en C. Este especifica el tipo de variables a las cuales NombreVariable puede apuntar. Usted habr notado que NombreVariable va precedido por un asterisco *. Este le informa al compilador que NombreVariable es una variable puntero. Por ejemplo, la siguiente declaracin crea un puntero a un tipo entero.
int *ptr;

Los dos operadores especiales que estn asociados con los punteros son *, &. La direccin de una variable puede ser accesada colocando antes de la variable el operador &. El operador * retorna el valor de la direccin apuntada por la variable. Por ejemplo:
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { int *a,b; //ms de 1 byte puede ser asignado a a b = 6; a = &b; printf("%d",*a); }

NOTA: por defecto, el compilador emplea 1 byte para punteros. Esto significa que solo las posiciones de 0 hasta 255 pueden ser apuntadas. Para partes con una memoria ms extensa es necesario usar un puntero de 2 bytes (16 bits). Para seleccionar un puntero de 16 bits, use la siguiente directiva:
#device *=16

Sea consciente de que ms cdigo ROM se generara debido a que la aritmtica de 16 bits es menos eficiente. La primera instruccin declara dos variables: a, que es un puntero a entero y b, que es un entero. La siguiente instruccin asigna el valor 6 a b. Lugo la direccin de b (&b) es asignada a la variable puntero a. Esta lnea puede entenderse como la asignacin de a a la direccin de b. Finalmente, el 58

valor de b se imprime en pantalla usando el operador * con la variable puntero a. Esta lnea puede imprimir el valor de la direccin apuntada por a. este proceso de referenciar un valor a travs de un puntero es llamado direccionamiento indirecto. Un ejemplo grafico se muestra a continuacin:
Direccin: Variable: Contenido: 100 i 3 102 j 5 104 k -1 106 ptr 102

int i, j, k; int *ptr;

Inicialmente, i es 3, &i es 100 (la direccin de i). Como ptr contiene el valor 102, *ptr es 5 (el contenido de la direccin 102). Es adems posible asignar un valor de posicin de memoria usando un puntero. Por ejemplo, reestructuremos el programa anterior de la siguiente manera:
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { int *a,b; a = &b; *a=6; printf("%d",b); }

En este programa, primero asignamos la direccin de la variable b en a, despus asignamos un valor a b usando a. La lnea *a=6; puede entenderse como la asignacin del valor 6 a la posicin de memoria apuntada por a. obviamente, el uso de punteros en los dos ejemplos anteriores no es necesario pero ilustran los usos de los punteros. Ejercicio 1. Escriba un programa en Microsoft Visual C++ Express donde con un lazo for realice una cuenta de 0 a 9 e imprima los nmeros en pantalla. Imprima los nmeros empleando un puntero. RESTRICCIONES PARA PUNTEROS En general, los punteros pueden tratarse como variables. Sin embargo, existen algunas pocas reglas y excepciones que se deben tener en cuenta. En adicin de los operadores *, &, hay solo otros cuatro operadores que se pueden aplicar a punteros: +, ++, -, --. Solo cantidades enteras pueden sumarse o restarse de variables puntero. Cuando es incrementada una variable puntero, este apunta a la siguiente posicin de memoria. Si por ejemplo asumimos que la variable puntero p contiene la direccin 100, despus de que la instruccin p++; se ejecuta, p tendr el valor 102 asumiendo que los enteros tienen un tamao de 2 bytes. Si p hubiera sido un puntero tipo float, p contendra el valor 104 despus del 59

incremento asumiendo que los nmeros en punto flotante tienen un tamao de 4 bytes. El nico puntero que aparece como se espera es el de tipo char, debido a que los caracteres tienen un tamao de 1 byte. Usted puede sumarle o restarle cualquier valor entero que quiera, a un puntero o desde un puntero. Por ejemplo, la siguiente instruccin:
int *p; . p = p+200;

Causa que p apunte desde la posicin de memoria actual 200 posiciones ms adelante. Es posible incrementa o decrementar tanto el puntero como el objeto al cual apunta. Debe ser cuidadoso cuando incrementa o decrementa el objeto apuntado por el puntero. Qu cree usted que haga la siguiente instruccin si el valor de p es 1 antes de que la instruccin se ejecute?
*p++;

Esta instruccin obtiene al valor apuntado por p, luego incrementa p. para incrementar el objeto que es apuntada por un puntero, use la siguiente instruccin:
(*p)++;

El parntesis causa que el valor apuntado por p se incremente. Esto debido a la precedencia de * versus ++. Los punteros tambin pueden usarse en operaciones relacionales. Sin embargo, estos solo tienen sentido si el puntero si los punteros estn relacionados los unos con los otros, por ejemplo, si apuntan al mismo objeto. Los punteros no se pueden crear en ROM. Por ejemplo, la siguiente instruccin es ilegal:
char const nombre[5] = "JOHN"; . ptr=&nombre[0];

Esta es vlida sin el const, el cual pone el dato en ROM. Ejercicios 1. Declare las siguientes variables y asigne sus direcciones a la variable puntero. Imprima el valor de cada variable usando %p. Luego incremente cada puntero e imprima el valor de la variable nuevamente. Cules son los tamaos de cada tipo de dato en su ordenador?
char *cp,ch; int *ip,i; float *fp,f; double *dp,d;

2. Cul es el error en el siguiente fragmento de cdigo? 60

int *p,i; p = &i; p = p/2;

PUNTEROS Y ARREGLOS En C, punteros y arreglos estn relacionados y son algunas veces intercambiables. Es la relacin entre estos dos lo que hace el poder de C cada vez ms aparente. Si usted emplea el nombre de un arreglo sin el ndice, entonces est usando un puntero que direcciona al comienzo del arreglo. En el capitulo anterior, se uso la funcin gets(), con la cual nosotros solo pasamos el nombre de la cadena. Lo que actualmente se pasa a una funcin, es un puntero al primer elemento de la cadena. Nota importante: cuando un arreglo se entrega a una funcin, solo un puntero que direcciona al primer elemento es entregado; estos no pueden crearse para usarse con arreglos constantes o estructuras. Debido a que el nombre de un arreglo sin un ndice es un puntero, puede asignar ese valor a otro puntero. Esto te permitir acceder al arreglo usando la aritmtica puntero. Por ejemplo:
#include <stdio.h> int a[5]={1,2,3,4,5}; void main(void) { int *p,i; p=a; for(i=0;i<5;i++) printf("%d",*(p+i)); getchar(); }

Este es un programa claramente vlido en C. Abra notado que en la funcin printf() usamos *(p+i), donde i es el ndice del arreglo. Adems se habr sorprendido del hecho de que podemos colocar un ndice al puntero como si este fuera un arreglo. El siguiente programa tambin es vlido.
#include <stdio.h> int a[5]={1,2,3,4,5}; void main(void) { int *p,i; p=a; for(i=0;i<5;i++) printf("%d",p[i]); getchar(); }

Una cuestin a recordar es que un puntero solo podr indexarse cuando este apunte a un arreglo. Debido a que los punteros a arreglos direccionan solo al primer elemento o base de la cadena, es invalido incrementar el puntero. Por lo tanto, la instruccin p++; ser invalida utilizndola en el programa anterior.

61

Ejercicios 1. El siguiente segmento de cdigo es correcto?


int cont[10]; . cont = cont+2;

2. Qu valor imprime el siguiente segmento d cdigo?


int valor[5]={5,10,15,20,25}; int *p; p = valor; printf("%d",*p+3);

PASANDO PUNTEROS A FUNCIONES En el captulo 3, hablamos acerca de las dos formas en las que los argumentos podan pasar a las funciones, llamado por valor y llamado por referencia. El segundo mtodo pasa la direccin a la funcin, o en otras palabras un puntero es pasado a la funcin. En este punto cualquier cambio hecho a las variables empleando el puntero cambiara el valor de la variable desde la rutina de llamado. Los punteros pueden pasarse a funciones al igual que otras variables. El siguiente ejemplo muestra como pasar una cadena a una funcin usando punteros.
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void puts(char *p); void main(void) { puts("Microchip es genial!"); } void puts(char *p) { while(*p) { printf("%c",*p); p++; } printf("\n"); }

En este ejemplo, el puntero p direcciona al primer caracter de la cadena, la letra M. La instruccin while(*p) es testeada en bsqueda de la condicin vacio (null) al final de la cadena. Cada vez que pasa a travs del lazo while, el carcter que es apuntado por p se imprime. Luego p se incrementa para apuntar al siguiente caracter de la cadena. Otro ejemplo de pasar un puntero a una funcin es el siguiente:
void IncBy10(int *n) { *n += 10; } void main(void)

62

{ int i=0; IncBy10(i); }

El ejemplo anterior puede reescribirse para mejorar la legibilidad, usando una clase especial de parmetros de puntero llamado parmetro de referencia.
void Incby10(int & n) { n += 10; } void main(void) { int i=0; Incby10(i); }

Ambos ejemplos muestran como retornar un valor desde una funcin por medio de la lista de parmetros. Ejercicios 1. Escriba un programa que pase un valor tipo float a una funcin. Dentro de esta funcin, el valor -1 se asigna al parmetro de la funcin. Despus que la funcin retorna al main, imprime el valor de la variable float. 2. Escriba un programa que el puntero fl a una funcin. Dentro de la funcin, el valor -1 se asigna a la variable. Despus que la funcin retorna al main, imprime el valor de la variable.

63

8. ESTRUCTURAS Y UNIONES Las estructuras y uniones representan dos de los ms importantes tipos de datos definidos por usuario que tiene C. Las estructuras son un grupo de variables relacionadas que pueden tener diferentes tipos de datos. Las uniones son un grupo de variables que comparten el mismo especio de memoria. INTRODUCCION A LAS ESTRUCTURAS Una estructura es un grupo relacionado de elementos o tems que pueden ser accesados a travs de un nombre comn. Cada tem dentro de una estructura tiene su propio tipo de dato, y pueden ser diferentes entre ellos. C define las estructuras de la siguiente manera:
struct NombreEstructura { tipo elemento1; tipo elemento2; . tipo elementoN; } ListaVariables;

La palabra clave struct le informa al compilador que una estructura est por definirse. Dentro de la estructura cada type es uno de los tipos de datos validos en C. estos tipos no tienen que ser los mismos. NombreEstructura es el nombre con el cual vamos a identificar la estructura. ListaVariables declara algunas variables que tienen un tipo de dato de la estructura. ListaVariables es opcional. Cada uno de los elementos en la estructura es referenciado comnmente como un campo o miembro. Nosotros nos referiremos a estos como miembros. En general, la informacin almacenada en una estructura lgicamente debe estar relacionada. Por ejemplo, usted podra emplear una estructura para guardar el nombre, direccin y nmero telefnico de todos sus clientes o compaeros. El siguiente ejemplo es para una referencia bibliogrfica en una biblioteca.
struct ReferenciaBib { char autor[40]; char titulo[40]; char editorial[40]; unsigned int pag; unsigned char rev; } tarjeta;

En este ejemplo, el nombre de la estructura es ReferenciaBib. Este no es el nombre de una variable, solo el nombre de este tipo de estructura. La variable tarjeta es declarada como una estructura del tipo ReferenciaBib. Para acceder a cualquier miembro de la estructura, se debe especificar tanto el nombre de la variable como el nombre del miembro. Estos nombres deben separase con un punto (.). Por ejemplo, para acceder al miembro titulo de la estructura ReferenciaBib, usted debe usar tarjeta.titulo donde tarjeta es el nombre de la variable y titulo es el miembro.

64

El operador es empleado para acceder a miembros de una estructura. Para imprimir el miembro autor de la estructura ReferenciaBib, usted debe escribir lo siguiente:
printf("El autor es %s\n",tarjeta.autor);

En memoria la estructura ReferenciaBib se parece a lo siguiente:


autor titulo editorial pag rev 40 bytes 40 bytes 40 bytes 2 bytes 1 byte

Si usted quiere obtener la direccin del miembro pag de la estructura debe usar &tarjeta.pag. Si quiere imprimir el nombre de la editorial, debe usar printf("%s",tarjeta.editorial). Qu pasa si usted quiere acceder a un elemento especifico del ttulo, por ejemplo la tercera letra de la cadena?, entonces debe usar:
Tarjeta.titulo[2];

El primer elemento del ttulo esta en 0, el segundo en 1 y, finalmente el tercero esta en 2. Una vez usted ha definido una estructura, puede crear ms variables de estructura en cualquier lugar del programa de la siguiente manera:
struct NombreEstructura ListaVariables;

Por ejemplo, si la estructura ReferenciaBib se definiera al comienzo del programa, usted podra definir dos variables ms de la siguiente manera:
struct ReferenciaBib liro, lista;

C le permite declarar arreglos de estructuras de la misma manera que cualquier otro tipo de dato. El siguiente ejemplo declara un arreglo de 50 elementos para la estructura ReferenciaBib.
struct ReferenciaBib grande[50];

Si usted quiere acceder a una estructura individual dentro del arreglo, debe indexar la variable estructura (i.e grande[10]). Cmo puede acceder al miembro titulo del elemento 10 del arreglo estructura grande?, de la siguiente manera:
Grande[9].titulo;

Las estructuras tambin pueden pasarse a funciones. Una funcin puede retornar una estructura de la misma manera que cualquier otro tipo de dato. Tambin puede asignar valores de una estructura a otra simplemente usando una asignacin. El siguiente fragmento es perfectamente vlido:
struct temp { int a; float b; char c;

65

} var1,var2; var1.a=37; var2.b=53.65; var2 = var1;

Despus que este fragmento de cdigo ejecuta la estructura, la variable var2 tendr el mismo contenido de la variable var1. El siguiente es un ejemplo de cmo inicializar una estructura.
struct ejemplo { char nombre[50]; char ch; int i; } var1[2]={"Rodger", 'Y',27,"Jack",'N',30};

NOTA: Cuando pasa una estructura a una funcin, toda la estructura pasa empleando el mtodo llamado por valor. Por consiguiente, cualquier modificacin hecha a la estructura en la funcin no afectara el valor de la estructura en la rutuna de llamado. El nmero de elementos tampoco afecta la forma en la cual es pasada a una funcin. Un ejemplo de estructura emplendola sobre un PIC para configurar una interfaz con LCD podra ser el siguiente:
struct cont_pins { boolean en1; //habilita para todos los displays boolean en2; //habilita para displays de 40x4 boolean rs; //selector de registro int data:4; } cont; #byte cont = 8; //control sobre el puerto d

Este pone la estructura para cont_pins para luego ser manejada dentro del programa NOTA: La notacin :4 en data indica que se van a utilizar 4 bits par ese elemento. En este caso D0 ser en1, y D3-D6 sern los datos.
void LcdSendNibble(byte n) { cont.data=n; //dato actual delay_cycles(1); //retardo cont.en1=1; //pone la linea en1 en ALTO delay_us(2); //retardo de tiempo cont.en1=0; //pone la linea en1 en BAJO }

Ejercicios 1. Escriba un programa que tenga una estructura con un variable carcter y una cadena de 40 caracteres. Lea un carcter desde el teclado y gurdelo en la primera variable usando getch(). Lea una cadena y gurdela en la segunda variable usando gets(). Por ltimo imprima los valores de cada miembro en pantalla. 66

2. En el siguiente fragmento de cdigo cual es el error?


struct tipo { int i; long l; char str[50]; } s; . . i = 10;

PUNTEROS A ESTRUCTURAS Algunas veces es til poder acceder a una estructura a travs de un puntero. Punteros a estructuras son declarados de la misma manera que punteros a otros tipos de datos. Por ejemplo, el siguiente fragmento de cdigo declara una variable del tipo estructura p y un puntero a estructura q con el tipo de estructura temp.
struct temp { int i; char ch; } p,q;

Empleando esta definicin de la estructura temp, la instruccin q=&p es perfectamente valida. Dado que q apunta a p, debe usar el operador flecha como se muestra a continuacin:
q->i=1;

Esta instruccin asigna el valor de 1 al nmero i de la variable p. Note que el operador flecha es un signo menos seguido del signo mayor que sin ningn espacio entre estos. Debido a que C pasa toda la estructura a una funcin, estructuras largas pueden reducir la velocidad de ejecucin del programa debido a la gran cantidad de datos transferidos. Por esta razn, es fcil pasar un apuntador a estructura a la funcin. NOTA: Cuando se accese a un miembro de una estructura empleando una variable, use el punto. Cuando accese a un miembro de una estructura empleando un puntero a estructura, usted debe usar el operador flecha. El siguiente ejemplo muestra como un puntero a estructura debe inicializase:
#include <16F84A.h> #include <string.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) struct s_type { int i; char str[40]; } s,*p;

67

void main(void) { p=&s; s.i=10; p->i=10; strcpy(p->str,"Utilizo estructuras"); printf("%d %d %s",s.i,p->i,p->str); }

Las dos lneas s.i=10 y p->i=10 son equivalentes. Ejercicios 1. El siguiente segmento de cdigo es correcto?
struct s_type { int a; int b; } s, *p; void main(void) { p=&s; p.a=100; }

2. Escriba un programa que cree un arreglo de estructuras de tamao 3 para las familias de PIC bsicas. Necesitara cargar la estructura con un dispositivo PIC12, PIC16 Y PIC 18. El usuario seleccionara la estructura a imprimir usando el teclado para entrar 1, 2, o 3. El formato de la estructura ser el siguiente:
struct PIC { char nombre[20]; unsigned char progmem; unsigned char datamem; char rasgo[80]; };

ESTRUCTURAS ANIDADAS Hasta ahora, usted solo ha visto que los miembros de una estructura son uno de los tipos de datos de C. sin embargo, los miembros de las estructuras tambin pueden ser otras estructuras. A esto se le llama estructuras anidadas. Por ejemplos:
#define NumerodePICS 25 struct PIC { char nombre[40]; unsigned char progmem; unsigned char datamem; char rasgo[40]; }; struct productos { struct PIC dispositivos[NumerodePICS]; char tipoEncapsulado[40];

68

float precio; } lista1;

La estructura productos tiene tres elementos: un arreglo de la estructura PIC llamado dispositivos, una cadena que tiene el tipo de encapsulado, y el precio. Estos elementos pueden accesarse usando la variable lista1. INTRODUCCION A LAS UNIONES Una unin es definida como una posicin de memoria sencilla que es compartida por dos o ms variables. Las variables que comparten la misma posicin de memoria pueden tener diferentes tipos de datos. Sin embargo, solo podemos usar una variable a la vez. Una unin se parece mucho a una estructura. La forma general de una unin es la siguiente:
union NombreUnion { tipo elemento1; tipo elemento2; . . tipo elementoN } ListaVaribles;

Nuevamente, NombreUnion es el nombre que le damos a la unin y ListaVariables son las variables que son del tipo unin NombreUnion. La diferencia entre uniones y estructuras es que cada miembro de una unin comparte el mismo espacio. Por ejemplo, la siguiente unin contiene tres miembros: un entero, un arreglo de caracteres, y un tipo double.
union u_type { int i; char c[3]; double d; } temp;

La manera en que una unin aparece en memoria se muestra a continuacin. Usaremos el ejemplo anterior para ilustrar una unin. El entero emplea dos bytes, el arreglo utiliza tres bytes y el tipo double emplea cuatro bytes.
-----------------------------tipo double---------------------------- ----c[2]---- ----c[1]---- ----c[0]---- ---------entero(int)--------- Elemento0 Elemento1 Elemento2 Elemento3

Accesar los miembros de la unin se realiza de la misma manera que como en las estructuras, se emplea el punto (.). La instruccin temp.i accesa al miembro tipo entero de dos bytes. Si quiere acceder a una unin a travs de un puntero, debe usar el operador flecha (->) como se hizo para las estructuras. Es importante notar que el tamao de la unin es establecido en tiempo de compilacin acomodndolo al miembro ms grande de la unin. Asumiendo que los tipos double tienen cuatro bytes de tamao, la unin temp tendr un tamao de cuatro bytes. 69

Un buen ejemplo de la aplicacin de las uniones es cuando un Microcontrolador de 8-bits tiene un conversor A/D externo de de 12-bits conectado a un puerto serie. El Microcontrolador lee el A/D en dos bytes. Entonces nosotros podramos configurar una unin que tenga dos unsigned char y un signed short como miembros.
union muestra { unsigned char bytes[2]; signed short palabra; }

Cuando quiera leer al A/D, leer dos bytes desde el A/D y los almacenara en el arreglo bytes. Entonces, cuando quiera usar la muestra de 12-bits deber usar el miembro palabra para acceder al nmero de 12-bits. Ejercicios 1. Cules son las diferencias entre una estructura y una unin? Cules son las similitudes? 2. Escriba un programa que tenga una unin con un miembro long int y un arreglo de caracteres de cuatro bytes. Su programa debe imprimir el miembro long int en pantalla un byte a la vez.

70

9. LENGUAJE C ESPECIFICO PARA PIC Habiendo estudiado las bases de C, es tiempo para continuar con las instrucciones, funciones y operaciones relacionadas con el PIC. El compilador C de CCS tiene un extenso set de funciones que ahorran tiempo, y aceleran el proceso de aprendizaje para los estudiantes y programadores principiantes. ENTRADAS Y SALIDAS Los puertos de entrada/salida del PIC estn compuestos por dos registros: PORT y TRIS. Estos son designados dependiendo de la disponibilidad de puertos del PIC en cuestin: PORTA, B, C, D, E y TRISA, B, C, D, E. Un PIC de 8 pines tiene un solo registro GPIO y un TRIS para 6 lneas I/O. el PIC16F84A posee 2 puertos (13 lneas I/O) por lo que tiene los siguientes registros: PORTA, PORTB, TRISA, TRISB. El puerto A tiene de 5 a 6 lneas dependiendo del PIC, las cuales pueden configurarse como entradas o como salidas. Esto se hace por medio del registro TRISA, las entradas son configuradas con un 1 y las salidas con un 0. En Assembler para configurar individualmente los pines como entrada o salida se utilizan las instrucciones BSF o BCF. Adems para leer el puerto se emplea la siguiente instruccin MOVF PORTA,W. NOTA: en dispositivos con conversores A/D, asegrese que el registro ADCON1 est configurado correctamente, la configuracin I/O por defecto es ANALOGICO. El compilador C puede interrumpir entradas y salidas de muchas maneras: fija, rpida, o estndar. En el modo estndar, el registro TRIS es configurado antes de cada operacin I/O. esto aade lneas a un programa y por lo tanto disminuye la velocidad, pero mejora la parte de seguridad del cdigo asegurando que las lneas I/O estn siempre como se especificaron. El modo I/O rpido habilita al usuario para configurar la direccin del puerto y este permanece as hasta que sea redefinido. El compilador no aade lneas de cdigo para configurar la direccin del puerto antes de cada operacin I/O. El siguiente ejemplo configura el puerto B como entrada y luego lee su valor:
set_tris_b(0xff); //Puerto B como entrada b_valor = portb; //lee puerto B

El enmascaramiento de bit se consigue fcilmente mediante la adicin de & y el patrn de la mscara despus del nombre del puerto.
b_valor = portb & 0b00000011; //enmascara los bits no deseados

El valor almacenado en b_valor puede usarse para establecer un valor de retorno a una funcin. Lo siguiente es el conjunto de una funcin empleada para leer algunos dip switches y establecer una velocidad de transmisin (baudios) para una rutina comn. 71

byte bd_sw_get() //seleccin de velocidad de transmisin { byte b_rate; b_rate = portb & 0b00000011; //enmascara los bits no deseados switch(b_rate) { case 0: set_uart_speed(1200); break; case 1: set_uart_speed(2400); break; case 2: set_uart_speed(4800); break; case 3: set_uart_speed(9600); break; } }

Cuando configuramos el puerto B, es aconsejable establecer las condiciones del puerto antes que el registro TRIS. Esto previene que el puerto genere una condicin no requerida antes de que sea configurado. Cuando configure los bits en el registro o puertos, trabaje en forma binaria, esto har que su cdigo fuente sea ms fcil de escribir para usted y ms fcil de entender para otros. La manipulacin de datos desde y hacia los puertos I/O se realiza de forma sencilla con el uso de las numerosas funciones construidas. Sobre el nivel de bit se tienen las siguientes:
bit_set(variable, bit); bit_clear(variable, bit); bit_test(variable, bit); //se emplea para poner en ALTO un bit //se emplea para poner en BAJO un bit //se emplea para testear un bit

Las tres funciones anteriores pueden aplicarse sobre variables y I/O


b = input(pin); output_bit(pin, value); output_float(pin); output_high(pin); output_low(pin); //obtiene //pone un //pone un //pone un //pone un el estado o valor de un pin pin del puerto en un valor especifico pin como entrada o flotante pin de salida en ALTO pin de salida en BAJO

Sobre el puerto completo se emplean las siguientes instrucciones:


port_b_pullups(true/false);

Habilita o deshabilita las resistencias de pullup internas para el puerto B


set_tris_a(value);

Establece la combinacin de entradas y salidas para un puerto dado (ponga 1 para entradas y 0 para salidas). Esta funcin aplica para todos los puertos. El registro de direccin de puerto (TRIS) es configurado cada vez que un puerto es accesado a menos que las siguientes directivas del preprocesador se usen:
#use fast_io(port)

Deja el estado del puerto sin cambios a menos que se reconfigure


#use fixed_io(port_outputs=pin, pin)

72

Permanentemente configura el registro TRIS para el puerto


#use standard_io(port)

Por defecto para configurar el puerto cada vez que este se use. MEZCLANDO C Y ASSEMBLER Habr momentos en los cuales cdigo en Assembler ser requerido en nuestro programa escrito en C. Buscando cdigo compacto, limitaciones en temporizaciones, o simplemente porque se necesita alguna rutina especial. El siguiente ejemplo encuentra la paridad de un valor d pasado a una rutina cuyo valor retornado es asignado a la variable a al momento de realizar el llamado.
BuscaParidad(byte d) { byte cont; #asm Movlw 8 Movwf cont clrw bucle: xorwf d,w rrf d,f decfsz cont,f goto bucle movwf _return_ #endasm } void main(void) { byte a,d=7; a=BuscaParidad(d); }

Cuando se compila, el programa luce de la siguiente manera:


BuscaParidad(byte d) { byte cont; #asm 0005: MOVLW 0006: MOVWF 0007: CLRW 0008: XORWF 0009: RRF 000A: DECFSZ 000B: GOTO #endasm 000C: MOVWF 000E: GOTO } void main(void) { 0011: MOVLW 0012: MOVWF byte a,d=7; a=BuscaParidad(d);

8 cont 27,W 27,F 28,F 008 21 016

07 26

73

0013: 0014: 0015: 0016: 0017: }

MOVF MOVWF GOTO MOVF MOVWF

26,W 27 005 21,W 25

Set de instrucciones para el PIC16F84A Las 35 instrucciones fundamentalmente se dividen en tres tipos. Esta divisin viene dada por el tipo de datos con los que trabajan: Instrucciones orientadas a los registros o bytes (byte-oriented operations). Instrucciones orientadas a los bits (bit-oriented operations). Operaciones con literales y de control (literal and control operations). Las 35 instrucciones mnemnicos de la gama media de Microchip las encontraremos resumidas en las siguientes tablas. w es el registro acumulador, f representa un registro cualquiera y C, DC, Z los flags (banderas) del registro STATUS.
Tabla 5. Instrucciones orientadas a registros Instrucciones orientadas a registros MNEMNICO OPERANDOS ADDWF ANDWF CLRF CLRW COMF DECF f,d f,d f f,d f,d DESCRIPCIN w + f d w AND f d 00 h f 00 h w Complemento de f d f - 1 d f - 1 d (si es 0 salta) f + 1 d f + 1 d (si es 0 salta) w OR f d f d w f No operacin Rota f izq por carry d Rota f dcha por carry d f - w d CDIGO OP BANDERAS NCIC NOTAS 1 1 1 1 1 1 1,2 1,2 2 1,2 1,2

00 0111 dfff ffff C, DC, Z 00 0101 dfff ffff 00 0001 1fff ffff 00 0001 0xxx xxxx 00 1001 dfff ffff 00 0011 dfff ffff 00 1011 dfff ffff 00 1010 dfff ffff 00 1111 dfff ffff 00 0100 dfff ffff 00 1000 dfff ffff 00 0000 1fff ffff 00 0000 0xx0 0000 00 1101 dfff ffff 00 1100 dfff ffff 00 0010 dfff ffff Z Z Z Z Z Ninguna Z Ninguna Z Z Ninguna Ninguna C C C,DC,Z Ninguna Z

DECFSZ f,d INCF f,d

1(2) 1,2,3 1 1,2

INCFSZ f,d IORWF MOVF MOVWF NOP RLF RRF SUBWF SWAPF XORWF f,d f,d f f,d f,d f,d

1(2) 1,2,3 1 1 1 1 1 1 1 1 1 1,2 1,2 1,2 1,2 1,2 1,2 1,2

f,d Intercambia nibbles de f d 00 1110 dfff ffff f,d w XOR f d 00 0110 dfff ffff

Fuente: http://perso.wanadoo.es/pictob/instrucciones.htm

74

Tabla 6. Instrucciones orientadas a bit Instrucciones orientadas a bit MNEMNICO OPERANDOS BCF BSF f,b f,b DESCRIPCIN Pone a 0 bit b de registro f Pone a 1 bit b de registro f CDIGO OP 01 00bb bfff ffff 01 01bb bfff ffff BANDERAS NCIC NOTAS Ninguna Ninguna Ninguna Ninguna 1 1 1(2) 1(2) 1,2 1,2 3 3

BTFSC f,b Salto si bit b de reg. f es 0 01 10bb bfff ffff BTFSS f,b Salto si bit b de reg. f es 1 01 11bb bfff ffff Fuente: http://perso.wanadoo.es/pictob/instrucciones.htm Tabla 7. Instrucciones con literales y de control Instrucciones con literales y de control MNEMNICO OPERANDOS ADDLW ANDLW CALL CLRWDT GOTO IORLW MOVLW RETFIE RETLW RETURN SLEEP SUBLW XORLW k k k k k k k k k DESCRIPCIN w + k w w AND k w Llamada a subrutina k Borra temporizador del WDT Ir a direccin k w OR k w k w
Retorno de una interrupcin

CDIGO OP 11 111x kkkk kkkk 11 1001 kkkk kkkk 10 0kkk kkkk kkkk 00 0000 0110 0100 10 1kkk kkkk kkkk 11 1000 kkkk kkkk 11 00xx kkkk kkkk 00 0000 0000 1001 11 01xx kkkk kkkk 00 0000 0000 1000 00 0000 0110 0011 11 110x kkkk kkkk 11 1010 kkkk kkkk

BANDERAS C,DC,Z Z Ninguna TO,PD Ninguna Z Ninguna Ninguna Ninguna Ninguna TO, PD C,DC,Z Z

NCIC NOTAS 1 1 2 1 2 1 1 2 2 2 1 1 1 -

Retorno con k en w Retorno de una subrutina Modo Standby k - w w w XOR k w

Fuente: http://perso.wanadoo.es/pictob/instrucciones.htm

MANIPULACION AVANZADA DE BIT El compilador C de CCS posee un nmero de funciones enfocadas a la manipulacin de bit que se necesitan comnmente en los programas con PIC. bit_set, bit_clear, y bit_test simplemente ponen en ALTO o BAJO un bit de una variable o testean el estado de un solo bit. La numeracin de los bits se realiza dejando el bit menos significativo (la primera posicin de derecha a izquierda) como 0 y el bit ms significativo como 7. Shift_left y shift_right desplazan una posicin de bit a travs de cualquier nmero de bytes. En adicin, estas nos permiten especificar el valor del bit para colocarlo en la posicin vacante. Estas funciones devuelven los valores 0 o 1 representando los bits desplazados. Note que,

75

estas funciones consideran el byte menos significativo en memoria como LSB. El segundo parmetro es el nmero de bytes y el ltimo parmetro es el nuevo bit. Ejemplo:
int x[3] = {0b10010001, 0b00011100, 0b10000001}; short bb; //el primer msb de x es: 10000001,00011100,10010001 bb = shift_left(x,sizeof(x),0); // el primer msb de x es: 00000010,00111001,00100010 //bb es 1 bb = shift_left(x,sizeof(x),1); // el primer msb de x es: 00000100,01110010,01000101 //bb es 0

NOTA: El primer parmetro es un puntero. En este caso, debido a que x es un arreglo el identificador sin ndice es un puntero. Si se usara una simple variable o estructura, se debe aadir el operador &. Por ejemplo:
long y; struct { int a,b,c} z; shitf_left(&y,2,0); shitf_right(&z,3,0);

rotate_left y rotate_right trabajan muy parecido a las funciones de desplazamiento de arriba con la diferencia de que el bit desplazado que sale por uno de los lados es insertado por el otro lado. Por ejemplo:
int x[3] = {0b10010001, 0b00011100, 0b10000001}; //el primer msb de x es: 10000001, 00011100, 10010001 rotate_left(x,sizeof(x)); //el primer msb de x es: 00000010, 00111001, 00100011

La funcin swap intercambia los 4 bits ms significativos con los 4 bits menos significativos de un byte. Por ejemplo:
int x; x = 0b10010110; swap(x); //x es ahora 01101001

TEMPORIZADORES Todas las gamas de PICs tienen un temporizador (timer) de 8-bits y adems algunos PICs tienen dos temporizadores avanzados. Las capacidades se presentan a continuacin:
rtcc (timer0) = 8Bit.

Se puede incrementar con el reloj interno o con una fuente externa Aplicando un preescaler se puede retrasar su incremento Cuando timer0 se desborda de 255 a 0, una interrupcin puede generarse
timer1 = 16Bit.

Se puede incrementar con el reloj interno o con una fuente externa Aplicando un preescaler se puede retrasar su incremento Cuando timer1 se desborda de 65635 a 0, una interrupcin puede generarse 76

En el modo captura, la cuenta del timer1 puede guardarse en otro registro cuando el estado de un pin de entrada cambia, una interrupcin puede generarse En el modo captura, un pin de salida cambia su estado cuando la cuenta alcanza un valor preestablecido, y una interrupcin puede generarse Este temporizador se emplea como parte de la generacin de PWM (Modulacin por anchura de pulsos)
timer2 = 8Bit.

Se puede incrementar con el reloj interno o con una fuente externa Aplicando un preescaler se puede retrasar su incremento Cuando timer2 se desborda de 255 a 0, una interrupcin puede generarse La interrupcin puede retrasarse aplicando un post-escaler, con lo que requiere un cierto nmero de desbordamientos antes que la interrupcin ocurra Este temporizador se emplea como parte de la generacin de PWM

En el siguiente ejemplo se usa rtcc (timer0) para temporizar cuanto tiempo dura un pulso en estado ALTO.
#include <16F84A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=1024000) #use RS232 (Baud=9600, xmit=pin_a3, RCV=pin_a4) void main(void) { int time; setup_counters(rtcc_internal, rtcc_div_256); //incrementa 1024000/4*256 veces por segundo //o cada milisegundo while(input(PIN_B0)); //espera por estado ALTO set_rtcc(0); while(!input(PIN_B0)); //espera por estado BAJO time = get_rtcc(); printf("tiempo en ALTO = %u ms.",time); }

El siguiente ejemplo emplea el timer1 en el modo captura para temporizar cuanto tiempo le toma al pin C2 cambiar al estado ALTO despus de que el pin B0
#include <16F877A.h> #fuses XT,NOWDT, PUT, NOPROTECT #use delay(clock=8000000) #use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7) #bit capture_1 = 0x0c.2 //registro pir1 //bit 2 = la captura se ha realizado void main(void) { long time; setup_timer_1(t1_internal | t1_div_by_2); //incrementa cada 1 us setup_ccp1(ccp_capture_re); //configura CCP1 para captura en flanco subida capture_1=0; set_timer1(0); output_high(PIN_B0); while(!capture_1);

77

time = ccp_1; printf("Tiempo de reaccion = %1u us.",time); }

COMVERSION A/D Los conversores A/D de Pag117

78

You might also like