You are on page 1of 22

Sistemas Embebidos 2012

Sistemas embebidos
Apunte de C embebido
La intencin de este apunte es brindar un breve repaso sobre ciertas caractersticas del
lenguaje de programacin C haciendo principal hincapi en las metodologas y formas que
sern utilizadas al programar sistemas embebidos con microcontroladores.

1. Preprocesador y Macros
Uno de los temas que queda mencionado muy superficialmente en los cursos bsicos de
programacin en C son las caractersticas del preprocesador y sus directivas. El
preprocesador de C es el primer programa invocado por el compilador y tiene ms o
menos su propio lenguaje el cual puede ser una herramienta muy poderosa para el
programador. Todas las directivas de preprocesador o comandos inician con un # como
por ejemplo #include, #define, #ifdef, #ifndef, etc.
Las ventajas que tiene usar el preprocesador son:
Los programas son ms fciles de desarrollar.
Son ms fciles de leer.
Son ms fciles de modificar.
El cdigo de C es ms transportable entre diferentes arquitecturas.
Particularmente, en nuestro caso mencionaremos las propiedades de las directivas
#include y #define, dejando de lado las directivas de compilacin condicional.
1.1 Directiva include
La directiva del preprocesador #include instruye al compilador para incluir otro archivo
fuente. El archivo fuente que se leer se debe encerrar entre comillas dobles o parntesis
de ngulo. Cuando se indica <archivo> se le dice al compilador que busque donde estn
Pgina 1 de 22

Sistemas Embebidos 2012


los archivos incluidos o include del sistema. Si se usa la forma "archivo" es buscado en el
directorio actual, es decir, donde el programa est siendo compilado. Los archivos
incluidos usualmente contienen los prototipos de las funciones y las declaraciones de los
archivos cabecera (header files) y no tienen cdigo de C (algoritmos).
#include <stdio.h>

//Inclusin de la librera stdio.h

int main (void)


{
printf (" Hola Mundo Embebido!\n");
return 0;
}

1.2 Directiva define


La directiva #define define un alias, es decir una sustitucin de texto. Esto, que usbamos
en los programas de PC para definir constantes, se puede utilizar de la misma manera para
definir macros. En efecto, podemos poner parmetros a esas sustituciones, que se
comportan entonces como si de una pseudo-funcin se tratara.
En una macro con argumentos, los argumentos se sustituyen en el texto de reemplazo, y a
continuacin la macro se expande, es decir, en el programa el texto de reemplazo
reemplaza al identificador y a la lista de argumentos. Por ejemplo:
#define PI 3.14
#define AREA_CIRCULO(x) PI * (x) * (x)

Ahora podemos usar la macro como si fuera funcin normal:


int main (void)
{
int a;
a = AREA_CIRCULO(3);
return 0;
}

Durante la compilacin la macro se expande a:


a = 3.14 * (3) * (3)
Pgina 2 de 22

Sistemas Embebidos 2012


y obtenemos el resultado esperado.
Las macros nos permiten insertar cdigo en el programa directamente, evitando la
sobrecarga de invocar a una funcin (pasar parmetros a la pila, realizar un salto, recibir
parmetros) pero conservando la legibilidad del programa. Por otra parte permite realizar
clculos durante la compilacin, en lugar de realizarlos durante la ejecucin. As en el
ejemplo que nos ocupa el compilador le da directamente el valor adecuado a la variable
a, en lugar de insertar instrucciones para que se evale cada vez que se use.
Es importante no olvidar los PARNTESIS alrededor de los parmetros en la definicin de
la macro, de lo contrario la expansin de parmetros puede ser incorrecta, por ejemplo:
#define PI 3.14
#define AREA_CIRCULO(x) PI * x * x
int main (void)
{
int a,b;
a = AREA_CIRCULO(b+3);
return 0;
}

Se expande a:
a = 3.14 * c + 3 * c + 3
que, por culpa de la precedencia de operadores (ver precedencia), es equivalente a
a = (3.14 * c) + (3 * c) + 3
en lugar de expandir a:
a = 3.14 * (c + 3) * (c + 3)
que es lo que queramos.
Uno de los usos ms frecuentes de las macros que tendremos en la prctica con sistemas
embebidos es la funcin para determinar la posicin de un determinado bit dentro de un
registro, pero este tema ser mencionado en la seccin de Operaciones de Bits.

Pgina 3 de 22

Sistemas Embebidos 2012

