Lenguaje C para Microcontroladores Parte 1 Prof. Ing. Julián R. Camargo.

1 Lenguaje C para Microcontroladores Parte 1 Prof. Ing. J...
Author: María Jesús Chávez Castillo
0 downloads 0 Views

1 Lenguaje C para Microcontroladores Parte 1 Prof. Ing. Julián R. Camargo

2 Lenguaje C para Microcontroladores Ventajas: El Lenguaje C un Lenguaje Universal. Portabilidad del Código. El Lenguaje C Permite el Fácil Trabajo en Equipo. El Lenguaje C Permite la Modularización. El Lenguaje C es una Herramienta Rápida de Programación. El Lenguaje C permite incorporar código en Ensamblador. El Compilador usa todo el Set de Instrucciones. Desventajas: El Lenguaje C ocupa mayor espacio en Memoria de Programa Los buenos Compiladores tienen un costo, los Ensambladores no. Los Compiladores son a su vez programas y pueden tener “bugs”.

3 Componentes de un Programa en C Un proyecto en C esta dividido en varios archivos, cada uno contiene una parte del texto de la aplicación. Algunas partes están previamente escritas y son usadas como librerías. Otras partes pueden estar escritas en lenguaje ensamblador donde el C no sea lo suficientemente óptimo, o no permita el acceso directo a los recursos del procesador. Cada uno de los archivos del proyecto deberá ser compilado, transformando el código texto en un archivo llamado objeto que es relocalizable (aun sin dirección fija en la memoria del microcontrolador). Luego que los archivos son compilados, el programa “linker” o enlazador, es usado para agrupar en un todo los archivos resultantes incluyendo las librerías para así producir el archivo ejecutable, el cual contiene un formato específico para poder ser transferido a la máquina que finalmente lo ejecutará.

4 Cada texto en C contiene un conjunto de líneas, cada línea contiene a su vez caracteres y es finalizada con una nueva línea (un Line Feed y un Return). El compilador de C permite que varias líneas sean concatenadas en una sola línea, con una longitud que no exceda los 511 caracteres para cumplir estrictamente con el estandard ANSI C. Los comentarios son parte del texto y aunque no tienen ningún significado para el compilador son parte importante del texto, ya que brindan la posibilidad de entender el código que a su vez facilita su modificación. Un comentario inicia con la secuencia /* y finaliza con la secuencia */, que puede cubrir varias líneas. Como una extensión del estándar ANSI C la mayoría de los compiladores aceptan los comentarios en estilo C++, los cuales inician con la secuencia // y finalizan con una nueva línea de código. A = b; // este es un comentario C = d; /* este es otro tipo de comentario Que cubre varias líneas de C*/

5 Conceptos de programación en C Identificadores Un identificador es usado para dar nombre a un objeto. Este empieza por una letra o el carecer “underscore” _, seguido por una letra o un dígito. Los identificadores son sensibles a mayúsculas y minúsculas, así que vBle1 es diferente de vBLE1 e identifican objetos diferentes. Un identificador puede contener hasta 255 caracteres pero no se recomiendan identificadores con más de 20 caracteres.

6 Palabras Reservadas Son usadas por el lenguaje C para describir algo especial y propio de dicho lenguaje. Es usado en declaraciones para describir el tipo básico de un objeto, o dentro de una función para describir la ejecución de una secuencia. Una palabra reservada no puede ser usada como nombre de objeto. Todas las palabras reservadas se escriben en minúsculas, de tal manera que la versión en mayúscula esta disponible como nombre de objetos, aunque no es una buena práctica hacerlo.

7 Las palabras reservadas son: auto double int struct break else long switch case num register typedef charreturn union const float srt unsigned extern static continue for signed void default goto sizeof volatile do if static while Variables Las variables son localizaciones de memoria que contienen algún valor. El programa podrá usar su valor o su dirección para acceder o modificar el dato que contiene. Dependiendo de los valores máximos y mínimos que ha de tomar una variable, se hace su declaración, y de igual manera si tomara valores solo positivos o tomara tanto valores positivos como negativos se define su signo.

