You are on page 1of 66

1. INTRODUCCION La primera pregunta que deberamos hacernos es Por qu mutar a un lenguaje de alto nivel como el C y no seguir programando en assembler?.

Algunas de las razones posibles son: El C es un lenguaje de alto nivel: esto implica que nos aisla hasta cierto punto del hardware independiente del tipo de microcontrolador que se est programando. Es ms rpido y fcil de aprender que el assembler: Tiene una cantidad de sentencias reducidas y son las mismas independientes del microcontrolador. En cambio en assembler cada microcontrolador tiene su propio conjunto de instrucciones que varan en un nmero de 30 a ms de 100 instrucciones que deberamos aprender adems de todos los modos de direccionamiento asociados. Portabilidad: Si quisiera trasladar un programa en lenguaje C de un microcontrolador a otro (incluso de otra marca) lo podra hacer consiguiendo el compilador adecuado e introduciendo mnimos cambios. En cambio en assembler tendra que aprender en nuevo conjunto de instrucciones del nuevo microcontrolador y rehacer completamente todo el programa Flexibilidad y mantencin: Es comn que en los programas se tengan que hacer modificaciones y correcciones que debido a su organizacin estructurada del lenguaje C resultan programas ms claros y ms fcil de entender, a diferencia del assembler, que con el tiempo y con la extensin del programa se vuelve extremadamente difcil de entender incluso para el programador original del software. Facilita el trabajo en equipo: Cuando los sistemas son complejos, se puede necesitar que varios programadores trabajen sobre el mismo. Al permitir el desarrollo de programas modulares se podra dividir el software en varios modulos, cada uno desarrollado por un programador diferente. Luego el compilador se encargara de integrarlos y compatibilizar los recursos del microcontrolador con los recursos requerido por cada modulo individual. Los programas en lenguaje C se desarrolla mucho ms rpido que un programa en assembler Mas fcil de entender por otros programadores diferente al programador original

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