2. Precedencia de operadores
Una expresin est compuesta por operadores, variables y constantes. Para simplificar,
podemos pensar que la forma en la que C evala esta expresin es dividiendo el todo en
subexpresiones. Las reglas que definen que subexpresin evaluar primero, se denominan
reglas de precedencia. Aunque siempre podemos alterar dichas reglas mediante la
utilizacin de parntesis. En la siguiente tabla detallamos la precedencia entre los
operadores de C.

2.1 Asociatividad
En una expresin tal como 3 * 4 + 5 el compilador realiza primero la multiplicacin
(por tener el operador * mayor precedencia) y luego la suma, por tanto, produce 17. Para
forzar un orden en las operaciones se deben utilizar parntesis. 3 * (4 + 5) produce
27, ya que 4 + 5 se realiza en primer lugar.

Pgina 4 de 22

Sistemas Embebidos 2012


La asociatividad determina el orden en que se agrupan los operadores de igual prioridad;
es decir, de izquierda a derecha o de derecha a izquierda. Por ejemplo:
x y + z

se agrupa como (x y) + z, ya que y +, con igual prioridad, tienen

asociatividad de izquierda a derecha. Sin embargo, x = y = z se agrupa como x = (y =


z) dado que su asociatividad es de derecha a izquierda.

3. Operaciones de Bits
Aunque normalmente no se suelen utilizar, es bueno conocer cmo actan estos
operadores y de cuales disponemos.
En la mayor parte de un programa para un sistema embebido con un microcontrolador
nos interesar manipular datos a nivel de bit; por ejemplo activar o desactivar flags. Un flag
es una variable que puede tomar 2 valores, por lo que se suele representar con un bit.
Debido a que en C no existen tipos predefinidos de un bit, lo que se suele hacer es agrupar
hasta ocho flags en una variable de tipo carcter (char).
Para acceder a estos flags o simplemente para activarlos es necesario utilizar operadores a
nivel de bit.

A continuacin describiremos cada uno de estos operadores brevemente.

Pgina 5 de 22

Sistemas Embebidos 2012


El operador AND (&): El operador AND compara dos bits; si los dos son 1 el resultado es 1,
en otro caso el resultado ser 0. Ejemplo:
c1 = 0x45 01000101
c2 = 0x71 01110001
c1 & c2 = 0x41 01000001
Particularmente en los sistemas embebidos con microcontroladores podemos citar el
siguiente ejemplo.
Loop que espera que el pin B1 valga uno:
while ( ! (PINB & _BV(PB1)))
{
; //Bucle
}

La razn por la cual debemos utilizar esta mascara es que no podemos leer el pin B1 de
forma individual ya que solamente podemos acceder al estado de los 8 bits del puerto
simultneamente.
El operador OR (|): El operador OR compara dos bits; si cualquiera de los dos bits es 1,
entonces el resultado es 1; en otro caso ser 0. Ejemplo:
i1 = 0x47 01000111
i2 = 0x53 01010011
i1 | i2 = 0x57 01010111
El operador XOR (^): El operador OR exclusivo o XOR, dar como resultado un 1 si
cualquiera de los dos operandos es 1, pero no los dos a la vez. Ejemplo:
i1 = 0x47 01000111
i2 = 0x53 01010011
i1 ^ i2 = 0x14 00010100
Uno de los usos ms comunes de este operador es en lo que se llama Toggling de bits.
Por ejemplo si queremos que un bit de un registro cambie de estado, pero que el mismo
dependa del estado anterior (si estaba en 0 pase a 1 y viceversa). En el siguiente ejemplo
hacemos un toggling de un pin en puerto de salida.

Pgina 6 de 22

Sistemas Embebidos 2012

PORTB = PORTB ^ 0b00000010 // Toggling del PB1

El operador de complemento (~): Este operador devuelve como resultado el