8 Constantes Una constante es usada para describir un valor numérico o una cadena de caracteres. Las constantes numéricas pueden ser expresadas como constantes reales, constantes enteras o de carácter. Las constantes enteras pueden ser expresadas en base decimal, binaria, octal o hexadecimal. Operadores y Signos de Puntuación Un operador es usado para describir una operación aplicada a uno o varios objetos. Los operadores son ampliamente usados en muchas expresiones y en diversos tipos de declaraciones. El compilador tratara de solucionar las operaciones que le sean posibles en tiempo de compilación, previniendo así que sea el microcontrolador quien lo deba hacer.

9 Los operadores consisten en una secuencia de caracteres no alfanuméricos (1 en su mayoría, a veces 2). Un signo de puntuación se usa para separar o terminar una lista de elementos. Operadores Aritméticos + - * / % = += -= *= /= ++ -- Operadores Booleanos& | ~ ^ == && || |= &= != ? ! ^= Operadores de Comparación> = > >= Manejo de apuntadores -> & *. Paréntesis, Corchetes[ ] { } ( ) Puntuación; :, Compilación y Declaración # ## Algunos de ellos son usados como operadores en algunos casos y en otros como signo de puntuación. Eventualmente son usados por pares, tal es el caso de ( ), [ ], { }.

10 Declaración de datos en C Declaración de Constantes Para declarar constantes se tienen dos formas dependiendo de la naturaleza de la misma: Cuando se desea que el valor sea reemplazado a lo largo del código se usa la sentencia #define con el signo ‘#’ en la primera columna del editor. Algunos ejemplos: #define FIN_CADENA ‘/r’ /*constante */ #define NRO_PI3.1415926536 #define TRUE1 #define FALSE0 #define AREA_CIRCUNFER NRO_PI*RADIO_MAX*RADIO_MAX

11 El segundo tipo de constante se refiere a aquellas que por la naturaleza deben ocupar un lugar en la memoria flash y aunque su valor no se puede cambiar (por ser constante) el código deberá acceder a una dirección fija para obtener el valor. La utilidad de esta variable radica en que su valor esta en una dirección conocida y fija, la cual se hace útil al momento de necesitar algún modificador especial o si el valor necesitara ser cambiado en tiempo de ejecución, mediante un programa especial de la flash, lo cual repercute en un cambio en la variable aunque esta se declare como constante, sin perder el valor cuando la energía del sistema falle o se genere un reset. Ejemplos de declaración de este tipo de variables: const unsigned char peso_maximo=123; const unsigned char temperatura_minima=12;

12 Declaración de Variables Al momento de declarar una variable se deben considerar 2 aspectos: El signo de la variable: Es decir, ¿la variable tomara valores positivos, negativos o ambos?, si para el caso es indiferente se selecciona el positivo, los procesadores de 8 y 16 bits ejecutarán más rápido operaciones con valores positivos (incrementos, decrementos, comparaciones, etc.), que con los valores negativos. Tamaño: ¿Cual es el valor mínimo y máximo que tomara la variable? Se deberá seleccionar el tipo mínimo que cubra todos los variables de la memoria.

13 Para declarar o separar espacio para una variable se debe elegir un nombre, por sintaxis deberá empezar siempre por una letra y puede tener combinaciones alfanuméricas incluyendo el carácter ‘_’ (underscore), es útil por práctica de programación en equipo, que la primera letra sea minúscula, de esta forma permitirá al programador conocer en cualquier parte del programa que se trata de una variable. Dependiendo de la longitud en bytes requeridos para almacenar todos los posibles valores de la variable se puede hacer uso de los tipos de almacenamiento de datos según el ANSI C: charintlong floatdouble