La principal desventaja respecto al assembler es que el cdigo en assembler puede llegar a resultar ms eficiente y usar menos memoria de programa. 1.1 Definicin de la Funcin Main Al comenzar a ejecutarse el programa dentro de un microcontrolador el Contador de Programa apunta a la primera posicin de memoria y es all donde busca la primera instruccin a ejecutar. Siendo que un programa en C es una agrupacin de funciones que interactan entre si, el compilador C necesitar saber cul es la primera funcin a ejecutar. Esta funcin est definida por defecto y tiene el nombre de main(). void main(void) { // Instrucciones en C o llamada a funciones C } Las llaves limitan el comienzo y el final de la funcin main() y de todas las funciones en C Todas la instrucciones deben finalizar con un punto y coma. 1.2 Comentarios. Comentar un programa es una prctica muy importante para el mantenimiento y las modificaciones futuras del mismo. Ya que con el tiempo hasta el propio programador olvida cual era el objetivo de una secuencia determinada de cdigo. Existan dos formas en que se pueden introducir comentarios en un programa en C: Con una doble barra invertida: // comentario Barra con asterisco: /* Comentario */ 1.3 Nombres de Identificadores Los objetos que utiliza el lenguaje C (funciones, variables, etc.) necesitan tener un nombre o identificador para poder ser referenciados. Las reglas respectos a los identificadores son: 565 6 P POLITECNI GI CO N NA 2

No pueden tener smbolos grficos a excepcin del guion bajo ( _ ) No pueden tener acentuacin graficas Un identificador no puede comenzar con un numero Solo los primeros 32 caracteres son significativos Diferencia entre maysculas y minsculas No puede ser una palabra reservada de C o del compilador Ejemplos: contador, Contador, CONTADOR: son tres identificadores diferentes para C Palabras reservadas del C:

1.4 Tipos de Datos Todas las variables de C deben ser previamente definidas antes de poder ser usadas dentro del programa. El tipo de datos que se le asigne a una variable le dice al compilador cual es la cantidad de memoria que tiene que reservar para esa variable, asi como el rango numrico mximo que puede representar. Esto le permite optimizar el usa de la memoria ram ya que de esta forma no dispondr mas byte de lo necesario, para esa variable en particular Existen distintos tipos de formato para representar valores numricos, donde los dos ms distintivos son: representacin en punto fijo y representacin en punto flotante. El punto fijo se usa para representar valores enteros y representa valores consecutivos.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

El punto flotante puede representar valores decimales ya que usa una porcin de los bits para la mantisa y otra para los exponentes lo que le da la facilidad de representar nmeros extremadamente grandes o extremadamente pequeos, aunque pierde continuidad entre los nmeros.

1.5 Modificadores de Datos Excepto para void los tipos bsicos tienen modificadores que le preceden. Se usa un modificador para alterar el significado de un tipo base para encajar con las necesidades de diversas situaciones mas precisamente. Una lista de los modificadores es: Signed Unsigned Long Short Los modificadores signed y unsigned se usan para definir si la variable va a representar nmeros solamente positivos o nmeros positivos y negativos. La diferencias entre nmeros con y sin signo es la manera en que el compilador interpreta el bit mas alto. Los nmeros negativos se presentan utilizando el mtodo de complemento a dos En cambio los modificadores long y short se refieren a la cantidad de memoria usada para representar esa variable.

565 6 P GI N NA 2

POLITECNI CO

1.6 Declarando Variables El formato general para la declaracin de variables es: tipo lista_variables; Ej: int i, j, l; short int sf; unsigned int Contador = 0; double balance, ganancias, perdidas; Observar que las variables pueden ser inicializadas en el momento de su declaracin, como pasa con la variable Contador.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

1.7 Regla de Alcance de las Variables El lugar donde se declara una variable determina la visibilidad de la misma para las diversas partes del programa. Las reglas que determinan como se puede usar una variable basndose en el lugar del programa que fue declarada se llama regla de alcance. En los programas en C hay tres sitios donde se pueden declarar variables: Fuera de todas las funciones, incluyendo la funcin main(). Se llaman globales Dentro de una funcin. Se las denomina locales. Dentro de la declaracin de los parmetros formales de una funcin.

Las variables globales pueden ser accedidas (ledas y modificadas) por cualquier funcin que forma parte del programa. Las variables locales solo pueden ser accedidas dentro de la funcin en la cual fue declarada, por lo tanto es desconocida para el resto del programa. Por esta razn, dos variables locales definidas en dos funciones diferentes pueden tener el mismo nombre sin que por ello se origine algn conflicto. Sin embargo dos variables globales no pueden tener el mismo nombre. Otra diferencia importante es que las variables locales son creadas al entrar a la funcin y descartadas al salir de la misma. Por lo tanto el uso de variables locales optimiza el uso de la memoria ram. Cuando una variable usa el modificador static, su comportamiento es un hibrido entre una definicin global y una local. Ya que la variable as definida, como sucede con la local, solo es conocida en el interior de la funcin que contiene su definicin. Pero a igual que sucede con la variable global, en ningn momento es destruida y por lo tanto conserva su valor a lo largo del programa. O sea, es una variable local que conserva su valor entre llamadas a la funcin que la define. Ej:

565 6 P GI N NA 2

POLITECNI CO

1.8 Modificadores de Tipo de Acceso El lenguaje C posee dos tipos de modificadores que se usan para controlar la forma en que se pueden acceder o modificar las variables. Estos modificadores son llamados const y volatile Una variable definida como const no puede ser alterado el contenido de la variable dentro del programa, salvo para darle un valor inicial. Una variable definida como volatile le informa al compilador que se puede cambiar el valor de la variable de forma no explicita por el programa. Esto es lo que sucede con las posiciones de memoria ram asociadas a los registros especiales del microcontrolador, ya que su valor puede ser alterados por el hardware. Unos modificadores anexados por el MCC18 y que no corresponde al ANSIS C son: ram y rom que nos permiten determinar donde se guardan las constantes, si en memoria de datos o de programa.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

1.9 Constantes Se refieren a los valores fijos que se le asignan a las variables, algunos ejemplos son:

Para formatos diferentes al decimal tenemos:

1.10 Operadores El lenguaje C es muy rico en operadores, entendindose como operador a un smbolo que le dice al compilador que realice manipulaciones matemticas, relacionales y lgicas y sobre bits. Operadores Aritmticos

565 6 P GI N NA 2

POLITECNI CO

Los 4 primeros operadores no requieren mayor explicacin, el operador % devuelve el resto de una divisin, as por ejemplo: 10 % 3 = 1(resto) Aunque este operador no se puede usar sobre los tipos float o double. El operador ++ aade 1 a su operando y el -- le resta 1. x++ es igual a x = x + 1 y-- es igual a y = y 1 Estos operadores pueden preceder o seguir a su operando, pero esto implica una diferencia en el resultado obtenido. As, por ejemplo: x = 10 y = ++x el resultado es y = 11 x = 10 y = x++ el resultado es y = 10 Operadores Relacionales y Lgicos

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Se refieren a las relaciones que pueden tener los valores unos con otros. La clave de los conceptos de operadores relacionales y lgicos es la idea de verdadero o falso. En C, cualquier valor distinto de cero es verdad, mientras que si el cero es falso. Las expresiones que usan operadores relacionales y lgicos devolvern 0 para falso y 1 para verdadero. Ejemplo: If( (tecla == 0) &&(Contador = 0 ) ) { LED = 1; } Operadores Bit a Bit

Realizan operaciones lgicas, pero esta vez, bit por bit de las palabras que intervienen en la operacin. Adems se agregan 2 operadores ms, que consisten en desplazar la palabra tanto bits a la derecha o a la izquierda, emulando las operaciones shift rigth y shift left que suelen formar parte del set de instrucciones de todos los microcontroladores. Ejemplo:

565 6 P GI N NA 2

POLITECNI CO

Cuando el resultado de alguna de estas operaciones en general implica a uno de los operando, el C permite una notacin simplificada como la siguiente:

1.11 Expresiones Los operadores, constantes y variables constituyen las expresiones. Una expresin en C es cualquier combinacin valida de estas piezas. Cuando se mezclan constantes y variables de diferentes tipos en una expresin, C las convierte al mismo tipo. El compilador C convertir todos los operandos al tipo del operando ms grande en una operacin segn la base de esta operacin. Tal como describen en estas reglas de conversin de tipos: 1. Todos los char y short int se convierten a int. Todos los float a double. 2. Para todos los operandos, los siguientes ocurre en secuencia. Si uno de los operandos es un long double, el otro se convierte a long double. Si uno de los operandos es double, el otro se convierte a double. Si uno de los opernados es long, el otro se convierte a long. Si uno de los operandos es unsigned, el otro se convierte a unsigned

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Despus de que el compilador aplique estas reglas de conversin, cada par de operandos ser del mismo tipo, y el resultado de la operacin ser del tipo de los operandos. Ejemplo:

Operador Cast Se puede forzar una expresin a ser de un tipo especifico usando las construccin llamada cast. El formato general de un cast es: (tipo) expresin Por ejemplo, si x es un entero y se quiere uno asegurar que la computadora evale la expresin x/2 como de tipo float para obtener componente fraccional, podramos usar el operador cast de la siguiente forma: (float) x/2 Aqu el cast (float) se asocia con x, que provoca que el compilador transforme a x de entero a float y como vimos antes, el resultado tambin ser float. 565Sin embargo, hay que ser cuidadoso ya que si la expresin fuera: 6 P POLITECNI GI CO N NA 2

(float) (x/2) el compilador hara primero la divisin entera y luego, ese resultado entero, lo pasara el tipo float. Otro ejemplo: int z; int x = 150; char y = 63; z = (y * 10) + x Qu resultado da? Cmo se puede solucionar? Este ejemplo pone de manifiesto uno de los inconvenientes que se presentan al existir una expresin con varios operadores, que se refiere en qu orden y con qu prioridad se aplican los mismos. Esto se resuelve en la reglas de precedencia. Precedencia de los operandos Los operandos tienen la siguiente precedencia:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

El compilador evala de izquierda a derecha los operadores con el mismo nivel de precedencia. Ejemplo: y=3+2*4 // y = 11

y = (3 + 2) * 4 // y = 20 y = 4 >> 2 * 3 // y = 4 >> 6 y = 3 * 2 >> 4 // y = 6 >> 4

565 6 P GI N NA 2

POLITECNI CO

SENTENCIAS DE CONTROL DE PROGRAMA Las sentencias de control de programa son la esencia de cualquier lenguaje de computadora, ya que gobiernan el flujo de la ejecucin del programa. La sentenciad de control del lenguaje C son ricas y poderosas y ayudan a explicar la popularidad del lenguaje. Se pueden dividir las sentencias de control de programa en tres categoras. La primera consta de unas instrucciones condicionales if y switch. La segunda son las sentencias de control del buche while, for y do-while. La tercera catergoria contiene la instruccin de bifurcacin incondicional goto. a. La Sentencia if La forma general de la sentencia if es: if(condicin) sentencia_1; else sentencia_2; o if(condicin) { sentencias_1; } else { sentencias_2; } La clausula else es opcional. Si es verdad la condicin (distinta de cero), se ejecutara la

sentencia o bloque que forma el objetivo de if; de lo contrario, si existe else, se ejecutara la sentencia o bloque que es el objetivo de else. O sea que solo el cdigo asociado con if o el asociado con else se ejecutara, nunca ambos.

El escalonador de if-else-if

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Una construccin corriente en programacin es el escalonador if-else-if. Que se ver como: if(condicin) sentencia; else if(condicin) sentencia; else if(condicin) . . . . else sentencia; El compilador evala las expresiones condicionales desde arriba descendiendo, tan pronto como la computadora encuentra una condicin verdadera, ejecuta la sentencia o bloque de sentencias asociada y salta el resto de la escalera. Si no son verdad ninguna condicin, se ejecutara el else final. Si no hay else final y son falsas todas las condiciones, no se hace nada. La expresin condicional Dentro de la expresin condicional del if, se puede usar cualquier expresin valida de C, no se necesita restringir el tipo de expresiones a solo las que invocan operadores relacionales y lgicos. Todo lo que se requiere es que la expresin evaluada de cero o no cero.

b.

La Sentencia switch 565 6 P GI N NA 2

POLITECNI CO

Aunque el escalonador de if-else-if puede realizar comprobaciones mltiples, el cdigo puede ser difcil de seguir y puede confundir al programador. Por esta razones, el C tiene incorporada una sentencia de bifurcacin mltiple llamada switch. En l, el programa comprueba una variable sucesivamente frente a una lista de constantes enteras o de carcter. Despus de encontrar una coincidencia, se ejecuta la sentencia o bloque de sentencias que se asocian con la constante. La forma general de la sentencia switch es: switch(variable){ case constante1: secuencia de sentencias; break; case constante2: secuencia de sentencias; break; . . . . default: secuencia de sentencias; } Donde se ejecutar la sentencia default si no coincide ninguna. El default es opcional; si no est presente, no se hace nada. Cuando se encuentra una coincidencia, el programa ejecuta las sentencias asociadas con el case hasta encontrar un break o en el caso del default (o el ltimo case, si no est presente el default) el final de la sentencia switch. Hay dos cosas importantes que saber sobre la sentencia switch:

1. La sentencia switch solo puede comprobar por igualdad, mientras que la if puede ser de cualquier tipo.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

2. No pueden tener dos constantes case valores idnticos en el mismo switch, aunque un switch puede contener a otro switch con constantes de case iguales. La sentencia switch es ms eficiente y es ms clara de leer que el escalonador if-else-if La sentencia break Las sentencias break son opcionales dentro de la sentencia switch. Se usan las sentencias break para terminar la secuencia asociada con cada constante. Si se omite esta sentencia, la ejecucin continuar en las sentencias del siguiente case hasta que se encuentre un break o el final del switch. Ejemplo: switch(t){ case 1: sentencias1; break; case 2: sentencias2; break; case 3: sentencias3; case 4: case 5: sentencias5; break; case 10: sentencias10; break; } De este ejemplo podemos hacer varias observaciones: 565 6 P GI N NA 2

POLITECNI CO

1. Las constantes asociadas a los case, no tienen que ser nmeros consecutivos, en este ejemplo las constantes 6, 7, 8, 9, no existen, y por lo tanto si t asume alguno de estos valores no se ejecuta nada porque no est implementada la opcin default. 2. Las sentencias5 tienen asociadas dos constantes: 4 y 5, si t asume cualquiera de estos dos valores se ejecutarn estas sentencias. 3. Si t asume el valor 3 se ejecutaran las sentencias3, pero al no contener un break, seguir con las sentencias5 hasta encontrar el prximo break. c. Bucles Los bucles permiten repetir un conjunto de instrucciones hasta que se cumple cierta condicin. Los bucles de C son el for, el while y do-while. 2.3.1 El bucles for El formato general de la sentencia for es: for(inicializacin; condicin; incremento) sentencia; En su forma ms simple, la inicializacin es un sentencia de asignacin que el compilador usa para establecer la variable de control del bucle. La condicin es una expresin que comprueba la variable de control del bucle cada vez para determinar cundo salir del bucle. El incremento define la manera en que cambia la variable de control del bucle cada vez que se repite el mismo. Se deben separar estas tres grandes secciones usando punto y coma. El bucle for continuar la ejecucin mientras la condicin sea verdad. Una vez que es falsa, la ejecucin del programa, continuar en la sentencia que sigue al for. Ejemplo vamos a sumar los nmeros naturales del 1 al 100: Resultado = 1 + 2 + 3 + 4 + 5 + ..+ 99 + 100 void main(void)

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

{ char i; int Res = 0; for(i = 1; i <= 100; i++) Res = Res + i; } Esto va a dar como resultado que la suma se repita 100 veces. No es necesario que la variable se tenga que incrementar, obtendramos el mismo resultado si usramos la siguiente sentencia: for(i = 100; i > 0; i--) Res = Res + i; El incremento podra ser cualquier expresin vlida de C como: i = i + 5 El bucle for a igual que las sentencias anteriormente vistas, se puede aplicar a un conjunto de sentencias, encerrando las mismas entre corchetes. El bucle infinito Uno de los usos mas interesante para el bucle for es crear un bucle infinito. Debido a que ninguna de las tres expresiones que forman el bucle for se dan, se puede hacer un bucle sin fin teniendo la expresin condicional vaca. for( ; ; ;) { sentencias; }

2.3.2 El bucles while 565 6 P POLITECNI GI CO N NA 2

El formato general es: while(condicin) sentencia; donde sentencias, puede ser una sentencia vaca, una sentencia nica o un bloque de setencias que se repetirn. La condicin puede ser cualquier condicin vlida. El bucle itera mientras la condicin sea verdad. Cuando llega a ser falsa, el control del programa pasa a la lnea que sigue al bucle. Como en el bucle for, el bucle while comprueba la condicin en lo alto del bucle, lo que significa que el cdigo del mismo no se ejecutara siempre. Debido a que while realiza la prueba en lo alto, es bueno para situaciones donde no se quiera ejecutar el bucle. La posicin de la prueba elimina la necesidad de realizar una prueba condiciona separada antes del bucle. 2.3.3 El bucles do/while Al contrario del los buches for y while que comprueban la condicin en lo alto del bucle, el bucle do/while la examinan en la parte baja del mismo. Esta caracterstica provoca que un bucle do/while siempre se ejecutara al menos un vez. La forma general del bucle do/while es: do{ sentencias; } while(condicin); 2.4 Anidamiento de Bucles Cuando un bucle est dentro del otro, el bucle ms interno se dice que est anidado. El anidamiento de bucles permite resolver en forma compacta algunos problemas complejos.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

2.5 Rompiendo Bucles La sentencia break tiene dos usos. El primer uso es terminar un case en la sentencia switch. El segundo es forzar la terminacin inmediata de un bucle, saltando la prueba condicional normal del bucle. Cuando se encuentra la sentencia break en un bucle, el programa termina inmediatamente el bucle y el control del programa va a la sentencia siguiente al bucle. Ej: for(t=0; t< 100; t++) { U = t + U; if( t == 5 ) break; } ; En este programa se obtiene solo la suma de los 5 primeros nmeros, ya que la sentencia break se ejecuta en la sexta iteracin y corta el bucle. 2.6 La sentencia continue La sentencia continue funciona de manera similar a la sentencia break. Sin embargo a diferencia de break que rompe el bucle, continue fuerza la siguiente iteracin y salta cualquier cdigo entre medias. Ej: for(x = 0; x<100; x++) { if(x%2) continue; y = x ^ 2 + y; 565} 6 P GI N NA 2

POLITECNI CO

Cada vez que x asume un valor impar, se ejecuta la sentencia continue que fuerza la prxima iteracin y salta el clculo de la variable y. En los bucles while y do/while, una sentencia continue provoca que el control del programa vaya directamente a la prueba condicional y despus continu el proceso del bucle. 2.7 Etiquetas y goto La sentencia goto requiere un etiqueta para poder funcionar. Una etiqueta es un identificador vlido de C que se sigue por dos puntos. An ms, la etiqueta debe estar en la misma funcin que usa el goto. Por ejemplo, se podra escribir un bucle de 1 a 100 usando goto y una etiqueta, como se ve aqu: x = 1; Loop: x++; if(x<100) goto Loop; Un buen momento para usar goto es cuando se requiere salir de varias capas de anidamiento for() { for(.) { while(.){ if()goto Stop; .. . } } } Stop: .;

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

El uso excesivo de la sentencia goto hace al programa confuso e ilegible, mas cuando en el C se dispone de muchas y verstiles estructuras que pueden reemplazar sin problema su funcionalidad. Por lo que no es aconsejable su uso.

565 6 P GI N NA 2

POLITECNI CO

ARRAY Y CADENAS Un array es una coleccin de variables del mismo tipo que se referencia utilizando un nombre comn. El compilador los coloca en posiciones contiguas de memoria. Un array puede tener una o varias dimensiones. Para acceder a un elemento especfico dentro del array se usa un ndice o varios ndices, si es multidimensional. 3.1 Array Unidimensional El formato general para la declaracin de un array unidimensional es: tipo nombre_array[tam]; Ej: int BufferRx[16]; Aqu, tipo declara el tipo base del array. El tipo base determina el tipo de datos de cada elemento del array. El tam define cuantos elementos guardar. En nuestro ejemplo, es un array de tipo entero signado, llamado de BufferRx de 16 elementos. En C, todos los arrays usan cero como ndice del primer elemento. Por tanto BufferRx[0] es el primer elemento del array y BufferRx[15] es el ltimo elemento del mismo. Los array son muy comunes en programacin porque permiten tratar fcilmente muchas variables relacionadas bajo un mismo nombre. Por ejemplo, podramos poner en un array los datos que vayamos recibiendo del puerto serie ( comunicacin RS-232), para luego ser procesados en otra parte del cdigo. Otro uso bastante comn es crear un array para los datos leidos de los conversores A/D, ya que en general se suelen promediar un grupo de muestras antes de darla como una lectura vlida, disminuyendo de esta forma el ruido inherente a este tipo de medicin. Por ejemplo:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Aqu se ve, como definimos un array para contener 16 mediciones seguidas de temperatura para luego promediarlas en un nico valor. La cantidad de bytes que ocupa un array depende del tamao del mismo y el tipo de dato guardado, en este ltimo ejemplo el array ocupa 32 bytes. 3.1.1 No comprobacin de Contornos El lenguaje C no realiza comprobacin de contornos en los arrays: as no detiene la escritura fuera del final del array, si el ndice sobrepasa el mximo valor del mismo. Esto trae aparejado que se sobrescriban las posiciones de ram siguientes a las ocupadas por el array, alterando el valor de la variable que ocupa dicha posicin. Este tipo de error es muy difcil de descubrir, por lo que, ser responsabilidad del programador hacer la comprobaciones de contorno cuando las considere necesarias. 3.2 Cadenas En C una cadena no es ms que un array unidimensional de caracteres que termina en un nulo o cero. Los componentes de una cadena (las letras), son representadas numricamente a travs del cdigo ASCII, por lo que cada letra es guardada en un byte de acuerdo a este cdigo. 565 6 P POLITECNI GI CO N NA 2

Una cadena se puede inicializar en C18 de la siguiente manera: const rom char Mensaje [10] = { Curso C18} Donde el tamao del array debe tener en cuenta no solo todos los caracteres, incluido los espacios en blanco, sino tambin un espacio adicional para el terminador nulo. Los valores realmente guardados en este array sern: Mensaje[0] = 67 (C) Mensaje[1] = 117 (u) Mensaje[2] = 114 (r) Mensaje[3] = 115 (s) Mensaje[4] = 111 (o) Mensaje[5] = 32 ( ) Mensaje[6] = 67 (C) Mensaje[7] = 49 (1) Mensaje[8] = 56 (8) Mensaje[9] = 0 (/0) 3.3 Array Bidimensionales C permite array multidimensionales. La forma mas sencilla de array multidimensional es la de dos dimensiones. En esencia, un array bidimensional es un array de arrays unidimensionales. Para declarar un array de enteros bidimensionales dosd de tamao 10,20 se escribir: int dosd[10][20]; Recordar que a igual que pasaba con lo array unidimensional el primer elemento es dosd[0][0] y el ltimo dosd[9][19]. Veamos un ejemplo:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

main() { int t,i,num[3][4]; for(t=0;t<3;t++) { for(i=0;i<4;i++) num[t][i] = (t*4)+i+1; } } En este ejemplo, num[0][0] tendr el valor 1, num[0][1] tendr el valor 2, num[0][2] tendr el valor 3, y as sucesivamente. El valor de num[2][3] ser 12. El lenguaje C guarda los arrays bidimensionales en un array de filas y columnas, donde el primer ndice indica la fila y el segundo la columna. Esta estructura significa que el ndice ms a la derecha cambia ms rpido que el ms a la izquierda cuando se accede a un array de elementos en el orden que C realmente almacena en memoria. Supongamos que definimos el siguiente array: Buffer[5][8] , que se coloca en memoria a partir de la direccin m

565 6 P GI N NA 2

POLITECNI CO

3.4 Array de Cadenas En programacin es corriente usar un array de cadenas, como por ejemplo para contener todas los textos que va a hacer aparecer el programa en un LCD. Para crear un array de cadenas, se usa un array de caracteres de dos dimensiones, en la que el tamao del ndice izquierdo determina el nmero de cadenas y el tamao del derecho especifica la longitud mxima de todas las cadenas. Por ejemplo, esto declara un array de 30 cadenas con cada cadena como mximo de 80 caracteres (incluido el carcter nulo). char Cad[30][80]; 3.5 Array Multidimensionales C permite arrays con ms de dos dimensiones. El formato general de un declaracin de array multidimensional es: tipo nombre[tam1][tam2].[tamN]; Por ejemplo, la siguiente sentencia crea un array multidimensional de 4x10x3 int tresd[4][10][3]; No se suelen usar array de tres o ms dimensiones porque se incrementa geomtricamente la cantidad de memoria requerida. Por ejemplo un array de cuatro dimensiones de enteros con dimensiones de 10x6x9x4 requerira: 2 x 10 x 6 x 9 x 4 = 4.320 bytes Si el array fuera double (8 bytes) la memoria requerida sera de 17.280 bytes

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

3.6 Inicializacion de Arrays El formato general de la inicializacin de un array es similar al de otras variables, segn se muestra aqu: especificador_de_tipo nombre_array[tam1][tamN] = { lista_de_valores}; La lista de valores es una lista separada por comas de constantes que son de tipo compatible con el tipo base del array. Por ejemplo: int i[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; esta sentencia indica que i[0] tendr el valor 1 e i[9] el 10. Los array de caracteres que guardan cadenas permiten una inicializacin abreviada que tiene la forma: char nombre_array[tam]=cadena; Por ejemplo: char str[5] = hola; este cdigo es el mismo que este fragmento: char str[5] = {h, o, l, a, \0}; Recordar que cuando se inicializa una cadena, hay que dejar un espacio para el caracter nulo.

565 6 P GI N NA 2

POLITECNI CO

Los arrays multidimensionales se inicializan de la misma manera que los de una dimensin, solo hay que tener en cuenta el orden en que el compilador asigna memoria al array, para saber a que ndice van los valores asignados. int Pot2[10][2] = { 1, 1, 2, 4, 3, 9, 4, 16, 5, 25, 6, 36, 7, 49, 8, 64, 9, 81, 10, 100}; 3.7 Inicializacion de Arrays sin tamao En una sentencia de incializacin de arrays, si no se especifica el tamao de los mismos, el compilador C crear automticamente un array lo suficientemente grande para guardar todos los inicializadores presentados. char CadsT[]={tamao no definido de antemano} Despus de esta sentencia el compilador le asignara a la cadena un tamao de 31 bytes. Adems de ser menos tedioso, el mtodo de inicializacin del array sin tamao permite cambiar cualquier mensaje sin tener que preocuparse por errores accidentales de contaje. El lenguaje C no restringe las inicializaciones de arrays sin tamaos slo a los arrays unidimensionales. Para arrays multidimensionales, se debe especificar todas las dimensiones excepto las de ms a la izquierda para permitir indexar el array apropiadamente. En este sentido, se puede

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

contruir tablas de longitud variables mientras el compilador asigna automticamente el almacenamiento suficiente. Por ejemplo, esta es la declaracin de Pot2 de un array sin tamao: int Pot2[][2] = { 1, 1, 2, 4, 3, 9, 4, 16, 5, 25, 6, 36, 7, 49, 8, 64, 9, 81, 10, 100}; La ventaja de esta declaracin sobre la de tamao definido es que se puede alargar o acortar la tabla sin cambiar las dimensiones del array.

565 6 P GI N NA 2

POLITECNI CO

FUNCIONES
El lenguaje C est basado en el concepto de bloques construidos. Los bloques construidos se llaman funciones. Un programa C es una coleccin de una o ms funciones. Para escribir un programa, primero se crean las funciones y despus se ponen juntas. En C, una funcin es una subrutina que contiene una o ms sentencias de C y eso realiza una o mas tareas 4.1 El formato general de una funcin El formato general de un funcin C es: especificador_de_tipo nombre_de_la_funcion ( declaracin_de_parametros) { cuerpo de la funcin } El especificador_de_tipo especifica el tipo del valor que la funcin devolver mediante el uso de la sentencia return. El valor puede ser cualquier tipo vlido. Si no se especifica un valor, entonces el compilador asume por defecto que la funcin devolver un resultado entero. La lista de declaracion_de_parametros es una lista de nombres de tipos y nombres de variables separados por comas que recibe los valores de los argumentos cuando se llama a la funcin. No se tiene por qu incluir parmetros en una funcin, en ese caso la lista de parmetros estar vaca. Sin embargo, incluso si no se incluyen parmetros, se precisan los parntesis. Se usar la seccin que contiene las declaraciones de parmetros para definir el tipo de parmetros en la lista, si la funcin no tiene parmetros, no se necesita declaracin de parmetros. El tipo de declaracin es opcional (si no est una declaracin explcita de tipo, el tipo de la funcin por defecto es entero). Las funciones terminan y devuelven automticamente al procedimiento que las llam cuando se encuentra la ltima llave. Se puede forzar la vuelta antes, usando la sentencia return. A diferencia de lo que puede ocurrir con la declaracin de las variables, en la lista de parmetros de la funcin, cada parmetro tiene que tener su tipo y su nombre por separado de los otros.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Esto quiere decir que la lista de declaracin de parmetros de una funcin adquiere este formato general: F(tipo varnombre1, tipo varnombre2..tipo varnombreN) Una declaracin correcta sera: F(int x, int y, float z) Sin embargo, la siguiente declaracin es incorrect: F(int x, y, float z) 4.2 La sentencia return La sentencia return tiene dos usos importantes. Primero , se puede usar para provocar la salida inmediata de la funcin en la que se est; es decir, return provocar que el programa en su ejecucin vuelva al cdigo que llamo a la funcin. Segundo, se puede usar return para devolver un valor. Cuando no est presente esta sentencia, simplemente se vuelve de la funcin al llegar al corchete de cierre de la misma. Cuando una funcin que no es void no contiene una sentencia return devuelve un cero. 4.3 Regla de mbito de las funciones Las reglas de mbito de un lenguaje gobiernan si un trozo de cdigo sabe o no, o accede o no, a otro trozo de cdigo o datos. En C, cada funcin es un bloque discreto de cdigo. Un cdigo de funcin es exclusivo de la funcin y no es accesible desde otra funcin, por ejemplo, no se puede usar goto para saltar en medio de otra funcin.

565 6 P GI N NA 2

POLITECNI CO

El cdigo que comprende el cuerpo de la funcin est oculto del resto del programa y (a menos que el cdigo use variables o datos globales), no puede afectar ni ser afectado por otras partes del programa. O sea, el cdigo y los datos que se definen en una funcin no interactan con el cdigo o los datos que se definen en otra funcin, porque las dos funciones tienen un mbito diferente. Hay tres tipos de variables: variables locales, parmetros formales y variables globales. Las reglas de mbito gobiernan como pueden acceder otras partes del programa a estos tipos y establece el tiempo de vida de las variables. 4.3.1 Variables locales Las variables locales pueden ser referenciadas por las sentencias que estn dentro del bloque en el que dichas variables se declaran. As las variables locales no son conocidas fuera de su propio bloque de cdigo y su mbito est limitado al bloque en el que se declaran. Uno de los aspectos ms importantes de las variables locales es que existen slo durante la ejecucin de la funcin, y el almacenamiento de las mismas es en la pila, por lo tanto, no tienen asignado una posicin de memoria fija durante toda la ejecucin del programa, ya que esta asignacin se hace en el momento de crearla, despus de entrar a la funcin donde est definida, que depender del estado actual de la pila en ese momento. Por ejemplo:

Func1() { int x; x = 10; }

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Func2() { int x; x = -199; } Aqu la variable entera x se declara dos veces, una vez en cada funcin. La x en la Func1() no tiene conexin con la x de la Func2(), ya que cada x se conoce slo por el cdigo que est en el mismo bloque que la declaracin de la variable. Si le echaramos un vistazo al stack veramos algo como lo siguiente.

Donde se aprecia el espacio que se le asigna en el stack a los parmetros de la funcin a ser llamada y a las variables locales de la misma.

565 6 P GI N NA 2

POLITECNI CO

4.3.2 Parmetros formales Si una funcin va a utilizar argumentos, deben declararse variables que acepten los valores de estos argumentos. Aparte de recibir los parmetros de entrada de la funcin, se comportan como otras variables locales dentro de la misma. Se tiene que tener cuidado, que los argumentos que se pasan son del mismo tipo que los parmetros de la funcin. 4.3.3 Variables Globales Al contrario de las variables locales, las globales se conocen a travs del programa entero y se pueden usar en cualquier trozo del cdigo, ya que tienen asignado una posicin definida y fija en la memoria Ram. La variables globales se crean declarndolas fuera de cualquier funcin, de esta forma es accesible a cualquier funcin dentro del archivo fuente donde fueron creadas. Por lo tanto para que estas variables globales sean visibles en los otros archivos fuentes que conforman el programa se usa el especificador extern que le dice al linqueador que esas variables ya han sido previamente declaradas. Por ejemplo:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Si una variable global y una local tienen el mismo nombre, todas las referencias a ese nombre de variable dentro de la funcin en la que se declara como local se referir a la local y no afectar a la variable global. Las variables globales son muy tiles cando se usa el mismo dato en muchas funciones del programa. Sin embargo, se debera evitar el uso innecesario de las variables globales, por las siguientes razones: Ocupan memoria durante toda la ejecucin del programa y no solo cuando se necesita El uso de una variable global en lugar de una local restringir la generalidad de una funcin ya que depende de una variable que debe definirse fuera de si misma. Usando una serie grande de variables globales se pueden conducir a errores de programa con efectos laterales desconocidos e indeseables. Una de las caractersticas principales de un lenguaje estructurado es la compartimentacin del cdigo y los datos. En C se construye la compartimentacin usando variables locales y funciones. Por ejemplo, he aqu dos formas de escribir mul(): General mul(int x, int y) { return(x*y); } } Especifico int x,y; mul() { return(x*y);

Ambas funciones devuelven el producto de las variables x e y. Sin embargo, se puede usar la versin generalizada, o parametrizada, para devolver el producto de cualesquiera dos nmeros, mientras que se puede usar la versin especfica para encontrar solo el producto de las variables globales x e y.

565 6 P GI N NA 2

POLITECNI CO

4.3.4 Punteros Un puntero es una variable que contiene una direccin de memoria. Si una variable contiene la direccin de otra variable, entonces se dice que la primera variable apunta a la segunda. Como ilustra la siguiente figura:

El formato general para la declaracin de una variable de puntero es: tipo *nombre_variable; donde tipo puede ser cualquier tipo base en C y nombre_variable es el nombre de la variable, adems aparece el operador * que define que la variable va a contener como dato direcciones de memoria. Por ejemplo: char *pch; int *pint; En este ejemplo la variable pch es un puntero a una variable tipo char, es decir se le debe asignar la direccin de una variable de este tipo, la variable pint es un puntero a una variable tipo int, es decir se le debe asignar la direccin de una variable de este tipo.

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

4.3.5 Los operadores punteros Un puntero tiene la capacidad de leer el contenido de la posicin de memoria a la que apunta, por esta razn al trabajar con punteros necesitamos dos operadores bsicos: Operador &: Nos permita extraer la direccin de una variable para poder asignrsela al puntero Operador *: Nos permite extraer el contenido de la direccin a la que apunta el puntero Ejemplo: main() { int cont, *punt_cont, val; cont = 100; punt_cont = &cont; val = *punt_cont; } Despus de las definicin de las variables, la primera lnea le asigna el valor 100 a la variable cont, la segunda le asigna la direccin de cont a la variable de puntero punt_cont (no su contenido) y la ltima sentencia asigna el contenido de la variable apuntada por punt_cont (o sea 100) a la variable val. Aqu en esta ltima sentencia se ve la importancia de asignarle al puntero el tipo que corresponda ya que en esta toma 2 bytes a partir de la direccin que contiene y lo asigna a la variable val, porque es un puntero a un entero. 4.3.6 Aritmtica de Punteros En C se puede usar solo dos operaciones aritmticas sobre punteros: + y - . 565 6 P GI N NA 2

POLITECNI CO

Cada vez que se incremente un puntero , apuntar a la posicin de memoria del elemento siguiente en funcin de su tipo base. Veamos el siguiente ejemplo int *p1, Varint; p1 = &Varint; p1++; (supongamos que la variable Varint esta en la direccin de memoria 2000) (Despus del incremento p1 = 2002 y no 2001!)

El lenguaje C no limita slo a incremento y decrementos. Tambin se pueden sumar o restar enteros a punteros. La expresin: p1 = p1 + 5; har que p1 apunte al quinto elemento del tipo base de p1 despus del que apunta actualmente, todo lo dicho para la suma vale tambin para la resta. 4.3.7 Punteros y Array Existe un relacin estrecha entre los punteros y los arrays. Veamos el siguiente ejemplo. char str[20], *p1; p1= str; Este fragmento pone en p1 la direccin del primer elemento del array str. En C, un nombre de array sin un ndice es la direccin de comienzo del array. El mismo resultado se puede generar interpretando el primer elemento del array como variable: p1 = &str[0];

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Un punto importante ms a tener en cuenta que si quisiramos acceder al quinto elemento de este array podemos hacerlo de alguna de las 3 siguientes formas: str[4] = *(p1+4) = p1[4] O sea en lenguaje C la sentencia p[t] es idntica a (p+t). Por ltimo a pesar de la gran flexibilidad que aporta al lenguaje debe advertirse que los punteros entraan un peligro potencial, ya que si se usa un puntero antes de inicializarlo con una direccin vlida, las consecuencias para el programa pueden ser catastrficas. El programador es responsable del inicializar los punteros del programa antes de utilizarlos por primer vez.

4.4 Argumentos y parmetros de una funcin Al pasar los argumentos hacia una funcin el programador debe asegurarse que son del tipo esperado por las misma, de lo contrario se podran producir resultados inesperados. Por ejemplo, si una funcin espera un argumento char pero se la llama con un entero, entonces el compilador usar el primer byte del entero y lo asignara al argumento de la funcin. Existen dos formas en que se pueden pasar los argumentos a una funcin; el primero se denomina llamada por valor y la segunda llamada por referencia. 4.4.1 Llamada por valor Este mtodo copia el valor de un argumento en el parmetro formal de la funcin. Por tanto, los cambios que se hacen a los parmetros de la funcin no tienen efecto en las variables que se usan para llamarla.

565 6 P GI N NA 2

POLITECNI CO

Por ejemplo: main() { int t = 10; Sqr(t); } long Sqr(int x) { x = x*x; return(x); } Este programa copia el valor del argument a Sqr(), que es 10, en el argumento x. Cuando tiene lugar la asignacin x=x*x, la nica cosa que se modifica es la variable local x. La variable t, usada para llamar a Sqr(), todava tendr el valor 10. 4.4.2 Llamada por referencia Este mtodo copia la direccin de un argumento en el parmetro. Dentro de la funcin, la direccin se utiliza para acceder al argumento real usado en la llamada. Esto significa que lo que se hace con el parmetro afectar la variable usada para llamar a la funcin. Aunque la convencin de paso de parmetros de C es llamar por valor, se puede simular una llamada por referencia pasando un puntero al argumento. Veamos un ejemplo: Intercambiador(int *y, int *x) { int temp; temp = *x;

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

*x = *y; *y = temp; } La funcin intercambiara el contenido entre dos variables enteras, de esta forma: main() { int x = 10; int y = 20; Intercambiador(&x, &y); } Despus de ejecutar este cdigo se tendr: x = 20, y = 10. Cuando se usa un array como un argumento a la funcin, se pasa slo la direccin del array y no una copia del array completo. O sea se hace una llamada por referencia. 4.5 Prototipos de Funciones El lenguaje C interpreta por defecto que la funciones devolvern un valor entero (int), por lo tanto cuando la funcin devuelve un tipo de datos diferente o no devuelve datos (void), se le debe informar previamente al compilador antes de hacer la primera llamada a la misma. Esta definicin que se hace de la funcin previ a cualquier cdigo se la denomina: el prototipo de la funcin, donde tambin se definen el tipo y la cantidad de argumentos que la misma toma. Este proceso es la nica manera para que el compilador C pueda generar el cdigo correcto para estas funciones.

565 6 P GI N NA 2

POLITECNI CO

Ejemplo: void sqr(int *i); main() { int x; x = 10; sqr(&x); } void sqr(int *i) { *i = *i * *i; }

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

565 6 P GI N NA 2

POLITECNI CO

ESTRUCTURAS, UNIONES Y CAMPOS DE BITS


El lenguaje C, permite la creacin de tipos de datos personalizados. Entre los cuales analizaremos las estructuras, las uniones y las enumeraciones. 5.1 Estructuras Una estructura es una coleccin de variables que se referencian bajo el mismo nombre. Una estructura proporciona un medio conveniente para mantener juntas informacin relacionada. Generalmente, todos los elementos en la estructura son relacionados lgicamente unos con otros. Por ejemplo, se puede representar la informacin de nombre y direccin de una lista de correo en una estructura. El formato general de una definicin de estructura es: struct nombre_estructura{ tipo nombre_var1; tipo nombre_var2; ..; tipo nombre varN; } variables_estructuras; La palabra clave struct dice al compilador que se est definiendo una plantilla de estructura. Struct Dat{ char nombre[30]; char calle[40]; char ciudad[20]; char provincia[3]; unsigned long cdigo; };

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

As como est, realmente no ha sido declarada ninguna variable. El cdigo slo ha definido el formato de los datos. Para declarar un variable real con esta estructura, se escribira: struct Dat Info; Esta lnea declarar una variable de estructura de tipo Dat llamda Info, tambin se puede definir una o ms variables en el momento en que la estructura es definida.

Struct Dat{ char nombre[30]; char calle[40]; char ciudad[20]; char provincia[3]; unsigned long cdigo; }Info, marketing, social; En este caso se declara tres variables de estructuras del tipo Dat. Si solo se necesita una variable estructurada, no se necesita incluir el nombre de la estructura: Struct { char nombre[30]; char calle[40]; char ciudad[20]; char provincia[3]; unsigned long cdigo; }Info;

565 6 P GI N NA 2

POLITECNI CO

5.1.1 Referenciando elementos estructurados Para referenciar una variable de la estructura se usa el siguiente formato: nombre_estructura.nombre_variable As, por ejemplo si quisiramos asignarle un valor a la variable cdigo de la estructura Info, tendramos que hacerlo as: Info.codigo = 12345; 5.2 Arrays de estructuras Para declarar un array de estructura, primero se debe definir una estructura y despus declarar una variable de array de ese tipo. Por ejemplo, para declarar una array de cien elementos de la estructura Dat que a sido definida, se debera escribir: struct Dat Info[100]; As, para referenciar a la variable cdigo de la tercera estructura del array, tendramos que hacerlo de la siguiente forma: Info[2].codigo = 12548; 5.2.1 Pasando Estructuras a funciones La variables que conforman una estructuras no son diferentes a la dems variables no estructuradas, por lo tanto se pueden pasar a una funcin de las dos formas ya conocidas, por valor o por referencia. Supongamos tener la siguiente estructura:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

struct T{ char x; int y; float z; char s[10]; }muestra; Si pasamos los valores por valor tenemos: Func(muestra.x); Func1(muestra.z); Func2(muestra.s[3]); // se pasa el valor del carcter x // se pasa el valor float de z // pasa el valor del cuarto elemento de la cadena s

Si pasramos los valores por referencia y con ello la funcin podra alterar el valor de la variable de la estructura, tenemos: Func3(muestra.s); Func4(&muestra.y); // pasa la direccin de la cadena s // pasa la direccin de la varible y

Func5(&muestra.s[2]);// pasa la direccin del tercer elemento de la cadena s 5.2.2 Punteros a Estructuras Se declara un puntero de estructura poniendo el * delante del nombre de la variable de estructura. Por ejemplo: struct T *pmuestra; El principal uso para los puntero de estructuras es la de poder pasarla hacia una funcin por referencia, es decir, pasar su direccin. 565Por ejemplo: 6 P POLITECNI GI CO N NA 2

pmuestra = &muestra; Se pueden referenciar los elemento de la estructura con su puntero de la siguiente forma: (*pmuestra).z = 2.3654; Los parntesis son necesarios alrededor del puntero porque el operador de punto tiene una prioridad ms alta que el operador *. Otra forma de escribir lo mismo es a travs de un nuevo operador: pmuentra->z = 2.3654; 5.2.3 Inicializacin de Estructuras Como cualquier otra variable la estructuras pueden ser inicializadas. Ejemplo: struct T{ char x; int y; float z; char s[10]; }; struct T muestra = {25,5896,52,"Valor"}; 5.3 Campos de Bits Al desarrollar software para los microcontroladores se hace rutinario acceder a bits dentro de los registros que configuran su funcionamiento. Adems muchas veces necesitamos, dentro del software, usar banderas para indicar alguna condicin o estado del sistema y sera un desperdicio de memoria usar un byte completo para solo guardar 2 valores posibles (Verdadero o Falso). Otras veces

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

necesitamos guardar un rango de valores pequeos, que nos alcanzara agrupando una pequea cantidad de bits, por ejemplo, 4 bits para 16 opciones o condiciones. O si quisiramos representar un valor menor a 512 nos alcanzara con 9 bits. Se ve claro que ofrecera una ventaja, un lenguaje que pueda superar la discretizacin de 8 bits de las memorias, permitindonos formar variables de un ancho arbitrario de bits que pueda contener lo ms justo posible la cantidad de valores a representar. El lenguaje C provee un mtodo muy eficiente para poder acceder a los bits de una posicin de memoria, o agrupar un nmero arbitrario de bits formando una variable. Este mtodo se basa en las estructuras. Un campo de bits es justamente un tipo especial de estructura que define la longitud en bits que tendr cada elemento. El formato general de una definicin de campo de bit es: struct nombre_estructura{ type nombre_1:longitud; type nombre_2:longitud; type nombre_3:longitud; type nombre_4:longitud; }variable; Por ejemplo: struct S{ unsigned EEpWr:2; unsigned ApagEquip:1; unsigned ContManual:1; unsigned OptLCD:3; unsigned char ValorPWM; }Op; 565 6 P GI N NA 2 // 0:Nada 1:Erase 2:Espera 3:Write // 1: Apagado en proceso // 1: La salida del los PWM se controla desde el display // unsigned ActEquip:1; // 1:Encendido en proceso

POLITECNI CO

Como se ve en el ejemplo se pueden mezclar tipos estndar como char, int, etc. con campos de bits. 5.4 Uniones En C, una unin es una posicin de memoria que se usa por varias variables similares, que pueden ser de tipos diferentes. La definicin de una unin es similar a la de una estructura, como se demuestra aqu: unin u_tipo{ int i; char ch; }; Como con una declaracin de estructura, esta definicin no declara ninguna variable. Se puede declarar una variable poniendo su nombre al final de la definicin o usando una sentencia separada de declaracin. Para declarar una variable unin cnvt de tipo u_tipo se escribir: unin u_tipo cnvt; En cnvt, tanto el entero i y como el carcter ch, comparten la misma posicin de memoria, a pesar de tener distintas dimensiones. La siguiente figura muestra en la forma en que lo hacen:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Para acceder a un elemento de unin se usa la misma sintaxis que se usara para las estructuras: el operador de punto y el operador de flecha. Si se est accediendo el elemento de unin directamente, se usa el operador punto. Si se accede a la unin a travs de un puntero, se usa el operador flecha. Por ejemplo, para asignar el entero 20 al elemento i de cnvt, se escribir: cnvt.i = 20; En el ejemplo siguiente, el cdigo pasa un puntero a la variable de unin del tipo u_tipo: void func(unin u_tipo *un) { un->i = 20; } En C se usan frecuentemente las uniones para extraer los bytes constituyentes de una variable no char. Supongamos que tenemos un entero y queremos extraer y byte bajo y el byte alto, para resolver este problema, el C no ofrece muchas alternativas, pero usando una unin podramos hacerlo de la siguiente forma: unin pw{ int i; char bytes[2]; }Entero; Si quisiramos transmitir los bytes constituyentes de la variable entera ValNum se podra hacer de la siguiente forma: Entero.i = ValNum; Rx(Entero.bytes[0]); 565Rx(Entero.bytes[1]); 6 P POLITECNI GI CO N NA 2 // Se transmite el byte bajo de ValNum // Se transmite el byte alto de ValNum

Las uniones podran contener estructuras y viceversa. Ejemplo: struct S{ unsigned EEpWr:2; unsigned ApagEquip:1; unsigned ContManual:1; unsigned OptLCD:3; unsigned char ValorPWM; }; unin Con{ struct S Flag; int Banderas; } Proceso; Supongamos que queremos poner todos los valores de la estructura a cero, una alternativa sera de la siguiente forma: Proceso.Banderas = 0; // Todos los bits de la estructura se iran a cero // 0:Nada 1:Erase 2:Espera 3:Write // 1: Apagado en proceso // 1: La salida del los PWM se controla desde el display // unsigned ActEquip:1; // 1:Encendido en proceso

Para poner a 1 el bit ActEquip lo haramos: Proceso. Flag.ActEquip = 1;

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

5.5 Enumeraciones Una enumeracin es un conjunto de constantes enteras. A la enumeracin se le asigna un nombre que, a todos los efectos, se comporta como un nuevo tipo de datos, de manera que las variables de ese tipo son variables enteras que solo pueden contener los valores especificados en la enumeracin. La definicin de una enumeracin suele hacerse as: enum nombre_enumeracin {constante1 = valor1, constante2 = valor2, ..., constanteN = valorN }; Por ejemplo: enum dias_semana {Lunes=1, Martes=2, Miercoles=3, Jueves=4, Viernes=5, Sabado=6, Domingo=7 }; Las variables que se declaren del tipo dias_semana sern, en realidad, variables enteras, pero slo podrn recibir los valores del 1 al 7, as: dias_semana dia; dia = Lunes; dia = 1; /* Las dos asignaciones son equivalentes */ Si no se especifican los valores en la enumeracin, C les asigna automticamente nmeros enteros a partir de 0. Por ejemplo: enum dias_semana { Lunes, Martes , Mircoles, Jueves, Viernes, Sabado, Domingo}; en la anterior definicin, la constante Lunes valdr 0, Martes, 1, etc. Podemos alterar el orden en que se desarrolla la numeracin de la siguiente forma: enum dias_semana { Lunes, Martes , Mircoles, Jueves, Viernes=100, Sabado, Domingo}; Entonces tendremos: Lunes= 0, Martes= 1, Miercoles= 2, Jueves= 3, Viernes= 100, Sabado= 101, Domingo= 102

565 6 P GI N NA 2

POLITECNI CO

MPLAB C18 -VARIOS 6.1 Cdigo START-UP Todo cdigo en C comienza en la direccin de memoria 0, que es el vector de reset de los microcontroladores PIC. Desde el vector de reset se salto a una funcin (start-up code) que inicializa los registros FSR1 y FSR2 para referenciar a la pila (software stack), opcionalmente se llama a una funcin para inicializar la seccin idata ( donde estn los datos que deben ser inicializados) desde la memoria de programa, y por ultimo llama en un loop a la funcin main(). Si el cdigo de arranque (start-up code), inicializa o no la seccin idata, va a ser determinado por cual de todas las opciones posibles de cdigo de arranque fue usado. Los mdulos c018i.o y c018i_e.o realizan la inicializaciones, mientras que los mdulos c018.0 y c018_e.o no lo hacen. Segn el ANSI C todas las variables definidas como static que no fueron inicializadas, deben ser explcitamente puestas a cero. Los cdigos de start-up antes mencionados no lo hacen, para ello hay que usar los siguientes: c018iz.o o c018iz_e.0. Para realizar la inicializacin de la memoria de datos, el enlazador MPLINK crea una copia de los datos a inicializar en la memoria de programa que el cdigo de arranque se encarga de copiar a la memoria de datos. La eleccin del cual cdigo de arranque que se usa se decide alterando el archivo .lkr del microcontrolador que estemos usando en el proyecto. El siguiente texto es el comienzo del contenido del archivo 18F242.lkr:
// File: 18f242.lkr // Sample linker script for the PIC18F242 processor LIBPATH . FILES c018i.o FILES clib.lib FILES p18f242.lib

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

6.2 Mezclando C y Assembler Es posible que cdigo en C trabaje en forma coordinada con cdigo escrito en lenguaje Assembler. Es decir, se puede llamar a rutinas de C desde Assembler y rutinas de Assembler desde el C, para que esto funcione adecuadamente, tambin tiene que poderse compartir variables entre estos cdigos escritos en diferentes lenguajes. A continuacin se ver cada caso particular. 6.2.1 Llamando a una function C desde Assembler Cuando se llama a una funcin C desde assembler: La funcin C debe ser declarada como un smbolo extern en el archivo de assembler Para llamar a la funcin se debe usar la sentencia de assembler call o rcall. Ejemplo: Dado el siguiente prototipo para la funcin C: char add(auto char x, auto char y); El parmetro auto pone los datos en la pila (stack) desde la derecha a la izquierda. Para datos multi-bytes el byte ms bajo es puesto primero en la pila. Para llamar a la funcin add con los valores x = 0x61 e y = 0x65, el valor de y debe ser puesto primero en la pila, seguido del valor de x. El valor retornado, como es de 8 bits, ser puesto en el registro WREG. La forma de llamar a esta funcin desde un fuente de assembler ser: EXTERN add ... MOVLW 0x65 MOVWF POSTINC1 ; y = 0x65 se carga este valor en la pila 565MOVLW 0x61 6 P POLITECNI GI CO N NA 2 ; funcin definida en algn modulo C

MOVWF POSTINC1 ; x = 0x61 se carga este valor en la pila CALL add MOVWF result ; el resultado de la funcion es retornado en WREG ... Ejemplo: Dado el siguiente prototipo de funcin C: int sub(auto int x, auto int y); Para llamar a esta funcin con los valores x = 0x7861 e y = 0x1265, el valor de y debe ser puesto primero, seguido por el valor de x. El valor retornado, puesto que es de 16 bits, ser retornado en los registros PRODH:PRODL. EXTERN sub ... MOVLW 0x65 MOVWF POSTINC1 MOVLW 0x12 MOVWF POSTINC1 ; y = 0x1265 se la coloca en la pila MOVLW 0x61 MOVWF POSTINC1 MOVLW 0x78 MOVWF POSTINC1 ; x = 0x7861 se la coloca en la pila CALL sub MOVFF PRODL, result MOVFF PRODH, result+1 ; el resultado es devuelto en PRODH:PRODL ... 6.2.2 Llamando a una funcin Assembler desde C ; definida en un modulo C

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

Cuando se llama a una funcin de assembler desde el C: El nombre de la funcin debe ser declarada global en el modulo ASM La funcin de assembler debe ser declarada como extern en el modulo C La funcin es llamada desde C usando notacin estndar de C para llamar una funcin Ejemplo, suponiendo la siguiente funcin en assembler: UDATA_ACS delay_temp asm_delay GLOBAL asm_delay SETF delay_temp not_done DECF delay_temp BNZ not_done done RETURN END Para llamar la funcin asm_delay desde el archivo de C, se debe agregar un prototipo externo para la funcin de assembler y llamar la funcin usando el formato estndar de C. extern void asm_delay (void); void main (void) { asm_delay (); } 565 6 P GI N NA 2 ; exporta el nombre de tal forma que RES 1 CODE

POLITECNI CO

Otro ejemplo: Dada la siguiente funcin de assembler: INCLUDE "p18c452.inc" CODE asm_timed_delay GLOBAL asm_timed_delay ; se hace visible para el linkeador not_done ; el dato del tiempo es pasado en la pila y debe extraerse de la misma ; restando 1 al valor del puntero de pila FSR1 MOVLW 0xff DECF PLUSW1, 0x1, 0x0 BNZ not_done done RETURN END Al igual que antes para llamar a la funcin desde un fuente de C, se debe agregar un prototipo para la funcin de assembler y la funcin debe ser llamada en forma estndar desde el cdigo C. extern void asm_timed_delay (unsigned char); void main (void) { asm_timed_delay (0x80); } 6.2.3 Usando variables C en Assembler Cuando usamos variables de C en Assembler: ; se carga el valor 1 ; se extrae el valor de la pila y se la almacena en WREG

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

La variable de C debe ser global en el cdigo C La variable de C debe ser declarada como un smbolo extern en el archivo de Assembler Dado el siguiente cdigo en C: unsigned int c_variable; void main(void) { . } Para poder modificar la variable c_variable desde el assembler, en el archivo fuente de Assembler se le debe agregar una declaracin extern para esta variable. EXTERN c_variable ; variable definida en el modulo de C MYCODE asm_function GLOBAL asm_function MOVLW 0xff MOVWF c_variable MOVWF c_variable+1 done RETURN END 6.2.3 Usando variables de Assembler en C Cuando usamos variables de Asseembler en C: 565 6 P GI N NA 2 La variable debe ser declarada como global en el modulo de ASM ; se va a cargar el valor 0xffff en la variable CODE

POLITECNI CO

La variable debe ser declarada como extern en el modulo de C Dado el siguiente cdigo escrito en Assembler: MYDATA asm_variable UDATA RES GLOBAL END Para poder usar la variable asm_variable desde el fuente de C, se debe agregar una definicin de extern para la variable en el cdigo C. A partir de ah se puede usar como cualquier otra variable de C. extern unsigned in asm_variable; void Funcion(void) { asm_variable = 0x1234; } 2;se guardan dos posiciones de memoria para la variable asm_variable

6.3 Inline Assembler El MPLAB C18 permite provee una assembler interno que usa una sintaxis similar a la del MPASM. Este assembler permite colocar instrucciones de assembler, en medio del cdigo C. El bloque de cdigo assembler, debe comenzar con la directiva _asm y terminar con la _endasm. Este assembler tiene algunas limitaciones respecto del MPASM. Ejemplo: _asm MOVLW 10 MOVWF count, 0 // Mueve el decimal 10 a la variables count

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

start: DECFSZ count, 1, 0 GOTO done BRA start done: _endasm

// Realiza un loop hasta que count es cero

Es recomendable limitar el uso del assembler inline a un mnimo. Cualquier funcin conteniendo un porcin de assembler inline no ser optimizada por el compilador. Si se necesita escribir fragmentos largos de assembler conviene usar el MPASM y enlazarlos con los mdulos de C a travs del linqueador. 6.4 Interrupciones en C18 Los PIC 18Fxxxx poseen la posibilidad de elegir 2 niveles de prioridad para las interrupciones: la interrupcin de baja prioridad cuyo vector de interrupcin est en la direccin de la Flash 0x18 y la interrupcin de alta prioridad cuyo vector de interrupcin est en la direccin de la Flash 0x08. La interrupcin de alta prioridad puede interrumpir a la de baja prioridad, pero no al revs. Cualquier fuente de interrupcin del microcontrolador (Timer, conversor AD, PWM, etc) puede ser asignado indistintamente a una de las dos prioridades. Sabemos que al producirse una interrupcin, si que esta estaba habilitada, el microcontrolador carga el contador de programa con la direccin de memoria 0x08 para la de alta prioridad o la 0x18 para la de baja prioridad. La pregunta que tenemos que hacernos es Cmo lograremos manejar las interrupciones desde el lenguaje C?. Para lograr esto, no valdremos de una directiva de compilacin del C llamada #pragma esta nos permite ubicar cdigo en puntos especfico de la memoria. En el MPLAB C18 disponemos de las siguientes directivas especficas: #pragma interruptlow fname #pragma interrup fname

565 6 P GI N NA 2

POLITECNI CO

El pragma interrupt declara a una funcin ser una rutina de servicio de interrupcin de alta prioridad. El pragma interruplow declara a una funcin ser una rutina de servicio de interrupcin de baja prioridad. Una interrupcin suspende la ejecucin de la aplicacin actual, guarda el su contexto, y transfiere el control a su rutina de servicio de interrupcin (RSI). Una vez completada la RSI, la informacin del contexto previo es restaurada y se reasume la ejecucin normal de la aplicacin. El contexto mnimo guardado y restaurado por una interrupcin son los registros: WREG, BSR and STATUS. Una interrupcin de alta prioridad usa los registros sombras para guardar y restablecer este contexto mnimo, mientras que una interrupcin de baja prioridad usa la pila del programa. Como consecuencia de esto la interrupcin de alta prioridad puede entrar y salir de las interrupciones mucho ms rpido que la de baja prioridad. 6.4.1 Rutina de Servicio de Interrupcin (RSI) En MPLAB C18 una RSI, es como cualquier otra funcin de C, que puede tener variables locales y acceder a variables globales, sin embargo, esta debe ser declarada sin parmetros y no puede retornar un valor ya que una RSI es invocada asincrnicamente en respuesta a una interrupcin de hardware. Las variables globales que son usadas por el programa principal y por la RSI deben ser declaradas volatile. Las RSI deben ser invocadas solo por los eventos de hardware, y no por otra funcin C del programa. Debido a que la RSI usa la instruccin RETFIE para salir de la interrupcin en vez de la interrupcin RETURN usada por el resto de las funciones. 6.4.2 Vector de Interrupcin El MPLAB C18 no coloca automticamente, en el vector de interrupcin correspondiente (0x08 o 0x18) un salto hacia muestra rutina de interrupcin, sino que, es el programador que debe colocar una instruccin goto hacia las rutinas RSI. Ejemplo:

POLITECNIC O

57

Lenguaje C para Microcontroladores PIC

#include <p18cxxx.h> void low_isr(void); void high_isr(void); /* * Para el PIC18 el vector de interrupcin de baja prioridad est a partir en : 0x18 */ #pragma code low_vector=0x18 void interrupt_at_low_vector(void) { _asm GOTO low_isr _endasm } #pragma code void low_isr (void) { /* ... */ } /* * Para el PIC18 el vector de interrupcin de alta prioridad est a partir en : 0x08 */ #pragma code high_vector=0x08 void interrupt_at_high_vector(void) { _asm GOTO high_isr _endasm } #pragma code void high_isr (void) { /* ... */ } 565 6 P GI N NA 2 /* retorna a la seccin de cdigo por defecto */ #pragma interrupt high_isr /* retorna a la seccin de cdigo por defecto */ #pragma interruptlow low_isr

POLITECNI CO

You might also like