complemento a uno del operando:
c = 0x45 01000101
~c = 0xBA 10111010
Los operadores de desplazamiento a nivel de bit (<< y >>): Desplazan a la izquierda o a la
derecha un nmero especificado de bits. En un desplazamiento a la izquierda los bits que
sobran por el lado izquierdo se descartan y se rellenan los nuevos espacios con ceros. De
manera anloga pasa con los desplazamientos a la derecha. Veamos un ejemplo:
c = 0x1C 00011100
c<<1 = 0x38 00111000
c>>2 = 0x07 00000111
Los operadores de bit o tambin conocidos como operadores Bitwise se utilizan en los
sistemas embebidos con microcontroladores para la realizacin de mscaras que permiten
asignar un 1 o un cero a determinados bits de un registro de configuracin.
Como ejemplo de aplicacin supongamos que queremos que el puerto B de un
microcontrolador se configure con los pines 7, 6 y 5 como salidas. Para ello los tres bits
ms significativos del registro de direccin de datos (DDR) del puerto B deben colocarse
en 1. Esto puede efectuarse de distintas maneras:
Utilizando como mscara el byte completo:
DDRB = DDRB l 0b11100000;
O bien:
DDRB l = 0b11100000;

Pgina 7 de 22

Sistemas Embebidos 2012


Utilizando el operador desplazamiento 1<<XXX :
DDRB = DDRB l (1<<PD7) l (1<<PD6) l (1<<PD5);
O bien:
DDRB l = (1<<PD7) l (1<<PD6) l (1<<PD5);

! En este caso es importante aclarar que tanto el nombre del registro DDRx
los valores PDx se encuentran previamente asignados mediante una
directiva #define incluidas en la librera avr/io.h.
Utilizando la macro _BV():
#define _BV(bit) (1 << (bit))

La macro _BV definida en sfr_defs.h realiza la misma operacin que


utilizando el operador desplazamiento sin embargo deja un cdigo mas
entendible que el mtodo anterior.
DDRB l = _BV(PD7) l _BV(PD6) l _BV(PD5);

Otro ejemplo: queremos que el puerto B se configure con los pines 3, 2 y 1 como
entradas.
Utilizando byte completo
DDRB & = 0xF1;
Utilizando el operador desplazamiento 1<<XXX
DDRB & = ~ ((1<<PD3) l (1<<PD2) l (1<<PD1));
Utilizando la macro _BV():
DDRB & = ~ (_BV(PD3) l _BV(PD2) l _BV(PD1));

Pgina 8 de 22

Sistemas Embebidos 2012

! Es importante tener en cuenta que no todos los registros de configuracin son siempre
direccionables bit a bit, esto hace que deba generarse una variable de 8 bits en espejo
para luego mover los datos de configuracin de una sola vez hacia el registro.

4. Operadores lgicos y relacionales


4.1 Operadores lgicos
Como operadores lgicos designamos a aquellos operadores que nos permiten conectar
un par de propiedades (al igual que en lgica):
numero = 2701;
if ( EsPrimo(numero) && (numero > 1000) )
{
/* Ejecutaremos este cdigo si numero */
/* es primo y numero es mayor que 100 */
}

Los operadores lgicos de los que disponemos en C son los siguientes:

Al igual que con la igualdad hay que tener especial cuidado con los operadores && y , ya
que si ponemos slamente un & o un | , nos estamos refiriendo a un and o un or a
nivel de bit, por lo que el cdigo puede que no haga lo que queremos (o que algunas veces
lo haga y otras veces no).
4.2 Operadores Relacionales
Al igual que en matemticas, estos operadores nos permitirn evaluar las relaciones
(igualdad, mayor, menor, etc.) entre un par de operandos (en principio, pensemos en
nmeros). Los operadores relacionales de los que disponemos en C son:

Pgina 9 de 22

Sistemas Embebidos 2012

El resultado de cualquier evaluacin de este tipo, es un valor cierto' (true) o falso


(false). La mayora de lenguajes tienen algn tipo predefinido para representar estos
valores (boolean, bool, etc); sin embargo en C, se utilizan valores enteros para representar
esto:

Volviendo a los operadores relacionales, el resultado de una evaluacin ser un valor


entre 0 y 1, que indicar como hemos dicho, la falsedad o certeza de esa relacin.
En sistemas utilizaremos los operadores relacionales por ejemplo para verificar el estado
de un registro.
un par de propiedades (al igual que en lgica):
// Loop que espera que PB1 valga uno:
while ((PINB & _BV(PB1)) == 0);
// Loop que espera que PB1 valga 0:
while ((PINB & 0x02) == 0x02);

Pgina 10 de 22

Sistemas Embebidos 2012


4.3 Ejemplo de comparacin de operadores
Supongamos que (x = 2) y (y = 5)

Operacin
(x
(x
(x
(x
(x
(x
(x
(x
(x
(x

&&
&
ll
l
==
!=
<
>
<=
>=

Resultado

y)
y)
y)
y)
y)
y)
y)
y)
y)
y)

