Introduccion a La Programacion de Microcontroladores Pic en C-John Caipa
Comments
Description
2011INTRODUCCION A LA PROGRAMACION DE MICROCONTROLADORES PIC EN CCS C Por John Caipa Roldan Este material es una traducción y actualización 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 Institución Educativa Politécnico "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 2 .................................................................................................................................................................................................................................................................................................. 67 INTRODUCCION A LAS UNIONES .................................................................................................... 71 ENTRADAS Y SALIDAS .................................................................... 64 PUNTEROS A ESTRUCTURAS......................................................................................... 69 9........................................................................ LENGUAJE C ESPECIFICO PARA PIC ............................................................................ 75 TEMPORIZADORES ................................... 61 PASANDO PUNTEROS A FUNCIONES ............................................................... 73 MANIPULACION AVANZADA DE BIT .............. 76 3 ......................................................... 59 PUNTEROS Y ARREGLOS ......................................RESTRICCIONES PARA PUNTEROS ...................................................................... ESTRUCTURAS Y UNIONES.................... 64 INTRODUCCION A LAS ESTRUCTURAS .................... 71 MEZCLANDO C Y ASSEMBLER ......................................................................... 62 8................................................................................................................................................. PORQUE USAR C? El lenguaje C fue desarrollado en los laboratorios Bell a finales de los 60s por Dennis Ritchie y Brian Kernighan. mientras que en H8 son 0=Input (entrada) 1=Output (salida). estandarizado y se ha establecido en toda la industria de software como el lenguaje de programación más empleado. C es un lenguaje portable desarrollado para tener mínimas modificaciones cuando se transfieran programas de un computador a otro. 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 Electrónics. Desde su introducción. El lector de este material aunque debe tener las bases de la programación en el lenguaje Assembler y conocer las características principales de los microcontroladores PIC no debe preocuparse. en este caso el PIC de Microchip®. 4 . 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®. Con dicha herramienta teniendo algo de conocimiento del lenguaje C facilita mucho el desarrollo de programas y aplicaciones con microcontroladores. Una de las primeras plataformas para su implementación fue PDP-11 que corría bajo el ambiente UNIX.INTRODUCCION Un gran avance en la programación de microcontroladores son las herramientas que utilizan el lenguaje de alto nivel C como es el caso del compilador C de CCS®. El uso del lenguaje C en las aplicaciones con Microcontroladores es debido a que provee programas más complejos con un desarrollo más fácil y tiempo de diseño más corto comparado con el lenguaje Assembler. El Microcontrolador empleado en este material es el PIC16F84A de Microchip®. Esto está bien cuando se trabaja con PCs y capas principales. Pero los Microcontroladores y Microprocesadores son un caso diferente. este ha evolucionado. pero con el inconveniente que no es tan eficiente en lo relacionado con memoria de programa. el cual puede descargarse gratuitamente de la página de Microsoft®. pero las diferentes configuraciones de puertos y control de periféricos son específicos para cada Microcontrolador. Un ejemplo de esto es que los registros de puertos sobre un PIC son configurados 1=Input (entrada) 0=Output (salida). El flujo del programa principal básicamente permanece sin cambios. ya que el objetivo de este material es presentar los fundamentos del lenguaje C apoyado en algunos ejemplos y ejercicios. además los programas ejemplo que utilizan este Microcontrolador se escribieron y compilaron en PIC C Compiler de CCS®. PICKIT® de Microchip. El compilador C que se utiliza en este material es PIC C Compiler de CCS®. fuentes de voltaje. I/O es necesario en la mayoría de los casos para permitirle al Microcontrolador comunicarse. ejemplo: ISIS PROTEUS ®.TERMINOLOGIA Comencemos con la terminología básica empleada en este contexto. La extensión es . Compilador – Paquete software que convierte al archivo fuente en un archivo objeto. Simulador – Aplicación en la que puede probar y depurar sus diseños de manera interactiva y rápida evitando la programación del dispositivo real. 5 . Este tiene que estar libre errores para una aplicación exitosa.HEX. ejemplos: PICSTART PLUS®. y es el archivo que necesita el Simulador y el Microcontrolador para funcionar. controlar y leer información. y todos los demás componentes conectados a este para hacer que trabaje y se comunique con el mundo exterior. Pascal o Assembler. Archivo Objeto – Archivo que se produce después de compilar el archivo fuente. Este archivo tiene que procesarse antes de que el Microcontrolador lo reciba. I/O – Pin de conexión con el mundo exterior que puede ser configurado como entrada o salida. Puede ser escrito en una variedad de lenguajes como C. memorias. Software – La información que el Microcontrolador necesita para operar o correr. Archivo Fuente – Programa escrito en un lenguaje como Assembler o C que usted puede entender. Estos vienen en diferentes formas. Programador – Unidad que permite al programa ser cargado dentro de la memoria del Microcontrolador. 1). periféricos. protocolos de comunicación y precios. 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.OBJ o . Hardware – El Microcontrolador. microchip. (a) Diagrama de pines PIC16F84A.pdf 6 .com/downloads/en/devicedoc/35007b. 1.(a) (b) Fig. (b) Diagrama de bloques PIC16F84A Fuente: http://ww1. utilice Indentación para que su código luzca organizado. haga el código portable. Etiquetas – Defínalas para que identifiquen su función Los nombres de las etiquetas son el corazón de la programación. Longitud de línea Mantenga una longitud de línea de máximo 78 caracteres para mantener compatibilidad entre monitor e impresora. Las siguientes recomendaciones tomadas del estándar C++ han sido adaptadas para el PIC. el resto del código permanecerá solido. ejemplo: if (condicion1) { } else if (condición2) { } else { …… //decide cualquier condición no cubierta anteriormente } Inicialice todas las variables Coloque todas las variables en un valor conocido para prevenir condiciones aleatorias o flotantes. recuerde que la configuración de tabulación de un editor no es la misma en otro. Comentarios Los comentarios permiten descifrar la historia que usted está escribiendo. o puede alguien diferente entender su programa como lo tiene ahora? Use comentarios para marcar áreas donde hay que hacer alguna modificación. si la base es firme.RECOMENDACIONES EN LA ESCRITURA DE CODIGO C La escritura de un programa es como construir una casa. ¿Usted sabe como su programa está funcionando hoy pero en dos semanas o dos años podrá recordar. 7 . así que defínalas apropiadamente a su función y su uso en el programa. Formato de instrucción else if Se puede incluir una instrucción else extra para decidir cualquier condición no cubierta por las instrucciones precedentes if. Corchetes o llaves – úselas de manera ordenada. Ejemplo: use ErrorCheck en vez de ERRORCHECK. ejemplo: if (condición) { …… } Tabulación e Indentación Use espaciado en lugar de tabulación (8 espacios). Pero si la base es débil. errores a depurar o complementos futuros de su código. el código perderá su firmeza en algún punto. funciones y tipos usados en el programa. El objetivo es proporcionar al lector un conocimiento básico de C tal que puede comprender los ejemplos de los siguientes capítulos. declaraciones. Expresiones Una expresión es una combinación de operadores y operandos que producen un único valor Instrucciones Controlan el flujo y orden en la ejecución del programa en un código C. Las funciones no se pueden anidar en C.1.142 /* directiva del preprocesador */ /* Incluye el encabezado estándar de C*/ /* define una constante simbólica */ 8 . Función main Todos los programas hechos en C deben incluir una función llamada main donde la ejecución del programa comienza. Se entregará una vista rápida de cada aspecto. Una definición también asigna el espacio de almacenaje necesario para las variables y funciones. y la directiva #include. Declaraciones Una declaración establece el nombre y atributos de las variables. #include <stdio. el cual puede descargarlo desde la web de Microsoft®. Directivas del preprocesador Una directiva es un comando del preprocesador de C (el cual es invocado automáticamente como el primer paso en la compilación de un programa). Una variable local es declarada dentro de una función y es visible desde el final de la declaración hasta el final de la función. definiciones. la cual substituye una etiqueta por un identificador especifico. expresiones. El siguiente ejemplo muestra la estructura general de un programa C escrito en Microsoft Visual C++ Express®.h> #define PI 3. Las directivas más comunes son la directiva #define. expresiones e instrucciones que desempeñan una tarea específica. FUNDAMENTOS DE C Este capítulo presenta algunos de los aspecto clave del lenguaje de programación C. Los corchetes encierran el cuerpo de una función. ESTRUCTURA DEL CODIGO C Todos los programas escritos en C contienen: directivas del preprocesador. Funciones Una función es una colección de declaraciones. la cual incluye funciones de un archivo externo al programa actual. Los corchetes que encierran la función main definen el comienzo y punto de finalización del programa. instrucciones y funciones. Definiciones Una definición establece el contenido de una variable o función. Las variables globales son declaradas fuera de las funciones y son visibles desde el final de la declaración hasta el final del archivo. definiciones. contiene muchas de las funciones de entrada y salida. getchar().area). cada una de las cuales contienen una o más instrucciones y pueden ser llamada por otras partes del programa. r_alCuadrado = r * r. Las funciones son subrutinas. /* mi 1er código en C */ es un comentario en C. /*declaración e inicialización*/ radio_alCuadrado = cuadrado(radio). Cuando escribe programas con líneas en blanco. La extensión . Esta función es el punto de entrada del programa. Todos los programas escritos en C deben tener la función main(). /*función de usuario*/ area = PI * radio_alCuadrado. Todos los comentarios están precedidos por un /* y terminan con un */. /* declaración variable global */ void main() { /* Comienza la función main */ int radio_alCuadrado. /* función de terminación*/ } /*fin de la función main */ int cuadrado(int r) /* encabezado de la función*/ { int r_alCuadrado.h pertenece a archivos de encabezado. /* termina el programa oprimiendo ENTER */ } La sentencia #include <stdio. sino también para aquellas personas interesadas en su trabajo. no solo para usted en el futuro. } COMPONENTES DEL CODIGO C Todos los programas escritos en C contienen ciertos componentes esenciales tales como instrucción y funciones. printf("El area es %6. Todos los programas en C contienen una o más funciones. El siguiente ejemplo muestra algunas de las partes requeridas de un programa en C. return(r_alCuadrado).h> le dice al compilador que incluya el archivo fuente llamado stdio. getchar(). int cuadrado (int r).h al programa. usar indentación y comentarios mejora la visualización. Un archivo de encabezado contiene información acerca de funciones estándar que está usando el programa como printf() y getchar(). El archivo stdio.h> /* mi 1er codigo en C */ void main() { printf("Hola Mundo!").4f unidades cuadradas\n". #include <stdio. Todas las funciones tienen el mismo formato. Los comentarios son ignorados por el compilador y por consiguiente no afectan la velocidad ni el tamaño del código compilado.h (Standard Input and Output).float area. /* declaración variable local */ int radio = 3. el cual es el siguiente: 9 . Las instrucciones son la parte del programa que realiza las operaciones. etc.). La excepción a esta regla es con el comando if donde el punto y coma necesita estar al final de la siguiente línea. ejemplo: if (EstoEsVerdad) HagaEstaFuncion().TipoFuncion NombreFuncion() { Código. representa una instrucción típica en C. El carácter final de línea no es reconocido por C como un terminador de línea. #PRAGMA El comando pragma le informa al compilador que desempeñe una acción particular al momento de realizar la compilación como el PIC especifico usado en la aplicación. printf("Hola Mundo"). Ejemplo: #include <16F84A.) al final para informar al compilador que ha alcanzado el final de la instrucción. comenzando con el corchete abierto y terminando con el corchete cerrado. no hay restricciones sobre la posición de las instrucciones en una línea ni del número de estas. void main() { Cuerpo del programa } #INCLUDE El archivo de encabezado. En consecuencia. Casi todas las instrucción en C terminan con punto y coma (. (denotado por la extensión .h) contiene información acerca librerías de funciones tales como registros especiales de un PIC específico. Como main está clasificada como función. el siguiente comando es el que más se utiliza: #device PIC16F84A MAIN() Como ya se ha dicho anteriormente todo programa debe incluir una función main la cual puede aparecer solo una vez en el programa. Ningún parámetro es necesario dentro de los paréntesis (). Los corchetes {} muestran el comienzo y el fin de bloques de código en C. } Las instrucciones dentro de una función son ejecutadas secuencialmente. Finalmente.h> 10 . El error común de no incluirlo es señalado como error en la línea siguiente. todo el código siguiente a esta función debe estar dentro de un par de corchetes {}. Todas las líneas tienen un punto y coma (. Ejemplo: #pragma device PIC16F84A En CCS C el comando pragma es opcional. 11 . NOPROTECT //palabra de configuración #use delay(clock=4000000) //frecuencia del oscilador #use rs232(baud=9600. La cadena de caracteres puede ser una combinación de letras. FUNCION PRINTF Es una función estándar contenida en el archivo de encabezado stdio.NOWDT.h.Esta información la utiliza el compilador para conectar detalles específicos de hardware y el programa fuente. La función toupper() se encuentra en el archivo de encabezado ctype. El siguiente ejemplo es hecho en el compilador C de CCS®. printf() le permite al programa imprimir información en pantalla. pero en cambio es una instrucción del compilador.h> //definicion del PIC especifico #include <ctype. printf("Microchip es el #%d!".h> #fuses XT.h. Algunos símbolos especiales de formato están denotados con el símbolo %. Habrá notado probablemente que la directiva #include no termina con punto y coma. printf("Hola Mundo!"). El formato general para printf() es: printf("Texto de control".1).xmit=PIN_B0. números y símbolos. %d da el formato para la constante que se está imprimiendo. Argumentos es una entrada opcional y puede estar compuesto por constantes y variables que utilice el programa y que se quieren imprimir.rcv=PIN_B1) void main() { Printf("Escriba caracteres:"). Texto de control es una cadena de caracteres entre comillas dobles. //toupper convierte a mayúsculas } Las definiciones de PIN_B0 y PIN_B1 se encuentran en el archivo de encabezado 16F84A. Los siguientes dos ejemplos muestran a printf() imprimiendo un mensaje e imprimiendo una constante del programa. Ambos archivos deben estar en el programa para que el compilador tenga la información acerca de las funciones que usted está usando. Argumentos). La Tabla 1 muestra todos los especificadores de formato de C y el tipo de datos que afectan. La razón para esto es que esta directiva no es una palabra clave de C.h. while(TRUE) putc(toupper(getc())). PUT. Todo el contenido de la librería incluida es insertado en el archivo fuente en la etapa de compilación. #include <16F84A. Caracteres especiales. %u. VARIABLES Una variable es un nombre que se le da a una localización específica de memoria.Tabla 1. Especificadores de formato en C. entonces retomando un ejemplo anterior: printf("El area es %6. En C. %x.4f unidades cuadradas\n". Imprime en pantalla lo siguiente: El Hex del decimal 12 es 00c Tabla 2. todas las variables deben declararse antes de ser usadas. Una declaración de variables le 12 . %X Enteros hexadecimales sin signo %p Apuntador %o Enteros octales sin signo l Prefijo usado con %d.area). Ejemplo: printf("El Hex del decimal 12 es %03x\n".presicion]. El siguiente número especifica el tamaño del campo a imprimir. cualquiera en su formato corto) %u Enteros decimales sin signo %x.12). Esta instrucción imprime en pantalla el valor de area en un espacio con ancho de 6 y precisión decimal de 4 posiciones. \n \t \r \f \’ \” \\ Símbolos Especiales Línea nueva Tab horizontal Retorno de carro Formfeed Comilla sencilla Comilla doble Backslash \? \a \b \0 \v \xhhh %% Símbolo de interrogación Beep Backspace Carácter nulo(null) Tab vertical Inserta código hex hhh Signo de porcentaje El formato de especificación puede ser también así: %[flags][ancho][. Especificador de formato Tipo de dato que afecta %c Carácter sencillo %uc Carácter sin signo %s Cadena de caracteres %d Enteros decimales con signo %f Punto flotante (notación decimal) %e Punto flotante (notación exponencial o científica) %g Punto flotante (%f o %e. Esta localización de memoria puede guardar varios valores dependiendo de cómo haya sido declarada la variable. %o para especificar entero largo NOTA: Un 0 (cero) seguido del símbolo % dentro de un formato de cadena obliga a imprimir los ceros que están al comienzo. Un ejemplo de declaración de un variables es: char ch. use la palabra clave const en la declaración de una variable. Las constantes no son almacenadas en memoria.informa al compilador que tipo de variable está siendo usada. por defecto. El número 456. Los cinco tipos básicos de datos que soporta C son: char. 25 es una constante. El compilador C podrá. son resueltas en tiempo de compilación. Las cuales son una serie de caracteres encerrados entre comillas dobles. Por ejemplo 0xA4 es una constante hexadecimal valida. CONSTANTES Una constante es un valor fijo que no puede ser alterado por el programa. int.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. valor es el valor que se le asignara a <etiqueta>. Los caracteres también son constantes y deben estar encerradas por comillas sencillas como 'A' o '&'. este decide qué tipo de constante es. C soporta constantes tipo string (cadena de caracteres).75 es una constante en punto flotante. Por ejemplo: char const id[5]={"1234"}. 13 . Constantes en punto flotante requieren punto decimal seguido por su componente fraccional. Cuando el compilador encuentra una constante en el programa. Para guardar constantes en la memoria ROM del chip. Así que 15 es un tipo int. Las constantes enteras son especificadas sin ninguna componente fraccional como -100 o 40. Una constante es declarada usando la sentencia #define. El formato general para declarar una variable es el siguiente: TipoVariable NombreVariable. En adición a las constantes numéricas.. float. Ejemplos: #define VERDADERO 1 #define pi 3. void. acomodar la constante dentro del tipo de dato más pequeño que lo pueda contener. Todas las declaraciones de variables son instrucciones en C y por consiguiente deben finalizar con un punto y coma. double. Las constantes hexadecimales deben tener el prefijo '0x'. 64000 es un tipo long y 105020 es un tipo int32. Por ejemplo. #define <etiqueta> valor <etiqueta> define el nombre que usará a través del programa. El compilador interpreta esta instrucción como la definición de la variable ch como tipo char (caracter o entero sin signo de 8 bits). Finalmente. El siguiente es un ejemplo de dos funciones en C. Los comentarios pueden abarcar muchas líneas y pueden ser usados para remover temporalmente una línea de código. 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 mayoría de compiladores y es el siguiente: // Este es un comentario FUNCIONES Las funciones son los bloques de construcción básicos de un programa en C. Tradicionalmente main() no es llamada por ninguna otra función. Todos los comentarios son ignorados por el compilador. #include <stdio. La mayoría de los programas que usted escribe contendrá muchas funciones. 14 . Los comentarios tienen dos formatos. todos los programas en C contienen al menos una función. pueden ser llamadas por cualquier otra función en el programa. Un comentario puede ser puesto en cualquier parte del programa excepto en la mitad de alguna instrucción.Se están usando cinco localizaciones de memoria para guardar la cadena porque esta debe terminar con el carácter null (\0) como se explicará más adelante. funcion1() y funcion2().h> void main() { printf("Me "). nombre de función o nombre de variable. los comentarios no se pueden anidar. no hay restricciones en C con respecto a esto. Las otras funciones. main(). COMENTARIOS Los comentarios son empleados para documentar el significado y operación del código fuente. sin embargo. El formato para un programa en C con muchas funciones es el siguiente: void main() { } funcion1() { } funcion2() { } main() es la primera función llamada cuando el programa se está ejecutando. "). Un programa típico en el compilador C de CCS® puede comenzar así: #include <16F84A.rcv=PIN_C7) #use i2c(master. I2C En adición. la mayoría de los compiladores de C agregan varias palabras adicionales que toman ventaja de la arquitectura del procesador. La última línea le informa al compilador la velocidad del oscilador. La segunda línea establece la palabra de configuración del PIC. Después que son definidas. pueden ser utilizadas en un programa como cualquier otra variable. RS232 //config. En C. printf("C. ciertas palabras son reservadas del compilador para definir tipos de datos y para su uso en loops (bucles o lazos).h> #fuses XT. Típicamente. se habilita el Power-up Timer y se desactiva la protección de código. } Una cuestión que hay que tener en cuenta al escribe sus propias funciones es que cuando el corchete de cierre es alcanzado. COMPATIBILIDAD CON HARDWARE El compilador necesita saber acerca del hardware para que el código pueda ser compilado correctamente. Todas las palabras claves de C deben escribirse en minúscula para que el compilador las reconozca. PUT.NOWDT.scl=PIN_B6.xmit=PIN_C6. registros especiales. el programa continuará ejecutándose una línea después del punto en el cual la función ha sido originalmente llamada.0 #byte portb=6 #byte intcon=11 PALABRAS CLAVES DE C El estándar C ANSI define 32 palabras clave para usar en el lenguaje C. etc. Las siguientes líneas son otro ejemplo: #use rs232(baud=9600. NOPROTECT #use delay(clock=4000000) La primera línea incluye las definiciones específicas del dispositivo tales como nombre de pines. Estas variables pueden ser bits o bytes. para este caso se deja al oscilador de cristal como oscilador del dispositivo. Ejemplo: #bit carry=3. } funcion1() { printf("gusta programar en").funcion1(). las variables de C pueden ser creadas y mapeadas a registros del hardware. La siguiente es una lista de las palabras claves de C: 15 . se desactiva el Watch Dog Timer.sda=PIN_B7) //config. Escriba un programa en Microsoft Visual C++ Express® que imprima en pantalla su nombre. su documento de identidad y su email e líneas diferentes.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. usando la función printf(). Escriba un programa en Microsoft Visual C++ Express® que declare una variable tipo entera llamada year. 16 . 2. Esta variable debe entregar el valor del año actual y luego. El resultado de su programa debe parecerse a lo siguiente: El año es 2011. mostrar el valor del año en pantalla. Este capítulo examinará más en detalle cómo se usan las variables en C para almacenar datos.5E45 a 3. Tipo Tamaño 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. Tabla 4. Todos los tipos. Para encontrar el complemento a 2 de un número simplemente invierta todos los bits y sume 1 al resultado. short int. short o long sin la palabra int.4E38 0 a 255 Sin valor -128 a 127 -32768 a 32767 -2147483648 a 2147483647 NOTA: revisar la documentación del compilador C para tipos de datos y rangos numéricos actualizados. Tipos de datos y modificadores en C. TIPOS DE DATOS El lenguaje de programación C soporta cinco tipos de datos básicos (como se había comentado en el capitulo anterior) y cuatro tipos de modificadores. que pueden cambiar dependiendo del modificador usado. Para realizar las operaciones aritméticas de una forma sencillas para la CPU. Tipo Significado Carácter Dato carácter Entero Números enteros con signo Flotante Números con punto flotante Doble precisión Números con punto flotante de doble precisión Vacio (void) Sin valor Con signo Números positivos o negativos Sin signo Solo números positivos Largo Duplica el tamaño de un número corto Mitad del tamaño de un número Palabra clave char int float double void signed unsigned long short Cada tipo de dato representa un rango particular de números. La Tabla 4 muestra los posibles rangos de valores para algunos tipos de datos básicos y los modificadores en CCS C.2. Simplemente use las palabras unsigned. para convertir el número con signo +29 en complemento a 2 se procede así: 17 . por defecto son unsigned. Tabla 3. C representa todos los números negativos en el formato complemento a 2. VARIABLES Un aspecto importante del lenguaje C es como este almacena datos. excepto float. La Tabla 3 muestra la definición de los tipos de datos básicos y el tipo de modificador. C permite una notación más corta para los tipos unsigned int. y long int. Rangos de valores de los tipos de datos en CCS C. Por ejemplo. i = u. Las variables locales deben declararse al comienzo de la función antes de las instrucciones. Considere el siguiente ejemplo.//detiene la ejecución hasta oprimir ENTER } DECLARACION DE VARIABLES Las variables pueden ser declaradas en dos lugares básicos: dentro de una función o por fuera de todas las funciones. Escriba la siguiente declaración de otra manera. El valor de una variable local no puede ser accesado por otras funciones por fuera de la función de origen. printf("%d %u\n". getchar(). Las variables son llamadas locales o globales respectivamente. Las variables son declaradas de la siguiente forma: TipoVariable NombreVariable. #include <stdio. for (cont=0.00011101= 11100010 + 1 ________ 11100011= 29 se invierten todos los bits se suma 1 -29 Ejercicios 1. a signed int16 en CCS unsigned short int u. La cosa más importante a recordar acerca de las variables locales es que son creadas al momento de entrar a la función y destruidas cuando la función ha finalizado. cont++) 18 . Para entender la diferencia entre un número con signo y uno sin signo. El entero sin signo 35000 es representado por -30536 en el formato entero con signo. escriba el siguiente programa en Microsoft Visual C++ Express®.i. 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 función) solo pueden ser usadas por las instrucciones dentro de la función donde ha sido declarada. Es aceptable para variables locales que estas tengan nombres iguales en diferentes funciones. // entero con signo equiv. cont<10.h> void main() { short int i. 2.h> void f2() { int cont. long int i. // entero sin signo equiv.u). a int16 en CCS u = 35000. #include <stdio. por otra parte. f1(). ¿Ambos tipos de variables podrían compartir el mismo nombre en C?. return 0. i<max. #include <stdio. getchar(). ambas funciones main() y f1() hacen referencia a la variable max. for (cont=0. } int main() { max = 10. void f1() 19 . Escriba el siguiente programa. La función main() asigna un valor a max y la función f1() usa el valor de max para controlar el bucle del for.h> int cont.printf("%d \n".cont). for (i=0. return 0. } void f1() { int cont. i++) printf("%d ". void f1() { int i. #include <stdio. cont<10. las variables locales no son destruidas hasta que la ejecución del programa haya sido completada.i). } En este ejemplo. Las variables globales. Ejercicios 1. pueden ser usadas por todas las funciones definidas en el programa. Las variables globales deben ser declaradas antes que cualquier función que las vaya a usar. } int main() { f1(). La operación del programa no es afectada por la variable cont que está definida en ambas funciones.h> int max. cont++) f2(). } Este programa imprime los números del 0 al 9 en pantalla 10 veces. ¿Cuáles son las diferencias principales entre variables locales y variables globales? 2. Lo más importante. El siguiente ejemplo muestra como se emplean las variables globales. return 0. En la función f1() la variable local cont domina sobre el uso de la variable global. Todos los números enteros se emplean cuan se asignan valores a enteros. } int main() { cont = 10.cont).{ int cont. void main() { int i. Escriba un programa en Microsoft Visual C++ Express® que declare una variable tipo entero llamada cont. printf("contador en main(): %d\n ".0. int j. una constante tipo carácter esta especificado porque debe estar encerrada en comillas sencillas. De un valor de 100 a cont y use la función printf() para imprimir el valor en pantalla. use 100. 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. } En la función main() la referencia a cont es una variable global. A una variable también se le puede asignar el valor que tiene otra variable diferente. Números en punto flotante deben usar valores con punto decimal. Un ejemplo de cómo asignar el valor 100 a la variable tipo entera cont es: cont = 100. j=i. como 'M'. La asignación de valores a variables es sencilla. Diferentes tipos de constantes existen en C. printf("contador en f1(): %d\n ". Debido a que la asignación de valores a variables se hace con una instrucción. La salida debe lucir así: 100 es el valor de cont 20 . así: NombreVariable = valor. Por ejemplo. i=0. f1(). El siguiente programa ilustra esta asignación. El valor 100 es una constante. } Ejercicios 1. para decirle a C que el valor 100 está en punto flotante. cont = 100. se debe incluir el punto y coma al final.cont). ). el nombre puede usarse para crear variables adicionales en otros puntos del programa. La lista de constantes creadas con enumeración puede ser usada en cualquier lugar. y 156. La variable también puede probarse contra otro variable diferente: if (color==fruta) //haga esto Esencialmente. enumeración ayuda a documentar el código. Por ejemplo. enum TipoColor {rojo.5 a f. Una vez la enumeración es definida.2. 50. A la variable color solo se le pueden asignar los valores rojo. Asignar el valor 'R' a ch.amarillo} color. es posible crear una lista de constantes enteras. La forma general para crear una enumeración es la siguiente: enum nombre {lista de enumeracion} variable(s). 21 .amarillo} color. en la instrucción: enum TipoColor {rojo. La salida debe lucir como la siguiente: ch esta asignada con r f esta asignada con 50. la variable MiColor puede crearse con la enumeración TipoColor asi: enum TipoColor MiColor. Cada entrada es una unidad más grande que la anterior. verde o amarillo (esto es. Imprima el valor de todas las variables en pantalla. color = rojo. En lugar de asignar un valor a una variable. Este tipo de declaración es llamada enumeración. en el ejemplo anterior rojo es 0. char. El siguiente ejemplo ilustra la técnica. Esta instrucción asigna 0 a rojo. 9 a verde y 10 a amarillo.007 ENUMERACION En C. Por consiguiente.verde=9. La lista de variables es opcional en la enumeración. f. Escriba un programa en Microsoft Visual C++ Express® que declare tres variables.007 a d.5 d esta asignada con 156. una enumeración puede usarse para distinguir el significado del valor. y d.verde. Las variables de enumeración pueden contener solo los valores que están definidos en la lista de enumeración. float. Estos valores por defecto pueden cambiarse especificando un valor para la constante. Por ejemplo. El compilador asignara valores enteros a la lista de enumeración comenzando con 0 en la primera entrada. verde es 1 y amarillo es 2. y double con los nombres ch. 3. usted debe recordar dos puntos clave: La declaración typedef no desactiva el nombre original o el tipo. Crear una enumeración con la denominación de la más baja a la más alta de la moneda colombiana. Use este typedef en un programa corto que declare una variable usando UL. profundo d. Si el programa que se está escribiendo va a ser usando en maquinas con enteros de 16-bits y de 32bits. typedef largo profundo. La segunda razón para usar la declaración typedef es para ayudarle a documentar su código. for(i=0. varias declaraciones typedef pueden usarse para crear nuevos nombres para el mismo tipo original. dispositivo). typedef altura largo. en el ejemplo anterior signed char todavía es un tipo valido. } Cuando se usa typedef.i<10. el siguiente programa usa el nombre smallint para el tipo signed char.i).i++) printf("%d ". Su formato es el siguiente: typedef NombreAnterior NombreNuevo El nuevo nombre puede usarse para declarar variables. Ejercicios 1.PIC16C53} dispositivo.h> typedef signed char smallint. asigne un valor a está y muestre el valor en pantalla. 22 . Por ejemplo. void main() { smallint i.PIC16C52.Ejercicios 1. printf("el primer PIC es %s\n". La primera para crear un programa portable. 2. Typedef es típicamente usado por dos razones. #include <stdio. ¿El siguiente fragmento de código es correcto? ¿Por qué? enum {PIC16C51. TYPEDEF La declaración typedef es usada para definir nuevos tipos de datos por medio de tipos existentes. ¿El siguiente fragmento de código es correcto? typedef int altura. usted querría asegurar que solo los enteros de 16-bits van a ser empleados. Crear un nuevo nombre para el tipo unsigned long que se llame UL. dispositivo = PIC16C52. Crear una enumeración con la familia de microcontroladores PIC16F8X. 2. Una promoción de tipo solo es válida durante la evaluación de la expresión.6 La combinación de tipos de datos es gobernado por un conjunto estricto de reglas de conversión que le informan al compilador como resolver las diferencias. float f = 25. Entonces si necesita un valor tipo long como respuesta. int. printf("%d". estática y registro. c = (long) a * b. Si dos enteros de 8-bits son multiplicados. El resultado será 2500 porque a primero ha sido convertido multiplicación se haya completado. El siguiente fragmento de código muestra como imprimir la porción entera de un número en punto flotante: float f. CLASES DE ALMACENAMIENTO PARA VARIABLES a tipo long antes que la Cada variable y función en C tiene dos atributos. La primera parte del conjunto de reglas es una promoción de tipo. entonces al menos un valor necesita definirse inicialmente como long o escribir lo siguiente. el compilador C convertirá todas las variables en una expresión sobre el tipo de la variable más larga. debido a que la aritmética es desarrollada antes de que el resultado sea asignado a la nueva variable. El numero 100 se imprimirá en pantalla al ejecutarse el código. el tipo y clase de almacenamiento. el resultado será un valor de 8-bits. Esta tarea se completa sobre una operación por medio de operaciones básica.CONVERSION DE TIPO C le permite combinar diferentes tipos de datos en una sola expresión. c = a * b. Por ejemplo.(int)f). int i = 15. El resultado será 196 el cual es erróneo. f = 100. el resultado todavía seguirá siendo un entero de 8-bits. Si el resultado es asignado a un tipo long. Ahora que la promoción de tipo automática ha sido completada. etc.2. El compilador C automáticamente promoverá un dato char o short int en una expresión a un dato int cuando la expresión sea evaluada. int a = 250. Hay cuatro clases de almacenamiento: automática. b = 10. Estas clases tienen los siguientes nombres en C: auto extern static register 23 . long c. la variable por sí misma no se hace físicamente más larga. el siguiente ejemplo es un fragmento de código valido: char ch = '0'. El tipo ya ha sido discutido como char. externa. } La variable cont es inicializada una vez.++cont).e. printf("cont = %d\n". Register no está definida en el compilador C de CCS®.y. Extern no está definida en el compilador C de CCS®. Esto significa que su nombre es visible en otros archivos diferentes en donde fue definida.Auto Las variables declaradas dentro de una función son por defecto auto. } Es lo mismo que: { auto char c. static int cont = 4.b. y después se incrementa cada vez que la función test es llamada. void test() { char x. 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. int a. } Cuando un bloque de código es introducido.b. tal que. { char c. auto int a. Static La clase static define variables globales activas que son inicializadas a cero.e. Las localizaciones RAM son utilizadas en ese bloque ‘local’ de código y puede ser usado por otros bloques de código. 24 . Extern La palabra clave extern declara una variable o una función y específica que esta variable tiene conexión externa.z. a menos que se defina lo contrario. el compilador asigna espacio RAM para las variables declaradas. Si está empleando una función estándar de C. todas las instrucciones deben estar dentro de funciones. Una forma es usar función prototipo. El prototipo debe corresponder con la declaración de la función exactamente. La forma general es la siguiente: tipo NombreFuncion(). hay dos caminos para corregir este error. La razón es que la función f1() debe declararse o definirse antes de usarse. Si está empleando una función que usted ha creado. tal como las variables. FUNCION PROTOTIPO Hay dos métodos empleados para informarle al compilador que tipo de valor retorna una función. la declaración en sum() podría decirle al compilador que la función sum() retorno un entero. FUNCIONES Las funciones son los bloques básicos del lenguaje C. La segunda forma para informarle al compilador acerca del valor que retorna una función es la función prototipo. por ejemplo: void main() { f1(). } int f1() { return 1. FUNCIONES En secciones anteriores. } El error ya no se genera porque la función f1() es definida antes que sea llamada en el main(). } void main() { f1(). este programa producirá un error. 25 . } En realidad.3. Una función prototipo no solo entrega el valor a retornar de la función. que serán explicadas en la siguiente sección. Por ejemplo. el archivo de encabezado que incluye en el comienzo del programa ya ha informado al compilador acerca de la función. se estudiaron varios ejemplos de funciones siendo llamadas desde un programa principal. En este capítulo se discutirá como pasar argumentos a funciones y como recibir un argumento de una función. La otra forma es reorganizar el programa de la siguiente manera: int f1() { return 1. sino que también declara el número y tipo de los argumentos que la función acepta. como los que se han realizado hasta ahora. int s3). cambie el programa anterior para mandar cuatro parámetros a la función volumen: vol = volumen(5. Para mostrar como los errores son capturados por el compilador. vol = volumen(5. la importancia de los prototipos en la depuración de errores será evidente. tipo var2.vol). } double myfunc(double num) { return num/2. } int volumen(int s1. } Note que return usa una expresión en lugar de una constante o variable. } 26 . Además reporta si el número de argumentos enviados a una función no concuerdan con los especificados en la declaración de la función. 2. void main() { printf("%f\n".15). La importancia de los prototipos no es significativa en programas cortos.12). int s2.0. Un ejemplo de una función prototipo se entrega en el siguiente programa.Los prototipos le ayudan a identificar errores en el programa reportando cualquier conversión ilegal de tipos entre los argumentos que pasan a una función y en su declaración.12. int volumen(int s1. Sin embargo. void main() { int vol. ancho y alto.2)). tipo var3).7.myfunc(10. En el ejemplo anterior. La función calcula el volumen definido por largo. int s3) { return s1*s2*s3. el tipo de cada variable puede ser diferente. El formato general para una función prototipo es como se muestra a continuación: tipo NombreFuncion(tipo var1. int s2. Ejercicios 1.7. cuando el tamaño de los programas crezca de unas pocas líneas a cientos de éstas. printf("El volumen es: %d\n". ¿El siguiente programa es correcto? ¿por qué? double myfunc(void). sum(15. El número de argumentos que una función puede aceptar depende del compilador.10). void sum(int a. la función que sigue calcula e imprime la suma de dos enteros que son enviados a la función cuando esta es llamada. pero el estándar C ANSI especifica que una función debe ser capaz de aceptar al menos 31 argumentos. C permite de ningún a varios argumentos en la declaración de una función.a+b). sum(100. //llama el valor de pi printf("pi= %f\n". } 27 . //esto es una funcion prototipo void main() { sum(1.a+b). Estas variables especiales son definidas como parámetros formales. //pero retorna el valor de pi } void main() { double pi_val. } Un ejemplo de cómo la función podría ser llamada en un programa es el siguiente: void sum(int a. } USANDO LOS ARGUMENTOS DE UNA FUNCION Un argumento de función es un valor que se le pasa a una función cuando se le hace un llamado. int b) { printf("%d\n". Los parámetros son declarados entre los paréntesis que siguen al nombre de la función.VOID Se hace una excepción cuando una función no tiene ningún parámetro de entrada ni de salida. Cuando una función es definida.6). int b) { printf("%d\n". int b). Esta función podría declararse como sigue: void NombreFuncion(void) Un ejemplo de esto podría ser: double pi(void) //definicion de la funcion { //sin parametros de entrada return 3. Por ejemplo.pi_val).1415926536.25). } void sum(int a. deben ser declaradas variables especiales para recibir parámetros. pi_val = pi(). } USANDO FUNCIONES QUE RETORNAN VALORES Cualquier función en C puede retornar un valor a la rutina que realiza el llamado. Una función puede retornar cualquier tipo de dato excepto un arreglo (array). Ejercicios 1. el parámetro formal se usa para acceder a la variable actual en la rutina de llamado. Dentro de esta función. Las funciones pueden pasar argumentos en dos formas. pero puede usarse en una función printf(). La primera forma es nombrada como llamado por valor (call by value). Escriba una función que tome un argumento entero e imprima su valor en pantalla.Cuando sum() es llamada. 25) son llamadas argumentos y las variables a y b son los parámetros formales. ¿Cuál es el error en el siguiente programa? imprimirEsto(int num) { printf("%d\n".num). 2.h> 28 . Esto significa que se pueden realizar cambios a la variable usando el parámetro formal. 100. 15. 6. En este método. } Donde tipo especifica el tipo de dato del valor que retorna la función. la función es puesta al lado derecho del signo igual (=). es importante recordar que los valores pasados a una función (1. solo se usará el primer método cuando se pasen argumentos a una función. Por ahora. entonces el compilador C asume que la función retorna un entero (int). el estándar C ANSI especifica que la función debe retornar nada (void). El valor de retorno no necesariamente necesita usarse en una instrucción asignada. Cualquier cambio hecho a un parámetro formal no afecta el valor original de la rutina de llamado. Este método copia el valor de un argumento dentro de un parámetro formal en una función. Típicamente. Esto explícitamente le informa al compilador que la función no retorna un valor. 10. el compilador copiará el valor de cada argumento dentro de las variables a y b. Este método se discutirá en el capítulo sobre apuntadores. #include <stdio. Si no se especifica un tipo de dato. } void main() { imprimirEsto (156. Si su función no retorna un valor. El siguiente ejemplo muestra un uso típico para una función que retorna un valor.7). la dirección del argumento es copiada dentro del parámetro formal de una función.h> #include <math. El segundo método es nombrado como llamado por referencia (call by reference). El formato general para informarle al compilador que una función retorna un valor es el siguiente: tipo NombreFuncion(parámetros formales) { <instrucciones> return valor. num = sum(5. variable o cualquier expresión valida en C que tenga el mismo tipo de dato que el valor de retorno. El siguiente ejemplo muestra los dos tipos de funciones. printf("%d\n". } int func() { return 6. int sum(int a. el valor se pierde de esta manera. int b). printf("%f\n". es cuando una declaración return es encontrada. Este número es asignado a la variable result.h es incluido debido a que contiene la información acerca sqrt() usada por el compilador. printf("%d\n". sin embargo. } int sum(int a. num = func(). Lo mismo se tiene que garantizar para los argumentos que se entregan a la función.void main() { double result.result). } Una cosa importante para resaltar. } Este programa hace un llamado a la función sqrt() la cual retorna un número en punto flotante. void main() { int num. result = a + b.0).num). Ejercicio 1. int b) { int result. la función retorna inmediatamente a la rutina de llamado. ¿Cuál es el error en la siguiente función? 29 . return result. ¿Cómo se debe retorna un valor desde una función? La forma general es la siguiente: return NombreVariable. Note que el archivo encabezado math. result = sqrt(16. Entonces. El valor retornado de una función no requiere ser asignado a una variable o usado en alguna expresión. Cualquier instrucción después del return no será ejecutada. num). int func().127). Es importante que el tipo de dato que retorna la función sea el mismo tipo de dato de la variable a la cual se le asignará el valor de retorno. Donde NombreVariable es una constante. varn. Esta forma. tanto el tipo de dato como los nombres de los parámetros formales son especificados dentro de los paréntesis.….result). result = f1(). El propósito es mantener la compatibilidad con programas C antiguos donde hay literalmente millones de líneas de código. su compilador C debe ser capaz de manejarlo. Si encuentra la forma clásica en un fragmento de código. } DECLARACION CLASICA Y MODERNA DE FUNCIONES La versión original de C usa un método diferente para la declaración de parámetros formales.tipo varn) En este tipo de declaración de función. Convierta el siguiente programa de la forma clásica de declaración de funciones a la forma moderna. llamada la forma clásica.tipo var2. Solo los nombres de los parámetros son incluidos dentro de los paréntesis. que se ha usado en ejemplos anteriores. ¿Qué es una función prototipo y cuáles son los beneficios de usarla? 2. <instrucciones> Note que la declaración está dividida en dos partes. area(10.…. es la siguiente: tipo NombreFuncion(tipo var1. El estándar C ANSI permite ambos tipos de declaración de funciones.varn) var1.var2. void main(void) { printf("area = %d\n". Ejercicios 1. Por fuera de los paréntesis el tipo de datos y los nombres de los parámetros formales son especificados.void main() { double result. var2. 30 . no tiene de que preocuparse. y es la siguiente: tipo tipo tipo … tipo { } NombreFuncion(var1. usted debería usar la forma moderna cuando escriba su código. Como consejo. La forma moderna.15)). printf("%f\n". } int f1() { return 60. .w) int l. lcd_putc("d"). } PASANDO CADENAS CONSTANTES A FUNCIONES Debido a que los microcontroladores PIC tienen limitaciones en cuanto a acceso ROM. . entones la función es llamada para cada carácter en la cadena de caracteres. Si una cadena de caracteres es pasada a una función que permita solo un parámetro carácter. Es lo mismo que: lcd_putc("a").} area(l. 31 . } lcd_putc("abcd").w. lcd_putc("b"). cadenas de caracteres constantes (constant strings) no pueden ser pasadas a funciones de la manera ordinaria. lcd_putc("c"). . { return 1*w. . Por ejemplo: void lcd_putc(char c) { . El compilador C de CCS® maneja esta situación de una manera no estándar. puede usarse solo con números enteros. El operador – puede usarse de dos formas. división y modulo. este operador no tiene significado cuando se aplica a números en punto flotante.* y /. Este método puede usarse con los operadores +. 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 código C y comparándolo con su versión en Assembler se muestra a continuación como es lograda la función aritmética dentro del PIC. + * / % suma resta multiplicación división modulo Los operadores +. Uno de los ejemplos anteriores. Por consiguiente. los operadores C siguen las reglas del algebra y deben parecerle familiares. a = a – b.-. La razón principal es que C define más operadores que la mayoría de lenguajes. OPERADORES En C. %. El siguiente ejemplo muestra varias abreviaturas.* y / pueden ser usados con cualquier tipo de dato. //resta //inversión de signo Los operadores aritméticos pueden usarse con cualquier combinación de constantes y/o variables. la primera siendo el operador de resta.-. result = cont – 163. puede escribirse a-=b. la siguiente expresión es una instrucción valida en C. multiplicación. En la mayoría de los casos. El operador modulo. La segunda forma es para invertir el signo de un número. Por ejemplo.. El siguiente ejemplo ilustra las dos formas de manejar el signo menos: a = a – b. 32 .4. OPERADORES ARITMETICOS El lenguaje C define cinco operadores aritméticos para la suma. Una expresión es una combinación de operadores y operandos. las expresiones juegan un papel importante. a = -a. resta. El operador modulo entrega residuo de una división entre enteros. C además permite algunas abreviaturas cuando usa operadores aritméticos. carga b . //si var es menor o igual a 15.c. aun cuando C defina verdad como cualquier valor diferente de cero.resta c de a Ejercicios 1.suma c con b . //si var es mayor o menor a 15.c. OPERADORES RELACIONALES Los operadores relacionales en C comparan dos valores y devuelven un resultado verdadero o falso basado en la comparación. 5/4. 5/2. 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 comparación es siempre 0 o 1. 2. 2. a = b + c. ¿Cuándo la siguiente expresión es verdadera o falsa? cont >= 35. Escriba un programa en Microsoft Visual C++ Express® que encuentre el residuo de 5/5. y 5/1. Reescriba la siguiente expresión usando un operador relacional diferente. cont != 0. Los siguientes ejemplos muestran algunas expresiones con operadores relacionales.guarda en a . 5/3.W a . el resultado es 1 (verdadero) Ejercicios 1. 33 . Se convierte en: 0007: 0008: 0009: 000A: MOVF ADDWF MOVWF b.W a c.b. Se convierte en: 0007: 0008: 0009: a = b . el resultado es 0 (falso) var != 15. var > 15.guarda en a MOVF MOVWF MOVF SUBWF b.int a.carga b .carga c .F .. Falso siempre es definido como cero.W c.W a. Escriba un programa en Microsoft Visual C++ Express® que calcule el número de segundos de un año. De nuevo. y NOT. OR. Reescriba la siguiente expresión usando cualquier combinación de operadores relacionales y lógicos. El resultado de usar cualquiera de estas operaciones es una operación bit a bit de los operandos. escriba una función XOR basada en la siguiente tabla de verdad. estos operadores retornan 0 para falso o 1 para verdadero. Estas operaciones de bit pueden usarse solo sobre tipos de datos enteros y caracteres. 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 números. cont == 0. 2. result <= 5. Dado que C no entrega explícitamente una función OR exclusiva. 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 lógicos son las instrucciones de control de programa que se estudiarán en el siguiente capítulo.OPERADORES LOGICOS Los operadores lógicos soportan las operaciones lógicas básicas AND. Los operadores lógicos y la tabla de verdad para estas operaciones se muestran a continuación: 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 lógicos y relacionales están estrechamente acoplados cuando se evalúa una expresión. Ejercicios 1. 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 << expresión 34 . del registro para a 35 .W c.W a . Se convierte en: 0007: 0008: 0009: a = b & c. debido a que se obtiene un resultado indefinido.guarda en a MOVF ANDWF MOVWF b.opera b AND c . Cada desplazamiento hacia la izquierda causa que todos los bits de corran una posición de bit a la izquierda. Se convierte en: 0007: 0008: 0009: a = b >> 3. Se convierte en: 0007: 0008: 0009: 000A: 000B: 000C: 000D: j = ~a.carga b .W a a. Un ejemplo de todos los operadores bit a bit se muestra a continuación.F a.variable >> expresión El valor de expresión determina cuantos lugares hacia la izquierda o derecha variable es desplazada.guarda en a MOVF MOVWF RRF RRF RRF MOVLW ANDWF b.carga b . Los operadores de desplazamiento son casi siempre más rápidos que su equivalente operación aritmética debido a cómo trabaja la CPU. El bit que es desplazado fuera del final de variable se pierde. a = b | c. MOVF IORWF MOVWF b.hacia la derecha .F .carga b . La única situación a resaltar acerca del uso de los desplazamientos es que un desplazamiento hacia la izquierda es equivalente a multiplicar un número por 2 y un desplazamiento hacia la derecha es equivalente a dividir un número por 2.F a. 00000101 (5) << 2 -----------00010100 (20) OR 00000101 (5) 00000110 (6) -----------00000111 (7) NOT (complemento a uno) 00000101 (5) -----------11111010 (250) DESP DER.rota el contenido . 00000101 (5) >> 2 -----------00000001 (1) & | ~ ^ NOTA: no realice desplazamientos mas allá de los bits que el operando posea.guarda en a .opera b OR c .W a . y un cero es insertado sobre el lado derecho.tres veces . AND 00000101 (5) 00000110 (6) -----------00000100 (4) XOR 00000101 (5) 00000110 (6) -----------00000011 (3) DESP IZQ.enmascara el contenido .F 1F a.W c. el valor de la variable se usa en la expresión y luego se incrementa. Escriba un programa que invierta los bits más significativos (MSB) de un dato tipo signed char.almacena w en j.a = 5 .van después de la variable. j = 4 NOTA: no utilice la siguiente expresión.W a.preceden la variable. a--.1. para incrementar para decrementar Cuando los signos ++ o -. a = a++.valor de a cargado en w .Se convierte en: 0007: 0008: 0009: MOVF MOVWF COMF a. la variable es incrementada y después el valor es usado en una expresión. a = 3. o o ++a. o a = a . Escriba un programa que imprima la representación binaria de un número tipo char. --a. OPERADORES INCREMENTALES Y DECREMENTALES ¿Cómo podría usted incrementar o decrementar en una unidad una variable? Probablemente una de dos formas le vendría a la mente. Porque se genera el siguiente error en el código generado: MOVF INCF MOVWF a. 2.carga a en w .F a .W a.valor incrementado 36 . Cuando los signos ++ o -.carga el valor de a en w .F . 000C: 000D: 000E: MOVLW MOVWF INCF MOVF MOVWF MOVF INCF MOVWF 03 a a. int j.el valor previo es recargado sobrescribiendo el .guarda en j . 0007: 0008: j = ++a.W j j. los creadores de C han llegado con una notación abreviada para incrementar o decrementar un número. Los formatos generales son los siguientes: a++.almacena w en j .F a.a = 4 .complementa j Ejercicios 1.F j .registro asignado a a .carga a . Una vez más. 0009: 000A: 000B: j = a++.W j a.valor de a incrementado . Tal vez las siguientes: a = a + 1. por ejemplo. 37 . printf("a=%d. j = %d\n". a = 1. b = --a + ++b. j = ++i. } El primer printf() imprimirá un 11 para i y un 10 para j. printf("i = %d. i = 10. b. a = a+1.h> void main(void) { int i.j). a.i. printf("i = %d. b=%d\n".j.b). Reescriba los operadores de asignación en el siguiente programa para incrementar o decrementar las variables.j). si la expresión a+b*c se encontrará en su programa. i = 10. el segundo printf() imprimirá un 11 para ambos i y j. j = %d\n". b = 0. a = ++a + b++.El siguiente ejemplo ilustra los dos usos. } 2. ¿Qué operación ocurriría en primer lugar. void main(void) { int a.i. La siguiente lista muestra la precedencia desde la más alta a la más baja. b = b-1. 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. b = a. #include <stdio. ¿Cuáles son los valores de a y b después de que el siguiente segmento de código se ha ejecutado? a = 0. PRECEDENCIA DE OPERADORES La precedencia se refiere al orden en el cual los operadores son procesados por el compilador C. b++. a++. j = i++. la suma o la multiplicación? El lenguaje C mantiene precedencia para todos los operadores. pero no se preocupe. Un par de ejemplos del uso del paréntesis para clarificar o cambiar la precedencia de una declaración son los siguientes: 10-2*5 = 0 (10-2)*5 = 40 cont*sum+88/val-19%cont (cont*sum) + (88/val) – (19%cont) 38 . Los paréntesis pueden usarse para fijar el orden específico en el cual las operaciones se lleven a cabo. los estudiaremos más adelante. o derecho mayor/menor/igual que AND bit a bit XOR bit a bit OR bit a bit AND lógico OR lógico asignación Algunos de estos operadores todavía no los hemos estudiado.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. carga w con 2 . Además aprenderá como los operadores relacionales y lógicos se usan con estas declaraciones de control. 0007: 0008: if (j == 2) 0009: 000A: 000B: 000C: { j = a..carga a con 3 . Nuevamente. MOVLW MOVWF MOVLW SUBWF BTFSS GOTO 03 a 02 j.. Esto le informa al compilador que si la expresión es verdadera. ejecute el código dentro de los corchetes. DECLARACIONES DE CONTROL DEL PROGRAMA En este capítulo aprenderá acerca de las instrucciones que C usa para controlar el flujo de ejecución en un programa.W STATUS. cualquier valor diferente de cero es verdadero y cualquier valor cero es falso. . Ejemplo: if (cont < 0) { Cont = 0. } if (TestMode == 1) { .5.si es cero salta . printf("cuenta descendente\n"). a = 3.j no es igual a 2 39 .testeo entre w y j . } Los corchetes {} se usan para encerrar el boque de código. DECLARACION if La declaración if es una instrucción del tipo condicional. El formato más sencillo es el siguiente: if (expresion) { . El bloque de código asociado con la instrucción if es ejecutado basado en el resultado de una condición.Z 00F . Haga esto } Otros operadores de comparación usados en la instrucción 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 lógica OR lógica Un ejemplo de una de estas funciones convertida en Assembler es el siguiente: int j. instruccones. DECLARACION if-else ¿Qué haría si se tienen dos bloques de código que se ejecutan basados en el resultado de una misma expresión? Si la expresión es verdadera. .\n"). } Tenga en cuenta que tanta la instrucción if como else pueden tener tantas instrucciones como necesiten. d.000D: 000E: } 000F: MOVF MOVWF a.W j .\n"). Además los corchetes pueden descartarse cuando solo haya una instrucción en cualquiera de los dos casos. Un ejemplo de única instrucción if-else es el siguiente: if (num<0) printf("Número negativo. e.como es cero . instruccones. Escriba una función que le informe cuando un número sea par o impar. b. c. si la expresión es falsa el segundo bloque de código se ejecuta.carga a en j Ejercicios 1. probablemente usted emplearía una declaración if en combinación con una declaración else. el primer bloque de código se ejecuta. } else { . El formato para una declaración if-else que use boques de código (más de una línea) es el siguiente: if (expresion) { . 0 1 -1 5*5<25 1==1 2. . Entonces. instruccones. La función retorna 0 cuando el número sea par y 1 cuando el número sea impar. else intruccion2. ¿Cuáles de las siguientes expresiones resultan en un valor verdadero? a. 40 . El formato general para la instrucción if-else es el siguiente: if (expresion) intruccion1. else printf("Número positivo. . 10.La adición de la declaración else proporciona dos opciones en la toma de decisiones. instruccones. if(num == 1) printf("lleva 1\n"). Ejercicios 1. 50 pesos o 100 pesos dependiendo del valor de una variable. 50 y 100. por ejemplo. 5 pesos. == o || que actúan como comparadores de la variable bajo prueba. 10 pesos. NOTA: dentro de la declaración if. } Aquí se puede apreciar que se pueden evaluar las diferentes expresiones para elegir un bloque de código a ejecutar. } else { . count-. 20. 5. los operadores sencillos &. Los únicos valores validos de la variable son 1. aquí hay un ejemplo sencillo. } else if (expresion2) { . ¿El siguiente fragmento de código es correcto? if (cont>20) printf("cont es mayor de 20"). else printf("no lleva nada\n"). ¿Pero qué pasa si quiere combinar varias declaraciones if y else para realizar más decisiones? Coincidencia o no. El formato general es el siguiente: if (expresion1) { . opuesto a los operadores dobles &&. C provee un método con el cual usted puede combinar varios if con else para suministrar diversos niveles de decisión. = o | tiene el efecto de causar que la función actué sobre la variable. 41 . .. instruccones. 20 pesos. . else if(num == 2) printf("lleva 2\n"). instruccones. else if(num == 3) printf("lleva 3\n"). hay que asegurar el correcto uso de los comparadores sencillos y dobles. } 2. Escriba un programa que imprima 1 peso. debido a que el compilador no lo toma como error. Antes de explica más acerca de este método. Este es un error común y no es fácil de encontrar en códigos extensos. i ? j=0 : j=1. Pero si el resultado es FALSO (cero). entonces se evalúa expr2. Puesto que i es 1 o diferente de cero. La forma más común de un lazo for es la siguiente: for (inicialización . como BASIC o Pascal. i = j. DECLARACION for Uno de los tres bucles o lazos (loops) que suministra C es el lazo for. o j=i ? 0 : 1. se evalúa expr1. for(i=0. será evaluada la expresión j=0. El formato básico para un lazo for es similar en todos los lenguajes. El formato es el siguiente: (expr1) ? (expr2) : (expr3). Por ejemplo: i = (i<20) ? i+1 : 10. La sección incremento del lazo for normalmente incrementa la variable de cuenta del lazo. entonces se le asignará el valor 10. entonces se evalúa expr3. Si tiene una instrucción o un conjunto de instrucciones que necesitan repetirse.j. Distinto a la declaración if el operador ? retorna un valor. i++) 42 . Donde cada expr es una declaración C válida. Primero. Esta sección del lazo for se ejecuta solo una vez. incremento) { instrucciones. Si el resultado es VERDADERO (recuerde este es cualquier valor diferente de cero). La sección condición de finalización es evaluada antes de cada ejecución del lazo. Si la condición de finalización es verdadera el lazo se ejecuta. void main(void) { int i. } La sección inicialización 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. Normalmente esta sección testea la variable de cuenta del lazo en busca de una condición VERDADERO o FALSO.OPERADOR ? El operador ? es actualmente una expresión de la declaración if-else. Si la condición de finalización es falsa el lazo termina su ejecución y el programa sigue su flujo. condición de finalización . A continuación se muestra un ejemplo de un lazo for. Donde i se incrementará a menos que este sea igual o mayor a 20. con un lazo for puede fácilmente implementar esto. Lo siguiente es un ejemplo del operador ? int i. i<10. int h. cont<50. El programa trabaja de la siguiente manera: primero la variable de cuenta del lazo. Este proceso continua hasta que la expresión i<10 se convierte en falsa. el lazo while repite una declaración o un bloque de código. la variable de cuenta del lazo es incrementada.resta w de h .borra h . Cuando una expresión es verdadera. 000C: 000D: 000E: 000F: CLRF MOVLW SUBWF BTFSC GOTO INCF INCF GOTO h 0A h.a. Si esta declaración es verdadera la instrucción printf("%d ". Escriba un programa que imprima todos los múltiplos de un número. i. si el chequeo es falso desde el comienzo.F h. .regresa Ejercicios 1. o 43 .i++) for( . la condición de finalización es ejecutada en el comienzo de cada iteración del lazo.increment h . for (num=100.Z 00F a. num.printf("%d ". ¿Qué función cumplen las siguientes declaraciones for()? for(i=1.y testeo por cero .h++) 0007: 0008: 0009: 000A: 000B: a++.F 008 .i). } Este programa imprimirá los números 0-9 en pantalla. sale del lazo . la siguiente expresión i<10 se evalúa.i). Cada vez después que la instrucción printf("%d ". se ejecuta. A continuación se muestran algunas variaciones sobre el lazo for. Usted no está restringido a solo incrementos en la variable de cuenta. num=num-1) for (cont=0. El formato general es el siguiente: while (expresion) instruccion.W STATUS. Por consiguiente. count++) A continuación se convierte un ejemplo en Assembler para verificar que sucede.i). num++) 2. DECLARACION while Otro bucle en C es el lazo while. En este punto.carga 10 en w .h!=10.incrementa a . es ejecutada dando fin al programa. ) for(num=1. for (h=0. printf("hecho"). num>0. es puesta a cero. es ejecutada. . cont+=5) for (cont=1.si i=10. Como se afirmo anteriormente. el lazo for nunca llegara a ejecutarse. el lazo for termina su ejecución y la instrucción printf("hecho"). cont<10 && error==false. Este se puede combinar con la declaración while de la siguiente manera: do { instrucciones. PUT.i++). 2. RCV=pin_a4) void main(void) { char ch.NOWDT. debe finalizar el programa. while(ch!='q') ch=getch(). Esto significa que si expresión es falsa la declaración o bloque de código no serán ejecutados. while(i<10) { printf("%d ".while (expresion) { instrucciones.h> #fuses XT. la función printf es ejecutado y el programa termina. NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. } Donde expresión es cualquier expresión valida en C. Una vez una q se reciba. } while(expresion) 44 . Escriba un programa que pida un carácter desde el teclado usando la declaración ch=getch(). A continuación se muestra un ejemplo (hecho en el compilador C de CCS®) de un lazo while: #include <16F84A. DECLARACION do-while El ultimo tipo de bucle en C es el lazo do. printf("Escribe la letra q\n"). Cuando se encuentre retorno de carro (Enter). Ejercicios 1. e imprímalo en pantalla. Siempre y cuando el valor de ch no sea igual a q. ¿Qué función cumplen las siguientes declaraciones while? a. ch=getch(). } Habrá notado que la primera declaración pide un carácter desde el teclado. printf("Escribiste la letra correcta!\n").i). } b. el programa continuará solicitando otro carácter desde el teclado. el valor de expresión es comprobado antes de cada iteración de la declaración o bloque de código. i++. xmit=pin_a3. Luego la expresión es evaluada. while(1) printf("%d ". Cada vez que un carácter sea leído.) en pantalla. Ejercicio 1. Cualquier bucle o lazo de C u otra declaración de control pueden anidarse dentro de cualquier otra. el segundo lazo se dice que esta anidado dentro del primero. el programa debe finalizar.En este caso las instrucciones siempre se ejecutan antes que expresión es evaluada (al menos una sola vez). printf("Escribiste la letra q!\n"). use su valor ASCII para imprimir un igual número de puntos (.j++) printf("%d ". while(i < 10) { for(j=0.j<10. RCV=pin_a4) void main(void) { char ch. } while(ch != 'q'). El parámetro expresión puede ser cualquier expresión válida en C. xmit=pin_a3. } Este programa es equivalente al ejemplo estudiado en la sección anterior. } Esta rutina imprime los números de 00-99 en pantalla. Reescriba el ejercicio 2 de la sección anterior usando el lazo do-while. i++. 45 . Reescriba los puntos a y b del ejercicio 1 de la sección anterior usando el lazo do-while. un ejemplo de un lazo do-while se muestra a continuación: #include <16F84A. 2.NOWDT.). Un ejemplo de un lazo for anidado se muestra a continuación: i = 0. PUT. El estándar C ANSI especifica que los compiladores debe tener al menos 15 niveles de anidado.h> #fuses XT. DECLARACIONES DE CONTROL ANIDADAS Cuando el cuerpo de un lazo contiene a otro.i*10+j). do { ch = getch(). NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. si se lee la letra 'D' (con valor ASCII 68). Por ejemplo. su programa imprime 68 puntos en pantalla. Cuando se lea la letra 'Q'. Ejercicios 1. Escriba un programa que obtenga un carácter desde el teclado (ch=getch(). i). kbhit() retorna 1 cuando una tecla esta presionada y 0 en otro caso.i++) { printf("Microchip® es #1!").i++) { continue. Ejercicios 1. estos deben realizar una cuenta hasta que se oprima una tecla.h> #fuses XT. printf("%d ". kbhit() requiere el archivo de encabezado conio. C suministra la declaración continue.1. Por ejemplo: #include <16F84A. PUT. if(getch()=='q') break. RCV=pin_a4) void main(void) { int i.i). for(i=0.i<100. Esta declaración se desvía de la terminación normal de una expresión. } } Este programa imprime los números 0-15 en pantalla. } 2. DECLARACION continue Vamos a asumir que cuando una cierta condición ocurre en un lazo. if(i==15) break.h. el programa salta a la siguiente línea después del lazo. } } 46 . ¿Qué función cumple el siguiente lazo? for(i=0.NOWDT. Por ejemplo: void main(void) { int i. for(i=0. xmit=pin_a3.DECLARACION break La declaración break permite terminar cualquier lazo en cualquier punto dentro de su cuerpo. este salta todas las otras instrucciones entre continue y la condición de terminación del lazo. Puede usar la función kbhit() para detectar cuando una tecla ha sido presionada. Cuando el programa se encuentra con esta declaración. Escriba tres programas que usen los lazos de C.i<50. Cuando la declaración break es encontrada en la ejecución de un lazo. La declaración break trabaja con todos los lazos de C.i++) { printf("%d ". usted quiere saltar al final del lazo pero sin salir de este. NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. El default es opcional. case '2': printf("Martes\n"). La forma general para la declaración switch es la siguiente: switch (variable) { case constante1: instrucciones. break. switch(ch) { case '0': printf("Domindo\n"). if(ch=='x') return 0. el programa salta el printf() y evalúa la expresión i<100 después de incrementar i. case constante2: instrucciones. Un ejemplo de una declaración switch se muestra a continuación: #include <stdio. Si ninguna constante corresponde. break. Una declaración continue puede causar que el programa vaya directamente a la condición de terminación para los lazos while y do-while. Cuando se encuentra una correspondencia. } La entrada variable es sucesivamente testeada contra una lista de enteros o caracteres constantes. break. break.h> int main() { char ch. default: instrucciones. break.. 47 . Esta declaración es equivalente a tener múltiples declaraciones if-else. pero se vuelve engorroso cuando existen muchas alternativas.Este lazo nunca ejecutará la declaración printf(). el cuerpo de la declaración asociado con esa constante se ejecuta hasta que un break es encontrado. case '1': printf("Lunes\n"). un continue puede causar que se ejecute la parte incremental de un lazo y que se evalúe la condición de terminación. DECLARACION switch La declaración if es buena para seleccionar entre un par de alternativas. las instrucciones asociadas con la declaración default son ejecutadas. for(. De nuevo C cumple con la tarea al suministrarle la declaración switch. case constanteN: instrucciones. break.) { ch = getchar(). Cada vez que continue es alcanzado. break. También las declaraciones switch pueden anidarse. printf("D = Division\n"). printf("M = Multiplicacion\n"). break. Si el número esta fuera de este rango. break.case '3': printf("Miercoles\n"). case 1: printf("b es verdadero"). char ch. case '6': printf("Sábado\n"). break. . void main(void) { int a=6. El estándar ANSI declara que un compilador C debe soportar al menos 257 declaraciones case. } break. default: printf("Entrada Invalida\n"). La declaración break dentro de un switch también es opcional. Esto significa que dos declaraciones case pueden compartir la mismo porción de código. 48 . switch (a) { case 1: switch (b) { case 0: printf("b es falso").b=3. siempre y cuando los switch interiores no tengan conflicto con valores de switch exteriores. printf("R = Resta\n"). Un compilador ANSI debe suministrar al menos 15 niveles de anidamiento para las declaraciones switch. case 2: . printf("S = Suma\n"). case '4': printf("Jueves\n"). Un ejemplo de esto se muestra a continuación. case '5': printf("Viernes\n"). Valores dentro del rango se convierten en un día de la semana. el mensaje Entrada Invalida se imprime. } } } Este ejemplo lee un número entre 0 y 6. break. Dos declaraciones case no pueden tener el mismo valor dentro del mismo switch. break. A continuación se muestra un ejemplo de switches anidados. 3. la cual introduce un ciclo de retardo. case 'M': printf("\t\t%d".05: . case 'S': printf("\t\t%d".a*b). . a diferencia de la instrucción NOP de Assembler. default: printf("Opcion Incorrecta"). de 50. Este puede aparecer dondequiera que una declaración sea esperada. for y while requieren que una instrucción ejecutable aparezca como el cuerpo de la declaración.a+b). entonces no se requieren más instrucciones.) La declaración null es una instrucción que contiene sola el símbolo punto y coma (. ¿Cuáles son las ventajas del uso de la declaración switch sobre la declaración ifelse? DECLARACION null(. ch=getch(). de 100. En este ejemplo. case 'D': printf("\t\t%d".). switch (ch) { case 'R': b=-b. Declaraciones como do. break. switch(f) { case 10. break.a/b). Nada pasa cuando la declaración null es ejecutada. break. 2.printf("Escoja una Opcion:\n"). 49 .linea[i++]=0) . ¿Qué error tiene el siguiente segmento de código? float f. La instrucción del cuerpo es un null. la expresión del lazo for linea[i++]=0 inicializa los 10 primeros elementos de linea a 0. La declaración null satisface la sintaxis para esos casos. for (i=0. Las fases para describir las monedas son: de 20. } } Ejercicios 1.i<10. Use una declaración switch para imprimir el valor de una moneda. El valor de la moneda está contenido en la variable moneda. de 200 y de 500. return c. } void GetNothing(c) int c. el valor retornado es indefinido. { c++. el control es aun pasado a la función que hizo el llamado después de ejecutar la última línea de código. } 50 . GetValue(c) int c.DECLARACION return La declaración return termina la ejecución de una función y retorna el control a la rutina que hizo el llamado. declare la función para ser del tipo void. return. } void main() { int x. Si no se incluye un return en la función. Si no se necesita retornar un valor. Un valor puede retornarse a la función que realiza el llamado si es requerido pero si este es omitido. GetNothing(). x = GetValue(). { c++. W STATUS.W 00 i.borra i .6. ARREGLOS UNIDIMENSIONAL Un arreglo es una lista de variables que son todas del mismo tipo de dato y que pueden referenciarse a través del mismo nombre.si no es así. Una variable individual en un arreglo es llamada un elemento del arreglo.i<10. NombreArreglo es el nombre 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 [tamaño].testea si i<10 . C almacena el arreglo unidimensional en posiciones de memoria contiguas.carga el comienzo de num El arreglo num se verá así en memoria: 51 . Una cadena es definida como un arreglo de caracteres. C define el primer elemento con un índice 0. digamos que quiero indexar el elemento 25 del arreglo altura y asígnale un valor de 60. int altura[50]. int num[10]. El primer elemento está en la dirección más baja. Cuando se declara un arreglo. y tamaño especifica cuantos elementos tiene el arreglo. si se quiere definir un arreglo de 50 elementos se emplea la siguiente declaración. Si el arreglo tiene 50 elementos. Por ejemplo. 000C: 000D: 000E: 000F: 0010: 0011: 0012: 0013: CLRF MOVLW SUBWF BTFSC GOTO MOVLW ADDWF MOVWF MOVF MOVWF INCF GOTO i 0A i.W 04 i. ARREGLOS Y CADENAS En este capítulo se discutirán los arreglos (array) y cadenas de caracteres (strings). Un arreglo es simplemente una lista de variables relacionadas del mismo tipo de dato. altura[24] = 60. El siguiente ejemplo muestra cómo hacer esto. el último elemento tiene índice 49. int i.C 013 0E i. for(i=0. Donde tipo es un tipo de dato válido en C.i++) 0007: 0008: 0009: 000A: 000B: num[i] = i. Si el siguiente segmento de código es ejecutado. Usando el ejemplo anterior. deja la rutina .F 008 . y también es conocido como el arreglo más común de una sola dimensión (unidimensional). se podría leer o escribir en un elemento no declarado en el arreglo. . RCV=pin_a4) void main(void) { int num[10].i++) num[i] = i * i. Ejercicios 1. Si usted quiere copiar el contenido de un arreglo dentro de otro. . char cont[10]. #include <16F84A. C no le permite asignar los valores de un arreglo a otro simplemente usando una asignación como la siguiente: int a[10].NOWDT. for(i=0. El programa reportará si cualquiera de estos caracteres concuerda con uno específico. for(i=0. for(i=0. for(i=0. luego se imprimen todos los elementos.num[i]). 2.i<10. 52 . A continuación se muestra otro ejemplo hecho en el compilador C de CCS®. Sin embargo.i++) cont[i]=getch(). NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. ¿El siguiente fragmento de código es correcto? int i. El siguiente ejemplo muestra como copiar el arreglo a[] dentro del b[] asumiendo que cada arreglo tiene 20 elementos. en este simplemente se le asigna el cuadrado del índice al elemento del arreglo. usted debe copiar cada elemento individual desde el primer arreglo dentro del segundo arreglo.i<10.i++) b[i] = a[i].h> #fuses XT. El ejemplo anterior es incorrecto. PUT. A menudo esto causará que el programa colapse y e incluso a veces que el ordenador se bloquee. Escriba un programa que lea 10 caracteres desde el teclado usando getch(). int i. Por lo tanto. esto por lo general tendrá resultados desastrosos.i<100.i++) printf("%d ". a=b.i<20. b[10].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. } ¿Qué pasa si se tiene un arreglo con diez elementos y usted accidentalmente escribe en el onceavo elemento? C no tiene ninguna comprobación de límites de los índices en arreglos. xmit=pin_a3. i++) printf("%c". "Microcontrolador"). for(i=0. getchar(). printf("\n%s". int i. Una cadena es definida con un 0. ¿Cuál es el error en el siguiente programa? La función strcpy() copia el segundo argumento dentro del primero. ¿Cómo se puede introducir una cadena en el programa mediante el teclado? La función gets(str) leerá los caracteres desde el teclado hasta que un retorno de carro (Enter) es encontrado. gets(str). La cadena de caracteres que ha sido leída será almacenada en el arreglo str declarado. Ejercicios 1. printf("Escriba una cadena (<20 caracteres):\n"). A continuación se muestra un ejemplo de cómo usar la función gets() en un programa. #include <stdio.h> void main(void) { char str[10]. C no tiene incorporado un tipo de dato cadena (string). Usted debe asegurar que el tamaño del arreglo sea más grande o igual que el número de caracteres leídos desde el teclado incluyendo el cero (null = \0). Si cada cadena debe terminar con un vacio. Este elemento adicional almacenara el cero.str[i]).str[i]. strcpy(str. #include <string. printf(str). Puesto que estamos utilizando cadenas. En lugar de esto.CADENA DE CARACTERES El arreglo unidimensional más común es la cadena de caracteres. entonces cuando se declare la cadena debe agregarse un elemento adicional.str).h> void main(void) { char str[20]. Todas las cadenas constantes son automáticamente terminadas en cero por el compilador C. } 2. este soporta cadenas usando arreglos unidimensionales de caracteres. Escriba un programa que lea una cadena de caracteres desde el teclado y los imprima en orden inverso en pantalla.//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. 53 . La Fig. // 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 . Por ejemplo.i++) { for(j=0. Por ejemplo. Usted puede crear arreglos de dos o más dimensiones. //usa 25 posiciones de memoria Dimensiones adicionales pueden hacerse simplemente añadiendo otro conjunto de paréntesis cuadrados.j<4. 2 muestra una representación gráfica de un arreglo de 5x5. } getchar().j++) arreglo[i][j]=i*j. puede hacerlo de la siguiente manera: int numero[5][5]. entonces imprime el contenido del arreglo en el formato fila/columna. de izquierda a derecha. para crear un arreglo entero llamado numero de dimensión 5x5.j. for(i=0. los arreglos de dos dimensiones son accesado una fila a la vez. Por lo tanto. La mejor forma de representar un arreglo de dos dimensiones es por medio del formato fila/columna.j++) printf("%d ". #include <stdio. printf("\n"). Por simplicidad. Fig.h> void main(void) { int arreglo[5][4]. 2. int i.j<4.i++) for(j=0.i<5. Arreglo de 5x5 Los arreglos de dos dimensiones se usan de la misma manera que se usan los arreglos unidimensionales.arreglo[i][j]). for(i=0. solo se trataran los arreglos de dos dimensiones (Matrices).i<5. el siguiente programa carga un arreglo de 5x4 con el producto de los índices.ARREGLOS MULTIDIMENSIONALES C no está limitado a los arreglos unidimensionales. 'b'.8. Abra notado que no se utilizan corchetes para encerar la cadena. El siguiente ejemplo muestra como se inicializa un arreglo tipo entero de 5 elementos.5. 4. La forma general para un arreglo unidimensional se muestra a continuación: tipo NombreArreglo[tamaño] = {ListaValores}.6. Una cadena (arreglo de caracteres) puede inicializarse de dos formas. como se muestra a continuación: char nombre [5] = "John".4. Sin embargo. El siguiente ejemplo muestra como inicializar un arreglo de 3x3. El Segundo método consiste en usar una cadena entre comillas. Primero. Ejercicios 1. C suministra un método con el cual usted puede asignarle un valor inicial a un arreglo tal como se hace para las variables. Escriba un programa que declare un arreglo de 3x3x3 y lo cargue con los números del 1 al 27. arreglos de 100 o 10x10 no son posibles. El elemento i[0] tendrá un valor de 1 y el elemento i[4] tendrá un valor de 5. La primera constante será asignada al primer elemento del arreglo.5}. 55 . El parámetro ListaValores es una lista de constantes separadas por comas que son compatibles con el tipo de arreglo. Es sencillo simular el formato fila/columna cuando define un arreglo de dos dimensiones. imprima la suma de cada fila y de cada columna.3. INICIALIZANDO ARREGLOS Hasta el momento se ha estudiado como asignarle valores a elementos individuales de un arreglo.3. usted puede hacer una lista de cada carácter individual como se muestra a continuación: char str[3] = {'a'. debido a que no son necesarios en este tipo de inicialización debido a que las cadenas en C deben terminar con un vacio. Imprima el arreglo en pantalla. 2.9}. Debido a la capacidad de memoria del PIC16F84A. Empleando el programa del ejercicio 1. 7. int num[3][3]={ 1. cuando se usa un arreglo multidimensional al número de variables necesarias para acceder a cada elemento individual del arreglo s incrementan.2. El compilador automáticamente añade un vacio al final de "John". un arreglo de 50 elementos podría implementarse o podría utilizar un Microcontrolador con más capacidad de memoria como por ejemplo el PIC16F877A. 'c'}.Como se puede dar cuenta. Los arreglos multidimensionales se inicializan de la misma manera que los unidimensionales. la segunda constante al segundo elemento y así. int i[5] = {1.2. 2.6. Ejercicio 1. Un ejemplo es copiando una cadena desde una fuente a un destino por medio de la función strcpy(). puede usar el siguiente comando. Por ejemplo. ¿la siguiente declaración es correcta? int cont[3] = 10. Para obtener un índice dentro de la tabla. si el arreglo animales se declarara de la siguiente manera: char animales[5][4][80]. reste cero al carácter tecleado. para acceder a la segunda cadena de la tercera lista. se escribe animales[2][1]. printf("%s". Esta función permite a una cadena constante ser introducida en RAM. pueden declararse e inicializarse como cualquier otro arreglo.Ejercicios 1.7. Escriba un programa que defina una tabla con el valor al cuadrado y al cubo de un rango de números.0. Cada fila debe tener los números.nombres[4]).&num). 5. Esta instrucción especifica que el arreglo nombres contiene 10 nombres. Por ejemplo. ¿Qué define la siguiente declaración? char nombres[10][40]. Cree un arreglo de 9x3 que mantenga esta información para los números del 1 al 9. Escriba un programa que cree una tabla que contenga las palabras para los números del 0 al 9. 56 . 15. de hasta 40 caracteres de largo cada uno (incluyendo el vacio). Para acceder a una cadena de esta tabla. entonces imprime al número y su valor al cuadrado y al cubo. Debe preguntarle al usuario por un número usando la función scanf("%d". Pero la manera en la cual usted usa el arreglo es un tanto diferente. debe usar las dos primeras dimensiones. hay que definir solo el primer índice. Permita que el usuario escriba un digito para que entonces su programa imprima en pantalla la palabra respectiva. Lo mismo pasa para arreglos con dimensiones mayores a dos.. Para acceder a una cadena especifica. su valor al cuadrado y al cubo. Por ejemplo. FUNCIONES PARA MANIPULAR CADENAS Las cadenas de caracteres pueden manipularse de diferentes maneras dentro de un programa. para imprimir el quinto nombre de este arreglo. ARREGLOS DE CADENAS Arreglos de cadenas de caracteres son comunes en C. Por ejemplo. h> void main() { char s1[10]. . buscando desde el principio Localiza un caracter en una cadena. //define un arreglo string . //imprime el número 6 printf(s1). //imprime abcdef if(strcmp(s1./minus."abc"). #include <stdio. strcat(s1.s2). s2[10]. } Algunas funciones que manipulan cadenas están disponibles a continuación: strcat strchr strrchr strcmp strncmp stricmp strncpy strlen strlwr strpbrk strstr Añade una cadena al final de otra Localiza un caracter en una cadena. 57 .strlen(s1)). "Hi There").#include <string.h> #include <string.h> //la librería de funciones string char cadena[10]. getchar().//disposición 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 válida. strcpy(s2.) Copia los n primeros caracteres de una cadena en otra Devuelve la longitud de una cadena Reemplaza letras mayúsculas con minúsculas Encuentra la primera ocurrencia de algún caracter en dos arreglos Busca una cadena dentro de otra NOTA: Asegúrese de que el tamaño de los arreglos de cadenas sean acordes con el tamaño de las cadenas que se manipulen. strcpy(s1.s2)!=0) printf("\n No son Iguales"). printf("%u\n"."def"). A continuación se presenta otro ejemplo. strcpy (cadena. buscando desde el final Compara dos cadenas numéricamente ('a'!='A') Compara los n primeros caracteres de dos cadenas numéricamente Compara dos cadenas ignorando su forma (mayus. La dirección de una variable puede ser accesada colocando antes de la variable el operador &.b. Esto significa que solo las posiciones de 0 hasta 255 pueden ser apuntadas. Para partes con una memoria más extensa es necesario usar un puntero de 2 bytes (16 bits). La siguiente instrucción asigna el valor 6 a b. si b es una variable que está en la posición 100 de la memoria. Usted habrá notado que NombreVariable va precedido por un asterisco *. //más de 1 byte puede ser asignado a a b = 6. La primera instrucción declara dos variables: a. Esta línea puede entenderse como la asignación de a a la dirección de b. } NOTA: por defecto. Para seleccionar un puntero de 16 bits. el puntero. Este especifica el tipo de variables a las cuales NombreVariable puede apuntar. Finalmente. si una variable puntero a contiene la dirección de la variable b. Entonces a debería contener el valor 100.h> #fuses XT. el 58 . que es un entero. Lugo la dirección de b (&b) es asignada a la variable puntero a. xmit=pin_a3.7. Por ejemplo.*a). En un puntero type es uno de los tipos de datos validos en C. a = &b. use la siguiente directiva: #device *=16 Sea consciente de que más código ROM se generara debido a que la aritmética de 16 bits es menos eficiente. el compilador emplea 1 byte para punteros. Este le informa al compilador que NombreVariable es una variable puntero. Por ejemplo: #include <16F84A. INTRODUCCION A LOS PUNTEROS Un puntero es una posición de memoria (variable) que guarda la dirección de otra posición de memoria. PUT. PUNTEROS Este capítulo cubre un de los más importantes y más problemáticos rasgos de C. RCV=pin_a4) void main(void) { int *a.NOWDT. la siguiente declaración crea un puntero a un tipo entero. Los dos operadores especiales que están asociados con los punteros son *. que es un puntero a entero y b. &. La forma general para declarar una variable puntero es la siguiente: type *NombreVariable. Por ejemplo. El operador * retorna el valor de la dirección apuntada por la variable. entonces a apunta a b. NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. int *ptr. Un puntero es básicamente la dirección de un objeto en el programa. printf("%d". primero asignamos la dirección de la variable b en a. Esta línea puede imprimir el valor de la dirección apuntada por a. puede entenderse como la asignación del valor 6 a la posición de memoria apuntada por a. Sin embargo. ++. Imprima los números empleando un puntero. xmit=pin_a3.b. j. Ejercicio 1. este apunta a la siguiente posición de memoria.valor de b se imprime en pantalla usando el operador * con la variable puntero a. PUT.h> #fuses XT. k. Si p hubiera sido un puntero tipo float. hay solo otros cuatro operadores que se pueden aplicar a punteros: +. Si por ejemplo asumimos que la variable puntero p contiene la dirección 100. después de que la instrucción p++. Escriba un programa en Microsoft Visual C++ Express® donde con un lazo for realice una cuenta de 0 a 9 e imprima los números en pantalla. i es 3. *ptr es 5 (el contenido de la dirección 102). después asignamos un valor a b usando a. RCV=pin_a4) void main(void) { int *a. Como ptr contiene el valor 102. obviamente. Cuando es incrementada una variable puntero. reestructuremos el programa anterior de la siguiente manera: #include <16F84A. Por ejemplo. el uso de punteros en los dos ejemplos anteriores no es necesario pero ilustran los usos de los punteros. -. Un ejemplo grafico se muestra a continuación: Dirección: Variable: Contenido: 100 i 3 102 j 5 104 k -1 106 ptr 102 int i. NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. &i es 100 (la dirección de i). En adición de los operadores *. int *ptr. Solo cantidades enteras pueden sumarse o restarse de variables puntero. La línea *a=6. } En este programa.NOWDT. &. p contendría el valor 104 después del 59 . --. p tendrá el valor 102 asumiendo que los enteros tienen un tamaño de 2 bytes. los punteros pueden tratarse como variables. se ejecuta.b). RESTRICCIONES PARA PUNTEROS En general. Es además posible asignar un valor de posición de memoria usando un puntero. a = &b. printf("%d". Inicialmente. existen algunas pocas reglas y excepciones que se deben tener en cuenta. este proceso de referenciar un valor a través de un puntero es llamado direccionamiento indirecto. *a=6. incremento asumiendo que los números en punto flotante tienen un tamaño de 4 bytes. Declare las siguientes variables y asigne sus direcciones a la variable puntero. ptr=&nombre[0]. Esta instrucción obtiene al valor apuntado por p. Usted puede sumarle o restarle cualquier valor entero que quiera.d. Causa que p apunte desde la posición de memoria actual 200 posiciones más adelante. El paréntesis causa que el valor apuntado por p se incremente. Los punteros no se pueden crear en ROM. Debe ser cuidadoso cuando incrementa o decrementa el objeto apuntado por el puntero.f. 2. Esto debido a la precedencia de * versus ++. para incrementar el objeto que es apuntada por un puntero. double *dp. int *ip. estos solo tienen sentido si el puntero si los punteros están relacionados los unos con los otros. use la siguiente instrucción: (*p)++. ¿Cuál es el error en el siguiente fragmento de código? 60 . Imprima el valor de cada variable usando %p. la siguiente instrucción: int *p. El único puntero que aparece como se espera es el de tipo char. luego incrementa p. Los punteros también pueden usarse en operaciones relacionales. ¿Cuáles son los tamaños de cada tipo de dato en su ordenador? char *cp. por ejemplo. Ejercicios 1.i. ¿Qué cree usted que haga la siguiente instrucción si el valor de p es 1 antes de que la instrucción se ejecute? *p++. float *fp.ch. Sin embargo. Por ejemplo. Esta es válida sin el const. . la siguiente instrucción es ilegal: char const nombre[5] = "JOHN". el cual pone el dato en ROM. debido a que los caracteres tienen un tamaño de 1 byte. Luego incremente cada puntero e imprima el valor de la variable nuevamente. si apuntan al mismo objeto. . Es posible incrementa o decrementar tanto el puntero como el objeto al cual apunta. p = p+200. Por ejemplo. a un puntero o desde un puntero. *(p+i)). Además se habrá sorprendido del hecho de que podemos colocar un índice al puntero como si este fuera un arreglo. Debido a que los punteros a arreglos direccionan solo al primer elemento o base de la cadena. se uso la función gets(). PUNTEROS Y ARREGLOS En C. #include <stdio. for(i=0.4. es un puntero al primer elemento de la cadena. donde i es el índice del arreglo. Por ejemplo: #include <stdio. Esto te permitirá acceder al arreglo usando la aritmética puntero. } Una cuestión a recordar es que un puntero solo podrá indexarse cuando este apunte a un arreglo. p=a. } Este es un programa claramente válido en C. Abra notado que en la función printf() usamos *(p+i).3.i++) printf("%d".i.i++) printf("%d". El siguiente programa también es válido. Es la relación entre estos dos lo que hace el poder de C cada vez más aparente. Si usted emplea el nombre de un arreglo sin el índice.i<5. es invalido incrementar el puntero.2. punteros y arreglos están relacionados y son algunas veces intercambiables. Debido a que el nombre de un arreglo sin un índice es un puntero. entonces está usando un puntero que direcciona al comienzo del arreglo.i<5. p = &i. con la cual nosotros solo pasamos el nombre de la cadena. En el capitulo anterior. la instrucción p++.h> int a[5]={1. for(i=0. 61 . void main(void) { int *p.3.p[i]). void main(void) { int *p. será invalida utilizándola en el programa anterior. solo un puntero que direcciona al primer elemento es entregado.2.i.h> int a[5]={1. estos no pueden crearse para usarse con arreglos constantes o estructuras. Por lo tanto.int *p.5}. p = p/2.5}. Nota importante: cuando un arreglo se entrega a una función. getchar(). puede asignar ese valor a otro puntero. p=a. Lo que actualmente se pasa a una función.4.i. getchar(). llamado por valor y llamado por referencia. La instrucción while(*p) es testeada en búsqueda de la condición vacio (null) al final de la cadena. o en otras palabras un puntero es pasado a la función. RCV=pin_a4) void puts(char *p). } void puts(char *p) { while(*p) { printf("%c". .20.h> #fuses XT. Otro ejemplo de pasar un puntero a una función es el siguiente: void IncBy10(int *n) { *n += 10. hablamos acerca de las dos formas en las que los argumentos podían pasar a las funciones.15. En este punto cualquier cambio hecho a las variables empleando el puntero cambiara el valor de la variable desde la rutina de llamado. PASANDO PUNTEROS A FUNCIONES En el capítulo 3.*p+3).*p). ¿El siguiente segmento de código es correcto? int cont[10]. } En este ejemplo. p = valor.25}. el puntero p direcciona al primer caracter de la cadena. ¿Qué valor imprime el siguiente segmento d código? int valor[5]={5. El siguiente ejemplo muestra como pasar una cadena a una función usando punteros. 2.10. Cada vez que pasa a través del lazo while. xmit=pin_a3. } void main(void) 62 . Luego p se incrementa para apuntar al siguiente caracter de la cadena.Ejercicios 1. p++. el carácter que es apuntado por p se imprime. El segundo método pasa la dirección a la función. Los punteros pueden pasarse a funciones al igual que otras variables. void main(void) { puts("Microchip es genial!"). cont = cont+2. la letra “M”. NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. #include <16F84A.NOWDT. printf("%d". PUT. int *p. } printf("\n"). 2. Después que la función retorna al main. Escriba un programa que pase un valor tipo float a una función. usando una clase especial de parámetros de puntero llamado parámetro de referencia. Escriba un programa que el puntero fl a una función. imprime el valor de la variable float. el valor -1 se asigna a la variable. Después que la función retorna al main. } El ejemplo anterior puede reescribirse para mejorar la legibilidad. el valor -1 se asigna al parámetro de la función. } void main(void) { int i=0. imprime el valor de la variable. Dentro de la función. Dentro de esta función. void Incby10(int & n) { n += 10. Incby10(i). 63 .{ int i=0. IncBy10(i). Ejercicios 1. } Ambos ejemplos muestran como retornar un valor desde una función por medio de la lista de parámetros. 8. ESTRUCTURAS Y UNIONES Las estructuras y uniones representan dos de los más 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 través de un nombre común. 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 comúnmente como un campo o miembro. Nosotros nos referiremos a estos como miembros. En general, la información almacenada en una estructura lógicamente debe estar relacionada. Por ejemplo, usted podría emplear una estructura para guardar el nombre, dirección y número telefónico de todos sus clientes o compañeros. El siguiente ejemplo es para una referencia bibliográfica 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 dirección 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 título, por ejemplo la tercera letra de la cadena?, entonces debe usar: Tarjeta.titulo[2]; El primer elemento del título esta en 0, el segundo en 1 y, finalmente el tercero esta en 2. Una vez usted ha definido una estructura, puede crear más 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 podría definir dos variables más 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]). ¿Cómo puede acceder al miembro titulo del elemento 10 del arreglo estructura grande?, de la siguiente manera: Grande[9].titulo; Las estructuras también pueden pasarse a funciones. Una función puede retornar una estructura de la misma manera que cualquier otro tipo de dato. También puede asignar valores de una estructura a otra simplemente usando una asignación. El siguiente fragmento es perfectamente válido: struct temp { int a; float b; char c; 65 } var1,var2; var1.a=37; var2.b=53.65; var2 = var1; Después que este fragmento de código ejecuta la estructura, la variable var2 tendrá el mismo contenido de la variable var1. El siguiente es un ejemplo de cómo 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 función, toda la estructura pasa empleando el método llamado por valor. Por consiguiente, cualquier modificación hecha a la estructura en la función no afectara el valor de la estructura en la rutuna de llamado. El número de elementos tampoco afecta la forma en la cual es pasada a una función. Un ejemplo de estructura empleándola sobre un PIC para configurar una interfaz con LCD podría 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 notación :4 en data indica que se van a utilizar 4 bits par ese elemento. En este caso D0 será en1, y D3-D6 serán 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 carácter y una cadena de 40 caracteres. Lea un carácter desde el teclado y guárdelo en la primera variable usando getch(). Lea una cadena y guárdela en la segunda variable usando gets(). Por último imprima los valores de cada miembro en pantalla. 66 NOWDT. PUT.h> #fuses XT. usted debe usar el operador flecha. . la instrucción q=&p es perfectamente valida. Dado que q apunta a p. Note que el operador flecha es un signo menos seguido del signo mayor que sin ningún espacio entre estos. 67 . } p. char ch. Por ejemplo.h> #include <string. estructuras largas pueden reducir la velocidad de ejecución del programa debido a la gran cantidad de datos transferidos. } s.q. struct temp { int i. Empleando esta definición de la estructura temp. Punteros a estructuras son declarados de la misma manera que punteros a otros tipos de datos. ¿En el siguiente fragmento de código cual es el error? struct tipo { int i. . RCV=pin_a4) struct s_type { int i. char str[50]. Por esta razón.*p. long l. char str[40]. } s. El siguiente ejemplo muestra como un puntero a estructura debe inicializase: #include <16F84A. NOPROTECT #use delay(clock=4000000) #use RS232 (Baud=9600. i = 10.2. debe usar el operador flecha como se muestra a continuación: q->i=1. PUNTEROS A ESTRUCTURAS Algunas veces es útil poder acceder a una estructura a través de un puntero. es fácil pasar un apuntador a estructura a la función. Esta instrucción asigna el valor de 1 al número i de la variable p. use el punto. el siguiente fragmento de código declara una variable del tipo estructura p y un puntero a estructura q con el tipo de estructura temp. NOTA: Cuando se accese a un miembro de una estructura empleando una variable. Debido a que C pasa toda la estructura a una función. Cuando accese a un miembro de una estructura empleando un puntero a estructura. xmit=pin_a3. p->str). ¿El siguiente segmento de código es correcto? struct s_type { int a. strcpy(p->str. }. struct productos { struct PIC dispositivos[NumerodePICS]. }. } 2. unsigned char progmem.void main(void) { p=&s. 68 . printf("%d %d %s". void main(void) { p=&s. } Las dos líneas s. Escriba un programa que cree un arreglo de estructuras de tamaño 3 para las familias de PIC básicas. Necesitara cargar la estructura con un dispositivo PIC12. los miembros de las estructuras también pueden ser otras estructuras. sin embargo. p->i=10. *p. char rasgo[40]. A esto se le llama estructuras anidadas. ESTRUCTURAS ANIDADAS Hasta ahora.i.s. El usuario seleccionara la estructura a imprimir usando el teclado para entrar 1. 2. unsigned char datamem. char tipoEncapsulado[40]. Ejercicios 1.p->i."Utilizo estructuras"). s. o 3. int b.a=100. unsigned char datamem. PIC16 Y PIC 18. Por ejemplos: #define NumerodePICS 25 struct PIC { char nombre[40]. } s.i=10 y p->i=10 son equivalentes. p.i=10. unsigned char progmem. El formato de la estructura será el siguiente: struct PIC { char nombre[20]. usted solo ha visto que los miembros de una estructura son uno de los tipos de datos de C. char rasgo[80]. -----------------------------tipo double---------------------------- ----c[2]---- ----c[1]---- ----c[0]---- ---------entero(int)--------- Elemento0 Elemento1 Elemento2 Elemento3 Accesar los miembros de la unión se realiza de la misma manera que como en las estructuras.float precio. La forma general de una unión es la siguiente: union NombreUnion { tipo elemento1. tipo elementoN } ListaVaribles. La instrucción temp. se emplea el punto (. . Es importante notar que el tamaño de la unión es establecido en tiempo de compilación acomodándolo al miembro más grande de la unión. Sin embargo. una cadena que tiene el tipo de encapsulado. y un tipo double. char c[3]. 69 . La estructura productos tiene tres elementos: un arreglo de la estructura PIC llamado dispositivos. INTRODUCCION A LAS UNIONES Una unión es definida como una posición de memoria sencilla que es compartida por dos o más variables. Asumiendo que los tipos double tienen cuatro bytes de tamaño. NombreUnion es el nombre que le damos a la unión y ListaVariables son las variables que son del tipo unión NombreUnion. tipo elemento2. double d. debe usar el operador flecha (->) como se hizo para las estructuras. . Estos elementos pueden accesarse usando la variable lista1. el arreglo utiliza tres bytes y el tipo double emplea cuatro bytes. } temp. y el precio. Si quiere acceder a una unión a través de un puntero. } lista1. solo podemos usar una variable a la vez. Usaremos el ejemplo anterior para ilustrar una unión. un arreglo de caracteres. La manera en que una unión aparece en memoria se muestra a continuación. El entero emplea dos bytes. La diferencia entre uniones y estructuras es que cada miembro de una unión comparte el mismo espacio. la siguiente unión contiene tres miembros: un entero. Las variables que comparten la misma posición de memoria pueden tener diferentes tipos de datos. Nuevamente. union u_type { int i. la unión temp tendrá un tamaño de cuatro bytes.i accesa al miembro tipo entero de dos bytes. Por ejemplo.). Una unión se parece mucho a una estructura. Escriba un programa que tenga una unión con un miembro long int y un arreglo de caracteres de cuatro bytes. ¿Cuáles son las diferencias entre una estructura y una unión? ¿Cuáles son las similitudes? 2. El Microcontrolador lee el A/D en dos bytes. } Cuando quiera leer al A/D. leerá dos bytes desde el A/D y los almacenara en el arreglo bytes. signed short palabra. 70 . cuando quiera usar la muestra de 12-bits deberá usar el miembro palabra para acceder al número de 12-bits. union muestra { unsigned char bytes[2]. Ejercicios 1. Entonces nosotros podríamos configurar una unión que tenga dos unsigned char y un signed short como miembros.Un buen ejemplo de la aplicación 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. Su programa debe imprimir el miembro long int en pantalla un byte a la vez. Entonces. Un PIC de 8 pines tiene un solo registro GPIO y un TRIS para 6 líneas I/O. la configuración I/O por defecto es ANALOGICO. El compilador no añade líneas de código para configurar la dirección del puerto antes de cada operación I/O. y aceleran el proceso de aprendizaje para los estudiantes y programadores principiantes. b_valor = portb & 0b00000011.W. Estos son designados dependiendo de la disponibilidad de puertos del PIC en cuestión: PORTA.9. o estándar. esto añade líneas a un programa y por lo tanto disminuye la velocidad. //enmascara los bits no deseados El valor almacenado en b_valor puede usarse para establecer un valor de retorno a una función. pero mejora la parte de seguridad del código asegurando que las líneas I/O estén siempre como se especificaron. E y TRISA. NOTA: en dispositivos con conversores A/D. El compilador C puede interrumpir entradas y salidas de muchas maneras: fija. ENTRADAS Y SALIDAS Los puertos de entrada/salida del PIC están compuestos por dos registros: PORT y TRIS. LENGUAJE C ESPECIFICO PARA PIC Habiendo estudiado las bases de C. B. las cuales pueden configurarse como entradas o como salidas. asegúrese que el registro ADCON1 está configurado correctamente. Además para leer el puerto se emplea la siguiente instrucción MOVF PORTA. el PIC16F84A posee 2 puertos (13 líneas I/O) por lo que tiene los siguientes registros: PORTA. TRISA. rápida. D. funciones y operaciones relacionadas con el PIC. En Assembler para configurar individualmente los pines como entrada o salida se utilizan las instrucciones BSF o BCF. D. Lo siguiente es el conjunto de una función empleada para leer algunos dip switches y establecer una velocidad de transmisión (baudios) para una rutina común. //lee puerto B El enmascaramiento de bit se consigue fácilmente mediante la adición de & y el patrón de la máscara después del nombre del puerto. Esto se hace por medio del registro TRISA. PORTB. El puerto A tiene de 5 a 6 líneas dependiendo del PIC. El compilador C de CCS® tiene un extenso set de funciones que ahorran tiempo. El modo I/O rápido habilita al usuario para configurar la dirección del puerto y este permanece así hasta que sea redefinido. //Puerto B como entrada b_valor = portb. B. En el modo estándar. el registro TRIS es configurado antes de cada operación I/O. las entradas son configuradas con un 1 y las salidas con un 0. 71 . E. C. TRISB. El siguiente ejemplo configura el puerto B como entrada y luego lee su valor: set_tris_b(0xff). C. es tiempo para continuar con las instrucciones. //enmascara los bits no deseados switch(b_rate) { case 0: set_uart_speed(1200). bit). es aconsejable establecer las condiciones del puerto antes que el registro TRIS. } } Cuando configuramos el puerto B. bit_test(variable. case 2: set_uart_speed(4800). output_float(pin). break. case 1: set_uart_speed(2400). trabaje en forma binaria. El registro de dirección 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. break. pin) 72 . b_rate = portb & 0b00000011. //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). Establece la combinación de entradas y salidas para un puerto dado (ponga 1 para entradas y 0 para salidas). //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). esto hará que su código fuente sea más fácil de escribir para usted y más fácil de entender para otros. La manipulación de datos desde y hacia los puertos I/O se realiza de forma sencilla con el uso de las numerosas funciones construidas. output_high(pin). case 3: set_uart_speed(9600).byte bd_sw_get() //selección de velocidad de transmisión { byte b_rate. break. break. Cuando configure los bits en el registro o puertos. bit_clear(variable. output_bit(pin. Esto previene que el puerto genere una condición no requerida antes de que sea configurado. bit). value). Habilita o deshabilita las resistencias de pullup internas para el puerto B set_tris_a(value). output_low(pin). Sobre el nivel de bit se tienen las siguientes: bit_set(variable. Esta función aplica para todos los puertos. bit). limitaciones en temporizaciones. } Cuando se compila. 8 cont 27. BuscaParidad(byte d) { byte cont. #asm Movlw 8 Movwf cont clrw bucle: xorwf d. el programa luce de la siguiente manera: BuscaParidad(byte d) { byte cont.d=7.F 008 21 016 07 26 73 . #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.W 27. a=BuscaParidad(d). MEZCLANDO C Y ASSEMBLER Habrá momentos en los cuales código en Assembler será requerido en nuestro programa escrito en C.w rrf d. o simplemente porque se necesita alguna rutina especial. a=BuscaParidad(d).f decfsz cont.F 28.Permanentemente configura el registro TRIS para el puerto #use standard_io(port) Por defecto para configurar el puerto cada vez que este se use. 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.f goto bucle movwf _return_ #endasm } void main(void) { byte a.d=7. Buscando código compacto. d 1(2) 1.2 1.W 25 Set de instrucciones para el PIC16F84A Las 35 instrucciones fundamentalmente se dividen en tres tipos.d f.2.d f.2 00 0111 dfff ffff C.d INCF f. w es el registro acumulador.2 1.3 1 1 1 1 1 1 1 1 1 1.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 operación Rota f izq por carry → d Rota f dcha por carry → d f .w → d CÓDIGO OP BANDERAS NCIC NOTAS 1 1 1 1 1 1 1.d f f.1 → d f .d f f.2 1.htm 74 . DC. Tabla 5. Instrucciones orientadas a registros Instrucciones orientadas a registros MNEMÓNICO OPERANDOS ADDWF ANDWF CLRF CLRW COMF DECF f. f representa un registro cualquiera y C.d f.d IORWF MOVF MOVWF NOP RLF RRF SUBWF SWAPF XORWF f.2 2 1.2 1.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.d DESCRIPCIÓN w + f → d w AND f → d 00 h → f 00 h → w Complemento de f → d f .Z Ninguna Z DECFSZ f.d w XOR f → d 00 0110 dfff ffff Fuente: http://perso. Esta división viene dada por el tipo de datos con los que trabajan: Instrucciones orientadas a los registros o bytes (byte-oriented operations).2 1.es/pictob/instrucciones.2 1.2 1. Operaciones con literales y de control (literal and control operations).d 1(2) 1.0013: 0014: 0015: 0016: 0017: } MOVF MOVWF GOTO MOVF MOVWF 26.W 27 005 21.3 1 1.2 INCFSZ f.2 f. Instrucciones orientadas a los bits (bit-oriented operations).d f.d f.2 1.d Intercambia nibbles de f → d 00 1110 dfff ffff f.wanadoo. Z los flags (banderas) del registro STATUS. DC.2. Las 35 instrucciones ó mnemónicos de la gama media de Microchip® las encontraremos resumidas en las siguientes tablas. PD C.Tabla 6.es/pictob/instrucciones.wanadoo.DC. Instrucciones con literales y de control Instrucciones con literales y de control MNEMÓNICO OPERANDOS ADDLW ANDLW CALL CLRWDT GOTO IORLW MOVLW RETFIE RETLW RETURN SLEEP SUBLW XORLW k k k k k k k k k DESCRIPCIÓN w + k → w w AND k → w Llamada a subrutina k Borra temporizador del WDT Ir a dirección k w OR k → w k → w Retorno de una interrupción CÓDIGO 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. Instrucciones orientadas a bit Instrucciones orientadas a bit MNEMÓNICO OPERANDOS BCF BSF f.PD Ninguna Z Ninguna Ninguna Ninguna Ninguna TO. Estas funciones devuelven los valores 0 o 1 representando los bits desplazados.b Salto si bit b de reg. bit_set.2 3 3 BTFSC f. bit_clear.2 1. Shift_left y shift_right desplazan una posición de bit a través de cualquier número de bytes.w → w w XOR k → w Fuente: http://perso.htm MANIPULACION AVANZADA DE BIT El compilador C de CCS® posee un número de funciones enfocadas a la manipulación de bit que se necesitan comúnmente en los programas con PIC. La numeración de los bits se realiza dejando el bit menos significativo (la primera posición de derecha a izquierda) como 0 y el bit más significativo como 7.Z Z Ninguna TO.b Salto si bit b de reg.b f. Note que. y bit_test simplemente ponen en ALTO o BAJO un bit de una variable o testean el estado de un solo bit. f es 1 01 11bb bfff ffff Fuente: http://perso.htm Tabla 7. En adición. estas nos permiten especificar el valor del bit para colocarlo en la posición vacante.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 . 75 .wanadoo. f es 0 01 10bb bfff ffff BTFSS f.es/pictob/instrucciones.b DESCRIPCIÓN Pone a 0 bit b de registro f Pone a 1 bit b de registro f CÓDIGO OP 01 00bb bfff ffff 01 01bb bfff ffff BANDERAS NCIC NOTAS Ninguna Ninguna Ninguna Ninguna 1 1 1(2) 1(2) 1. 2. //x es ahora 01101001 TEMPORIZADORES Todas las gamas de PICs tienen un temporizador (timer) de 8-bits y además algunos PICs tienen dos temporizadores avanzados. // el primer msb de x es: 00000010. 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.1). shitf_left(&y. 0b00011100.00011100.c} z. struct { int a. // el primer msb de x es: 00000100. Por ejemplo: int x.estas funciones consideran el byte menos significativo en memoria como LSB. 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.sizeof(x). Las capacidades se presentan a continuación: rtcc (timer0) = 8Bit. Si se usara una simple variable o estructura. 00111001. x = 0b10010110.0). 10010001 rotate_left(x. se debe añadir el operador &. //el primer msb de x es: 10000001. 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.00100010 //bb es 1 bb = shift_left(x. una interrupción puede generarse 76 .01110010. debido a que x es un arreglo el identificador sin índice es un puntero.3. El segundo parámetro es el número de bytes y el último parámetro es el nuevo bit. 0b00011100. Por ejemplo: long y.00111001.0).0). 00011100. 00100011 La función swap intercambia los 4 bits más significativos con los 4 bits menos significativos de un byte. short bb. Por ejemplo: int x[3] = {0b10010001. 0b10000001}.b.10010001 bb = shift_left(x.sizeof(x)). swap(x).01000101 //bb es 0 NOTA: El primer parámetro es un puntero. //el primer msb de x es: 00000010. 0b10000001}. En este caso. una interrupción puede generarse timer1 = 16Bit.sizeof(x). shitf_right(&z. Ejemplo: int x[3] = {0b10010001. //el primer msb de x es: 10000001. con lo que requiere un cierto número de desbordamientos antes que la interrupción ocurra Este temporizador se emplea como parte de la generación de PWM En el siguiente ejemplo se usa rtcc (timer0) para temporizar cuanto tiempo dura un pulso en estado ALTO. xmit=pin_a3. //incrementa 1024000/4*256 veces por segundo //o cada milisegundo while(input(PIN_B0)). PUT. } El siguiente ejemplo emplea el timer1 en el modo captura para temporizar cuanto tiempo le toma al pin C2 cambiar al estado ALTO después de que el pin B0 #include <16F877A.time). //espera por estado BAJO time = get_rtcc(). setup_timer_1(t1_internal | t1_div_by_2). 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.NOWDT. set_timer1(0). #include <16F84A. rtcc_div_256). un pin de salida cambia su estado cuando la cuenta alcanza un valor preestablecido. RCV=pin_a4) void main(void) { int time. 77 . output_high(PIN_B0). una interrupción puede generarse La interrupción puede retrasarse aplicando un post-escaler. En el modo captura. //espera por estado ALTO set_rtcc(0).NOWDT. PUT. la cuenta del timer1 puede guardarse en otro registro cuando el estado de un pin de entrada cambia. y una interrupción puede generarse Este temporizador se emplea como parte de la generación de PWM (Modulación por anchura de pulsos) timer2 = 8Bit. while(!capture_1).xmit=PIN_C6.2 //registro pir1 //bit 2 = la captura se ha realizado void main(void) { long time. while(!input(PIN_B0)). //incrementa cada 1 us setup_ccp1(ccp_capture_re). printf("tiempo en ALTO = %u ms. NOPROTECT #use delay(clock=8000000) #use rs232(baud=9600. setup_counters(rtcc_internal. una interrupción puede generarse En el modo captura.h> #fuses XT.rcv=PIN_C7) #bit capture_1 = 0x0c.h> #fuses XT. NOPROTECT #use delay(clock=1024000) #use RS232 (Baud=9600.". //configura CCP1 para captura en flanco subida capture_1=0. time). printf("Tiempo de reaccion = %1u us.". } COMVERSION A/D Los conversores A/D de Pag117 78 .time = ccp_1.
Copyright © 2024 DOKUMEN.SITE Inc.