14 char (carácter), Reserva 1 byte en memoria, la variable de este tipo puede tomar 256 posible valores, si es positiva desde 0 hasta 255, si tiene signo desde -128 hasta +127. int (entero), Reserva 2 bytes continuos en memoria para el almacenamiento de la variable. Este tipo puede tomar valores desde 0 hasta 65535 si la variable es positiva y desde -32.768 hasta +32.767 si la variable tiene signo. long Reserva 4 bytes continuos en memoria para almacenar el valor de la variable. En valor con signo va desde - 2.147.483.648 a +2.147.483.647, en valor sin signo va desde 0 a 4.294.967.296 (2 32)

15 float El tipo de datos flotante IEEE32, reserva 4 bytes en memoria. Estas variables se representan por medio de la mantisa, que es un número entre 0.1 y 1.0, y un exponente que representa la potencia de 10 por la que hay que multiplicar la mantisa para obtener el número deseado. Tanto la mantisa como el exponente pueden ser positivos o negativos. De los 32 bits se utilizan 24 para la mantisa (1 bit para el signo y 23 para el valor) y 8 para el exponente (1 bit para el signo y 7 para el valor).

16 double Representa una variable de punto flotante de doble precisión. Reserva 8 bytes (64 bits) continuos en memoria. Se representan por una mantisa de 53 bits (1 para el signo y 52 para el valor). El exponente 11 bits (1 para el signo y 10 para el valor). Para el tipo double IEEE64 el número mínimo será 2.2259738585972014E-308, mientras el máximo será 1.7976931348623157E+308.

17 Los tipos de datos al momento de realizar su declaración, pueden ir acompañados de una o varias palabras reservadas del C, las cuales le proporcionan atributos especiales a la variable, sin cambiar la medida en bytes que ocupa la variable, pero si cambian el tratamiento y la forma como son manejadas y optimizadas. Estos modificadores deben ir antes del tipo de dato. Para el compilador de C el modificador brinda información adicional sobre la variable, con el objetivo de darle un tratamiento adecuado cuando genera el código en ensamblador que va a operar la variable, de esta manera generar código mas óptimo, veloz y robusto. La utilidad del modificador radica también en operaciones de variables que al momento de incurrir en una operación resulten ser de diferente naturaleza, de allí que sea necesario hacer un molde (cast) que permita a una variable declarada ajustarse a la naturaleza de otra variable.

18 Modificador “unsigned” El modificador "unsigned" le proporciona a la variable la característica positiva o lo que es lo mismo que su menor valor será 0 (cero), mientras su valor máximo será 255. El modificador unsigned interpreta el bit más significativo del dato como valor y no como signo. El tipo de datos positivo sigue siendo el tipo de datos mas eficiente para el manejo de una variable, de allí que si al momento de declarar la variable es indiferente el signo que esta manejara, se debe decidir que esta sea positiva, con lo cual su declaración y manejo será mejor y mas sencillo para la máquina, porque le permitirá al compilador generar un código más compacto y eficiente.

19 Modificador “signed” El modificador "signed" le indicara al compilador que el bit mas significativo de la variable corresponde al signo (1: negativo, 0:positivo), los bits restantes corresponden al complemento a dos del valor absoluto del valor de la variable. El C define este modificador por defecto, de tal manera que si una variable se declara sin el modificador de signo, asumirá el "signed“. Algunos ambientes de compilación permiten cambiar esta opción por defecto, sin embargo no es recomendable acudir a esta opción, y en su lugar se recomienda al momento de la declaración, ser específicos en el signo sin asumir los valores por defecto, así se independiza de la configuración especifica del compilador, creando un código mucho mas portable.