True
False
True
True
False
True
True
False
True
False

5. Tipos y tamao de datos


Los compiladores C tienen cierta libertad para elegir el tamao de los tipos de datos
comunes y lo suelen hacer en base a consideraciones de eficiencia, que dependen de cada
procesador. Sin embargo tienen que respetar ciertos requisitos:
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long)

Donde:
1. char por lo menos de tamao suficiente para el conjunto de caracteres bsico
2. short al menos de 16 bits
3. long al menos de 32 bits

Con el fin de evitar conflictos en los tamaos de las variables al traspasar cdigo desde
una plataforma a otra, el estndar C99 (Es la revisin de 1999 de C) incluy los
siguientes tipos de datos:
int8_t, int16_t, int32_t, int64_t - uint8_t, uint16_t, uint32_t, uint64_t

Pgina 11 de 22

Sistemas Embebidos 2012


Donde int8_t es un entero de 8 bits incluyendo el signo, int16_t es uno de 16 bits, etc.

Estas definiciones estn en un header llamado stdint.h y solucionan el inconveniente


mencionado anteriormente siempre y cuando el compilador sea compatible con C99.

6. Conversin Automtica de Tipos


El lenguaje C permite realizar operaciones entre variables de diferente tamao, o
combinando con y sin signo. Los compiladores generalmente cambian cada tipo de
variable a una de ms bits que incluye todos los valores posibles de la original. Por
ejemplo:
unsigned char a = 200;
int b = -20;
a = a+b;

En el ejemplo anterior, a es convertido a int durante la operacin, y el resultado es pasado


de vuelta a unsigned char, quedando igual a 180, como resulta lgico. Gracias a esto, el
programador frecuentemente ni se da cuenta de que hay una conversin de tipos. Es ms,
muchos compiladores convierten tipos aunque no sea necesario, para simplificar la
compilacin.
Sin embargo, las reglas de C son ms complejas porque tienen en cuenta la performance.
Por ejemplo:
unsigned int a = 200;
int b = -20;
a = a+b;

Pgina 12 de 22

Sistemas Embebidos 2012


si a es unsigned int y la aritmtica en long es ineficiente en este procesador, qu
conviene hacer?
1. Convertir todo a long?
2. O usar int a pesar de perderse rango?
3. O usar unsigned int a pesar de perderse el signo de b?
Los compiladores generalmente optan por la nmero 3, pero estas situaciones no estn
bien definidas, por eso traen riesgos de errores y problemas de portabilidad.
Recomendacines:
1. No mezclar valores con signo con valores sin signo
Si se lo hace, prescindir de la conversin automtica lo ms que se pueda
Por ejemplo, si a es unsigned y b es signed, en lugar de
if (a+b < 0) { };

Escribir:
if ((b < 0) && (-b > a)) { };

2. No hacer asignaciones que puedan perder informacin


En el primer ejemplo, adems de no cumplir la regla anterior, estbamos sumando
un int y poniendo el resultado en un char. Evitemos eso.

7. Modificadores
7.1 Modificador static
La caracterstica de las variables de perder su valor al salir del bloque en que han sido
definidas, es un serio inconveniente en algunas ocasiones. Para resolver el problema se
inventaron las variables estticas, un tipo especial de variable que no fuese destruida
cuando la ejecucin saliese de su mbito y que conservase su valor. Este nuevo tipo se
declara mediante el modificador de tipo de almacenamiento static, con lo que se
previene que una variable pierda su valor cuando se sale del bloque en que se ha definido.

Pgina 13 de 22

Sistemas Embebidos 2012


Por ejemplo, entre llamadas sucesivas a una funcin. Esta caracterstica presenta ventajas
importantes en sistemas embebidos al utilizarse dentro de rutinas de interrupcin.
Las variables globales son por defecto accesibles desde cualquier fuente del programa, si
declaramos una variable global con el modificador static impediremos que estas
variables sean visibles desde otro fuente. De igual modo, las funciones definidas en un
fuente son utilizables desde cualquier otro. Si queremos impedir que una funcin sea
llamada desde fuera del fuente en la que est definida usaremos el modificador static.
7.2 Modificador volatile
Este modificador se usa con variables que puedan ser modificadas desde el exterior del
programa por procesos externos. Obliga al compilador a leer de memoria el valor de la
variable siempre que vaya a ser usada aunque ya tenga dicho valor en algn registro de
una lectura anterior. Se usa con variables en las que no se sabe con certeza cundo se va a
modificar su contenido.
Para que el programa debugger permita visualizar el estado de ciertas variables cuando
el programa se encuentra detenido, es imprescindible haber declarado este tipo de
variables como volatile. Esto no se aplica cuando no se utiliza un alto grado de
optimizacin en la compilacin.
7.3 Modificador const
El modificador const indica que el cdigo no debe cambiar el valor de la variable
declarada por ejemplo:
int const foo = 10;

