You are on page 1of 37

Lenguaje C para Microcontroladores

http://www.cursomicros.com/avr/lenguaje-c/lenguajec.html

Contenido
Introduccin

o Por qu C y no Basic? o Qu compilador usar? o nimo! No es tan difcil


Estructura de un programa

o o o o o

Los comentarios Las sentencias Los bloques Las directivas Las funciones

Variables y Tipos de Datos

o Declaracin de variables o Especificadores de tipo de datos


Sentencias selectivas

o o o o

La sentencia if La sentencia if - else La sentencia if - else - if escalonada La sentencia switch

Sentencias iterativas

o La sentencia while o La sentencia do - while o La sentencia for


Bloques simples Los operadores

o o o o o o o o o o o o o o o o o o o o o o

Operadores aritmticos Operadores de bits Operadores relacionales Operadores lgicos Composicin de operadores Precedencia de operadores Funciones sin parmetros Parmetros por valor Parmetros por referencia Prototipos de funciones Variables locales y globales Variables static Variables volatile Los arrays o matrices Declaracin de arrays Inicializacin de arrays Cadenas de texto Los punteros Declaracin de punteros Apuntando a variables Asignaciones con punteros Punteros y arrays

Las funciones a Fondo

Arrays y Punteros

o Paso de punteros y arrays a funciones o Arrays constantes

INTRODUCCIN
Los lenguajes de alto nivel son mucho ms potentes que el ensamblador aunque su aprendizaje demanda un mayor esfuerzo. Para empezar a programar en ensamblador nos puede bastar con aprender unas 50 palabras (las instrucciones bsicas). En cambio dominar un lenguaje de alto nivel como el C es como aprender a hablar en un nuevo idioma. No basta con memorizar palabras nuevas, sino que debemos aprender a manejar una nueva estructura gramatical. Adems, los procesadores no son como las personas: si en un cdigo de 100 lneas te olvidaste de una sola coma, los compiladores no te lo pasarn por alto. Por qu C y no Basic Ciertamente, el Basic es el lenguaje ms fcil de aprender (no es exactamente la razn de su nombre). Y aunque los programadores en C de computadoras miren con desdn a los que usan el Basic, en el mundo de los microcontroladores los compiladores Basic no tienen motivo para sentirse menos. De hecho, algunos pueden ser casi tan eficientes como los mejores compiladores C. Las caractersticas (muchas veces complejas) del C fueron ideadas para el trabajo con sofisticados proyectos, propios de las computadoras. Muchas de esas caractersticas ya no resultan tan ventajosas en el limitado hardware de los microcontroladors y se convierten en prescindibles. Adems, la simplicidad de los compiladores Basic para microcontroladores tambin permite que varios de ellos, como MBasic o PIC Basic Pro (por citar algunos) mantengan una compatibilidad entre sus cdigos que no se encuentra entre los compiladores C. sas podran ser razones ms que convincentes para empezar por el Basic y, de hecho, es la opcin que muchos han elegido. Por qu nosotros no? Porque es verdad comprobable que los mejores programadores trabajan en C (no siempre exclusivamente, pero lo manejan). Por consiguiente, los proyectos ms fantsticos y alucinantes que se pueden encontrar estn en C. Es ms, la mayora de, por no decir todos, los programadores de Basic tarde o temprano se ven obligados a aprender el C. No s t, pero yo opino que esa razn pesa ms. Adems, dada la robustez y la aceptacin del lenguaje C, se lo ha tomado como referencia para lenguajes de otros propsitos como Java, JavaScript, php o de Matlab, entre otros. As que, el C podr servirte para trabajar en otros campos. El programador de C podra, inclusive, aprender luego el Basic sin el menor esfuerzo; lo contrario no es cierto. Qu compilador C utilizar? No quiero burlarme de nadie, pero una vez le en Internet el comentario de un novato: Quiero programar microcontroladores en C. Ya descargu el Visual C++. Qu ms necesito? :). Aparte del lenguaje, nada tiene que ver un compilador para computadoras con los compiladores para Cs. Poco tiene que ver un compilador para PICs que otro para otros Cs. Inclusive, poco tiene que ver un compilador de PICs de una compaa con otro de otra compaa. Veamos grosso modo algunos aspectos de los compiladores de PICs ms conocidos. Hi-tech C. Es uno de los compiladores producidos por la empresa htsoft. Es quiz el ms eficiente y el que mejor soporta el lenguaje C estndar. Su entorno IDE tambin incluye el mejor depurador ICD. Como contraparte, su apego al hardware del C le resta algo de portabilidad.

Tampoco luce libreras incorporadas como otros productos. Pero su principal desventaja es su elevado precio. Y, por si fuera poco, el compilador para la familia de partes PIC18 se vende por separado. AVR IAR C. Los compiladores C de la compaa iar systems tienen bsicamente las mismas caractersticas mencionadas de los compiladores de htsoft, incluyendo sus propios depuradores. As mismo, las versiones para los PIC16 y PIC18 se distribuyen por separado. Actualmente, no s por qu, ya no est disponible la primera versin. CCS C. La empresa ccsinfo decidi dotar a sus compiladores C una capa extra que asla al programador de los recursos intrnsecos del C. Esto puede afectar la portabilidad de sus cdigos a otros compiladores, pero resulta inmejorable, si solo se trabaja en el lenguaje de CCS C, para transportar los cdigos de un PIC a otro (de cualquier familia) con un esfuerzo sin comparacin. Adems, incluye en un solo paquete los compiladores para los PICs de las familias Baseline, Midrange (PIC16 bsicamente) y High performance (PIC18). Al igual que los softwares anteriores, sus libreras estndar, como stdlib.h, stdio.h, string.h y math.h, son muy completas y potentes; pero CCS C supera a sus rivales al incorporar libreras para controlar todos los mdulos internos del PIC y tambin muchsimos dispositivos externos. Mikro C. La compaa Mikroelektronika vende compiladores para PICs en los lenguajes C (MikroC), Basic (MikroBasic) y Pascal (MikroPascal). Yo dira que el estilo de Mikro C se parece al de Hi-tech C y sus facilidades tratan de acercarse a las de CCS C: aunque en muchos casos an es necesario acceder a los registros internos del PIC, cuenta con libreras para controlar sus mdulos internos. Tambin tiene una apreciable cantidad de libreras para interfacear dispositivos externos. Lo malo es que todas ellas estn precompiladas y no se podran modificar, en caso de ser necesario. Mikroelektronika y CCS tambin comercializan sus propias tarjetas de entrenamiento para el aprendizaje de sus productos. Para ms informacin puedes visitar sus sitios web. MPLAB C18. Excelente compilador desarrollado por los ingenieros de Microchip. No es gratuito como el MPLAB, pero creo que es el que ofrece la versin demo ms generosa: es 100 % funcional por 60 das. Lamentablemente, como sugiere su nombre, solo trabaja con las partes PIC18. Quiz lo probemos en otro momento. Otros. An hay otros compiladores C (como Bytecraft, BoostC y FedC) que algo menos reconocidos como los anteriores, lo que no significa que sean malos. Tambin he visto algunos de cdigo abierto, pero no son buenos: la gente del GNU trabaja ms con el AVR GCC, un Seor Compilador. Es uno de los pocos casos donde el software libre supera a los comerciales. Como se puede entrever, est orientado a los microcontroladores AVR, de Atmel. Es, adems, el compilador ms difcil de todos; por eso lo estudiaremos en el Mdulo 4. En cuanto a cul compilador usar: la idea de este curso no es aprender a programar con un compilador en particular, y tampoco pretendo promocionar alguno. Despus de todo, una victoria depende ms de la habilidad guerrero que de su espada. He visto super programas hechos con el compilador ms modesto. En este Mdulo 2 uso BoostC porque es muy fcil, porque nos permitir ver ms de cerca cmo funcionan las cosas dentro del PIC y, sobre todo, porque el salto de l a otros compiladores ser mucho ms fcil que hacerlo al revs. En el Mdulo 3 migraremos al CCS C (que adems del lenguaje C usa su propio argot) y en el Mdulo 4 trabajaremos especialmente con AVR GCC.

nimo! No es tan difcil Pienso que, comparado con el Basic para microcontroladores, el C es infinitamente ms difcil de aprender. Quienes lo usan, en gran parte, son personas que han tenido experiencia programando computadoras, personas que han estudiado ms de un libro para dominarlo. Es, literalmente, como aprender un nuevo idioma, y eso no es algo que se hace de la noche a la maana. Eso no suena muy alentador? Para simplificar las cosas, en este captulo no voy a exponer todas las reglas del lenguaje C, aunque s la mayora; digamos el 95 % de lo necesario. El resto: o es solo aplicable a los PCs, o son temas raros o que difieren demasiado entre de compilador a otro y conviene ms revisarlos en sus respectivos manuales. Tambin, y para ahorrar los ejemplos prcticos, asumo que no eres un novato cualquiera, asumo que conoces algo de programacin (aunque sea en ensamblador), que sabes cmo usar las subrutinas, que sabes cmo emplear los bucles, que sabes lo que significa redirigir el flujo de un programa, que sabes para qu sirven las variables, etc. Si no, estars algo perdido. Finalmente, no es necesario que te aprendas de golpe todo el captulo; bastar con que lo leas fluidamente una primera vez y regresar luego a consultar algunos puntos de duda. La parte ms complicada es Arrays y Punteros, sobre todo los punteros. As que, ten paciencia con ellos.

ESTRUCTURA DE UN PROGRAMA EN C
Tomaremos en cuenta este sencillsimo ejemplo, escrito para el compilador Hitech PICC. #include <pic.h> // Incluir este archivo