20 Modificador "volatile" El modificador "volatile", le indicara al compilador que el valor de una variable podría cambiar por medios no especificados explícitamente por la secuencia lógica del programa, en su lugar un agente externo como puede ser el hardware externo o una interrupción podrían cambiar su valor. Por tal razón cada vez que el programa haga una referencia a su valor no deberá asumirlo, sino que deberá hacer acceso (sea de lectura o escritura) a la dirección de la variable. El compilador no optimizará ningún uso de la variable y transportara línea por línea de C al ensamblador tal como fue digitado el código. El uso de este modificador se hace útil para todas aquellas variables que van a ser manipuladas dentro de una interrupción, ya que esta puede ocurrir en lugares no predecibles del código, y por tal motivo no se pueden asumir valores pasados de la variable. También se obliga su uso a aquellas variables cuyo valor dependa del hardware externo conectado al microcontrolador (como son los puertos) o registros internos del mismo.

21 Sin “volatile” Código en C Código Generado en Ensamblador unsigned char vble1;vble1 equ $80 void main(void){main: vble1 = 149;  MOV #149T,vble1 ;código no generado vble1 = 43;  MOV #43T,vble1 ;código no generado vble1 = 49:  MOV #49T,vble1 if(vble1 > 10){  LDA vble1 ;código no generado, ya que la condición SIEMPRE es Verdadera  CMPA #10T ;código no generado  BMI label1 ;código no generado PTA = vble1;  MOV vble1,PTA else{label1: PTA = 0;  CLR PORTA ;código no generado } vble1 = vble1 + 1;  INC vble1 vble1;  LDA vble1 ; código no generado for(;;){label2:BRA label2: ;loop infinito } }  RTS

22 Con “volatile” Código en CCódigo Generado en Ensamblador volatile unsigned char vble1;vble1 equ $80 void main(void){main: vble1 = 149;  MOV #149T,vble1 vble1 = 43;  MOV #43T,vble1 vble1 = 49:  MOV #49T,vble1 if(vble1 > 10){  LDA vble1  CMPA #10T  BMI label1 PTA = vble1;  MOV vble1,PTA }else{label1: PTA = 0;  CLR PTA } vble1 = vble1 + 1;  INC vble1 vble1;  LDA vble1 for(;;){label2:BRA label2: } }  RTS

23 Modificador “near” El modificador "near" le solicita al compilador asignarle a la variable una dirección dentro del rango de direcciones directa del mapa de memoria del microcontrolador (direcciones menores a 0x00FF que correspondan a la RAM). Así su manipulación será muy eficiente, porque para realizar un acceso a la variable, este se hará mediante direccionamientos directos, los cuales son muy eficientes tanto en tiempo de ejecución como en el espacio ocupado en memoria de programa. Es recomendable el uso del modificador "near" sobre aquellas variables que serán de uso muy frecuente en el programa porque permiten optimizar mucho el código, también para las variables que requieren acceso rápido en zonas críticas del programa o rutinas que deben ser ejecutadas de manera rápida.

24 Modificador “far” El modificador "far" asignara a la variable definida una dirección fuera de la zona directa de memoria o zona extendida (direcciones mayores a 0x00FF). En este caso el compilador deberá accesar la variable con direccionamientos extendidos, los cuales típicamente ocupan 3 bytes de longitud (1 byte opcode + 1 byte dirección alta + 1 byte dirección baja).

25 Modificador “register” El modificador “register” dedica uno de los registros del modelo de programación para la variable modificada. Se hace con el objetivo de realizar un acceso muy rápido a la variable en una función, debido a que resulta mucho más eficiente que su acceso si estuviera declarada en la memoria RAM. Este modificador solo aplica para las variables locales, nunca para las globales debido a que el compilador no puede dedicar uno de los registros de trabajo exclusivamente a una variable. El modificador es usado cuando se requiere realizar el manejo más óptimo posible, tanto en velocidad de ejecución, como en espacio en memoria de programa, sobre una variable local.

26 Modificador “static” El modificador "static" se aplica para localizar una variable en la RAM fija del mapa de memoria. Así una variable ocupa una dirección única, lo que significa que las localizaciones en memoria que ocupen, serán destinadas solo y únicamente a la variable. Además que conservará su valor a menos que explícitamente se actúe sobre la variable, ningún otro agente o módulo podrá interactuar sobre ella. Así se evita que módulos externos a la variable puedan usarla (leerla o escribirla) y efectuar cambios que puedan alterar el funcionamiento de un módulo particular. Las variables que contengan este modificador, no serán reconocida fuera del módulo donde esta declarada, únicamente en el módulo local. Si la variable esta declarada dentro de una función solo será reconocida y por lo tanto solo podrá ser operada dentro de la misma función, haciéndose invisible para el resto del módulo y por consiguiente para el resto del proyecto.

27 Modificador “extern” Este modificador sobre una variable realiza una seudo- declaración que especifica una variable que esta declarada en otro módulo o archivo del proyecto. Se usa para indicar en un archivo que la declaración de la variable ya existe y esta en otro módulo o archivo del proyecto. Si se considera un proyecto que tiene varios módulos.C y.H, cada uno de los módulos contiene variables propias, las cuales se declaran mediante el modificador “static”. Sin embargo existirán algunas variables que son globales y pueden modificarse o leerse en varios módulos, solo en uno de ellos se realizará la declaración de la variable, los demás si desean usar esta variable deberán tener la misma declaración antecedida del modificador “extern”.

28 Declaración de Macros Un macro es una secuencia definida de instrucciones, las cuales son incluidas en el código editado, cada vez que es invocado el nombre del macro. Un macro ayuda además a que el código quede parametrizable, lo que permitirá modificarlo de forma sencilla y segura. Su forma de definición es como sigue: #define NOMBRE_MACRO VALOR_O_SECUENCIA_EQUIVALENTE Aunque no es obligatorio si es estándar que el NOMBRE_MACRO se defina en letras mayúsculas cuando su equivalencia es un valor constante, y combinaciones mayúsculas minúsculas, cuando se trata de una secuencia de instrucciones. Se acostumbra además agregar un comentario al final, el cual indica las unidades del valor especificado y una breve descripción. Ejemplo: #define TEMP_MAX 200 //[=] centígrados, Temperatura Limite #define Led1_On() PTC_PTC3 = 1; DDRC_DDRC3 = 1 //enciende Led

29 Operadores Aritméticos Un operador aritmético es un símbolo que indica al compilador que lleve a cabo manipulaciones matemáticas. OperadorOperación Realizada -Resta o substracción +Suma o adición *Multiplicación o producto /Cociente entero de la división %Residuo entero de la división --Decremento en 1 ++Incremento en 1

30 - Resta El operador resta opera sobre 2 variables o expresiones y dará como resultado la substracción de ellos. Es responsabilidad del usuario conservar o no el signo del resultado. vble3 = vble1 – vble2; + Suma El operador suma, realiza la operación sobre 2 variables o expresiones. El programador deberá cuidar de posicionar el resultado en la variable adecuada, en caso de existir sobre flujo por ejemplo. unsigned char vble1,vble2; unsigned int resultado; resultado = (unsigned int)vble1 + (unsigned int)vble2;

31 * Multiplicación Este operador realiza la multiplicación de 2 expresiones, variables o constantes, entregando como resultado un nuevo dato y conservando el signo, según la ley de multiplicación. unsigned char vble1,vble2; unsigned long resultadoMul; resultadoMul = (unsigned long)vble1*(unsigned long)vble2; / Cociente de División El operador aplicado sobre un dato entero o carácter, entregará el cociente entero de la división, cualquier resto es truncado. Así que si se opera: 10/3 el resultado entregado, será 3.

32 % Residuo de División El operador aplicado sobre 2 expresiones, constantes o variables enteras (nunca flotantes), entregará como resultado el residuo o módulo de la división. Así por ejemplo: int x,y,result; x = 10; y = 3; result = x%y; // result quedara con el dato 1 ++ Incrementar en 1 Este operador adiciona un 1 a la variable sobre la cual esta aplicado, que es equivalente al operador suma con 1 así: vble = vble + 1; es equivalente a: vble++; y equivalente a: ++vble;

33 -- Decrementar en 1 El operador - - resta 1 a la variable sobre el cual esta aplicado, que es equivalente al operador resta con 1 así: vble = vble - 1; es equivalente a: vble--; y equivalente a: --vble; Existe una diferencia cuando los operadores - - y ++ se utilizan en una expresión. Cuando alguno de estos operadores precede a su operando, el C lleva a cabo una operación de incremento o decremento antes de usar el valor del operando (pre-incremento o pre- decremento), mientras que si el operador esta después de la variable, el C utiliza su valor después de incrementarlo o decrementarlo (post-incremento o post- decremento).

34 Considérese lo siguiente: x = 5; y = x++; Al final la variable y tomará el valor de 5 mientras que x tomará el valor de 6. Mientras que: x = 5; y = ++x; La variable y tomara el valor de 6, lo mismo que la variable x.

35 Operadores relacionales Todas las operaciones relacionales dan sólo dos posibles resultados: VERDADERO ó FALSO. En C, FALSO está representado por un valor entero nulo (cero) y VERDADERO por cualquier valor diferente de cero. SIMBOLODESCRIPCIONEjemplo mayor que if(a > b) = b) ==es exactamente igual que if(a == b) !=diferente que if(a != b)

36 Operadores de Comparación Cuantitativa Los operadores, =, dentro de una expresión, son usados para comparar el valor de 2 cantidades, arrojando como resultado un VERDADERO o un FALSO dependiendo del resultado de la comparación de ambos valores. Se usan también en varias sentencias de C como el while, el do{}while y el for, para generar iteraciones o sacar el programa de la sentencia bajo una condición o valor determinado de una variable. Cuando se comparan dos variables tipo char el resultado de la operación dependerá de la comparación de los valores ASCII de los caracteres contenidos en ellas. Así el carácter ‘a’ (ASCII 97) será mayor que el ‘A’ (ASCII 65) ó que el ‘9’ (ASCII 57).

37 Operador de Igualdad Los operadores == y != realizan la comparación de sus 2 valores a lado y lado de ellos y arrojan como resultado VERDADERO o FALSO, dependiendo del resultado de la comparación. Son útiles cuando se intenta verificar la igualdad de 2 valores y con ello tomar alguna decisión dentro de sentencias while, do{}while, if.. else, for. Uno de los errores más comunes es confundir el operador relacional “es exactamente igual que” (= =) con el de asignación (=). La expresión (a=b) copia el valor de b en a, mientras que (a = = b) retorna un cero, si a es distinto de b ó un número distinto de cero si son iguales.

38 Operadores Lógicos Booleanos Los operadores lógicos booleanos y de forma similar a los relacionales entregan solo 2 valores: VERDADERO o FALSO y se basan en las reglas de la algebra booleanas. result = OPERADOR_BOOL SIMBOLODESCRIPCIONEjemplo ||ORif(a || b) &&ANDif(a && b) !NOTif(!a)

39 Operadores a nivel de Bit El lenguaje C y a diferencia de otros lenguajes de programación, permite realizar la operación de expresiones a nivel de bit SIMBOLODESCRIPCIONEjemplo |OR bita = a | 0x80; a |= 0x80; &AND bita = a & 0x01; a &= 0x01; ~NOT bita = a & (~0x80); a &= (~0x80); ^XOR bita = a ^ 0x80; a ^= 0x80;

40 Los operadores a nivel de bit operan sobre las 2 expresiones a lado y lado de su expresión bit con bit correspondiente de la expresión, así: result = OPERADOR_BIT Las operaciones a nivel de bits no se pueden usar sobre los tipos float, double, long double u otros tipos más complejos. Las operaciones sobre bits son frecuentes en aplicaciones de controladores de dispositivos, tales como programas de modem, rutinas de archivo de disco, impresoras, debido a que permiten enmascarar ciertos bits, como el de paridad o de estados.

41 Operadores de Desplazamiento Los operadores de desplazamiento de bits >> y > o a la izquierda > nro_bits_desplazar; resultado = vble >= o