Si por error escribimos cdigo que la modifica, el compilador emite una advertencia, y
podemos repararlo sin que cause ms problemas. Por lo tanto, los const no son
imprescindibles, pero hacen que la verificacin esttica sea ms potente.

Pgina 14 de 22

Sistemas Embebidos 2012


Usmoslo en argumentos que son pasados mediante punteros pero que la
funcin no debe modificar, por ejemplo:
char *strcpy(char *dest, char const *fuente)

8. Punto Flotante
Los nmeros en coma flotante son usados, habitualmente, para aproximar valores
analgicos y contnuos, debido a que ofrecen una mayor resolucin que los enteros. Las
variables tipo float tienen el valor mximo 3.4028235E+38, y como mnimo pueden
alacanzar el -3.4028235E+38. Ocupan 4bytes (32bits).
Los floats tienen una precisin de 6 o 7 dgitos decimales. Esto significa el nmero total de
dgitos, no el nmero a la derecha de la coma decimal. Al contrario que en otras
plataformas, donde tu podras obtener mayor precisin usando una variable tipo double
(por ejemplo, por encima de 15 dgitos), en la plataforma de sistemas embebidos de 8 bits
los double tienen el mismo tamao que los float.
Los nmeros en coma flotante no son exactos, y muchos proporcionan falsos resultados
cuando son comparados. Por ejemplo, 6.0 / 3.0 puede no ser igual a 2.0. Debes comprobar
que el valor absoluto de la diferencia entre los nmeros pertenezca a un rango pequeo.
La matemtica en coma flotante es mucho ms lenta que la matemtica de enteros para
realizar operaciones, por lo que deberas evitarla si, por ejemplo, un bucle tiene que
ejecutarse a la mxima velocidad para funciones con temporizaciones precisas.
A continuacin se muestra una tabla con los tiempos resultantes de realizar la
multiplicacin algebraica de dos valores con distintos tipos de variables.
Variable
char
int
long int
float

Tiempo [us]
1,2
1,5
9,5
210

Pgina 15 de 22

Sistemas Embebidos 2012


Estos resultados fueron obtenidos en la plataforma de sistema embebido usando un
microcontrolador AtMega168 a 8Mhz y sin optimizacin del proceso de compilacin.

9. Declaracin y utilizacin de punteros


9.1 Qu es un puntero?
Podramos imaginar un puntero como una flecha que apunta a otra variable, pero esto es
slo para aclararnos a la hora de hacer esquemas. En realidad, una variable puntero no es
ms que una direccin de memoria, un puntero nos dice dnde se encuentra almacenado
el valor de la variable a la que apunta. Si nos quedamos con esta ltima definicin (la de
direccin de memoria), nos ser ms fcil aclararnos cuando trabajemos con punteros a
gran escala.
Como ya sabemos, al declarar una variable lo que estamos haciendo es reservar espacio
en memoria para esa variable (el tamao del espacio reservado depender del tipo de la
variable). Pues bien, al declarar un puntero lo que hacemos es reservar espacio para
almacenar una direccin de memoria, no el contenido de la variable.
9.2 Para qu necesitamos punteros?
Es posible que al abordar un concepto algo ms abstracto que los que surgen al aprender
un lenguaje de programacin, sea difcil imaginar para qu sirve. Al plantearnos esa
pregunta, surgen dos respuestas inmediatas:
para poder usar ``memoria dinmica''.
para poder usar ``argumentos por referencia''.
Dentro del mundo de los sistemas embebidos de 8 bits, el inters primordial es el de pasar
argumentos por referencia. La razn de esto es la optimizacin en velocidad y memoria
que hacemos del microcontrolador. Recordemos que al pasar valores por copia
necesitamos realizar una operacin de copia de cada argumento pasado y memoria
disponible para almacenar esa copia. Si utilizamos argumentos por referencia eliminamos
la necesidad de lo anterior.
Pgina 16 de 22

Sistemas Embebidos 2012