/* La siguiente directiva establece la Palabra de Configuracin */ __CONFIG ( PWRTEN & WDTDIS & XT & UNPROTECT ); void pausa(void) { unsigned int c;

// Llave de apertura del bloque de pausa // Declarar variable c (de 16 bits)

for(c=0; c<60000; c++) { // Llave de apertura del bloque de for /* este bloque est vaco, solo cuenta c desde 0 hasta 59999 */ } // Llave de cierre del bloque de for // Llave de cierre del bloque de pausa

void main(void) { TRISB0 = 0; while(1) { RB0 = 1; pausa(); RB0 = 0; pausa(); } }

// // // // // // // // // //

Llave de apertura del bloque de main Configurar pin RB0 como salida Bucle infinito Llave de apertura del bloque de while Setear bit RB0 Llamar funcin pausa Limpiar bit RB0 Llamar funcin pausa Llave de cierre del bloque de while Llave de cierre del bloque de main

No hay que ser muy perspicaz para descubrir lo que hace este programa: configura el pin RB0 como salida y luego lo setea y lo limpia tras pausas. Es como hacer parpadear un LED conectado al pin RB0. Parpadea porque el bloque de while se ejecuta cclicamente. Los elementos ms notables de un programa en C son las sentencias, las funciones, las directivas, los comentarios y los bloques. A continuacin, una breve descripcin de ellos.

Los comentarios Los comentarios tienen el mismo propsito que en ensamblador: documentar y adornar el cdigo. Es todo es texto que sigue a las barritas // y todo lo que est entre los signos /* y */. Se identifican fcilmente porque suelen aparecer en color verde. Ejemplos. // ste es un comentario simple /* sta es una forma de comentar varias lneas a la vez. Sirve mucho para enmascarar bloques de cdigo. */ Las sentencias Un programa en C, en lugar de instrucciones, se ejecuta por sentencias. Una sentencia es algo as como una mega instruccin, que hace lo que varias instrucciones del ensamblador. Salvo casos particulares, donde su uso es opcional, una sentencia debe finalizar con un punto y coma (;). As que tambin podemos entender que los; sirven para separar las sentencias. Alguna vez le que el compilador C lee el cdigo como si lo absorbiera con una caita, lnea por lnea, una a continuacin de otra (evadiendo los comentarios por supuesto). Por ejemplo, la funcin main del programa de arriba bien pudo escribirse del siguiente modo. void main(void) { TRISB0=0; while(1) { RB0=1; pausa(); RB0=0; pausa(); } } Sorprendido? Podrs deducir que los espacios y las tabulaciones solo sirven para darle un aspecto ordenado al cdigo. Es una buena prctica de programacin aprender a acomodarlas. Las sentencias se pueden clasificar en sentencias de asignacin, sentencias selectivas, sentencias iterativas, de llamadas de funcin, etc. Las describiremos ms adelante.

Los bloques
Un bloque establece y delimita el cuerpo de las funciones y algunas sentencias mediante llaves ({}). Como ves en el ejemplo de arriba, las funciones main y pausa tienen sus bloques, as como los bucles while y for. Creo que exager con los comentarios, pero sirven para mostrarnos dnde empieza y termina cada bloque. Podrs ver cmo las tabulaciones ayudan a distinguir unos bloques de otros. Afortunadamente, los editores de los buenos compiladores C pueden resaltar cules son las llaves de inicio y de cierre de cada bloque. Te ser fcil acostumbrarte a usarlas.

Las directivas
Son conocidas en el lenguaje C como directivas de preprocesador, de preprocesador porque son evaluadas antes de compilar el programa. Como pasaba en el ensamblador, las directivas por s mismas no son cdigo ejecutable. Suelen ser indicaciones sobre cmo se compilar el cdigo. Entre las pocas directivas del C estndar que tambin son soportadas por los compiladores C para PICs estn #include (para incluir archivos, parecido al assembler), #define (mejor que el #define del ensamblador) y las #if, #elif, #endif y similares. Fuera de ellas, cada compilador maneja sus propias directivas y sern tratadas por separado.

Las funciones
Si un programa en ensamblador se puede dividir en varias subrutinas para su mejor estructuracin, un programa en C se puede componer de funciones. Por supuesto que las funciones son muchsimo ms potentes y, por cierto, algo ms complejas de aprender. Por eso ni siquie-

ra el gran espacio que se les dedica ms adelante es suficiente para abarcarlas. Pero, no te preocupes, aprenderemos de a poco. En un programa en C puede haber las funciones que sean posibles, pero la nunca debe faltar la funcin principal, llamada main. Donde quiera que se encuentre, la funcin main siempre ser la primera en ser ejecutada. De hecho, all empieza y no debera salir de ella.

VARIABLES Y TIPOS DE DATOS


En ensamblador todas nuestras variables de programa eran registros de la RAM crudos, es decir, datos de 8 bits sin formato. En los lenguajes de alto nivel estos registros son tratados de acuerdo con formatos que les permiten representar nmeros de 8, 16 32 bits (a veces ms grandes), con signo o sin l, nmeros enteros o decimales. Esos son los tipos de datos bsicos. Las variables de los compiladores pueden incluso almacenar matrices de datos del mismo tipo (llamadas arrays) o de tipos diferentes (llamadas estructuras). Estos son los tipos de datos complejos. Los siguientes son los principales tipos de datos bsicos del lenguaje C: Tabla de variables y tipos de datos del lenguaje C Tipo de dato char signed char unsigned char (signed) int unsigned int (signed) long unsigned long float Tamao 8 8 8 16 16 32 32 32 Rango 0 a 255 -128 a 127 -128 a 127 0 a 255 -32,768 a 32,767 0 a 65,536 -2,147,483,648 a 2,147,483,647 0 a 4,294,967,295 +/- 1.18E38 a +/- 3.40E+38

Por desgracia, excepto signed char y unsigned char, los otros tipos establecen variables de tamaos y/o rangos que suelen variar de un compilador C a otro. Otros compiladores tambin manejan los tipos short, double, bool (o boolean), bit, etc. Esas divergencias pueden afectar la portabilidad de los cdigos, adems de confundir a los programadores. Los valores de esta tabla son los utilizados por la mayora de los compiladores C. Los especificadores signed (con signo) mostrados entre parntesis son opcionales. Es decir, da lo mismo poner int que signed int, por ejemplo. Es una redundancia que se suele usar para reforzar su condicin o para que se vea ms ilustrativo. Declaracin de variables Esta parte es comparable, aunque lejanamente a cuando identificbamos nuestras variables del ensamblador con las directivas equ o cblock endc. No se puede usar una variable si antes no se ha declarado. La forma general ms simple de hacerlo es la siguiente: data_type myvar; donde data_type es un tipo de dato bsico o complejo, del compilador o definido por el usuario y myvar es un identificador cualquiera, siempre que no sea palabra reservada. Ejemplos. unsigned char d; char b; // Variable para enteros de 8 bits sin signo // Variable de 8 bits (para almacenar

signed char c; int i; signed int j; unsigned int k;

// // // // //

caracteres ascii) Variable para enteros de 8 bits con signo i es una variable int, con signo j tambin es una variable int con signo k es una variable int sin signo

Tambin es posible declarar varias variables del mismo tipo, separndolas con comas. As nos ahorramos algo de tipeo. Por ejemplo: float area, side; unsigned char a, b, c; // Declarar variables area y side de tipo float // Declarar variables a, b y c como unsigned char

Especificadores de tipo de datos A la declaracin de una variable se le puede aadir un especificador de tipo como const, sta-

tic, volatile, extern, register, etc. Dichos especificadores tienen diversas funciones y, salvo const, se suelen usar en programas ms elaborados. Como no queremos enredarnos
tan pronto, lo dejaremos para otro momento. Una variable const debe ser inicializada en su declaracin. Despus de eso el compilador solo permitir su lectura ms no su escritura. Ejemplos: const int a = 100; int b; //... b = a; b = 150; a = 60; a = b; // Declarar constante a // Declarar variable b

// Vlido // Vlido // Error! a es constante // Error! a es constante

Por ms que las variables constantes sean de solo lectura, ocuparn posiciones en la RAM del C. Por eso muchas veces es preferible definir las constantes del programa con las clsicas directivas #define (como lo hacamos en el ensamblador). #define a 100 // Definir constante a

SENTENCIAS SELECTIVAS
Llamadas tambin sentencias de bifurcacin, sirven para redirigir el flujo de un programa segn la evaluacin de alguna condicin lgica. Las sentencias if e ifelse son casi estndar en todos los lenguajes de programacin. Adems de ellas estn las sentencias ifelse escalonadas y switchcase. La sentencia if La sentencia if (si condicional, en ingls) hace que un programa ejecute una sentencia o un grupo de ellas si una expresin es cierta. Esta lgica se describe en el siguiente esquema. La forma codificada sera as: sentenciaA; if ( expression ) // Si expression es verdadera, // ejecutar el siguiente bloque { // apertura de bloque sentenciaB; sentenciaC; // algunas otras sentencias } // cierre de bloque sentenciaX;

Diagrama de flujo de la sentencia if.

Despus de ejecutar sentenciaA el programa evala expression. Si resulta ser verdadera, se ejecutan todas las sentencias de su bloque y luego se ejecutar la sentenciaX. En cambio, si expression es falsa, el programa se saltear el bloque de if y ejecutar sen-

tenciaX.
La sentencia if else La sentencia if brinda una rama que se ejecuta cuando una condicin lgica es verdadera. Cuando el programa requiera dos ramas, una que se ejecute si cierta expresin es cierta y otra si es falsa, entonces se debe utilizar la sentencia if else. Tiene el siguiente esquema.

Diagrama de flujo de la sentencia if else. Expresando lo descrito en cdigo C, tenemos: (Se lee como indican los comentarios.) SentenciaA; if ( expression ) { sentenciaB; sentenciaC; // ... } else { sentenciaM; sentenciaN; // ... } sentenciaX; // ... // Si expression es verdadera, ejecutar // este bloque

// En caso contrario, ejecutar este bloque

Como ves, es bastante fcil, dependiendo del resultado se ejecutar uno de los dos bloques de la sentencia if else, pero nunca los dos a la vez. La sentencia if else if escalonada Es la versin ampliada de la sentencia if else. En el siguiente boceto se comprueban tres condiciones lgicas, aunque podra haber ms. Del mismo modo, se han puesto dos sentencias por bloque solo para simplificar el esquema. if ( expression_1 ) // Si expression_1 es verdadera ejecutar { // este bloque sentencia1; sentencia2; } else if ( expression_2 ) // En caso contrario y si expression_2 es { // verdadera, ejecutar este bloque sentencia3;

sentencia4; } else if ( expression_3 ) { sentencia5; sentencia6; } else { sentencia7; sentencia8; }; // todo...

// En caso contrario y si expression_3 es // verdadera, ejecutar este bloque

// En caso contrario, ejecutar este bloque

// ; opcional

Las expresiones se evalan de arriba abajo. Cuando alguna de ellas sea verdadera, se ejecutar su bloque correspondiente y los dems bloques sern salteados. El bloque final (de else) se ejecuta si ninguna de las expresiones es verdadera. Adems, si dicho bloque est vaco, puede ser omitido junto con su else. La sentencia switch La sentencia switch brinda una forma ms elegante de bifurcacin mltiple. Podemos considerarla como una forma ms estructurada de la sentencia if else if escalonada, aunque tiene algunas restricciones en las condiciones lgicas a evaluar, las cuales son comparaciones de valores enteros. Para elaborar el cdigo en C se usan las palabras reservadas switch, case, break y default. El siguiente esquema presenta tres cases pero podra haber ms, as como cada bloque tambin podra tener ms sentencias. switch ( expression ) { case constante1: sentencia1; sentencia2; break; case constante2: sentencia3; sentencia4; break; case constante3: sentencia5; sentencia6; break; default: sentencia7; sentencia8; break; } sentenciaX; // todo... donde constante1, constante2 y constante3 deben ser constantes enteras, por ejemplo, 2, 0x45, a, etc. (a tiene cdigo ascii 165, que es, a fin de cuentas, un entero.)

// Si expression = constante1, ejecutar este bloque

// Si expression = constante2, ejecutar este bloque

// Si expression = constante3, ejecutar este bloque

// Si expression no fue igual a ninguna de las // constantes anteriores, ejecutar este bloque

expresion puede ser una variable compatible con entero. No es una expresin que conduce a
una condicin lgica como en los casos anteriores. El programa solo ejecutar uno de los bloques dependiendo de qu constante coincida con ex-

pression. Usualmente los bloques van limitados por llaves, pero en este caso son opcionales,

dado que se pueden distinguir fcilmente. Los bloques incluyen la sentencia break. Qu es eso? La sentencia break hace que el programa salga del bloque de switch y ejecute la sentencia que sigue (en el boceto, sentenciaX). Atento!: de no poner break, tambin se ejecutar el bloque del siguiente case, sin importar si su constante coincida con expression o no. No sera necesario poner el default si su bloque estuviera vaco.

SENTENCIAS ITERATIVAS
Las sentencias de control iterativas sirven para que el programa ejecute una sentencia o un grupo de ellas un nmero determinado o indeterminado de veces. As es, esta seccin no habla de otra cosa que de los bucles en C. El lenguaje C soporta tres tipos de bucles, las cuales se construyen con las sentencias while, do while y for. El segundo es una variante del primero y el tercero es una versin ms compacta e intuitiva del bucle while. La sentencia while El cuerpo o bloque de este bucle se ejecutar una y otra vez mientras (while, en ingls) una expresin sea verdadera.

Diagrama de flujo de las sentencia while.


El bucle while en C tiene la siguiente sintaxis y se lee as: mientras (while) expression sea verdadera, ejecutar el siguiente bloque. sentenciaA; while ( expression ) { sentenciaB; sentenciaC; // ... }; sentenciaX; // ... // Este ; es opcional // Mientras expression sea verdadera, ejecutar el // siguiente bloque

Nota que en este caso primero se evala expression. Por lo tanto, si desde el principio ex-

pression es falsa, el bloque de while no se ejecutar nunca. Por otro lado, si expression
no deja de ser verdadera, el programa se quedar dando vueltas para siempre.

La sentencia do - while Como dije antes, es una variacin de la sentencia while simple. La principal diferencia es que la condicin lgica (expression) de este bucle se presenta al final. Como se ve en la siguiente figura, esto implica que el cuerpo o bloque de este bucle se ejecutar al menos una vez.

Diagrama de flujo de las sentencia do while.


La sintaxis para la sentencia do while es la siguiente y se lee: Ejecutar (do) el siguiente bloque, mientras (while) expression sea verdadera. sentenciaA; do { sentenciaB; sentenciaC; // ... } while ( expression ); // Este ; es mandatorio sentenciaX; // ... La sentencia for Las dos sentencias anteriores, while y do while, se suelen emplear cuando no se sabe de antemano la cantidad de veces que se va a ejecutar el bucle. En los casos donde el bucle involucra alguna forma de conteo finito es preferible emplear la sentencia for. (Inversamente, al ver un for en un programa, debemos suponer que estamos frente a algn bucle de ese tipo.) sta es la sintaxis general de la sentencia for en C: for ( expression_1 ; expression_2 ; expression_3 ) { sentencia1; sentencia2; // ... }; // Este ; es opcional Ahora veamos por partes cmo funciona:

expression_1 suele ser una sentencia de inicializacin. expression_2 se evuala como condicin lgica para que se ejecute el bloque. expression_3 es una sentencia que debera poner coto a expression_2.

Por la forma y orden en que se ejecutan estas expresiones, el bucle for es equivalente a la siguiente construccin, utilizando la sentencia while. Primero se ejecuta expression_1 y luego se ejecuta el bloque indicado tantas veces mientras expression_2 sea verdadera. expression_1; while ( expression_2 ) { sentencia1; sentencia2; // ... expression_3; } No obstante, de esa forma se ve ms rara an; as que, mejor, veamos estos ejemplos, que son sus presentaciones ms clsicas. (i es una variable y a y b son constantes o variables): for ( i = 0 ; i < 10 ; i++ ) { sentencias; } Se lee: para (for) i igual a 0 hasta que sea menor que 10 ejecutar sentencias. La sentencia

i++ indica que i se incrementa tras cada ciclo. As, el bloque de for se ejecutar 10 veces, desde que i valga 0 hasta que valga 9.
En este otro ejemplo las sentencias se ejecutan desde que i valga 10 hasta que valga 20. Es decir, el bucle dar 11 vueltas en total. for ( i = 10 ; i <= 20 ; i++ ) { sentencias; } El siguiente bucle for empieza con i inicializado a 100 y su bloque se ejecutar mientras i sea mayor o igual a 0. Por supuesto, en este caso i se decrementa tras cada ciclo. for ( i = 100 ; i >= 0 ; i-- ) { sentencias; } Se pueden hacer muchas ms construcciones, todas coincidentes con la primera plantilla, pero tambin son menos frecuentes. SENTENCIAS CON BLOQUES SIMPLES Cuando las sentencias selectivas (como if) o de bucles (como while o for) tienen cuerpos o bloques que constan de solo una sentencia, se pueden omitir las llaves. Aun as, es aconsejable seguir manteniendo las tabulaciones para evitarnos confusiones. Por ejemplo, las siguientes sentencias: if(a > b) { a = 0; } if(a == b) { a++; } else {

b--; } while( a >= b) { a = a + b; } for(i=0; i<=10; i++) { a = a*2; } bien se pueden escribir de la siguiente forma: if(a > b) a = 0; if(a == b) a++; else b--; while( a >= b) a = a + b; for(i=0; i<=10; i++) a = a*2;

LOS OPERADORES
Sirven para realizar operaciones aritmticas, lgicas, comparativas, etc. Segn esa funcin se clasifican en los siguientes grupos. Operadores aritmticos Adems de los tpicos operadores de suma, resta, multiplicacin y divisin, estn los operadores de mdulo, incremento y decremento.

Tabla de Operadores aritmticos Operador + * / % ++ -Ejemplos: int a, b, c; a b b a c b = = = = = = b + c; b * c; a / c; a + c b; (a + b) / c; a + b / c + b * // Declarar variables a, b y c // // // // // b; Sumar a y b. Almacenar resultado en c Multiplicar b por c. Resultado en b Dividir a entre c. Colocar resultado en b Sumar a y c y restarle b. Resultado en a Dividir a+b entre c. Resultado en c // Sumar a ms b/c ms bb. Resultado en b

Accin Suma Resta Multiplicacin Divisin Mdulo. Retorna el residuo de una divisin entera. Solo se debe usar con nmeros enteros. Incrementar en uno Decrementar en uno

c = a % b; a++; b--; ++c; --b;

// // // // //

Residuo de dividir ab a c Incrementar a en 1 Decrementar b en 1 Incrementar c en 1 Decrementar b en 1

Te recordaron a tus clases de lgebra del colegio? A diferencia de esas matemticas, estas expresiones no son ecuaciones; significan las operaciones que indican sus comentarios. Por lo visto, los operadores ++ y -- funcionan igual si estn antes o despus de una variable en una expresin simple. Sin embargo, hay una forma (tal vez innecesaria y confusa para un novato, pero muy atractiva para los que ya estamos acostumbrados a su uso) que permite escribir cdigo ms compacto, es decir, escribir dos sentencias en una. Si ++ o -- estn antes del operando, primero se suma o resta 1 al operando y luego se evala la expresin. Si ++ o -- estn despus del operando, primero se evala la expresin y luego se suma o resta 1 al operando. int a, b; a = b++; a = ++b; if (a++ < 10) { // algn cdigo } if (++a < 10) { // algn cdigo } Operadores de bits Se aplican a operaciones lgicas con variables a nivel binario. Aqu tenemos las clsicas operaciones AND, OR inclusiva, OR exclusiva y la NEGACIN. Adicionalmente, he incluido en esta categora las operaciones de desplazamiento a la derecha y la izquierda. Si bien son operaciones que producen resultados anlogos a los de las instrucciones de ensamblador iorlw y iorwf para la OR inclusiva, xorlw y xorwf para la OR exclusiva, // Declarar variables enteras a y b // Lo mismo que a = b; y luego b = b + 1; // Lo mismo que b = b + 1; y luego a = b; // Primero comprueba si a < 10 y luego // incrementa a en 1

// Primero incrementa a en 1 y luego // comprueba si a < 10

andlw y andwf para la AND y comf para la negacin; los operadores lgicos del C pueden
operar sobre variables de distintos tamaos, ya sean de 1, 8, 16 32 bits.

Tabla de operadores de bits


Operador & | ^ ~ << >> Ejemplos: char m; // variable de 8 bits Accin AND a nivel de bits OR inclusiva a nivel de bits OR exclusiva a nivel de bits Complemento a uno a nivel de bits Desplazamiento a la izquierda Desplazamiento a la derecha

int n; m m m m n n m m m = = = = = = = = = 0x48; m & 0x0F; m | 0x24; m & 0b11110000; 0xFF00; ~n; m | 0b10000001; m & 0xF0; m ^ 0b00110000;

// variable de 16 bits // // // // // // // // // m ser 0x48 Despus de esto m ser 0x08 Despus de esto m ser 0x2F Despus de esto m ser 0x20 n ser 0xFF00 n ser 0x00FF Setear bits 0 y 7 de variable m Limpiar nibble bajo de variable m Invertir bits 4 y 5 de variable m

m = 0b00011000; m = m >> 2; n = 0xFF1F; n = n << 12; m = m << 8;

// Cargar m con 0b00011000 // Desplazar m 2 posiciones a la derecha // Ahora m ser 0b00000110 // Desplazar n 12 posiciones a la izquierda // Ahora n ser 0xF000; // Despus de esto m ser 0x00

Fjate en la semejanza entre las operaciones de desplazamiento con >> y << y las operaciones del rotacin del ensamblador. La diferencia es que cuando una variable se desplaza hacia un lado, los bits que salen por all se pierden y los bits que entran por el otro lado son siempre ceros. Es por esto que en la ltima sentencia, m = m << 8, el resultado es 0x00. Por cierto, en el lenguaje C no existen operadores de rotacin. Hay formas alternativas de realizarlas.

Desplazamientos producidos por los operadores << y >>. Operadores relacionales Se emplean para construir las condiciones lgicas de las sentencias de control selectivas e iterativas, como ya hemos podido apreciar en las secciones anteriores. La siguiente tabla muestra los operadores relacionales disponibles. Operadores lgicos Generalmente se utilizan para enlazar dos o ms condiciones lgicas simples. Por suerte, estos operadores solo son tres y sern explicados en las prcticas del curso. Tabla de Operadores relacionales Operador
== != > < >= <=

Tabla de Operadores lgicos Operador Accin


AND lgica OR lgica Negacin lgica

Accin
Igual && No igual || Mayor que ! Menor que Mayor o igual que Menor o igual que

Ejemplos: if( !(a==0) ) { // sentencias } if( (a<b) && (a>c) ) { // sentencias } // Si a igual 0 sea falso

// Si a<b y a>c son verdaderas

while( (a==0) || (b==0) ) { // sentencias } Composicin de operadores

// Mientras a sea 0 b sea 0

Se utiliza en las operaciones de asignacin y nos permite escribir cdigo ms abreviado. La forma general de escribir una sentencia de asignacin mediante los operadores compuestos es: obtect op= expression; que es equivalente a la sentencia object = object op expression;

op puede ser cualquiera de los operadores aritmticos o de bit estudiados arriba. O sea, op
puede ser +, - , *, /, %, &, |, ^, ~,<< >>. Ver Tabla siguiente. Nota: no debe haber ningn espacio entre el operador y el signo igual. Operador Descripcin Asignacin de suma Asignacin de resta Asignacin de multiplicacin Asignacin de divisin Asignacin de resto de divisin Asignacin de desplazamiento a la izquierda Asignacin de desplazamiento a la derecha Asignacin de AND de bits Asignacin de OR de bits Asignacin de OR exclusivo de bits Asignacin de negacin de bits

+= -= *= /= %= <<= >>= &= |= ^^= ~=


Ejemplos: int a; a += 50; a += 20; a *= 2; a &= 0xF0; a <<= 1; Precedencia de operadores

// // // // // //

Declarar a Es lo mismo que a Tambin significa Es lo mismo que a Es lo mismo que a Es lo mismo que a

= a + 50; sumarle 20 a a = a * 2; = a & 0xF0; = a << 1;

Una expresin puede contener varios operadores, de esta forma: b = a * b + c / b; // a, b y c son variables

A diferencia del lenguaje Basic, donde la expresin se evala de izquierda a derecha, en esta sentencia no queda claro en qu orden se ejecutarn las operaciones indicadas. Hay ciertas reglas que establecen dichas prioridades; por ejemplo, las multiplicaciones y divisiones siempre se ejecutan antes que las sumas y restas. Pero es ms prctico emplear los parntesis, los cuales ordenan que primero se ejecuten las operaciones de los parntesis ms internos. Eso es como en el lgebra elemental de la escuela, as que no profundizar. Por ejemplo, las tres siguientes sentencias son diferentes. b = (a * b) + (c / b); b = a * (b + (c / b)); b = ((a * b) + c)/ b); Tambin se pueden construir expresiones condicionales, as: if ( (a > b) && ( b < c) ) { // ... } // Si a>b y b<c, ...

LAS FUNCIONES
Una funcin es un bloque de sentencias identificado por un nombre y puede recibir y devolver datos. En bajo nivel, en general, las funciones operan como las subrutinas de assembler, es decir, al ser llamadas, se guarda en la Pila el valor actual del PC (Program Counter), despus se ejecuta todo el cdigo de la funcin y finalmente se recobra el PC para regresar de la funcin. Dada su relativa complejidad, no es tan simple armar una plantilla general que represente a todas las funciones. El siguiente esquema es una buena aproximacin. data_type1 function_name (data_type2 arg1, data_type3 arg2, ... ) { // Cuerpo de la funcin // ... return SomeData; // Necesario solo si la funcin retorna algn valor } Donde: function_name es el nombre de la funcin. Puede ser un identificador cualquiera. data_type1 es un tipo de dato que identifica el parmetro de salida. Si no lo hubiera, se debe poner la palabra reservada void (vaco, en ingls). arg1 y arg2 (y puede haber ms) son las variables de tipos data_type1, data_type2..., respectivamente, que recibirn los datos que se le pasen a la funcin. Si no hay ningn parmetro de entrada, se pueden dejar los parntesis vacos o escribir un void entre ellos. Funciones sin parmetros Para una funcin que no recibe ni devuelve ningn valor, la plantilla de arriba se reduce al siguiente esquema: void function_name ( void ) { // Cuerpo de la funcin } Y se llama escribiendo su nombre seguido de parntesis vacos, as:

function_name(); La funcin principal main es otro ejemplo de funcin sin parmetros. Dondequiera que se ubique, siempre debera ser la primera en ejecutarse; de hecho, no debera terminar. void main (void) { // Cuerpo de la funcin } Funciones con parmetros (por valor) Por el momento, solo estudiaremos las funciones que pueden tener varios parmetros de entrada pero solo uno de salida. Si la funcin no tiene parmetros de entrada o de salida, debe escribirse un void en su lugar. El valor devuelto por una funcin se indica con la palabra reservada return. Segn el comportamiento de los parmetros de entrada de la funcin, estos se dividen en parmetros por valor y parmetros por referencia. Lo expuesto en este apartado corresponde al primer grupo porque es el caso ms ampliamente usado. Con esto en mente podemos seguir. Para llamar a una funcin con parmetros es importante respetar el orden y el tipo de los parmetros que ella recibe. El primer valor pasado corresponde al primer parmetro de entrada; el segundo valor, al segundo parmetro; y as sucesivamente si hubiera ms. Cuando una variable es entregada a una funcin, en realidad se le entrega una copia suya. De este modo, el valor de la variable original no ser alterado. Mejor, plasmemos todo esto en el siguiente ejemplo. int minor ( int arg1, int arg2, int arg3 ) { int min; // Declarar variable min min = arg1; // Asumir que el menor es arg1 if ( arg2 < min ) min = arg2; if ( arg3 < min ) min = arg3; return min; } void main (void) { int a, b, c, d; // Si arg2 es menor que min // Cambiar a arg2 // Si arg3 es menor que min // Cambiar a arg3 // Retornar valor de min

// Declarar variables a, b, c y d

/* Aqu asignamos algunos valores iniciales a 'a', 'b' y 'c' */ /* ... */ d = minor(a,b,c); // Llamar a minor // En este punto 'd' debera ser el menor entre 'a', 'b' y 'c' while (1); // Bucle infinito } En el programa mostrado la funcin minor recibe tres parmetros de tipo int y devuelve uno, tambin de tipo int, que ser el menor de los nmeros recibidos. El mecanismo funciona as: siempre respetando el orden, al llamar a minor el valor de a se copiar a la variable arg1; el valor de b, a arg2 y el valor de c, a arg3. Despus de ejecutarse el cdigo de la funcin, el valor de retorno (min en este caso) ser copiado a una variable temporal y de all pasar a d.

Aunque el C no es tan implacable con la comprobacin de tipos de datos como Pascal, siempre deberamos revisar que los datos pasados sean compatibles con los que la funcin espera, as como los datos recibidos, con los que la funcin devuelve. Por ejemplo, estara mal llamar a la funcin minor del siguiente modo: d = minor(-15, 100, 5.124); // Llamar a minor Aqu los dos primeros parmetros estn bien, pero el tercero es un nmero decimal (de 24 32 bits), no compatible con el tercer parmetro que la funcin espera (entero de 8 16 bits). En estos casos el compilador nos mostrar mensajes de error, o cuando menos de advertencia. Parmetros por referencia La funcin que recibe un parmetro por referencia puede cambiar el valor de la variable pasada. La forma clsica de estos parmetros se puede identificar por el uso del smbolo &, tal como se ve en el siguiente boceto de funcin. int minor ( int & arg1, int & arg2, int & arg3 ) { // Cuerpo de la funcin. // arg1, arg2 y arg3 son parmetros por referencia. // Cualquier cambio hecho a ellos desde aqu afectar a las variables // que fueron entregadas a esta funcin al ser llamada. } No voy profundizar al respecto porque he visto que muchos compiladores C no soportan esta forma. Otra forma de pasar un parmetro por referencia es mediante los punteros, pero eso lo dejamos para el final porque no es nada nada fcil para un novato. Prototipos de funciones El prototipo de una funcin le informa al compilador las caractersticas que tiene, como su tipo de retorno, el nmero de parmetros que espera recibir, el tipo y orden de dichos parmetros. Por eso se deben declarar al inicio del programa. El prototipo de una funcin es muy parecido a su encabezado, se pueden diferenciar tan solo por terminar en un punto y coma (;). Los nombres de las variables de entrada son opcionales. Por ejemplo, en el siguiente boceto de programa los prototipos de las funciones main, func1 y

func2 declaradas al inicio del archivo permitirn que dichas funciones sean accedidas desde
cualquier parte del programa. Adems, sin importar dnde se ubique la funcin main, ella siempre ser la primera en ejecutarse. Por eso su prototipo de funcin es opcional. #include <pic.h> void func1(char m, long p); // Prototipo de funcin "func1" char func2(int a); // Prototipo de funcin "func2" void main(void); // Prototipo de funcin "main". Es opcional void main(void) { // Cuerpo de la funcin // Desde aqu se puede acceder a func1 y func2 } void func1(char m, long p) { // Cuerpo de la funcin // Desde aqu se puede acceder a func2 y main } char func2(int a)

{ // Cuerpo de la funcin // Desde aqu se puede acceder a func1 y main } La llamada a main, por supuesto, no tiene sentido; solo lo pongo para ilustrar. Si las funciones no tienen prototipos, el acceso a ellas ser restringido. El compilador solo ver las funciones que estn implementadas encima de la funcin llamadora o, de lo contrario, mostrar errores de funcin no definida. El siguiente boceto ilustra este hecho. (Atiende a los comentarios.) #include <pic.h> void main(void) { // Cuerpo de la funcin // Desde aqu no se puede acceder a func1 ni func2 porque estn abajo } void func1(char m, long p) { // Cuerpo de la funcin // Desde aqu se puede acceder a main pero no a func2 } char func2(int a) { // Cuerpo de la funcin // Desde aqu se puede acceder a func1 y main } Para terminar, dado que los nombres de las variables en los parmetros de entrada son opcionales, los prototipos de func1 y func2 tambin se pueden escribir as void func1(char, long); char func2(int );

Variables locales y variables globales


Los lenguajes de alto nivel como el C fueron diseados para desarrollar los programas ms grandes y complejos que se puedan imaginar, programas donde puede haber cientos de variables, entre otras cosas. Imaginas lo que significara buscar nombres para cada variable si todos tuvieran que ser diferentes? Pues bien, para simplificar las cosas, el C permite tener varias variables con el mismo nombre. As es. Esto es posible gracias a que cada variable tiene un mbito, un rea desde donde ser accesible. Hay diversos tipos de mbito, pero empezaremos por familiarizarnos con los dos ms usados, que corresponden a las variables globales y variables locales. Las variables declaradas fuera de todas las funciones y antes de sus implementaciones tienen carcter global y podrn ser accedidas desde todas las funciones. Las variables declaradas dentro de una funcin, incluyendo las variables del encabezado, tienen mbito local. Ellas solo podrn ser accedidas desde el cuerpo de dicha funcin. De este modo, puede haber dos o ms variables con el mismo nombre, siempre y cuando estn en diferentes funciones. Cada variable pertenece a su funcin y no tiene nada que ver con las variables de otra funcin, por ms que tengan el mismo nombre. En la mayora de los compiladores C para PICs las variables locales deben declararse al principio de la funcin.

Por ejemplo, en el siguiente boceto de programa hay dos variables globales (speed y limit) y cuatro variables locales, tres de las cuales se llaman count. Atiende a los comentarios. char foo(long ); // Prototipo de funcin

int speed; // Variable global const long limit = 100; // Variable global constante void inter(void) { int count; // Variable local /* Este count no tiene nada que ver con el count de las funciones main o foo */ speed++; vari = 0; // Acceso a variable global speed // Esto dar ERROR porque vari solo pertenece // a la funcin foo. No compilar.

} void main(void) { int count; // Variable /* Este count no tiene nada que de las funciones inter o foo count = 0; // Acceso a speed = 0; // Acceso a } char foo(long count) // Variable { int vari; // Variable }

local count ver con el count */ count local variable global speed local count local vari

Algo muy importante: a diferencia de las variables globales, las variables locales tienen almacenamiento temporal, es decir, se crean al ejecutarse la funcin y se destruyen al salir de ella. Qu significa eso? Lo explico en el siguiente apartado. Si dentro de una funcin hay una variable local con el mismo nombre que una variable global, la precedencia en dicha funcin la tiene la variable local. Si te confunde, no uses variables globales y locales con el mismo nombre. Variables static Antes de nada debemos aclarar que una variable static local tiene diferente significado que una variable static global. Ahora vamos a enfocarnos al primer caso por ser el ms comn. Cuando se llama a una funcin sus variables locales se crearn en ese momento y cuando se salga de la funcin se destruirn. Se entiende por destruir al hecho de que la locacin de memoria que tena una variable ser luego utilizada por el compilador para otra variable local (as se economiza la memoria). Como consecuencia, el valor de las variables locales no ser el mismo entre llamadas de funcin. Por ejemplo, revisa la siguiente funcin, donde a es una variable local ordinaria. void increm() { int a; a++; }

// Declarar variable a // Incrementar a

Cualquiera que haya sido su valor inicial, crees que despus de llamar a esta funcin 10 veces, el valor de a se habr incrementado en 10?... Pues, no necesariamente. Cada vez que se

llame a increm se crea a, luego se incrementa y, al terminar de ejecutarse la funcin, se destruye. Para que una variable tenga una locacin de memoria independiente y su valor no cambie entre llamadas de funcin tenemos dos caminos: o la declaramos como global, o la declaramos como local esttica. Los buenos programadores siempre eligen el segundo. Una variable se hace esttica anteponiendo a su declaracin el especificador static. Por defecto las variables estticas se autoinicializan a 0, pero se le puede dar otro valor en la misma declaracin (dicha inicializacin solo se ejecuta la primera vez que se llama a la funcin), as: static int var1; static int var2 = 50; Ejemplos. void increm() { static int a = 5; // Variable local esttica inicializada a 5 a++; // Incrementar a } void main() { int i; // Declarar variable i // El siguiente cdigo llama 10 veces a increm for(i=0; i<10; i++) increm(); // Ahora la variable a s debera valer 15 while(1); // Bucle infinito } Variables volatile A diferencia de los ensambladores, los compiladores tienen cierta inteligencia. Es decir, piensan un poco antes de traducir el cdigo fuente en cdigo ejecutable. Por ejemplo, veamos el siguiente pedazo de cdigo para saber lo que suele pasar con una variable ordinaria: int var; //... var = var; // Declarar variable var // Asignar var a var // Variable static (inicializada a 0 por defecto) // Variable static inicializada a 50

El compilador creer (probablemente como nosotros) que la sentencia var = var no tiene sentido (y quiz tenga razn) y no la tendr en cuenta, la ignorar. sta es solo una muestra de lo que significa optimizacin del cdigo. Luego descubrirs ms formas de ese trabajo. El ejemplo anterior fue algo burdo, pero habr cdigos con redundancias aparentes y ms difciles de localizar, cuya optimizacin puede ser contraproducente. El caso ms notable que destacan los manuales de los compiladores C para microcontroladores es el de las variables globales que son accedidas por la funcin de interrupcin y por cualquier otra funcin. Para que un compilador no intente pasarse de listo con una variable debemos declararla como volatile, anteponindole dicho calificador a su declaracin habitual. Por ejemplo, en el siguiente boceto de programa la variable count debe ser accedida desde la funcin interrupt como desde la funcin main; por eso se le declara como volatile. Nota: el esquema de las funciones de interrupcin suele variar de un compilador a otro. ste es solo un ejemplo.

volatile int count;

// count es variable global voltil

void interrupt(void) // Funcin de interrupcin { // Cdigo que accede a count } void main(void) // Funcin principal { // Cdigo que accede a count }

ARRAYS Y PUNTEROS
Probablemente ste sea el tema que a todos nos ha dado ms de un dolor de cabeza y que ms hemos reledo para captarlo a cabalidad. Hablo ms bien de los punteros. Si ellos el C no sera nada, perdera la potencia por la que las mejores empresas lo eligen para crear sus softwares de computadoras. Pero bueno, regresando a lo nuestro, estos temas se pueden complicar muchsimo ms de lo que veremos aqu. Solo veremos los arrays unidimensionales y los punteros (que en principio pueden apuntar a todo tipo de cosas) los abocaremos a los datos bsicos, incluyendo los mismos arrays. Aun as, te sugiero que tengas un par de aspirinas al lado. Los arrays o matrices Un array es una mega variable compuesto de un conjunto de variables simples del mismo tipo y ubicadas en posiciones contiguas de la memoria. Con los arrays podemos hacer todos lo que hacamos con las tablas (de bsqueda) del ensamblador y muchsimo ms. Un array completo tiene un nombre y para acceder a cada uno de sus elementos se utilizan ndices entre corchetes ([ ]). Los ndices pueden estar indicados por variables o constantes. En el siguiente esquema se ve que el primer elemento de un array tiene ndice 0 y el ltimo,

N-1, siendo N la cantidad de elementos del array.

Estructura de un array unidimensional de N elementos.


Declaracin de arrays Para declarar un array unidimensional se utiliza la siguiente sintaxis: data_type identifier[ NumElementos ]; Donde data_type es un tipo de dato cualquiera, identifier es el nombre del array y Nu-

mElementos es la cantidad de elementos que tendr (debe ser un valor constante).


De este modo, el ndice del primer elemento es 0 y el del ltimo es NumElements - 1. Por ejemplo, las siguientes lneas declaran tres arrays. char letters10]; long HexTable[16]; int address[100]; // letters es un array de 10 elementos de tipo char // HexTable es un array de 16 elementos de tipo long // address es un array de 100 elementos de tipo int

Para el array letters el primer elemento es letters[0] y el ltimo, letters[9]. As, tenemos 10 elementos en total. Si quisiramos asignar a cada uno de los elementos de let-

ters los caracteres desde la a hasta la j, lo podramos hacer individualmente as:


letters[0] letters[1] letters[2] letters[3] letters[4] letters[5] letters[6] letters[7] letters[8] letters[9] = = = = = = = = = = 'a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; // Aqu el ndice es 0 // Aqu el ndice es 1 // ... //

// Aqu el ndice es 9

Pero as no tiene gracia utilizar arrays, verdad? En este caso lo mejor es utilizar un bucle, as: (Nota: los caracteres son, al fin y al cabo, nmeros en cdigos ascii y se les puede comparar.) char c; for ( c = 'a'; c <= 'j'; c++ ) letters[i] = c; Inicializacin de arrays Los elementos de un array se pueden inicializar junto con su declaracin. Para ello se le asigna una lista ordenada de valores encerrados por llaves y separados por comas. Por supuesto, los valores deben ser compatibles con el tipo de dato del array. Este tipo de inicializacin solo est permitido en la declaracin del array. Ejemplos: unsigned char mask[3] = { 0xF0, 0x0F, 0x3C }; // Ok int a[5] = { 20, 56, 87, -58, 5000 }; // Ok char vocals[5] = { 'a', 'e', 'i', 'o', 'u' }; // Ok int c[4] = { 5, 6, 0, -5, 0, 4 }; // Error, demasiados inicializadores Tambin es posible inicializar un array sin especificar en su declaracin el tamao que tendr, dejando los corchetes vacos. El tamao ser precalculado y puesto por el compilador. sta es una forma bastante usada en los arrays de texto, donde puede resultar muy incmodo estar contando las letras de una cadena. Por ejemplo: int a[] = { 70, 1, 51 }; // Un array de 3 elementos char vocals[] = { 'a', 'e', 'i', 'o', 'u' }; // Un array de 5 elementos char msg[] = "Este es un array de caracteres"; // Un array of 31 elementos Por qu el ltimo array tiene 31 elementos si solo se ven 30 letras? Lo sabremos luego. Cadenas de texto terminadas en nulo Son arrays de tipo de dato char. Hay dos caractersticas que distinguen a estas cadenas de los dems arrays. Primero: su inicializacin se hace empleando comillas dobles y segundo, el ltimo trmino del array es un carcter NULL (simplemente un 0x00). De ah su nombre. Ejemplos: char Greet[10] = "Hello"; char msg[] = "Hello"; // Un array de 10 elementos // Un array de 6 elementos

El array Greet tiene espacio para 10 elementos, de los cuales solo los 5 primeros han sido llenados con las letras de Hello, el resto se rellena con ceros. El array msg tiene 6 elementos porque adems de las 5 letras de Hello se le ha aadido un Null (0x00) al final (claro que no se nota). Es decir, la inicializacin de msg es equivalente a: char msg[] = { 'H', 'e', 'l', 'l', 'o', 0x00}; Visto grficamente, msg tendra la siguiente representacin: // Un array de 6 elementos

Estructura de una cadena de texto.


Los punteros Los punteros suelen ser el tema que ms cuesta entender en programacin. Pero si ya llegaste aqu, es el momento menos indicado para detenerte. Los punteros son un tipo de variables muy especial. Son variables que almacenan las direcciones fsicas de otras variables. Si tenemos la direccin de una variable, tenemos acceso a esa variable de manera indirecta y podemos hacer con ellas todo lo que queramos ;). Declaracin de punteros Los punteros pueden apuntar a todo tipo de variables, pero no a todas al mismo tiempo. La declaracin de un puntero es un tanto peculiar. En realidad, se parece a la declaracin de una variable ordinaria solo que se pone un asterisco de por medio. En este punto debes recordar las declaraciones de todo tipo de variables que hemos visto, incluyendo las influenciadas por los calificadores const, static, etc. Todas excepto los arrays; por qu? La forma general de declarar un puntero es la siguiente: data_type * PointerName; Los siguientes ejemplos muestran lo fcil que es familiarizarse con la declaracin de los punteros: int * ip; char * ucp; unsigned char * ucp; const long * clp; float * p1, *p2; Apuntando a variables Decimos que una variable puntero apunta a una variable x si contiene la direccin de dicha variable. Para ello se utiliza el operador &, el cual extrae la direccin de la variable a la que acompaa. Un puntero siempre debera apuntar a una variable cuyo tipo coincida con el tipo del puntero. En los siguientes ejemplos vemos cmo apuntar a variables de tipo bsico, como int, char o // // // // // ip es un puntero a variable de tipo int cp es un puntero a variable de tipo char Puntero a variable de tipo unsigned char Puntero a constante de tipo long Declara dos punteros a variable de tipo float

float. Ms adelante veremos cmo apuntar a arrays.

void main (void) { int height, width; char a, b, c; float max; int * ip; char * cp; float * fp; ip = &height; ip = &width; cp = &a; cp = &c; cp = &a; fp = &max; fp = &height; // ip es un puntero a variable tipo int // cp es un puntero a variable tipo char // Puntero a variable tipo float // Con esto ip tendr la direccin de height // Ahora ip apunta a width // cp apunta a a // Ahora cp apunta a c // Ahora cp apunta a a otra vez // fp apunta a max // Error! height no es una variable float

//... } Asignaciones indirectas mediante punteros Una vez que un puntero apunte a una variable cualquiera, se puede acceder a dicha variable utilizando el nombre del puntero precedido por un asterisco, de esta forma: void main (void) { int height, width, n; int * p, * q; p = &height; *p = 10; p = &width; *p = 50; height = *p; q = &height; n = (*p + *q)/2; //... } La expresin *p se debera leer: la variable apuntada por p. Eso tambin ayuda mucho a comprender a los punteros. Y para esto se inventaron los punteros? Yo me preguntaba lo mismo en mis inicios. El tema de los punteros se puede complicar casi hasta el infinito, por eso quiero ir con cuidado y poco a poco para que nadie se pierda. Punteros y arrays Cmo se declara un puntero a un array? Un puntero a un array es simplemente un puntero al tipo de dato del array. Cuando se asigna un puntero a un array, en realidad el puntero toma la direccin de su primer elemento, a menos que se especifique otro elemento. Luego, bastara con modificar el valor del puntero para que apunte a los otros elementos del

// Variables ordinarias // p y q son punteros a variables de tipo int

// p apunta a height // Esto es como height = 10 // p apunta a width // Esto es como width = 50 // Esto es como height = width // q apunta a height // Esto es como n = (height + width)/2

array. Todo lo indicado se refleja en el siguiente cdigo:


void main (void) {

int * p; // Declara p como puntero a int int n; // Alguna variable int mat[3] = { 78, 98, 26 }; // Array de variables int p = &mat; n = *p; p++; n = *p; p++; n = *p; *p = 10; p--; *p = 100; p = mat; p = NULL; // ... } En el fondo los arrays y los punteros trabajan de la misma forma, por lo menos cuando referencian a variables almacenadas en la RAM del microcontrolador. La nica diferencia es que los // p apunta a mat (a su primer elemento) // // // // // Esto da n = Incrementar Esto da n = Incrementar Esto da n = 78 p para apuntar a siguiente elemento 98 p para apuntar a siguiente elemento 26

// Con esto mat[3] valdr 10 // Decrementar p para apuntar a elemento anterior // Con esto mat[2] valdr 100 // p apunta a mat. Es lo mismo que p = &mat // Desasignar p. Lo mismo que p = 0x0000

arrays no pueden direccionar a datos diferentes de su contenido; por eso tambin se les llama punteros estticos. En la prctica esto significa que un array es siempre compatible con un puntero, pero un puntero no siempre es compatible con un array. Por ejemplo, a un array no se le puede asignar otro array ni se le pueden sumar o restar valores para que apunten a otros elementos. Por lo dems, las operaciones de asignacin son similares para punteros y arrays, tal como se puede apreciar en el siguiente cdigo. (Por si las moscas, str1 es el array y str2, el puntero.) void main(void) { char str1[] = { 'A', 'r', 'r', 'a', 'y' }; char * str2 = { 'P', 'o', 'i', 'n', 't', 'e', 'r' }; char a; a = str1[0]; a = str1[3]; a = str2[0]; a = str2[3]; str1 += 2; str2 += 2; str1++; str2++; a = *str2; // } Paso de punteros y arrays a funciones Recuerdas el paso de variables por valor y por referencia? Pues aqu vamos de nuevo. ... // Esto da a = 'A' // Esto da a = 'a' // Esto da a = 'P' // Esto da a = 'n' // Error! Str1 es esttico // Correcto. Ahora str2 apunta a 'i' // Error otra vez! Str1 es esttico // Correcto. Ahora str2 apunta a 'n' // Esto da a = 'n'

Bien, recordemos: una variable pasada por valor a una funcin, en realidad le entrega una copia suya; por lo que la variable original no tiene por qu ser afectada por el cdigo de la funcin. Ahora bien, pasar una variable por referencia significa que se pasa la direccin de dicha variable. Como consecuencia, la funcin tendr acceso a la variable original y podr modificar su contenido. Esto podra resultar riesgoso, pero, bien usada, la tcnica es una potente arma. Ya que los punteros operan con direcciones de variables, son el medio ideal para trabajar con parmetros por referencia. Hay dos casos de particular inters: uno, cuando deseamos en serio que la variable pasada a la funcin cambie a su regreso; y dos, cuando la variable pasada es demasiado grande (un array) como para trabajar con copias. De hecho, los arrays siempre se pasan por referencia ya que tambin son punteros al fin. La sintaxis de los punteros en el encabezado de la funcin no es nada nuevo, teniendo en cuenta que tambin tienen la forma de declaraciones de variables. En el siguiente ejemplo la funcin interchage intercambia los valores de las dos variables recibidas. En seguida explicar por qu vara un poco la forma en que se llama a la funcin. void interchange( int * p1, int * p2 ) { int tmp = *p1; // Guardar valor inicial de variable apuntada por p1. *p1 = *p2; // Pasar valor de variable apuntada por p2 a la // variable apuntada por p1. *p2 = tmp; // Variable apuntada por p2 valdr tmp. } void main (void) { int i, j; /* Hacer algunas asignaciones */ i = 10; j = 15; /* Llamar a funcin interchange pasando las direcciones de i y j */ interchange( &i, &j ); // En este punto i vale 15 y j vale 10 // ... } Al llamar a interchange le entregamos &i y &j, es decir, las direcciones de i y j. Por otro lado, la funcin interchange recibir dichos valores en p1 y p2, respectivamente. De ese modo, p1 y p2 estarn apuntando a i y j, y podremos modificar sus valores. Ten presente que se mantiene la forma de asignacin puntero = &variable (puntero igual a direccin de variable). Ahora veamos ejemplos donde la forma de asignacin cambia a puntero = puntero. Esto incluye a los arrays porque, recordemos, un puntero siempre puede ser tratado como un

array, aunque lo contrario no siempre es posible.


En el siguiente programa array1 y array2 se pasan a la funcin prom, la cual devuelve el valor promedio de los elementos del array recibido. Como para ese clculo se necesita conocer la cantidad de elementos que tiene el array, prom recibe dicho valor en el parmetro si-

ze.
float prom ( int * p, int size ) { int i; float tmp = 0; for ( i=0; i<size; i++ ) // Bucle para contar i desde 0 hasta size-1.

tmp += p[i]; return ( tmp/size ); }

// Sumar elemento p[i] a tmp. // Retornar valor promediado.

void main (void) { int array1[4] = { 51, 14, 36, 78 }; // Un array de 4 elementos int array2[] = { -85, 4, 66, 47, -7, 85 }; // Un array de 6 elementos float avrg; // Una variable tipo float, para decimales avrg = prom (array1, 8); // Ahora avrg debera valer (51 + 14 + 36 + 78 )/8 = 44.75 avrg = prom (array2, 6); // Ahora avrg debera valer (-85 + 4 + 66 + 47 - 7 + 85 )/6 = 18.3333 while( 1 ); } Finalmente, veamos un programa donde se utilizan las Cadenas de texto terminadas en nulo. Este programa tiene dos funciones auxiliares: mayus convierte la cadena recibida en maysculas, y lon calcula la longitud del texto almacenado en el array recibido. Ambas funciones reciben el array pasado en un puntero p dado que son compatibles. void mayus( char * p ) { while( *p ) // Mientras carcter apuntado sea diferente de 0x00 { if( ( *p >= 'a' ) && ( *p <= 'z' ) ) // Si carcter apuntado es // minscula *p = *p - 32; // Hacerlo mayscula p++; // Incrementar p para apuntar sig. carcter } } int lon( char * p) { int i = 0; // Declarar variable i e iniciarla a 0. while( *p ) // Mientras carcter apuntado sea diferente de 0x00 { i++; // Incrementar contador. p++; // Incrementar p para apuntar sig. carcter } return i; // Retornar i } void main (void) { int L; char song1[20] = "Dark Blue"; char song2[20] = "Staring Problem"; char song3[20] = "Ex-Girlfriend"; /* Obtener longitudes de los arrays de L = lon(song1); // Debera dar L = L = lon(song2); // Debera dar L = L = lon(song3); // Debera dar L = /* Convertir cadenas en maysculas */ mayus(song1 ); // Es lo mismo que mayus(&song1); // Ahora song1 debera valer "DARK BLUE" mayus(song2 ); // Es lo mismo que mayus(&song2); texto */ 9 15 13 // Bucle infinito

// Ahora song2 debera valer "STARING PROBLEM" mayus(song3 ); // Es lo mismo que mayus(&song3); // Ahora song3 debera valer "EX-GIRLFRIEND" while(1); } En el programa se crean tres arrays de texto de 20 elementos (song1, song2 y song3), pero el texto almacenado en ellos termina en un carcter 0x00. Segn la tabla de caracteres ascii, las letras maysculas estn ubicadas 32 posiciones por debajo de las minsculas. Por eso basta con sumarle o restarle ese valor a un carcter // Bucle infinito

ascci para pasarlo a mayscula o minscula.


En ambas funciones el puntero p navega por los elementos del array apuntado hasta que encuentra el final, indicado por un carcter nulo (0x00). Arrays constantes No es que me haya atrasado con el tema, es solo que los arrays constantes son uno de los temas cuyo tratamiento vara mucho entre los distintos compiladores. Veamos en qu. Un array constante es uno cuyos elementos solo podrn ser ledos pero no escritos; tan simples como eso. En principio, para que un array sea constante a su clsica declaracin con inicializacin de un array se le debe anteponer el calificador const. Por ejemplo: const int a[5] = { 20, 56, 87, -58, 5000 }; // Array constante const char vocals[5] = { 'a', 'e', 'i', 'o', 'u' }; // Array constante const char text[] = "Este es un array constante de caracteres"; De este modo, los arrays a, vocals y text sern de solo lectura, y sus elementos podrn ser ledos, mas no escritos. Es como si estuviramos frente a una tabla hecha en ensamblador (de PICs) a base de instrucciones retlw. De hecho, los compiladores Mikro C, CCS C, Hi-

tech C e AVR IAR C, construirn internamente tablas semejantes para representar estos arrays.
Si estos arrays constantes van a ser ledos directamente y mientras se utilice la notacin de los corchetes para especificar a cada elemento, todo estar ok. Por otro lado, cada compilador trabaja diferente el paso de arrays constantes a funciones y su compatibilidad con los punteros. Por ejemplo, Hi-tech C soporta magistralmente estos temas. Por otro lado, CCS C no acepta nada, pero ofrece excelentes mtodos alternativos para realizar esas tareas. De modo que ser necesario revisar el manual de cada compilador en particular. En otros compiladores, como MPLAB C18 o BoostC, debe adems aadirse el calificador rom (no perteneciente al C estndar). BoostC solo soporta arrays constantes con datos de 8 bits (de tipo char y compatibles) y su peculiar forma de declarlas es la siguiente. rom char * a = "cadena de texto constante en BoostC"; rom char * mat = { 20, 56, 87, -58, 50 }; // Array constante rom unsigned char * dec = { 10, 20, 30, 40, 50 }; // Array constante

Por lo dems, el acceso a los elementos de estos arrays tiene que seguir siendo mediante ndices y corchetes. En el compilador MPLAB C18, la palabra rom va junto al const. Por supuesto que en los PIC18 los arrays en ROM se implementan con ms eficiencia sin recurrir a los limitados retlw. rom rom rom rom const const const const char * a = "cadena de texto constante en MPLAB C18"; char a[] = "otra cadena de texto constante en MPLAB C18"; int mat[5] = { 20, 56, 87, -58, 5000 }; // Array constante long pots[] = {10, 100, 1000, 10000, 100000}; // Array constante

http://www.cursomicros.com/avr/practicas-del-curso.html http://www.cursomicros.com/pic/vidtuts/proteus/tutorial_de_proteus.html http://www.todopic.com.ar/foros/index.php?action=printpage;topic=4533.0


CODIGO 1 ///////////////////////////////////////////////////////////////////////// //// PARPADEO //// //// //// //// Ejemplo de parpadeo de todos los leds colocados en el PORTB //// //// //// //// Realizado por Manuel Jimnez (nocturno) para el foro TODOPIC //// //// //// //// 4/8/2004 //// //// //// ///////////////////////////////////////////////////////////////////////// #include <16F84.h> #fuses HS,NOWDT,NOPROTECT #use delay(clock=4000000) #byte port_b=6 void main() { int contador; set_tris_b(0); port_b=0; while (true) { contador=200; // declaramos el puerto B como salidas // bucle infinito // retraso, 200 ciclos de 1000 microsegundos, // o sea, 2 milisegundos

while(--contador!=0) delay_us(1000); port_b=0xff; // activa todos los pins del puerto B contador=200; while(--contador!=0) // retraso, 200 ciclos de 1000 microsegundos, // o sea, 2 milisegundos delay_us(1000); port_b=0x00; // apaga todos los pins del puerto B }; } CODIGO 2
Empezamos algo bueno!! Otro ejemplillo con leds, esta vez las lucecitas del coche fantstico en el puerto b

///////////////////////////////// PREPROCESADO ///////////////////////////////// #include <16F84.h> #use delay(clock=4000000) #fuses XT,NOWDT,NOPUT,NOPROTECT #use fast_io(B) #byte PORTB = 0x06 /////////////////////////////////// PRINCIPAL ////////////////////////////////// void main() { set_tris_b(0b00000000); PORTB = 1; for(;;) { while(PORTB != 128) { PORTB = PORTB * 2;

// Puerto B al completo configurado como salida // Encendemos el primer led // Bucle infinito // Avanzamos en una direccin // Apagamos el led que est encendido y // encendemos el siguiente // Retardo de 200 ms

delay_ms(200); } while(PORTB != 1) // Avanzamos en la otra direccin { PORTB = PORTB / 2; // Apagamos el led encendido y encendemos el anterior delay_ms(200); } } } CODIGO 3
Lo del parpadeo un poco ms corto:

#include <16F84.h> #fuses XT,NOWDT,NOPROTECT #use delay(clock=4000000) #use fast_io(B) void main() { set_tris_b(0x00); while (1) { output_b(0xff); delay_ms(200); output_b(0x00); delay_ms(200); } }
NOTA. El programa se puede hacer tambin as: - Sin el fast_io(B) y sin el set_tris_b(0x00) y todo lo dems como est. - O se deja el fast_io(B) y se aade en el main el set_tris_b(0x00) Personalmente soy partidario de la segunda opcin. En todos los programas se definan entradas y salidas con los correspondientes set_tris, as vamos a lo seguro. Adems usando el fast_io se consiguen al compilar, menos instrucciones de programacin en asembler ya que cada vez que en el programa se encuentre con un input o un output no se reprograma el puerto, solo se programa una vez con el primer input u output, cosa que no ocurre sin el fast. Un saludo

CODIGO 4 ///////////////////////////////////////////////////////////////////////// //// PARPADEO v.2 //// //// //// //// Ejemplo de parpadeo de todos los leds colocados en el PORTB //// //// //// //// Realizado por Manuel Jimnez (nocturno) para el foro TODOPIC //// //// //// //// 4/8/2004 //// //// //// ///////////////////////////////////////////////////////////////////////// #include <16F84.h> #fuses XT,NOWDT,NOPROTECT #use delay(clock=4000000) #use fast_io(B) void main() { int i; while (true) { // // // for (i=1;i<=24;++i) { // output_b(0xF0); // delay_ms (i); // output_b(0x0F); // delay_ms ((25-i)); }; // // for (i=1;i<=24;++i) { // output_b(0x0F); // delay_ms (i); // output_b(0xF0); // delay_ms ((25-i)); }; bucle infinito en esta primera parte va encendiendo la mitad alta del puerto B y apagando la mitad baja va cambiando el retraso, de 1 a 24 ms encendemos la mitad del puerto B hacemos una parte de la espera encendemos la otra mitad del puerto B // realizamos la espera complementaria en esta segunda parte va encendiendo la mitad baja del puerto B y apagando la mitad alta va cambiando el retraso, de 1 a 24 ms encendemos la mitad del puerto B hacemos una parte de la espera encendemos la otra mitad del puerto B // realizamos la espera complementaria

}; } CODIGO 5 MASCARA
Que diferencia hay entre:

valor == (valor && 0x80) valor & = 0x80

La primera no me funciona, la segunda si. Ttulo: Microcursillo en C La segunda lo que hace es asignar a valor la operacin AND, a nivel de bits, de su contenido con el literal 0x80. La primera no vale para asignar al ser un doble igual (==). Al ser una comparacin, supongo que lo tendrs como condicin de control en una estructura de control (while, for, if...). Tal y como est escrita, no puede desempear ninguna otra funcin. Saludos Ttulo: Microcursillo en C Gracias Modulay por responder, lo que quiero hacer es una mascara para saber el estado del bit mas significativo de un puerto... leo el puerto y lo guardo en valor, luego con un "and lgico" pongo todo a cero menos el ultimo bit para saber si esta a 1 o a cero.

Ttulo: Microcursillo en C Pues el segundo mtodo es el que te va al pelo. El operador && trabaja a nivel lgico, pero no a nivel de bits. Un uso correcto seria...

BOOLEAN condicion; int numero; if (condicion1 && numero == 15) { bla bla bla... }
Aunque si quieres saber el estado de un bit de cualquiera de los puertos basta con hacer:

if ( input(PIN_B7) ) { // Esto se ejecuta si RB7 est a 1 }


Espero que te ayude Saludos Ttulo: Microcursillo en C Bueno, lo importante es dar ideas y que cada uno aporte dentro de sus posibilidades. Qu mas da quien est al frente mientras sigamos aprendiendo unos de otros. Aun as...gracias!!!! Un saludote

CODIGO 5
Hola a todos, estoy empezando a programar en C y la verdad no parece tan difcil como el asm. Aqu os dejo otro programita de led's usando funciones.

#include <16F84.h> #use delay(clock=4000000) #fuses XT,NOWDT,NOPROTECT #use standard_io(B) //////////////////////// FUNCIONES ////////////////////// void Parpadea_0(int Repeticiones) { int i; for(i=1;i<=Repeticiones;++i) { output_high(pin_B0); delay_ms(500); output_low(pin_B0); delay_ms(500); } } void Parpadea_1(int Repeticiones) { int i; for(i=1;i<=Repeticiones;++i) { output_high(pin_B1); delay_ms(500); output_low(pin_B1);

delay_ms(500); } } void Parpadea_2(int Repeticiones) { int i; for(i=1;i<=Repeticiones;++i) { output_high(pin_B2); delay_ms(500); output_low(pin_B2); delay_ms(500); } } void Parpadea_3(int Repeticiones) { int i; for(i=1;i<=Repeticiones;++i) { output_b(0x07); delay_ms(500); output_b(0x00); delay_ms(500); } } ///////////////////////////// void main() { set_tris_b(0x00); output_b(0x00); Parpadea_0(5); Parpadea_1(5); Parpadea_2(5); Parpadea_3(5); while(true); } CODIGO 6
Ttulo: Microcursillo en C Hola, que tal a todos. Algunos que me conocen en el foro, saben que programo en ASM, peor despus de ver todo lo que se poda hacer con la serie 18f, y despus de ver todo lo que se necesitaba en ASM para hacer funcionar esos pics, tome la decisin de comenzar a tocar el lenguaje C para pics, el compilador con el cual estoy comenzando mis primeros pasos es el CCS 3.249. Y como no poda ser de otra manera haba que comenzar con algo fcil, simple y ameno. El parpadeo de un led, al cual le agregue una condicin con el PIN RA0 del PORTA, y bueno ac esta el resultado de eso, claro con la ayuda de el_guitre que me asesoro "on-line". Ahora me tendr a MI dndoles lata por que ahora tendr que pensar en C y dejar el ASM por un momento

PROGRAMA PRINCIPAL /////////////////////////

Cdigo

GeSHi (c): 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. #include <16f877A.h> #fuses XT,NOWDT,PUT,NODEBUG,NOPROTECT,NOBROWNOUT,NOLVP,NOCPD,NOWRT #use delay(clock=4000000) #use fast_io(b) #use fast_io(a) #zero_ram //pone a "0" todos los registros internos que son usados como //variables

void main() { set_tris_b(0x00); set_tris_a(0b00001); output_b(0x00); // configuro el puerto B como salida // configura el RA0=in, RA1-RA5=out // pongo en cero las salidas del portb

while(true) { while(!input(PIN_A0)) { output_high(PIN_B1); } output_low(PIN_B1); delay_ms(1000); output_toggle(PIN_B0); } }

//pregunto si presione RA0=0? //si, entonces enciendo RB1

//no, apago RB1, y RB0 esta //encendido y apagado cada 1s

P.D: Si la memoria no me falla, CCS tiene libreras para manejar LCD y teclado matricial, mi duda es si se pueden usar el LCD y el teclado en el mismo portB, ya que en asm yo lo hago as, el lcd lo tengo conectado en la parte alta del puerto y el detecto las pulsadas en el teclado por interrupcin (cambio de estado en RB7-RB4) as me permite usar los dos dispositivos en un solo puerto. No se si la librera que hizo aitopes puede hacer eso o tendr que hacer mis adaptaciones del asm de esas libreras al C: D

CODIGO 7
Ttulo: Microcursillo en C Funciones Segn tengo entendido, todas las funciones de C tienen un tipo de dato y regresa un valor cuando terminan. En este caso main es la funcin principal, as que no importa que no regrese algn valor al terminar de ejecutarse, por eso se le pone:

void main(void); el tipo de funcin es void y el tipo de dato que regresa es void, es decir, no importan. Si no mal recuerdo se puede hacer algo as:

suma(dato_x) { dato_x=10; return dato_x; }


y en el programa se le pone

dato_a=suma();
Si ando medio perdido por favor corrjanme

Ttulo: Microcursillo en C- Funciones Hola migsantiago: Yo creo que el comando "return" es exclusivo para las funciones. La forma de definir una funcin (la funcin "Suma") sera la siguiente:

int Suma(int aux) { return (aux+10); }


Esta funcin devuelve un valor de tipo "int", y tiene como parmetro de entrada otro "int". Por lo que los tipos de datos deben de coincidir tanto en la cabecera como en la llamada a la funcin. Esta funcin podra ser llamada de la siguiente forma

resultado=Suma(num);
Espero que sirva de ayuda... Saludos. Ttulo: Microcursillo en C-Funciones Hola pichi yo soy nuevo aqu pero creo que una funcin VOID es una funcin vaca. Que quiere decir? Quiere decir que no necesita ninguna variable de entrada. Ejemplo:

... int a; int x; FUNCIN(VOID) { return a + x; }


Otra forma:

FUNCIN(int x,int a) { return a + x; }


La diferencia de estas dos funciones radica en la forma de llamarlas: En la primera llamaramos solo a la funcin y las variables X y A ya estaran definidas en otra parte del programa. (Variables globales). En la segunda las variables X y A solo estn definidas dentro de esa funcin con lo que X y A son variables diferentes de cualquier otra variable X y A definidas fuera de esa funcin. (Variables locales). Espero que te sirva de ayuda Pichi, aunque yo tambin soy un poco novato en C. Saludos de Eleman. Ejercicio 1. Prender y apagar todos los pines del pic 16f877a durante un segundo respectivamente. Ejercicio 2. Configurar los puertos A y B para que 3 sean entradas y las restantes salidas. Ejercicio 3. Verificar las funciones de CODIGO 7

You might also like