9.3 Declaracin de punteros


Ante todo, un puntero es una variable. Al dedicar un inters especial en los punteros,
puede dar la impresin de que son elementos separados de las variables. No es as. Son
variables, y como veremos, podemos asignarles nuevos valores, e incluso realizar algunas
operaciones aritmticas tiles con ellos.
Una variable de tipo puntero est ntimamente ligada con el tipo al que apunta. Por ello,
en la declaracin, escribimos el nombre del tipo al que apuntar nuestro puntero, seguido
de asterisco, y por ltimo, el nombre del puntero. Ejemplo:
int *p_entero;
float *p_real;
char *caracter;

Sern declaraciones de punteros que contendrn la direccin de memoria de un entero,


un real y un carcter respectivamente.
9.4 Operadores de contenido y de indireccin
El operador de indireccin & se utiliza para referirse a la direccin de una variable, as el
cdigo siguiente:
/* Reservamos 4 bytes para almacenar una direccin de memoria */
int *p_entero;
/* Reservamos 4 bytes para almacenar un entero */
int entero;
/* Escribimos en la variable p_entero la direccin de entero */
p_entero = &entero;

Pgina 17 de 22

Sistemas Embebidos 2012


Es totalmente correcto ya que, aunque la variable entero no tenga ningn valor asignado
todava, lo que estamos haciendo es escribir en la variable p_entero la direccin de
memoria donde se almacena la variable entero.

El operador * se utilizar para manejar la direccin a la que apunta un puntero. Podramos


llamarlo el operador de acceso. Tanto el operador de acceso, como el de indireccin,
funcionan en notacin prefija. Veamos un ejemplo que combina ambos operadores:

int *p_entero;
int entero1, entero2;
entero1 = 2;
entero2 = 5;
p_entero = &entero1;
entero1 = *p_entero + entero2;

Del mismo modo podemos asignar un valor a la variable a la que apunta el puntero, de la
forma:
int *p_entero;
int entero1, entero2;
entero1 = 2;
entero2 = 5;
p_entero = &entero1;
*p_entero = entero1 + entero2;

Este ltimo cdigo ejecutara exactamente lo mismo que el anterior. Debemos notar que
no tendra sentido prescindir de la lnea 5, puesto que estaramos intentando introducir el
valor entero1 + entero2 en una direccin de memoria que no conocemos, puesto que no
le habramos dado valor a p_entero.

Pgina 18 de 22

Sistemas Embebidos 2012

10.

Asignacin de memoria

La mayora de los dispositivos con los cuales trabajaremos tienen una cantidad mnima de
RAM. Los microcontroladores compatibles con la plataforma C vienen con un minimo de
128 bytes de RAM. Esta memoria, debe compartirse entre las variables inicializadas y no
inicializadas (secciones .data y .bss), la asignacin dinmica de memoria, y la pila que se
usa para alojar variables locales (automticas) durante el llamado a subrutinas.
A diferencia de arquitecturas ms grandes, no hay un manejo de memoria realizado por
hardware, el cual ayudara a separar las regiones mencionadas anteriormente evitando
que se sobrescriban unas en otras.

La disposicin general de RAM es primero alojar las variables .data al comienzo de la RAM
interna, siguiendo con .bss. La pila se inicializa con el valor tope de la RAM interna,
despazndose (creciendo) hacia abajo. La porcin disponible llamada "heap", dedicada
para la asignacin dinmica de memoria se ubicar al final de la seccin .bss. Por lo tanto,
no hay riesgo de que la memoria dinmica en algn momento colisione con las variables
RAM (a menos de que hayan errores en la implementacin de la asignacin de lugares).
Pero an hay riesgo de que la seccin heap y de pila puedan colisionar si hay grandes
requerimientos tanto de espacio de memoria dinmica como de espacio en pila. sta
adems puede darse con requerimientos de espacios que no sean grandes pero que las
asignaciones se encuentren fragmentadas de tal modo que a lo largo del tiempo, nuevos
requerimientos de memoria dinmica no entren en las regiones de memoria libres.
Grandes asignaciones de espacio de Pila pueden aumentar en una funcin en C
conteniendo grandes (en tamao de bytes) y/ numerosas variables locales o cuando se
llama recursivamente a una funcin.

Pgina 19 de 22

Sistemas Embebidos 2012

En un dispositivo simple como es un microcontrolador, es un desafo implementar


asignacin dinmica de memoria que sea lo suficientemente simple para que los
requerimientos de tamao de cdigo permanezcan bajos, y tambin lo suficientemente
potente para evitar fregmentacin de memoria innecesaria y hacerlo todo
razonablemente con pocos ciclos de CPU. Generalmente, los microcontroladores tienen
poco espacio de memoria y corren a velocidades mucho ms bajas que las PC actuales.
El asignador de memoria implementado en avr-libc intenta hacer frente a todas estas
limitaciones, y ofrece algunas opciones de optimizacin que se pueden usar si hay ms
recursos disponibles que en la configuracin por defecto.
Por todo lo anterior, en nuestro caso particular, recomendamos no utilizar la asignacin
dinmica de memoria a fin de evitar problemas de solapamiento. Las aplicaciones sencillas
en las que trabajaremos se solucionarn fcilmente estableciendo arreglos de tamaos
fijos y asignndoles suficiente espacio para alojar la mxima cantidad de valores que
amerite cada caso en particular.

Pgina 20 de 22

Sistemas Embebidos 2012

11.

Nmeros Mgicos

A veces queremos escribir cosas como IVT[6] o estado = 8; Pero esos nmeros
carecen de sentido para alguien que desea interpretar el programa, o bien para uno
mismo luego de un tiempo de haber escrito el programa. A estos nmeros, que no se
entiende de dnde salieron, se los llama nmeros mgicos y conviene evitarlos.
Usemos enumeraciones y constantes en lugar de nmeros mgicos!!

12.

Enumeraciones

Las enumeraciones permiten definir una lista de constantes representadas por


identificadores. Estas constantes son, realmente, nmeros enteros. Si no se especifica una
correspondencia entre nombre y nmero el compilador se encarga de asignarles nmeros
correlativos (empezando por 0). Se pueden usar como enteros que son, pero la idea es
usarlos en comparaciones, haciendo as el cdigo ms legible. Para definirlas se usa la
palabra reservada enum tal que:
enum color{rojo, amarillo, azul};
enum bool{false=0, true=1};
A partir de entonces se puede poner por ejemplo:
if(color == rojo)...
en lugar de tener que poner:
if(color == 0)...

13.

Niveles de optimizacin de compilacin

Comnmente los compiladores C ofrecen varios niveles de optimizacin del cdigo. La


variable O controla el total de niveles de optimizacin y hay cinco configuraciones para
-O: -O0, -O1, -O2, -O3 y -Os. Examinemos cada nivel de optimizacin:

Pgina 21 de 22

Sistemas Embebidos 2012


-O0: Este nivel (la letra "O" seguida de un cero) desconecta por completo la optimizacin y
es el predeterminado si no se especifica ningn otro nivel. El cdigo no ser optimizado.
Esto, normalmente, no es lo que se desea.
-O1: Este es el nivel de optimizacin ms bsico. El compilador intentar producir un
cdigo rpido y pequeo sin tomar mucho tiempo de compilacin. Es bastante bsico,
pero conseguir acabar el trabajo siempre.
-O2: Un paso por encima de -O1. Este es un nivel recomendado de optimizacin, a no ser
que tenga necesidades especiales. -O2 activar unos pocos parmetros aadidos a los que
se activan con -O1. Con -O2, el compilador intentar aumentar el rendimiento del cdigo
sin comprometer el tamao y sin tomar mucho ms tiempo de compilacin.
-O3: Este es el ms alto nivel de optimizacin posible, y tambin el ms arriesgado.
Tomar ms tiempo compilar su cdigo con esta opcin. Este nivel es particular porque no
optimiza para conseguir el menor cdigo, sino el ms veloz, es decir, el cdigo que se
ejecuta en el menor tiempo posible. Para esto el compilador intentar incrustar en lnea
todas las funciones que pueda, como las que se llaman solo una vez.
-Os: Este nivel optimizar su cdigo para el tamao. Activa todas las opciones de -O2 que
no aumenten el tamao del cdigo generado. Es til para mquinas con capacidad
limitada de disco y/o con CPUs con poca cach.
Como se coment anteriormente, -O2 es el nivel de optimizacin recomendado. Si un
paquete muestra errores de compilacin, asegrese que no est usando -O3.
Una vez funcionando el programa correctamente en -O2 podremos pasar a -Os y verificar
si el tamao del cdigo es sensiblemente menor sin comprometer el correcto
funcionamiento del programa.

Pgina 22 de 22