Sei sulla pagina 1di 324

Lenguaje C para Microcontroladores > Los compiladores de alto nivel

Introduccin
Los compiladores de alto nivel
Los compiladores de alto nivel son las herramientas de programacin mas potentes que existen. En resumidas
cuentas, el compilador traducir el programa escrito en su lenguaje (C, Basic, Pascal u otro) en cdigo
ensamblador y luego generar los archivos de depuracin (*.dbg, *.cof, *.d31, etc.) necesarios y de ejecucin
(*.hex).
Al trabajar con un lenguaje de alto nivel el programador ya no tiene que preocuparse (o lo hace muy poco) por
las caractersticas hardware ni por el ensamblador nativo de cada microcontrolador. Esto simplifica de manera
asombrosa el desarrrollo de proyectos.
Los compiladores se encargan de traducir el cdigo fuente al cdigo objeto de cada microcontrolador sin
inportar mucho cul sea. Por ejemplo, un cdigo escrito para un PIC16F84 podra ser facilmente compilado
para un PIC16F877A u otro, y viceversa. Inclusive es posible adaptar un cdigo para un microcontrolador de
otra marca, por ejemplo, de Freescale o Atmel. Eso se llama portabilidad.

Por qu C y no Basic?
Ciertamente, el Basic es el lenguaje ms fcil de aprender (no es exactamente la razn de su nombre). Y
aunque los programadores en C de ordenadores miren con desdn a los que usan el Basic, en el mundo de los
microcontroladores los compiladores Basic no tienen motivo para sentirse menos. De hecho, algunos pueden
ser casi tan eficientes como los mejores compiladores C.
Las caractersticas (muchas veces complejas) del C fueron ideadas para el trabajo con sofisticados proyectos,
propios de los ordenadores. Muchas de esas caractersticas ya no resultan tan ventajosas en el limitado
hardware de los microcontroladors y se convierten en prescindibles.
Adems, la simplicidad de los compiladores Basic para microcontroladores tambin permite que varios de
ellos, como MBasic o PIC Basic Pro (por citar algunos) mantengan una compatibilidad entre sus cdigos que
no se encuentra entre los compiladores C.
sas podran ser razones ms que convincentes para empezar por el Basic y, de hecho, es la opcin que
muchos han elegido. Por qu nosotros no?
Porque es verdad comprobable que los mejores programadores trabajan en C (no siempre exclusivamente,
pero lo manejan). Por consiguiente, los proyectos ms fantsticos y alucinantes que se pueden encontrar estn
en C. Es ms, la mayora de, por no decir todos, los programadores de Basic tarde o temprano se ven
obligados a aprender el C. No s t, pero yo opino que esa razn pesa ms.
Adems, dada la robustez y la aceptacin del lenguaje C, se lo ha tomado como referencia para lenguajes de
otros propsitos como Java, JavaScript, php o de Matlab, entre otros. As que, el C podr servirte para trabajar
en otros campos. El programador de C podra, inclusive, aprender luego el Basic sin el menor esfuerzo; lo
contrario no es cierto.

Qu compilador C utilizar?

No quiero burlarme de nadie, pero una vez le en Internet el comentario de un novato: Quiero programar
microcontroladores en C. Ya descargu el Visual C++. Qu ms necesito? :). Aparte del lenguaje, nada
tiene que ver un compilador para ordenadores con los compiladores para Cs. Poco tiene que ver un
compilador para PICs que otro para otros Cs. Inclusive, poco tiene que ver un compilador de PICs de una
compaa con otro de otra compaa.
Veamos grosso modo algunos aspectos de los compiladores de PICs ms conocidos.
Hi-tech C. Es uno de los compiladores producidos por la empresa htsoft. Es quiz el ms eficiente y el que
mejor soporta el lenguaje C estndar. Su entorno IDE tambin incluye el mejor depurador ICD. Como
contraparte, su apego al hardware del C le resta algo de portabilidad. Tampoco luce libreras incorporadas
como otros productos. Pero su principal desventaja es su elevado precio. Y, por si fuera poco, el compilador
para la familia de partes PIC18 se vende por separado.
IAR C. Los compiladores C de la compaa iar systems tienen bsicamente las mismas caractersticas
mencionadas de los compiladores de htsoft, incluyendo sus propios depuradores. As mismo, las versiones
para los PIC16 y PIC18 se distribuyen por separado. Actualmente, no s por qu, ya no est disponible la
primera versin.
CCS C. La empresa ccsinfo decidi dotar a sus compiladores C una capa extra que asla al programador de los
recursos intrnsecos del C. Esto puede afectar la portabilidad de sus cdigos a otros compiladores, pero
resulta inmejorable, si solo se trabaja en el lenguaje de CCS C, para transportar los cdigos de un PIC a otro
(de cualquier familia) con un esfuerzo sin comparacin. Adems, incluye en un solo paquete los compiladores
para los PICs de las familias Baseline, Midrange (PIC16 bsicamente) y High performance (PIC18).
Al igual que los softwares anteriores, sus libreras estndar, como stdlib.h, stdio.h, string.h y math.h, son
muy completas y potentes; pero CCS C supera a sus rivales al incorporar libreras para controlar todos los
mdulos internos del PIC y tambin muchsimos dispositivos externos.
Mikro C. La compaa Mikroelektronika vende compiladores para PICs en los lenguajes C (MikroC), Basic
(MikroBasic) y Pascal (MikroPascal).
Yo dira que el estilo de Mikro C se parece al de Hi-tech C y sus facilidades tratan de acercarse a las de CCS
C: aunque en muchos casos an es necesario acceder a los registros internos del PIC, cuenta con libreras para
controlar sus mdulos internos. Tambin tiene una apreciable cantidad de libreras para interfacear
dispositivos externos. Lo malo es que todas ellas estn precompiladas y no se podran modificar, en caso de
ser necesario.
Mikroelektronika y CCS tambin comercializan sus propias tarjetas de entrenamiento para el aprendizaje de
sus productos. Para ms informacin puedes visitar sus sitios web.
MPLAB C18. Excelente compilador desarrollado por los ingenieros de Microchip. No es gratuito como el
MPLAB, pero creo que es el que ofrece la versin demo ms generosa: es 100 % funcional por 60 das.
Lamentablemente, como sugiere su nombre, solo trabaja con las partes PIC18. Quiz lo probemos en otro
momento.
Otros. An hay otros compiladores C (como Bytecraft, BoostC y FedC) que algo menos reconocidos como los
anteriores, lo que no significa que sean malos.

Tambin he visto algunos de cdigo abierto, pero no son buenos: la gente del GNU trabaja ms con el AVR
GCC, un Seor Compilador. Es uno de los pocos casos donde el software libre supera a los comerciales.
Como se puede entrever, est orientado a los microcontroladores AVR, de Atmel. Es, adems, el compilador
ms difcil de todos; por eso lo estudiaremos en el Mdulo 4.
En cuanto a cul compilador usar: la idea de este curso no es aprender a programar con un compilador en
particular, y tampoco pretendo promocionar alguno. Despus de todo, una victoria depende ms de la
habilidad guerrero que de su espada. He visto super programas hechos con el compilador ms modesto.
En este Mdulo 2 uso BoostC porque es muy fcil, porque nos permitir ver ms de cerca cmo funcionan las
cosas dentro del PIC y, sobre todo, porque el salto de l a otros compiladores ser mucho ms fcil que
hacerlo al revs. En el Mdulo 3 migraremos al CCS C (que adems del lenguaje C usa su propio argot) y
en el Mdulo 4 trabajaremos especialmente con AVR GCC.

nimo! No es tan difcil


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

Estructura de un programa en C
Tomaremos en cuenta este sencillsimo ejemplo, escrito para el compilador Hitech PICC.
#include <pic.h>

// Incluir este archivo

/* La siguiente directiva establece la Palabra de Configuracin */


__CONFIG ( PWRTEN & WDTDIS & XT & UNPROTECT );
void pausa(void)
{
unsigned int c;

// Llave de apertura del bloque de pausa


// Declarar variable c (de 16 bits)

for(c=0; c<60000; c++)


{
// Llave de apertura del bloque de for
/* este bloque est vaco, solo
cuenta c desde 0 hasta 59999 */
}
// Llave de cierre del bloque de for

// Llave de cierre del bloque de pausa

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

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

Llave de apertura del bloque de main


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

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

Los comentarios
Los comentarios tienen el mismo propsito que en ensamblador: documentar y adornar el cdigo. Es todo es
texto que sigue a las barritas // y todo lo que est entre los signos /* y */. Se identifican fcilmente porque
suelen aparecer en color verde.
Ejemplos.
// ste es un comentario simple
/*
sta es una forma de comentar varias lneas a la vez.
Sirve mucho para enmascarar bloques de cdigo.
*/

Las sentencias
Un programa en C, en lugar de instrucciones, se ejecuta por sentencias. Una sentencia es algo as como una
mega instruccin, que hace lo que varias instrucciones del ensamblador.
Salvo casos particulares, donde su uso es opcional, una sentencia debe finalizar con un punto y coma (;). As
que tambin podemos entender que los ; sirven para separar las sentencias. Alguna vez le que el compilador
C lee el cdigo como si lo absorbiera con una caita, lnea por lnea, una a continuacin de otra (evadiendo los
comentarios por supuesto). Por ejemplo, la funcin main del programa de arriba bien pudo escribirse del
siguiente modo.
void main(void) { TRISB0=0; while(1) { RB0=1; pausa(); RB0=0; pausa(); } }

Sorprendido? Podrs deducir que los espacios y las tabulaciones solo sirven para darle un aspecto ordenado
al cdigo. Es una buena prctica de programacin aprender a acomodarlas.

Las sentencias se pueden clasificar en sentencias de asignacin, sentencias selectivas, sentencias iterativas, de
llamadas de funcin, etc. Las describiremos ms adelante.

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

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

Las funciones
Si un programa en ensamblador se puede dividir en varias subrutinas para su mejor estructuracion, un
programa en C se puede componer de funciones. Por supuesto que las fuciones son muchsimo ms potentes
y, por cierto, algo ms complejas de aprender. Por eso ni siquiera el gran espacio que se les dedica ms
adelante es suficiente para abarcarlas. Pero, no te preocupes, aprenderemos de a poco.
En un programa en C puede haber las funciones que sean posibles, pero la nunca debe faltar la funcin
principal, llamada main. Donde quiera que se encuentre, la funcin main siempre ser la primera en ser
ejecutada. De hecho, all empieza y no debera salir de ella.

Variables y Tipos de Datos


En ensamblador todas nuestras variables de programa eran registros de la RAM crudos, es decir, datos de 8
bits sin formato. En los lenguajes de alto nivel estos registros son tratados de acuerdo con formatos que les
permiten representar nmeros de 8, 16 32 bits (a veces ms grandes), con signo o sin l, nmeros enteros o
decimales. Esos son los tipos de datos bsicos.
Las variables de los compiladores pueden incluso almacenar matrices de datos del mismo tipo (llamadas
arrays) o de tipos diferentes (llamadas estructuras). Estos son los tipos de datos complejos.
Los siguientes son los principales tipos de datos bsicos del lenguaje C:
Tipo de dato Tamao
char
8

Rango
0 a 255 -128 a 127

Tipo de dato Tamao


Rango
signed char
8
-128 a 127
unsigned char
8
0 a 255
(signed) int
16
-32,768 a 32,767
unsigned int
16
0 a 65,536
(signed) long
32 -2,147,483,648 a 2,147,483,647
unsigned long 32
0 a 4,294,967,295
float
32
+/- 1.18E38 a +/- 3.40E+38
Por desgracia, excepto signed char y unsigned char, los otros tipos establecen variables de tamaos y/o rangos
que suelen varar de un compilador C a otro. Otros compiladores tambin manejan los tipos short, double,
bool (o boolean), bit, etc. Esas divergencias pueden afectar la portabilidad de los cdigos, adems de
confundir a los programadores. Los valores de esta tabla son los utilizados por la mayora de los compiladores
C.
Los especificadores signed (con signo) mostrados entre parntesis son opcionales. Es decir, da lo mismo
poner int que signed int, por ejemplo. Es una redundancia que se suele usar para reforzar su condicin o
para que se vea ms ilustrativo.

Declaracin de variables
Esta parte es comparable, aunque lejanamente a cuando identificbamos nuestras variables del ensamblador
con las directivas equ o cblock endc. No se puede usar una variable si antes no se ha declarado. La forma
general ms simple de hacerlo es la siguiente:
data_type myvar;

donde data_type es un tipo de dato bsico o complejo, del compilador o definido por el usuario y myvar es un
identificador cualquiera, siempre que no sea palabra reservada.
Ejemplos.
unsigned char d;
char b;
signed char c;
int i;
signed int j;
unsigned int k;

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

Variable para enteros de 8 bits sin signo


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

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

// Declarar variables area y side de tipo float


// Declarar variables a, b y c como unsigned char

Especificadores de tipo de datos


A la declaracin de una variable se le puede aadir un especificador de tipo como const, static, volatile,
extern, register, etc. Dichos especificadores tienen diversas funciones y, salvo const, se suelen usar en
programas ms elaborados. Como no queremos enredarnos tan pronto, lo dejaremos para otro momento.

Una variable const debe ser inicializada en su declaracin. Despus de eso el compilador solo permitir su
lectura mas no su escritura. Ejemplos:
const int a = 100;
int b;

// Declarar constante a
// Declarar variable b

//...
b = a;
b = 150;

// Vlido
// Vlido

a = 60;
a = b;

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

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

// Definir constante a

Sentencias selectivas
Llamadas tambin sentencias de bifurcacin, sirven para redirigir el flujo de un programa segn la evaluacin
de alguna condicin lgica.
Las sentencias if e ifelse son casi estndar en todos los lenguajes de programacin. Adems de ellas estn las
sentencias ifelse escalonadas y switchcase.

La sentencia if
La sentencia if (si condicional, en ingls) hace que un programa ejecute una sentencia o un grupo de ellas si
una expresin es cierta. Esta lgica se describe en el siguiente esquema.

Diagrama de flujo de la sentencia if.


La forma codificada sera as:

sentenciaA;
if ( expression )
{

// Si expression es verdadera,
//
ejecutar el siguiente bloque
// apertura de bloque

sentenciaB;
sentenciaC;
// algunas otras sentencias
}
// cierre de bloque
sentenciaX;

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

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

Diagrama de flujo de la sentencia if else.


Expresando lo descrito en cdigo C, tenemos: (Se lee como indican los comentarios.)
SentenciaA;
if ( expression )
{
sentenciaB;
sentenciaC;
// ...
}
else
{
sentenciaM;
sentenciaN;
// ...
}
sentenciaX;
// ...

// Si expression es verdadera, ejecutar


// este bloque

// En caso contrario, ejecutar este bloque

Como ves, es bastante fcil, dependiendo del resultado se ejecutar uno de los dos bloques de la sentencia if
else, pero nunca los dos a la vez.

La sentencia if else if escalonada


Es la versin ampliada de la sentencia if else.
En el siguiente boceto se comprueban tres condiciones lgicas, aunque podra haber ms. Del mismo modo, se
han puesto dos sentencias por bloque solo para simplificar el esquema.
if ( expression_1 )
// Si expression_1 es verdadera ejecutar
{
// este bloque
sentencia1;
sentencia2;
}
else if ( expression_2 )
// En caso contrario y si expression_2 es
{
// verdadera, ejecutar este bloque
sentencia3;
sentencia4;
}
else if ( expression_3 )
// En caso contrario y si expression_3 es
{
// verdadera, ejecutar este bloque
sentencia5;
sentencia6;
}
else
// En caso contrario, ejecutar este bloque
{
sentencia7;
sentencia8;
};
// ; opcional
// todo...

Las expresiones se evualan de arriba abajo. Cuando alguna de ellas sea verdadera, se ejecutar su bloque
correspondiente y los dems bloques sern salteados. El bloque final (de else) se ejecuta si ninguna de las
expresiones es verdadera. Adems, si dicho bloque est vaco, puede ser omitido junto con su else.

La sentencia switch
La sentencia switch brinda una forma ms elegante de bifurcacin mltiple. Podemos considerarla como una
forma ms estructurada de la sentencia if else if escalonada, aunque tiene algunas restricciones en las
condiciones lgicas a evaluar, las cuales son comparaciones de valores enteros.
Para elaborar el codigo en C se usan las palabras reservadas switch, case, break y default.
El siguiente esquema presenta tres cases pero podra haber ms, as como cada bloque tambin podra tener
ms sentencias.
switch ( expression )
{
case constante1: // Si expression = constante1, ejecutar este bloque
sentencia1;
sentencia2;
break;
case constante2: // Si expression = constante2, ejecutar este bloque
sentencia3;

sentencia4;
break;
case constante3: // Si expression = constante3, ejecutar este bloque
sentencia5;
sentencia6;
break;
default:
// Si expression no fue igual a ninguna de las
// constantes anteriores, ejecutar este bloque
sentencia7;
sentencia8;
break;
}
sentenciaX;
// todo...

donde constante1, constante2 y constante3 deben ser constantes enteras, por ejemplo, 2, 0x45, a, etc. (a
tiene cdigo ascii 165, que es, a fin de cuentas, un entero.)
expresion puede ser una variable compatible con entero. No es una expresin que conduce a una condicin
lgica como en los casos anteriores.
El programa solo ejecutar uno de los bloques dependiendo de qu constante coincida con expression.
Usualmente los bloques van limitados por llaves, pero en este caso son opcionales, dado que se pueden
distinguir fcilmente. Los bloques incluyen la sentencia break. Qu es eso?
La sentencia break hace que el programa salga del bloque de switch y ejecute la sentencia que sigue (en el
boceto, sentenciaX). Atento!: de no poner break, tambin se ejecutar el bloque del siguiente case, sin
importar si su constante coincida con expression o no.
No sera necesario poner el default si su bloque estuviera vaco.

Sentencias iterativas
Las sentencias de control iterativas sirven para que el programa ejecute una sentencia o un grupo de ellas un
nmero determinado o indeterminado de veces. As es, esta seccin no habla de otra cosa que de los bucles en
C.
El lenguaje C soporta tres tipos de bucles, las cuales se construyen con las sentencias while, do while y for.
El segundo es una variante del primero y el tercero es una versin mas compacta e intuitiva del bucle while.

La sentencia while
El cuerpo o bloque de este bucle se ejecutar una y otra vez mientras (while, en ingls) una expresin sea
verdadera.

Diagrama de flujo de las sentencia while.


El bucle while en C tiene la siguiente sixtaxis y se lee as: mientras (while) expression sea verdadera, ejecutar
el siguiente bloque.
sentenciaA;
while ( expression )

// Mientras expression sea verdadera, ejecutar el


// siguiente bloque

{
sentenciaB;
sentenciaC;
// ...
};
sentenciaX;
// ...

// Este ; es opcional

Nota que en este caso primero se evala expression. Por lo tanto, si desde el principio expression es falsa, el
bloque de while no se ejecutar nunca. Por otro lado, si expression no deja de ser verdadera, el programa se
quedar dando vueltas para siempre.

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

Diagrama de flujo de las sentencia do while.


La sintaxis para la sentencia do while es la siguiente y se lee: Ejecutar (do) el siguiente bloque, mientras
(while) expression sea verdadera.
sentenciaA;
do
{
sentenciaB;
sentenciaC;
// ...
} while ( expression ); // Este ; es mandatorio
sentenciaX;
// ...

La sentencia for
Las dos sentencias anteriores, while y do while, se suelen emplear cuando no se sabe de antemano la
cantidad de veces que se va a ejecutar el bucle. En los casos donde el bucle involucra alguna forma de conteo
finito es preferible emplear la sentencia for. (Inversamente, al ver un for en un programa, debemos suponer
que estamos frente a algn bucle de ese tipo.)
sta es la sintaxis general de la sentencia for en C:
for ( expression_1 ; expression_2 ; expression_3 )
{
sentencia1;
sentencia2;
// ...
};
// Este ; es opcional

Ahora veamos por partes cmo funciona:

expression_1 suele ser una sentencia de inicializacin.


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

Por la forma y orden en que se ejecutan estas expresiones, el bucle for es equivalente a la siguiente
construccin, utilizando la sentencia while. Primero se ejecuta expression_1 y luego se ejecuta el bloque
indicado tantas veces mientras expression_2 sea verdadera.
expression_1;
while ( expression_2 )
{
sentencia1;
sentencia2;
// ...
expression_3;
}

No obstante, de esa forma se ve ms rara an; as que, mejor, veamos estos ejemplos, que son sus
presentaciones ms clsicas. (i es una variable y a y b son constantes o variables):
for ( i = 0 ; i < 10 ; i++ )
{
sentencias;
}

Se lee: para (for) i igual a 0 hasta que sea menor que 10 ejecutar sentencias. La sentencia i++ indica que i se
incrementa tras cada ciclo. As, el bloque de for se ejecutar 10 veces, desde que i valga 0 hasta que valga 9.
En este otro ejemplo las sentencias se ejecutan desde que i valga 10 hasta que valga 20. Es decir, el bucle dar
11 vueltas en total.
for ( i = 10 ; i <= 20 ; i++ )
{
sentencias;
}

El siguiente bucle for empieza con i inicializado a 100 y su bloque se ejecutar mientras i sea mayor o igual a
0. Por supuesto, en este caso i se decrementa tras cada ciclo.
for ( i = 100 ; i >= 0 ; i-- )
{
sentencias;
}

Se pueden hacer muchas ms construcciones, todas coincindentes con la primera plantilla, pero tambin son
menos frecuentes.

Sentencias con bloques simples


Cuando las sentencias selectivas (como if) o de bucles (como while o for) tienen cuerpos o bloques que
constan de solo una sentencia, se pueden omitir las llaves. Aun as, es aconsejable seguir manteniendo las
tabulaciones para evitarnos confusiones.
Por ejemplo, las siguientes sentencias:
if(a > b)
{

a = 0;
}
if(a == b)
{
a++;
}
else
{
b--;
}
while( a >= b)
{
a = a + b;
}
for(i=0; i<=10; i++)
{
a = a*2;
}

bien se pueden escribir de la siguiente forma:


if(a > b)
a = 0;
if(a == b)
a++;
else
b--;
while( a >= b)
a = a + b;
for(i=0; i<=10; i++)
a = a*2;

Los operadores
Sirven para realizar operaciones aritmticas, lgicas, comparativas, etc. Segn esa funcin se clasifican en los
siguientes grupos.

Operadores aritmticos
Adems de los tpicos operadores de suma, resta, multiplicacion y divisin, estn los operadores de mdulo,
incremento y decremento.
Operador
Accin
+
Suma
Resta
*
Multiplicacin
/
Divisin
% Mdulo. Retorna el residuo de una divisin entera. Solo se debe usar con nmeros enteros.

Operador
++ Incrementar en uno
-Decrementar en uno

Accin

Ejemplos:
int a, b, c;

// Declarar variables a, b y c

a = b + c;
// Sumar a y b. Almacenar resultado en c
b = b * c;
// Multiplicar b por c. Resultado en b
b = a / c;
// Dividir a entre c. Colocar resultado en b
a = a + c b;
// Sumar a y c y restarle b. Resultado en a
c = (a + b) / c;
// Dividir a+b entre c. Resultado en c
b = a + b / c + b * b; // Sumar a ms b/c ms bb. Resultado en b
c = a % b;
// Residuo de dividir ab a c
a++;
// Incrementar a en 1
b--;
// Decrementar b en 1
++c;
// Incrementar c en 1
--b;
// Decrementar b en 1

Te recordaron a tus clases de lgebra del colegio? A diferencia de esas matemticas, estas expresiones no son
ecuaciones; significan las operaciones que indican sus comentarios.
Por lo visto, los operadores ++ y -- funcionan igual si estn antes o despus de una variable en una expresin
simple. Sin embargo, hay una forma (tal vez innecesaria y confusa para un novato, pero muy atractiva para los
que ya estamos acostumbrados a su uso) que permite escribir cdigo ms compacto, es decir, escribir dos
sentencias en una.

Si ++ o -- estn antes del operando, primero se suma o resta 1 al operando y luego se evala la
expresin.
Si ++ o -- estn despus del operando, primero se evala la expresin y luego se suma o resta 1 al
operando.
int a, b;

// Declarar variables enteras a y b

a = b++;
a = ++b;

// Lo mismo que a = b; y luego b = b + 1;


// Lo mismo que b = b + 1; y luego a = b;

if (a++ < 10)


{
// algn cdigo
}

// Primero comprueba si a < 10 y luego


// incrementa a en 1

if (++a < 10)


{
// algn cdigo
}

// Primero incrementa a en 1 y luego


// comprueba si a < 10

Operadores de bits
Se aplican a operaciones lgicas con variables a nivel binario. Aqu tenemos las clsicas operaciones AND,
OR inclusiva, OR exclusiva y la NEGACIN. Adicionalmente, he incluido en esta categora los operaciones
de desplazamiento a la derecha y la izquierda.

Si bien son operaciones que producen resultados anlogos a los de las instrucciones de ensamblador iorlw y
iorwf para la OR inclusiva, xorlw y xorwf para la OR exclusiva, andlw y andwf para la AND y comf para la
negacin; los operadores lgicos del C pueden operar sobre variables de distintos tamaos, ya sean de 1, 8, 16
32 bits.
Operador
Accin
&
AND a nivel de bits
|
OR inclusiva a nivel de bits
^
OR exclusiva a nivel de bits
~
Complemento a uno a nivel de bits
<<
Desplazamiento a la izquierda
>>
Desplazamiento a la derecha
Ejemplos:
char m;
int n;
m
m
m
m
n
n
m
m
m

=
=
=
=
=
=
=
=
=

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

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

// variable de 8 bits
// variable de 16 bits
//
//
//
//
//
//
//
//
//

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

// Cargar m con 0b00011000


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

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

Desplazamientos producidos por los operadores << y >>.

Operadores relacionales
Se emplean para construir las condiciones lgicas de las sentencias de control selectivas e iterativas, como ya
hemos podido apreciar en las secciones anteriores. La siguiente tabla muestra los operadores relacionales
disponibles.

Operador
==
!=
>
<
>=
<=

Accin
Igual
No igual
Mayor que
Menor que
Mayor o igual que
Menor o igual que

Operadores lgicos
Generalmente se utilizan para enlazar dos o ms condiciones lgicas simples. Por suerte, estos operadores solo
son tres y sern explicados en las prcticas del curso.
OperadorAccin
&&
||
!

AND lgica
OR lgica
Negacin lgica

Ejemplos:
if( !(a==0) )
{
// sentencias
}

// Si a igual 0 sea falso

if( (a<b) && (a>c) )


{
// sentencias
}

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

while( (a==0) || (b==0) )


{
// sentencias
}

// Mientras a sea 0 b sea 0

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

que es equivalente a la sentencia


object = object op expression;

op puede ser cualquiera de los operadores aritmticos o de bit estudiados arriba. O sea, op puede ser +, - , *, /,
%, &, | , ^, ~,<< >>. Nota: no debe haber ningn espacio entre el operador y el signo igual.
Ejemplos:
int a;
a += 50;
a += 20;
a *= 2;
a &= 0xF0;
a <<= 1;

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

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

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

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

// a, b y c son variables

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

Tambin se pueden construir expresiones condicionales, as:


if ( (a > b) && ( b < c) )
{
// ...
}

// Si a>b

b<c, ...

Las funciones
Una funcin es un bloque de sentencias identificado por un nombre y puede recibir y devolver datos. En bajo
nivel, en general, las funciones operan como las subrutinas de assembler, es decir, al ser llamadas, se guarda
en la Pila el valor actual del PC (Program Counter), despus se ejecuta todo el cdigo de la funcin y
finalmente se recobra el PC para regresar de la funcin.
Dada su relativa complejidad, no es tan simple armar una plantilla general que represente a todas las
funciones. El siguiente esquema es una buena aproximacin.
data_type1 function_name (data_type2 arg1, data_type3 arg2, ... )
{
// Cuerpo de la funcin
// ...

return SomeData;

// Necesario solo si la funcin retorna algn valor

Donde:

function_name es el nombre de la funcin. Puede ser un identificador cualquiera.


data_type1 es un tipo de dato que identifica el parmetro de salida. Si no lo hubiera, se debe poner la
palabra reservada void (vaco, en ingls).
arg1 y arg2 (y puede haber ms) son las variables de tipos data_type1, data_type2..., respectivamente,
que recibirn los datos que se le pasen a la funcin. Si no hay ningn parmetro de entrada, se pueden
dejar los parntesis vacos o escribir un void entre ellos.

Funciones sin parmetros


Para una funcin que no recibe ni devuelve ningn valor, la plantilla de arriba se reduce al siguiente esquema:
void function_name ( void )
{
// Cuerpo de la funcin
}

Y se llama escribiendo su nombre seguido de parntesis vacos, as:


function_name();

La funcin principal main es otro ejemplo de funcin sin parmetros. Dondequiera que se ubique, siempre
debera ser la primera en ejecutarse; de hecho, no debera terminar.
void main (void)
{
// Cuerpo de la funcin
}

Funciones con parmetros (por valor)


De momento, solo estudiaremos las funciones que pueden tener varios parmetros de entrada pero solo uno de
salida.
Si la funcin no tiene parmetros de entrada o de salida, debe escribirse un void en su lugar. El valor devuelto
por una funcin se indica con la palabra reservada return.
Segn el comportamiento de los parmetros de entrada de la funcin, estos se dividen en parmetros por valor
y parmetros por referencia. Lo expuesto en este apartado corresponde al primer grupo porque es el caso ms
ampliamente usado. Con esto en mente podemos seguir.
Para llamar a una funcin con parmetros es importante respetar el orden y el tipo de los parmetros que ella
recibe. El primer valor pasado corresponde al primer parmetro de entrada; el segundo valor, al segundo
parmetro; y as sucesivamente si hubiera ms.
Cuando una variable es entregada a una funcin, en realidad se le entrega una copia suya. De este modo, el
valor de la variable original no ser alterado. Mejor, plasmemos todo esto en el siguiente ejemplo.

int minor ( int arg1, int arg2, int arg3 )


{
int min;
// Declarar variable min
min = arg1;
// Asumir que el menor es arg1
if ( arg2 < min )
min = arg2;

// Si arg2 es menor que min


// Cambiar a arg2

if ( arg3 < min )


min = arg3;

// Si arg3 es menor que min


// Cambiar a arg3

return min;
}
void main (void)
{
int a, b, c, d;

// Retornar valor de min

// Declarar variables a, b, c y d

/* Aqu asignamos algunos valores iniciales a 'a', 'b' y 'c' */


/* ... */
d = minor(a,b,c);
// Llamar a minor
// En este punto 'd' debera ser el menor entre 'a', 'b' y 'c'
while (1);
// Bucle infinito
}

En el programa mostrado la funcin minor recibe tres parmetros de tipo int y devuelve uno, tambin de tipo
int, que ser el menor de los nmeros recibidos.
El mecanismo funciona as: siempre respetando el orden, al llamar a minor el valor de a se copiar a la
variable arg1; el valor de b, a arg2 y el valor de c, a arg3. Despus de ejecutarse el cdigo de la funcin, el
valor de retorno (min en este caso) ser copiado a una variable temporal y de all pasar a d.
Aunque el C no es tan implacable con la comprobacin de tipos de datos como Pascal, siempre deberamos
revisar que los datos pasados sean compatibles con los que la funcin espera, as como los datos recibidos,
con los que la funcin devuelve. Por ejemplo, estara mal llamar a la funcin minor del siguiente modo:
d = minor(-15, 100, 5.124); // Llamar a minor

Aqu los dos primeros parmetros estn bien, pero el tercero es un nmero decimal (de 24 32 bits), no
compatible con el tercer parmetro que la funcin espera (entero de 8 16 bits). En estos casos el compilador
nos mostrar mensajes de error, o cuando menos de advertencia.

Parmetros por referencia


La funcin que recibe un parmetro por referencia puede cambiar el valor de la variable pasada. La forma
clsica de estos parmetros se puede identificar por el uso del smbolo &, tal como se ve en el siguiente boceto
de funcin.
int minor ( int & arg1, int & arg2, int & arg3 )
{
// Cuerpo de la funcin.
// arg1, arg2 y arg3 son parmetros por referencia.
// Cualquier cambio hecho a ellos desde aqu afectar a las variables
// que fueron entregadas a esta funcin al ser llamada.
}

No voy profundizar al respecto porque he visto que muchos compiladores C no soportan esta forma. Otra
forma de pasar un parmetro por referencia es mediante los punteros, pero eso lo dejamos para el final porque
no es nada nada fcil para un novato.

Prototipos de funciones
El prototipo de una funcin le informa al compilador las caractersticas que tiene, como su tipo de retorno, el
nmero de parmetros que espera recibir, el tipo y orden de dichos parmetros. Por eso se deben declarar al
inicio del programa.
El prototipo de una funcin es muy parecido a su encabezado, se pueden diferenciar tan solo por terminar en
un punto y coma (;). Los nombres de las variables de entrada son opcionales.
Por ejemplo, en el siguiente boceto de programa los prototipos de las funciones main, func1 y func2
declaradas al inicio del archivo permitirn que dichas funciones sean accedidas desde cualquier parte del
programa. Adems, sin importar dnde se ubique la funcin main, ella siempre ser la primera en ejecutarse.
Por eso su prototipo de funcin es opcional.
#include <pic.h>
void func1(char m, long p); // Prototipo de funcin "func1"
char func2(int a);
// Prototipo de funcin "func2"
void main(void);
// Prototipo de funcin "main". Es opcional
void main(void)
{
// Cuerpo de la funcin
// Desde aqu se puede acceder a func1 y func2
}
void func1(char m, long p)
{
// Cuerpo de la funcin
// Desde aqu se puede acceder a func2 y main
}
char func2(int a)
{
// Cuerpo de la funcin
// Desde aqu se puede acceder a func1 y main
}

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

// Desde aqu se puede acceder a main pero no a func2


}
char func2(int a)
{
// Cuerpo de la funcin
// Desde aqu se puede acceder a func1 y main
}

Para terminar, dado que los nombres de las variables en los parmetros de entrada son opcionales, los
prototipos de func1 y func2 tambin se pueden escribir asi
void func1(char, long);
char func2(int );

Variables locales y variables globales


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

// Prototipo de funcin

int speed;
// Variable global
const long limit = 100; // Variable global constante
void inter(void)
{
int count;
// Variable local
/* Este count no tiene nada que ver con el count
de las funciones main o foo */

speed++;

// Acceso a variable global speed

vari = 0;

// Esto dar ERROR porque vari solo pertenece


// a la funcin foo. No compilar.

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

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

Algo muy importante: a diferencia de las variables globales, las variables locales tienen almacenamiento
temporal, es decir, se crean al ejecutarse la funcin y se destruyen al salir de ella. Qu significa eso? Lo
explico en el siguiente apartado.
Si dentro de una funcin hay una variable local con el mismo nombre que una variable global, la precedencia
en dicha funcin la tiene la variable local. Si te confunde, no uses variables globales y locales con el mismo
nombre.

Variables static
Antes de nada debemos aclarar que una variable static local tiene diferente significado que una variable static
global. Ahora vamos a enfocarnos al primer caso por ser el ms comn.
Cuando se llama a una funcin sus variables locales se crearn en ese momento y cuando se salga de la
funcin se destruirn. Se entiende por destruir al hecho de que la locacin de memoria que tena una variable
ser luego utilizada por el compilador para otra variable local (as se economiza la memoria). Como
consecuencia, el valor de las variables locales no ser el mismo entre llamadas de funcin.
Por ejemplo, revisa la siguiente funcin, donde a es una variable local ordinaria.
void increm()
{
int a;
a++;
}

// Declarar variable a
// Incrementar a

Cualquiera que haya sido su valor inicial, crees que despus de llamar a esta funcin 10 veces, el valor de a
se habr incrementado en 10?... Pues, no necesariamente. Cada vez que se llame a increm se crea a, luego se
incrementa y, al terminar de ejecutarse la funcin, se destruye.
Para que una variable tenga una locacin de memoria independiente y su valor no cambie entre llamadas de
funcin tenemos dos caminos: o la declaramos como global, o la declaramos como local esttica. Los buenos
programadores siempre eligen el segundo.

Una variable se hace esttica anteponiendo a su declaracin el especificador static. Por defecto las variables
estticas se autoinicializan a 0, pero se le puede dar otro valor en la misma declaracin (dicha inicializacin
solo se ejecuta la primera vez que se llama a la funcin), as:
static int var1;
static int var2 = 50;

// Variable static (inicializada a 0 por defecto)


// Variable static inicializada a 50

Ejemplos.
void increm()
{
static int a = 5;
// Variable local esttica inicializada a 5
a++;
// Incrementar a
}
void main()
{
int i;
// Declarar variable i
// El siguiente cdigo llama 10 veces a increm
for(i=0; i<10; i++)
increm();
// Ahora la variable a s debera valer 15
while(1);
// Bucle infinito
}

Variables volatile
A diferencia de los ensambladores, los compiladores tienen cierta inteligencia. Es decir, piensan un poco
antes de traducir el cdigo fuente en cdigo ejecutable. Por ejemplo, veamos el siguiente pedazo de cdigo
para saber lo que suele pasar con una variable ordinaria:
int var;
//...
var = var;

// Declarar variable var


// Asignar var a var

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

// count es variable global voltil

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

Arrays y Punteros
Probablemente ste sea el tema que a todos nos ha dado ms de un dolor de cabeza y que ms hemos reledo
para captarlo a cabalidad. Hablo ms bien de los punteros. Si ellos el C no sera nada, perdera la potencia por
la que las mejores empresas lo eligen para crear sus softwares de ordenadores.
Pero bueno, regresando a lo nuestro, estos temas se pueden complicar muchsimo ms de lo que veremos aqu.
Solo veremos los arrays unidimensionales y los punteros (que en principio pueden apuntar a todo tipo de
cosas) los abocaremos a los datos bsicos, incluyendo los mismos arrays. Aun as, te sugiero que tengas un par
de aspirinas al lado.

Los arrays o matrices


Un array es una mega variable compuesto de un conjunto de variables simples del mismo tipo y ubicadas en
posiciones contiguas de la memoria. Con los arrays podemos hacer todos lo que hacamos con las tablas (de
bsqueda) del ensamblador y muchsimo ms.
Un array completo tiene un nombre y para acceder a cada uno de sus elementos se utilizan ndices entre
corchetes ([ ]). Los ndices pueden estar indicados por variables o constantes. En el siguiente esquema se ve
que el primer elemento de un array tiene ndice 0 y el ltimo, N-1, siendo N la cantidad de elementos del
array.

Estructura de un array unidimensional de N elementos.

Declaracin de arrays
Para declarar un array unidimensional se utiliza la siguiente sintaxis:
data_type identifier[ NumElementos ];

Donde data_type es un tipo de dato cualquiera, identifier es el nombre del array y NumElementos es la
cantidad de elementos que tendr (debe ser un valor constante).
De este modo, el ndice del primer elemento es 0 y el del ltimo es NumElements - 1.

Por ejemplo, las siguientes lneas declaran tres arrays.


char letters10];
long HexTable[16];
int address[100];

// letters es un array de 10 elementos de tipo char


// HexTable es un array de 16 elementos de tipo long
// address es un array de 100 elementos de tipo int

Para el array letters el primer elemento es letters[0] y el ltimo, letters[9]. As, tenemos 10 elementos en total.
Si quisiramos asignar a cada uno de los elementos de letters los caracteres desde la a hasta la j, lo
podramos hacer individualmente as:
letters[0]
letters[1]
letters[2]
letters[3]
letters[4]
letters[5]
letters[6]
letters[7]
letters[8]
letters[9]

=
=
=
=
=
=
=
=
=
=

'a';
'b';
'c';
'd';
'e';
'f';
'g';
'h';
'i';
'j';

// Aqu el ndice es 0
// Aqu el ndice es 1
// ...
//

// Aqu el ndice es 9

Pero as no tiene gracia utilizar arrays, verdad? En este caso lo mejor es utilizar un bucle, as: (Nota: los
caracteres son, al fin y al cabo, nmeros en cdigos ascii y se les puede comparar.)
char c;
for ( c = 'a'; c <= 'j'; c++ )
letters[i] = c;

Inicializacin de arrays
Los elementos de un array se pueden inicializar junto con su declaracin. Para ello se le asigna una lista
ordenada de valores encerrados por llaves y separados por comas. Por supuesto, los valores deben ser
compatibles con el tipo de dato del array. Este tipo de inicializacin solo est permitido en la declaracin del
array.
Ejemplos:
unsigned char mask[3] = { 0xF0, 0x0F, 0x3C };
// Ok
int a[5] = { 20, 56, 87, -58, 5000 };
// Ok
char vocals[5] = { 'a', 'e', 'i', 'o', 'u' };
// Ok
int c[4] = { 5, 6, 0, -5, 0, 4 };
// Error, demasiados inicializadores

Tambin es posible inicializar un array sin especificar en su declaracin el tamao que tendr, dejando los
corchetes vacos. El tamao ser precalculado y puesto por el compilador. sta es una forma bastante usada en
los arrays de texto, donde puede resultar muy incmodo estar contando las letras de una cadena. Por ejemplo:
int a[] = { 70, 1, 51 };
// Un array de 3 elementos
char vocals[] = { 'a', 'e', 'i', 'o', 'u' };
// Un array de 5 elementos
char msg[] = "Este es un array de caracteres"; // Un array of 31 elementos

Por qu el ltimo array tiene 31 elementos si solo se ven 30 letras? Lo sabremos luego.

Cadenas de texto terminadas en nulo

Son arrays de tipo de dato char. Hay dos caractersticas que distinguen a estas cadenas de los dems arrays.
Primero: su inicializacin se hace empleando comillas dobles y segundo, el ltimo trmino del array es un
carcter NULL (simplemente un 0x00). De ah su nombre.
Ejemplos:
char Greet[10] = "Hello";
char msg[] = "Hello";

// Un array de 10 elementos
// Un array de 6 elementos

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

// Un array de 6 elementos

Visto grficamente, msg tendra la siguiente representacin:

Estructura de una cadena de texto.

Los punteros
Los punteros suelen ser el tema que ms cuesta entender en programacin. Pero si ya llegaste aqu, es el
momento menos indicado para detenerte.
Los punteros son un tipo de variables muy especial. Son variables que almacenan las direcciones fsicas de
otras variables. Si tenemos la direccin de una variable, tenemos acceso a esa variable de manera indirecta y
podemos hacer con ellas todo lo que queramos ;).

Declaracin de punteros
Los punteros pueden apuntar a todo tipo de variables, pero no a todas al mismo tiempo. La declaracin de un
puntero es un tanto peculiar. En realidad, se parece a la declaracin de una variable ordinaria solo que se pone
un asterisco de por medio. En este punto debes recordar las declaraciones de todo tipo de variables que hemos
visto, incluyendo las influenciadas por los calificadores const, static, etc. Todas excepto los arrays; por qu?
La forma general de declarar un puntero es la siguiente:
data_type * PointerName;

Los siguientes ejemplos muestran lo fcil que es familiarizarse con la declaracin de los punteros:
int * ip;

// ip es un puntero a variable de tipo int

char * ucp;
unsigned char * ucp;
const long * clp;
float * p1, *p2;

//
//
//
//

cp es un puntero a variable de tipo char


Puntero a variable de tipo unsigned char
Puntero a constante de tipo long
Declara dos punteros a variable de tipo float

Apuntando a variables
Decimos que una variable puntero apunta a una variable x si contiene la direccin de dicha variable. Para
ello se utiliza el operador &, el cual extrae la direccin de la variable a la que acompaa. Un puntero siempre
debera apuntar a una variable cuyo tipo coincida con el tipo del puntero.
En los siguientes ejemplos vemos cmo apuntar a variables de tipo bsico, como int, char o float. Ms
adelante veremos cmo apuntar a arrays.
void main (void)
{
int height, width;
char a, b, c;
float max;
int * ip;
char * cp;
float * fp;

// ip es un puntero a variable tipo int


// cp es un puntero a variable tipo char
// Puntero a variable tipo float

ip = &height;
ip = &width;

// Con esto ip tendr la direccin de height


// Ahora ip apunta a width

cp = &a;
cp = &c;
cp = &a;

// cp apunta a a
// Ahora cp apunta a c
// Ahora cp apunta a a otra vez

fp = &max;
fp = &height;

// fp apunta a max
// Error! height no es una variable float

//...
}

Asignaciones indirectas mediante punteros


Una vez que un puntero apunte a una variable cualquiera, se puede acceder a dicha variable utilizando el
nombre del puntero precedido por un asterisco, de esta forma:
void main (void)
{
int height, width, n;
int * p, * q;

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

p = &height;
*p = 10;

// p apunta a height
// Esto es como height = 10

p = &width;
*p = 50;

// p apunta a width
// Esto es como width = 50

height = *p;

// Esto es como height = width

q = &height;
n = (*p + *q)/2;

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

//...
}

La expresin *p se debera leer: la variable apuntada por p. Eso tambin ayuda mucho a comprender a los
punteros.
Y para esto se inventaron los punteros? Yo me preguntaba lo mismo en mis inicios. El tema de los punteros
se puede complicar casi hasta el infinito, por eso quiero ir con cuidado y poco a poco para que nadie se
pierda.

Punteros y arrays
Cmo se declara un puntero a un array? Un puntero a un array es simplemente un puntero al tipo de dato del
array. Cuando se asigna un puntero a un array, en realidad el puntero toma la direccin de su primer elemento,
a menos que se especifique otro elemento.
Luego, bastara con modificar el valor del puntero para que apunte a los otros elementos del array. Todo lo
indicado se refleja en el siguiente cdigo:
void main (void)
{
int * p;
// Declara p como puntero a int
int n;
// Alguna variable
int mat[3] = { 78, 98, 26 };
// Array de variables int
p = &mat;

// p apunta a mat (a su primer elemento)

n = *p;
p++;
n = *p;
p++;
n = *p;

//
//
//
//
//

*p = 10;
p--;
*p = 100;

// Con esto mat[3] valdr 10


// Decrementar p para apuntar a elemento anterior
// Con esto mat[2] valdr 100

p = mat;

// p apunta a mat. Es lo mismo que p = &mat

p = NULL;
// ...

// Desasignar p. Lo mismo que p = 0x0000

Esto da n =
Incrementar
Esto da n =
Incrementar
Esto da n =

78
p para apuntar a siguiente elemento
98
p para apuntar a siguiente elemento
26

En el fondo los arrays y los punteros trabajan de la misma forma, por lo menos cuando referencian a variables
almacenadas en la RAM del microcontrolador. La nica diferencia es que los arrays no pueden direccionar a
datos diferentes de su contenido; por eso tambin se les llama punteros estticos. En la prctica esto significa
que un array es siempre compatible con un puntero, pero un puntero no siempre es compatible con un array.
Por ejemplo, a un array no se le puede asignar otro array ni se le pueden sumar o restar valores para que
apunten a otros elementos. Por lo dems, las operaciones de asignacin son similares para punteros y arrays,
tal como se puede apreciar en el siguiente cdigo. (Por si las moscas, str1 es el array y str2, el puntero.)
void main(void)
{
char str1[] = { 'A', 'r', 'r', 'a', 'y' };

char * str2 = { 'P', 'o', 'i', 'n', 't', 'e', 'r' };


char a;
a = str1[0];
a = str1[3];

// Esto da a = 'A'
// Esto da a = 'a'

a = str2[0];
a = str2[3];

// Esto da a = 'P'
// Esto da a = 'n'

str1 += 2;
str2 += 2;

// Error! Str1 es esttico


// Correcto. Ahora str2 apunta a 'i'

str1++;
str2++;

// Error otra vez! Str1 es esttico


// Correcto. Ahora str2 apunta a 'n'

a = *str2;

// Esto da a = 'n'

//

...

Paso de punteros y arrays a funciones


Recuerdas el paso de variables por valor y por referencia? Pues aqu vamos de nuevo.
Bien, recordemos: una variable pasada por valor a una funcin, en realidad le entrega una copia suya; por lo
que la variable original no tiene por qu ser afectada por el cdigo de la funcin. Ahora bien, pasar una
variable por referencia significa que se pasa la direccin de dicha variable. Como consecuencia, la funcin
tendr acceso a la variable original y podr modificar su contenido. Esto podra resultar riesgoso, pero, bien
usada, la tcnica es una potente arma.
Ya que los punteros operan con direcciones de variables, son el medio ideal para trabajar con parmetros por
referencia. Hay dos casos de particular inters: uno, cuando deseamos en serio que la variable pasada a la
funcin cambie a su regreso; y dos, cuando la variable pasada es demasiado grande (un array) como para
trabajar con copias. De hecho, los arrays siempre se pasan por referencia ya que tambin son punteros al fin.
La sintaxis de los punteros en el encabezado de la funcin no es nada nuevo, teniendo en cuenta que tambin
tienen la forma de declaraciones de variables.
En el siguiente ejemplo la funcion interchage intercambia los valores de las dos variables recibidas. En
seguida explicar por qu vara un poco la forma en que se llama a la funcin.
void interchange( int * p1, int * p2 )
{
int tmp = *p1; // Guardar valor inicial de variable apuntada por p1.
*p1 = *p2;
// Pasar valor de variable apuntada por p2 a...
// variable apuntada por p1.
*p2 = tmp;
// Variable apuntada por p2 valdr tmp.
}
void main (void)
{
int i, j;
/* Hacer algunas asignaciones */
i = 10;
j = 15;

/* Llamar a funcin interchange pasando las direcciones de i y j */


interchange( &i, &j );
// En este punto i vale 15 y j vale 10
// ...
}

Al llamar a interchange le entregamos &i y &j, es decir, las direcciones de i y j. Por otro lado, la funcin
interchange recibir dichos valores en p1 y p2, respectivamente. De ese modo, p1 y p2 estararn apuntando a i
y j, y podremos modificar sus valores.
Ten presente que se mantiene la forma de asignacin puntero = &variable (puntero igual a direccin de
variable).
Ahora veamos ejemplos donde la forma de asignacin cambia a puntero = puntero. Esto incluye a los arrays
porque, recordemos, un puntero siempre puede ser tratado como un array, aunque lo contrario no siempre es
posible.
En el siguiente programa array1 y array2 se pasan a la funcin prom, la cual devuelve el valor promedio de
los elementos del array recibido. Como para ese clculo se necesita conocer la cantidad de elementos que
tiene el array, prom recibe dicho valor en el parmetro size.
float prom ( int * p, int size )
{
int i; float tmp = 0;
for ( i=0; i<size; i++ ) // Bucle para contar i desde 0 hasta size-1.
tmp += p[i];
// Sumar elemento p[i] a tmp.
return ( tmp/size );
// Retornar valor promediado.
}
void main (void)
{
int array1[4] = { 51, 14, 36, 78 };
// Un array de 4 elementos
int array2[] = { -85, 4, 66, 47, -7, 85 }; // Un array de 6 elementos
float avrg;
// Una variable tipo float, para decimales
avrg = prom (array1, 8);
// Ahora avrg debera valer (51 + 14 + 36 + 78 )/8 = 44.75
avrg = prom (array2, 6);
// Ahora avrg debera valer (-85 + 4 + 66 + 47 - 7 + 85 )/6 = 18.3333
while( 1 );

// Bucle infinito

Finalmente, veamos un programa donde se utilizan las Cadenas de texto terminadas en nulo.
Este programa tiene dos funciones auxiliares: mayus convierte la cadena recibida en maysculas, y lon calcula
la longitud del texto almacenado en el array recibido. Ambas funciones reciben el array pasado en un puntero
p dado que son compatibles.
void mayus( char * p )
{
while( *p )
// Mientras carcter apuntado sea diferente de 0x00
{
if( ( *p >= 'a' ) && ( *p <= 'z' ) ) // Si carcter apuntado es

*p = *p - 32;
p++;

// minscula
// Hacerlo mayscula
// Incrementar p para apuntar sig. carcter

}
}
int lon( char * p)
{
int i = 0;
// Declarar variable i e iniciarla a 0.
while( *p )
// Mientras carcter apuntado sea diferente de 0x00
{
i++;
// Incrementar contador.
p++;
// Incrementar p para apuntar sig. carcter
}
return i;
// Retornar i
}
void main (void)
{
int L;
char song1[20] = "Dark Blue";
char song2[20] = "Staring Problem";
char song3[20] = "Ex-Girlfriend";
/* Obtener longitudes de los arrays de
L = lon(song1);
// Debera dar L =
L = lon(song2);
// Debera dar L =
L = lon(song3);
// Debera dar L =

texto */
9
15
13

/* Convertir cadenas en maysculas */


mayus(song1 );
// Es lo mismo que mayus(&song1);
// Ahora song1 debera valer "DARK BLUE"
mayus(song2 );
// Es lo mismo que mayus(&song2);
// Ahora song2 debera valer "STARING PROBLEM"
mayus(song3 );
// Es lo mismo que mayus(&song3);
// Ahora song3 debera valer "EX-GIRLFRIEND"
while(1);

// Bucle infinito

En el programa se crean tres arrays de texto de 20 elementos (song1, song2 y song3), pero el texto
almacenado en ellos termina en un carcter 0x00.
Segn la tabla de caracteres ascii, las letras maysculas estn ubicadas 32 posiciones por debajo de las
minsculas. Por eso basta con sumarle o restarle ese valor a un carcter ascci para pasarlo a mayscula o
minscula.
En ambas funciones el puntero p navega por los elementos del array apuntado hasta que encuentra el final,
indicado por un carcter nulo (0x00).

Arrays constantes
No es que me haya atrazado con el tema, es solo que los arrays constantes son uno de los temas cuyo
tratamiento vara mucho entre los distintos compiladores. Veamos en qu.

Un array constante es uno cuyos elementos solo podrn ser ledos pero no escritos; tan simple como eso.
En principio, para que un array sea constante a su clsica declaracin con inicializacin de un array se le debe
anteponer el calificador const. Por ejemplo:
const int a[5] = { 20, 56, 87, -58, 5000 };
// Array constante
const char vocals[5] = { 'a', 'e', 'i', 'o', 'u' }; // Array constante
const char text[] = "Este es un array constante de caracteres";

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

Por lo dems, el acceso a los elementos de estos arrays tiene que seguir siendo mediante ndices y corchetes.
En el compilador MPLAB C18, la palabra rom va junto al const. Por supuesto que en los PIC18 los arrays en
ROM se implementan con ms eficiencia sin recurrir a los limitados retlw.
rom
rom
rom
rom

const
const
const
const

char * a = "cadena de texto constante en MPLAB C18";


char a[] = "otra cadena de texto constante en MPLAB C18";
int mat[5] = { 20, 56, 87, -58, 5000 };
// Array constante
long pots[] = {10, 100, 1000, 10000, 100000}; // Array constante

Proteus VSM
Qu es Proteus VSM
A lo largo de este curso no muestro inters personal para promocionar algn producto en particular. Pero al
Csar lo que es del Csar y no tengo mayor reparo en asegurar que Proteus VSM es el mejor software de su
clase que existe para aficionados y profesionales dedicados al desarrollo de proyectos con microcontroladores.
Proteus ha revolucionado el concepto de simulacin de circuitos electrnicos al pasar de las tpicas
presentaciones grficas y textuales de informacin pre-procesada (al estilo PSPICE) a la simulacin
interactiva muchas veces en tiempo real.
Podra seguir listando las potentes caractersticas de Proteus, pero como tampoco pretendo desafiar sus
archivos de ayuda y manuales oficiales, los cuales son muy buenos y de lectura recomendada (para quien
tenga tiempo:), prefiero aconsejarte que le eches un vistazo a los excelentes ejemplos que trae incluido.
Quedars ms que asombrado.

Instalacin de Proteus VSM


El proceso de instalacin no difiere mucho de cualquier otro software de Windows. Solo tenemos que pasar de
Next en Next aceptando preferentemente todas las opciones que se nos ofrecen por defecto.

Instalacin de Proteus.
Terminada la instalacin podemos ir al men iniciar de Windows y ver todos los componentes disponibles
incluyendo sus respectivos archivos de ayuda. La aplicacin principal de Proteus se llama ISIS, tanto que el
asistente de instalacin coloc su acceso directo en el escritorio sin ni siquiera preguntrnoslo.

Componentes instalados de Proteus.


Vemos que tambin est presente el programa ARES, el cual sirve para crear PCBs de circuitos, como Eagle
o Protel. Es raro arrancar ARES para trabajar con l desde cero. Por eso no tiene acceso directo en el
escritorio. Lo normal es dibujar el esquema del circuito en el entorno de ISIS y luego llevarlo a ARES.

Los Drivers de USB de Proteus


Otro componente que nos podr ser de mucha importancia son los drivers de USB. Proteus utiliza estos
drivers para simular los microcontroladores con USB. Podemos ver en la figura de arriba que estn
disponibles el instalador y des-instalador de estos drivers. De hecho an no estn instalados. Podramos
hacerlo ahora dndole doble clic a Install USB Drivers pero la misma gente de Labcenter Electronics nos
sugiere pensarlo dos veces antes de hacerlo. Por qu?
Adems, est el hecho de que esos drivers de USB solo corren en sistemas operativos de 32 bits (al menos
hasta el momento de actualizar este contenido [Octubre de 2011]). Si tratramos de instalarlos en un SO de 64
bits nos saldra la siguiente ventanita.

Hablar de USB y Proteus es todo un tema aparte. As que lo dejaremos para ms adelante. All veremos cmo
programar microcontroladores con USB y cmo simularlos en Proteus, incluso en Windows 7 de 64 bits.

Instalacin de VSM Studio


VSM Studio es un IDE (Entorno de Desarrollo integrado) desarrollado por Labcenter Electronics como
plataforma alternativa para el desarrollo de proyectos con microcontroladores usando diferentes compiladores
o ensambladores. No es un IDE ms, como CodeBlocks o Eclipse, que son entornos genricos y por ende
presentan algunas falencias cuando se busca una configuracin ms personalizada. VSM Studio fue pensado
exclusivamente para el trabajo con los compiladores y ensambladores de microcontroladores.
Algunos de los compiladores que soporta VSM Studio son:

Para los PIC: CCS C (no incluye dsPIC), MPLAB C18, MPLAB C30, MPASM, Hi-Tech C para los
PIC10/12/16, Hi-Tech C para los PIC18, Hi-Tech C para los dsPIC.
Para los AVR: solo WinAVR y el ensamblador AVRASM.
Para los 8051: IAR para 8051, Keil uVision4 para 8051 y el ensamblador ASEM-51.
Para los ARM: IAR para ARM y GCC para ARM.

Se ve interesante, sobre todo si consideramos que VSM Studio se distribuye gratuitamente. No obstante VSM
Studio est lejos de igualar al MPLAB para los PIC y ms lejos del Studio 5 para los AVR. Ellos tambin son
IDEs flexibles y se conectan con muchos ms compiladores y con mejor soporte, adems del hecho de que
tambin pueden interactuar con Proteus gracias a los Pluggins respectivos.
Quiz muchas veces nos pueda atraer la extrema ligereza de VSM Studio, cuando nos cansemos de lo que
demoran en cargarse MPLAB o Studio 5. VSM Studio solo pesa cerca de 15 Mb, as que nada perdemos con
descargarlo, instalarlo y probarlo. Su instalacin no tiene nada de extraordinario. Conviene siempre visitar la
web Labcenter Electronics para obtener las versiones ms recientes de sta y otras extensiones de Proteus.

Instalacin de VSM Studio.

El Entorno de ISIS Proteus

Entorno de trabajo de ISIS.


ISIS es el entorno de trabajo de Proteus. Aqu se disean, dibujan y simulan los circuitos.
Bueno, no voy a describir cada uno de los elementos de esta ventana. T mismo puedes ir conocindolos con
tan solo sealarlos con el puntero del mouse. Adems de las clsicas sugerencias que suele ofrecer como
cualquier otro buen programa de Windows, Proteus muestra una pequea descripcin del elemento apuntado
cuando se pulsa la tecla F1.
Por ejemplo, la siguiente figura muestra la informacin contextual del botn Generator. Claro que est en
ingls, pero igual se entiende aunque sea un poquito. Adems, no me digas que no sabes lo que significan esas
frasecitas como Copy to clipboard, Save current design, Zoom in, Zoom out, etc. Por favor! En cuanto a los
elementos y comandos que son ms propios de Proteus, aprenderemos a usarlos de a poco. Esta toma
corresponde a Proteus en Windows XP porque en Windows 7 esta caracterstica no me funcion ;)

Informacin interactiva de los elementos de ISIS.

Habilitar Grficos de Open GL


Es probable que al abrir ISIS proteus por primera vez nos aparezca el siguiente mensaje:

Mensaje de soporte de Open GL graphics.


All nos dice que nuestra tarjeta grfica puede soportar los grficos Open GL a nivel hardware. Con Open GL
Proteus producir mejores resultados visuales tanto en el diseo del circuito como en las animaciones. Las
simulaciones tambin sern ms rpidas y fluidas, o mejor dicho, consumirn menos ciclos de CPU. Esto
ltimo es muchsimo ms importante que el aspecto visual, por ejempo, recuerdo que la simulacin de un
letrero electrnico como el del programa ledsign, que consuma el 90% del CPU, se aceleraba y reduca el
consumo de CPU al 60% con el soporte hardware de Open GL grfico habilitado.
Podemos marcar la casilla Dont display this message again para que no nos vuelva a mostrar esta ventana.
Y si queremos habilitar Open GL para Proteus y obtener las mejora visuales indicadas vamos al men System
y escogemos Set Display Options
En la ventana que se nos presenta seleccionamos Use Open GL Graphics (Hardware Accelerated).

Habilitacin de Open GL graphics para ISIS.


Cuando cerremos esta ventana aceptando el cambio realizado nos aparecer otra diciendo que nos aseguremos
de actualizar los drivers de la tarjeta grfica en caso de tener problemas con el display o el programa. Si los
problemas persisten podemos revertir la configuracin hecha.
Podremos apreciar algunos efectos de Open GL en las siguientes capturas de pantalla, por ejemplo, cuando un
componente apuntado se resalta con un rectngulo relleno en vez de un rectngulo de contorno punteado.

Open GL habilitado
Open GL sin habilitar

Creacin del Diseo

Un diseo est compuesto por diversos objetos, desde los cables de interconexin hasta los instrumentos
virtuales como el osciloscopio o el terminal serial. Los objetos estn agrupados segn su categora con sus
respectivos iconos en la barra de herramientas izquierda de ISIS.
A continuacin citamos algunas categoras, llamadas modos:
Components. Representan todos los componentes electrnicos presentes en un
circuito, como los LEDs, condensadores, microcontroladores, etc.
Virtual Instruments. Ofrece los instrumentos de medicin como el Osciloscopio,
voltmetro, frecuencmetro, etc.
Generator, para producir ondas cuadradas, sinusoidales, de reloj, etc.
Simulation Graphs, para visualizar los resultados de la simulacin en grficos, al
estilo de los programas como Electronics Workbench o Cadence Orcad.
Terminals. Nos permitir colocar los terminales de entrada, salida, as como tierra
GND y las fuentes de alimentacin VCC o VDD. Los terminales de entrada INPUT y
salida OUTPUT son muy tiles para conectar elementos sin necesidad de unirlos
directamente con cables.
Etc. Etc.

Colocando los Componentes


El panel izquierdo de ISIS (que en la figura de abajo se ve con el ttulo de TERMINALS) se llama Object
Selector y despliega los objetos de la categora seleccionada actualmente (Components, Generator, etc.).
Para aadir un objeto a la hoja de diseo primero debemos seleccionar su categora y luego sacar del Object
Selector el elemento deseado.
Por ejemplo, para colocar los smbolos de tierra y alimentacin seguimos los tres pasos mostrados en la
siguiente figura.

Colocando objetos en la hoja de diseo.


Para colocar los componentes electrnicos como resistencias o diodos aparece un paso intermedio que es
llenar el Object Selector con los dispositivos deseados. Para ello hacemos clic en el icono Components

de

la barra de herramientas y luego en el botoncito P. Un modo rpido es clicar el icono


de la barra de
herramientas superior o sencillamente presionar la tecla P. En cualquier caso nos veremos con la ventana
Pick Devices.

Sacando dispositivos de la ventana Pick Devices.


En la caja de texto Keywords se escribe el componente buscado. Nota que Proteus tiene un excelente motor de
bsqueda y no es necesario escribir el nombre completo de una parte ni usar comodines como en otros
programas. A medida que se va escribiendo, Proteus va mostrando todos los elementos coincidentes.

Una vez localizado el componente buscado, le damos doble clic para enviarlo al Object Selector. As podemos
ir sacando los dispositivos restantes. Cuando hayamos terminado con esta parte cerramos esta ventana dndole
al botn Ok, Cancel o, mejor an, presionando Escape en el teclado.
Ahora el panel Object Selector contiene los diversos dispositivos que formarn nuestro circuito. Seleccinalos
uno a uno y luego colcalos en el esquemtico con un clic izquierdo.

Colocacin de elementos en la hoja de diseo.


En este punto vamos a detenernos a comentar algunos componentes que resultarn muy frecuentes en el
futuro.

ATmegaNN4P o ATmegaNN8P. Puede que en algunos casos el AVR que veas en mis esquemas no se
parece al que tienes en tu Proteus. Lo que importa es que sea o tenga el mismo modelo, el resto es solo
cuestin de apariencia
LED-BLUE, LED-GREEN,... Hay varios tipos de diodos LED disponibles. Para las simulaciones se
suelen usar los LED animados (los que prenden). Puesto que son solo simulaciones, los colores quedan
en segundo plano.
RESISTOR. Al escribir resistor en la casilla Keywords se listarn muchsimas partes, desde los
modelos genricos hasta los resistores comerciales. En principio, podramos elegir cualquiera de ellas;
de todos modos, la impedancia puede reajustarse fcilmente. Tambin, se podra afinar la bsqueda
aadiendo el valor de la resistencia.
BUTTON. As se llaman los infaltables pulsadores.

Mover, Rotar y Reflejar los Objetos


Como los objetos no siempre se presentarn en la posicin deseada, conozcamos algunas formas de
cambiarlas.
Un objeto queda seleccionado por un clic izquierdo del mouse. Con ello aparecer resaltado en color rojo (por
defecto). Se quita la seleccin de un objeto haciendo otro clic sobre alguna parte vaca de la hoja de diseo.
Para editar la posicin de un elemento seleccinalo y abre su men contextual (de clic derecho). All
encontrars las diversas opciones disponibles, como mover, borrar, rotar en un sentido u otro, o reflejar
(Mirror) sobre el eje X o Y.

Men contextual de un objeto en ISIS.


Alternativamente, puedes usar otros atajos, por ejemplo:

Para mover un objeto, seleccinalo y luego arrstralo con el botn izquierdo hasta el lugar deseado.

Para rotar un objeto, seleccinalo y luego presiona la tecla + o - del teclado numrico tantas veces
hasta conseguir la orientacin deseada. Otra forma de hacerlo es aplicando los botones de rotacin al
objeto seleccionado. La rotacin solo es posible en ngulos rectos.

Tambin se pueden usar los botones de rotacin y reflexin mostrados abajo, aunque ellos tendrn efecto solo
en los elementos del Object Selector, o sea, antes de colocarlos en la hoja de diseo.

Botones de rotacin y reflexin.

Para eliminar un objeto, dale doble clic derecho, o seleccinalo y usa la tecla supr.

La seleccin de un grupo de objetos se logra encerrndolos en un rectngulo dibujado con el botn izquierdo.
Una vez hecho esto el men contextual del botn derecho desplegar las opciones de edicin de grupo como
copiar, mover o rotar/reflejar. Estas opciones tambin estn presentes en la barra de herramientas.
Entre todo, la forma ms fcil de mover un grupo de objetos seleccionado es arrastrarlos con el botn
izquierdo.

Botones para editar grupos de objetos.


En la siguiente figura se aprecia el copiado mltiple de un grupo de objetos.

Copiado mltiple de un grupo de objetos en ISIS.


Con frecuencia tras la edicin de los objetos an quedarn restos o marcas de dibujo sobre el esquemtico.
Para refrescar de forma rpida todo el diagrama se puede presionar la tecla R (de Redibujar).

Interconexin de Componentes
Los cables para la interconexin de los elementos del circuito no estn en una categora en especial. Se
colocan directamente, aprovechando la forma de lpiz del cursor y las marcas de snap (esos pequeos
cuadraditos ) que aparecen al acercar el lpiz a algn terminal conectable.

Interconexin del circuito.

Componentes Innecesarios
Para simplificar la tarea de simulacin Proteus permite obviar algunos elementos en el diseo.
Pines GND y VDD. Tal vez te inquiete la ausencia de los pines GND y VDD de los dispositivos digitales
como los microcontroladores, pero eso no cuenta para la simulacin porque Proteus asume que trabajarn con
alimentacin de 0 y 5 V.
El circuito oscilador del microcontrolador. Los microcontroladores pueden trabajar con varios tipos de
osciladores siendo el ms comn el conformado por un cristal de cuarzo y dos capacitores de estabilizacin.
Sin embargo, debes saber que para los microcontroladores este circuito no tiene directa intervencin en la
simulacin.
La frecuencia de operacin del microcontrolador se establece en su ventana de propiedades, con lo cual el
circuito del XTAL quedara de adorno. Muchas veces se lo suele ignorar para aligerar la carga del diseo y
mejorar en algo la velocidad de simulacin.

Circuito oscilador tpico de un microcontrolador.


Los capacitores de desacoplo. Ya que en el entorno ideal de ISIS no hay ruido ni interferencias de ese tipo, no
hacen falta los capacitores que en un circuito real se colocan en los pines de alimentacin VCC o VDD de los
circuitos integrados.

Simulacin del Diseo


En cualquier momento, inclusive con el diseo a medio armar, ya se puede correr la simulacin para ver cmo
va quedando y en el camino ir aadiendo, quitando o modificando componentes. Solo hay que jugar con los
botones Play y Stop mostrados abajo. Los otros botones centrales son para pausas aunque es en el modo
debugging del microcontrolador donde se les saca verdadero provecho.

Botones de control de la simulacin.


Sin embargo, si hay al menos un microcontrolador presente en el circuito, la simulacin no arrancar sino
hasta haber cargado su firmware. Este tema es muy crucial as que se le dedica toda una seccin ms adelante.

Edicin de las Propiedades de los Objetos


Una de las grandes ventajas de las simulaciones es que se pueden cambiar los elementos del diseo o sus
valores con suma facilidad.

Por lo general y como se puede comprobar, muchos de los componentes de Proteus no requieren nada de
edicin y, de hecho, se pueden encontrar partes con modelos que ya tienen todos los parmetros correctamente
calibrados. No obstante, muchas veces ser ms cmodo cambiar alguna propiedad de un dispositivo.
Para editar las propiedades de un objeto hay que darle doble clic izquierdo. As se accede a su ventana de
propiedades, la cual, por supuesto, variar de acuerdo con el objeto tratado.
Por ejemplo, si quisiramos cambiar el valor de una resistencia, solo habra que modificar el parmetro
Resistance en su ventana Edit Component.

Ventana de propiedades de una resistencia.


La fuente de alimentacin POWER tiene un valor de 5 V por defecto, aunque no se note as en el
esquemtico. Si quieres ver que al menos diga VDD o VCC, puedes abrir su ventana de propiedades y
escoger una opcin del combobox.

Ventana de propiedades de un terminal de alimentacin.


Claro que el susodicho combobox no ofrece tensiones ordinarias. Si las requiriramos las tendramos que
escribir a mano. Por ejemplo, para una fuente de 12V se escribe +12V al lado de String. La V no hace falta,
pero el signo al inicio es imprescindible. Adems, no se deben dejar espacios en blanco.

Cargar el Programa del Microcontrolador


La configuracin del microcontrolador puede pasar por cambiar muchsimos parmetros. En este momento
solo veremos los dos que realmente interesan en la gran mayora de diseos. El programa del
microcontrolador y su frecuencia de operacin.
Muy bien, ahora dale doble clic al microcontrolador de tu circuito para abrir su ventana de propiedades.
Obviamente ser una ventana diferente para cada tipo de microcontrolador, pero empezaremos por notar que
todas tienen en comn al menos la propiedad Program File. Como ejemplo se resalta a continuacin dicho
campo para los microcontroladores PIC18F4550, ATmega324P y ARM LPC2114.

Ventana de propiedades del PIC18F4550.

Ventana de propiedades del ATmega324P.

Ventana de propiedades del ARM LPC2114.


Cualquiera que sea el microcontrolador, al hacer clic en el punto indicado, se abrir el cuadro de dilogo
Select File Name. Solo queda buscar y seleccionar el archivo de programa del microcontrolador, el cual puede
tener diferentes formatos. La siguiente ventana corresponde al cargador de un AVR. Se nota que al menos en
el combobox Tipo aparecen como aceptables las extensiones HEX, COF, ELF, OBJ o de tipo UBROF,
dependiendo del compilador con que se trabaje. En otros casos como para los PIC este filtro indica poco o
nada.

Cuadro de dilogo para seleccionar el programa del microcontrolador.


Si no ests seguro/a de qu tipo de archivo genera tu compilador o cul debes elegir, a continuacin doy un
panorama sobre los ms comunes.

El Archivo HEX
Es el mismo que se utiliza para grabar el microcontrolador. Todos los compiladores o ensambladores generan
un archivo HEX al final, aunque su formato puede variar entre Intel hex o Motorola hex, generalmente. Con
este archivo la simulacin en Proteus solo ser fluida (como si fuera poco) y servir de nada si hacemos
pausas para ver por dnde anda la ejecucin del programa del microcontrolador.

El Archivo COFF
COFF (Code Object File Format) es el archivo de depuracin ms extendido y lo utilizan casi todos los
compiladores para PICs y algunos para AVR. Si en siguiente lista encuentras tu compilador, entonces sabrs
que debes buscar el archivo con extensin .COF para cargarlo en tu microcontrolador PIC o AVR.
Los compiladores CCS C para PICs y dsPICs.
Los compiladores de Hitech C para PICs y dsPICs.

Los compiladores MPLAB C18 y C30 para los PIC18F en adelante.


Los compiladores Mikro C para PICs y PIC32.
Los compiladores SDCC, BoostC y otros.
Incluso el ensamblador del MPLAB usa el archivo COFF para sus PICs.
Tambin hay compiladores para AVR que generan un archivo de depuracin COFF, por ejemplo.
El compilador CodeVisionAVR para los AVR desde los tinys hasta los ATxMegas. CodeVisionAVR no
soporta los AVR32.
El compilador ImageCraft C tambin para los AVR desde los tinys hasta los ATxMegas. ImageCraft C
tampoco soporta los AVR32, aunque s tiene compiladores para otros microcontroladores como los CPU12 de
Freescale, los PSOC1 de Cypress o los MSP430 de Texas Instruments.
Los compiladores Mikro C para los AVR.

El Archivo ELF
Tcnicamente la estructura de los archivos ELF provee mayor informacin de depuracin que sus similares y
es el ms recomendado por Proteus para sus simulaciones. No obstante ello, son pocos los compiladores que
lo promueven.
Los compiladores que sobresalen trabajando con los cargadores ELF son GCC AVR para todos los AVR
incluyendo los ATxMega, su compilador hermano GCC AVR32 para los AVR32 y su compilador primo
hermano GCC ARM para los microcontroladores ARM.
Puesto que hay otras herramientas de simulacin aparte de Proteus que trabajan preferentemente con los
archivos COF, estaba en desarrollo una utilidad que convierte los archivos ELF en archivos COFF o archivos
COFF Extendido de Atmel. Pero la migracin de Atmel al formato ELF desincentiv el proyecto, que
actualmente an est disponible, aunque con algunos bugs.
Vale la pena saber que tambin con el compilador Hitech C se pueden obtener archivos de depuracin ELF
entre algunos otros formatos.

Los Archivos UBROF


Este formato es exclusivo de los compiladores IAR C de la empresa IAR Systems. En las prcticas con
Proteus son los archivos que brindan la mejor informacin de depuracin.
UBROF ms bien hace referencia a la informacin que lleva el archivo, la extensin que en realidad debes
buscar es otra, por ejemplo D90. Los compiladores de IAR Systems se distinguen de los dems en que no
generan este archivo para Proteus de forma automtica. Es necesario modificar la configuracin
predeterminada. Para ver cmo hacerlo puedes ir a esta seccin.

Los Archivos BAS y OBJ

Debemos hacer una observacin especial en los compiladores Basic. O mejor, simplificar las cosas diciendo
que si trabajas con el compilador para PICs Proton Plus, de Crownhill Associates, es el mismo archivo BAS
del cdigo fuente el que debes cargar para tu simulacin. Esta tarea es ms sencilla porque se puede realizar
directamente desde el mismo entorno de Proton Plus.
Pero atencin, si trabajas con el compilador Basic Bascom AVR, el archivo que debes cargar es el que tiene
extensin OBJ. Esta carga es manual.
El archivo OBJ no es exclusivo para simular un programa compilado con Bascom AVR. Tambin los
proyectos en ensamblador con Studio 5 dan como resultado ese archivo y se cargan del mismo modo.

Ventana de un PIC18F con su archivo COFF cargado.

Simulacin del Microcontrolador


Imagino la ansiedad por simular el microcontrolador de quienes vienen leyendo el tutorial desde el principio.
Recordemos que Proteus puede simular incluso los circuitos a medio armar, pero ninguna simulacin
arrancar si tiene un microcontrolador sin su programa cargado.
Como en este punto ya cumplimos las condiciones mnimas, podemos ver los resultados de los siguientes
botones de simulacin.

Botones de control de la simulacin.

Simulacin de la prctica simpleseq.


Simulacin en tiempo real! Qu diferencia con otros simuladores, verdad?
Sin embargo, que la simulacin sea la que esperbamos o no es otra cosa. Editar componentes como
resistencias o condensadores es muy sencillo, y eso, si es que hace falta. Lo que quiero decir es que si la
simulacin es defectuosa lo ms probable es que el problema est en el microcontrolador. Tal vez no
ajustamos bien la frecuencia del procesador o quiz pusimos mal algn fuse del microcontrolador.

Configuracin de los Fuses del AVR


Puede que cada tipo de microcontrolador tenga un grupo de fuses diferente. Lo bueno del caso es que muchos
de los fuses tienen poca relevancia y muchos otros son conocidos y comunes. Por ejemplo, sin importar de
qu marca sean, casi todos los microcontroladores tienen fuses de configuracin del oscilador o de
habilitacin del Watchdog
Por otro lado, Proteus presenta inicialmente los microcontroladores con los fuses con su configuracin por
defecto o de fbrica, la cual suele ser suficiente.

Bueno, para ir directo al grano en la siguiente figura se muestra la configuracin de los fuses relacionados con
el oscilador del sistema.

Seleccin del oscilador de 8 MHz para un microcontrolador AVR.


El fuse CLKDIV8 viene programado por defecto, de modo que el valor inicial del prescaler del oscilador
principal del AVR ser 8, y como resultado veremos una simulacin 8 veces ms lenta de lo que establece la
fuente del oscilador. Y cul es sa?
Antes de responder debo aclarar que el valor del mencionado prescaler se puede modificar por software. En
ese caso no importar el valor de CLKDIV8 porque el mismo programa del AVR calibrar su prescaler en
tiempo de ejecucin. Pero como se no es un mtodo muy usado, lo ms recomendable ser poner este fuse en
Unprogrammed (no programado), como se indica arriba.
Ahora bien, la fuente del oscilador del sistema puede ser interna o externa y se define por los fuses CKSEL.
En casi todas las prcticas de cursomicros.com se utiliza un circuito externo con XTAL de 8MHz. Sin
embargo, para las simulaciones con Proteus, ya sea por practicidad o pereza, dar lo mismo (de hecho yo lo
hago) si utilizamos el oscilador interno RC que tambin brinda 8 MHz, por defecto. Esto es solo para la
simulacin porque all el oscilador RC es tan estable como el XTAL.

Para quienes no quieran entrar en rollos o para quienes deseen simulaciones a frecuencias diferentes de 8MHz
pueden escoger cualquiera de las opciones de los fuses CKSEL que indica XTAL externo y escribir la
frecuencia del XTAL usado en la categora Clock Frecuency.

Seleccin de un oscilador de frecuencia personalizada para un AVR.


Me parece que con la configuracin de fuses vista bastar para las simulaciones con AVR en Proteus. Lo aqu
tratado es aplicable a todos los AVR de las familias mejoradas, como los ATmegaxx4y, ATmegaxx8y,
ATmegaxx5y ATmegaxx9y, entre otros. En ciertos modelos pueden aparecer o desaparecer algunos fuses,
pero su efecto suele ser secundario.

Configuracin de las Propiedades del PIC


Tambin en los PICmicro los principales parmetros a configurar estn relacionados con los osciladores.

En los PICmicro la frecuencia de operacin del procesador no es la que marca su fuente de reloj, sino que
internamente se divide por 4. Si, por ejemplo, un PIC16F trabaja con un XTAL de 8MHz, entonces su
procesador operar a 2MHz.
En este caso para la simulacin en Proteus lo que cuenta es la frecuencia del XTAL, aunque la ventana de
propiedades indique Processor Clock Frequency.
Adicionalmente los PIC18F (y superiores) pueden multiplicar la frecuencia de su fuente de reloj para
conseguir una velocidad de operacin mayor.

Seleccin de un oscilador para un PICmicro.


Los modelos de los PICmicro de Proteus son tan completos que a veces, ms que ayudar, pueden derivar en
una mella para la performance de la simulacin. A continuacin veremos algunas de las propiedades del PIC
cuya configuracin sera ms conveniente que la hiciera el usuario directamente si as lo demandase.

Palabra de Configuracin y Propiedades avanzadas del PIC16F877A.


Program Configuration Word: La palabra de configuracin contiene los Bits de Configuracin. Aunque no se
reflejen directamente, sus valores se cargan desde el mismo archivo HEX, por lo que este campo no debera
quitarnos la atencin. Aun as, en Proteus hace falta algo ms para que los bits de configuracin entren en la
simulacin. Sigo hablando de esto en lo subsiguiente.
Randomize Program Memory: Raramente el programa del PIC alcanzar el lmite de la memoria de programa.
Las posiciones restantes normalmente quedan como datos 3FFF y as se cargarn para la simulacin. Si por
algn extrao motivo quisieras que esas posiciones se rellenaran con valores aleatorios, podras establecer esta
propiedad con un YES.
Randomize Data Memory: Igual que el caso anterior pero con la memoria de datos. Generalmente las
variables utilizadas en un programa deberan estar inicializadas. As que esta propiedad tampoco debera
interesar.
Model PIC Start-up Delays: Se refiere al retardo inicial producido principalmente por el temporizador Powerup Timer. Es un tiempo que dura 72 ms tras conectarse la alimentacin del PIC. Durante este lapso el PIC se
mantiene en estado de RESET (para que la alimentacin del circuito se estabilice) y luego recin ejecutar la
primera instruccin del programa. Es el mismo tiempo que se habilita con el fuse _PWRTE_ON. En
conclusin, si queremos simular el tiempo del Power-up Timer en Proteus, adems de activar el fuse en el
cdigo del programa, debemos poner un YES en este campo.
Model PIC Wake-up Delays: Se trata de un tiempo de 1024 ciclos de reloj (256 s para un XTAL de 4MHz)
generado por el temporizador interno Start-up Timer. Tambin complementa al Power-up Timer pero sirve
bsicamente para esperar que el reloj del sistema se estabilice luego de salir del modo Sleep. En el chip real

este temporizador no depende de nosotros ya que siempre estar activado. Es solo un tema de Proteus y la
verdad es que, como 1024 ciclos son poco apreciables, se suele ignorar para las simulaciones.
Generate Q Clocks on CLKOUT Pin: Cuando el PIC opera con un oscilador RC externo (de frecuencia Fosc)
en vez de un XTAL, el pin OSC2/CLKOUT sacar una onda cuadrada de frecuencia Fosc/4. Y nosotros
sabemos que a Proteus le interesa un bledo si se usa XTAL, circuito RC u otro oscilador. Por tanto, poniendo
YES en este campo el pin OSC2/CLKOUT mostrar la onda de Fosc/4 (siendo este Fosc la frecuencia
configurada en la ventana de propiedades del PIC), independientemente del oscilador usado. Como sea, una
seal de este orden en Proteus sera demasiado pesada de simular, por lo que es preferible dejarla tal como
est, inhabilitada.
Watchdog Timer Period: El Watchdog es un temporizador que tiene un tiempo base de 18ms, el cual vara
ligeramente con la temperatura del chip. Al igual que todas las partes digitales de Proteus, el PIC no tiene un
parmetro que interacte directamente con la temperatura establecida para el diseo. Si se desea una
simulacin con un valor de Watchdog Timer Period un poquito diferente del tpico, ste es el campo donde se
cambia.
Port Pin Low-High Delay y Port Pin High-Low Delay: En el mundo real los niveles de tensin en los pines
del PIC no permutan instantneamente. Estos tiempos de propagacin son del orden de los nanosegundos y
Proteus suele despreciarlos por defecto para la simulacin. En caso de tener algn diseo donde ese parmetro
realmente te importe debes indicar en este campo los tiempos pertinentes. (Los puedes hallar en el datasheet.)
Data EEPROM Write Delay. Segn su datasheet, los PIC16F tienen una EEPROM cuyos datos se graban en
un tiempo tpico de 4 ms, llegando a 10 ms como mucho. En este campo se puede establecer algn valor en
particular.
Initial contents of EEPROM. Es la misma EEPROM del punto anterior. Se supone que el contenido inicial de
esta memoria tambin puede formar del archivo HEX; as que este campo ser raramente cargado.

Depuracin del Programa del AVR


Con este modo podremos ver la ejecucin del programa paso a paso, ver en diversas ventanas el cambio de los
registros y dems datos de la RAM del microcontrolador, el contenido de la memoria EEPROM interna, la
evolucin de la Pila, etc. En general, digamos que es parecido al Depurador del Studio 5 pero muchsimo
mejor, como lo comprobars. Proteus no solo simula el microcontrolador, sino que lo hace en interaccin con
el resto de circuito.
Una simulacin paso a paso requiere que el microcontrolador tenga un archivo de depuracin cargado. Como
ya hablamos ampliamente de todo eso, vamos directamente a nuestro diseo de ISIS, presionamos el botn
Step o la combinacin Ctrl + F12 del teclado y voil!: tenemos la ventana AVR Source Code.
Por cierto, las capturas presentadas en adelante corresponden al debugging del programa del LED parpadeante
flashled.

El diseo en modo depuracin.

Botones de depuracin de la ventana Source Code.


En la esquina superior derecha de esta ventana estn los botones de depuracin, con nombres ya familiares
para nosotros: Sobra decir que es preferible acostumbrarse a las teclas de atajo que los identifican (las he
puesto en azul).
Play o Execute (F12). Ejecuta la simulacin de corrido.
Step over the current Function/Subroutine (F10). Como el Step over de cualquier otro
simulador. Si la instruccin actual es una llamada a una funcin o subrutina, se ejecuta
toda de golpe sin entrar en su interior. En el caso de los delays relativamente largos
notaremos una breve desaparicin de la ventana de cdigo.
Step into the current Function/Subroutine (F11). Tampoco nada nuevo. Si la actual
instruccin es una llamada a subrutina o funcin, se entra en su interior.
Step out of the current Function/Subroutine. Lo mismo de siempre. Si ya te cansaste de
estar dentro de una subrutina o funcin, presiona aqu para salir de ella.
Execute until the current cursor position is reached. Lo que dice su nombre: ejecutar el
programa hasta alcanzar la posicin actual del cursor. Este botn solo se activa si se apunta
a una posicin vlida y diferente de la instruccin actual. Se suele usar para medir el
tiempo empleado en ejecutarse el cdigo entre dos puntos del programa.
Toggle breakpoint at source line off->on->disabled (F9). Supongo que an recuerdas qu
son los breakpoints: puntos del programa donde se pausar la simulacin. Con este botn
los pones, quitas o inhabilitas. Tambin se puede usar el doble clic.

Ventanas de Depuracin
Cuando se arranca la simulacin, al final del men Debug aparecen los tems de varias ventanas de
depuracin (Simulation Log, Watch, etc.) que nos recuerdan al men Debug Windows del Studio 5 cuando

se inicia el modo Debugging. Obviamente Proteus soporta mucho ms que un microcontrolador y este men
podr crecer segn el diseo.
Excepto Source Code y Variables, las dems ventanas siempre deberan estar disponibles, incluso para una
depuracin que solo use el archivo HEX.

Opciones del men Debug en tiempo de depuracin.


En la siguiente toma se aprecian las ventanas de depuracin relacionadas con el microcontrolador. El aspecto
de todas ellas es fcilmente configurable mediante su men contextual. All podrs cambiar colores, tipos de
fuente, formatos numricos de presentacin, campos a visualizar (si es el caso), etc.

Las ventanas de depuracin.

Simulation Log. Enva un reporte de los detalles de la simulacin. En principio muestra las libreras de
simulacin cargadas, la versin del kernel de SPICE utilizada y el tamao del archivo (HEX, COF u
otro) cargado. Pero, ms importante, tambin desplegar los errores, mensajes y advertencias de los
eventos ocurridos durante la simulacin, normalmente referidos al microcontrolador; por ejemplo,
cortocircuitos (contention on net), acceso a un registro no implementado del microcontrolador, etc. As
que cuando sientas que algo anda mal con la simulacin no dudes en revisar en primer lugar esta
ventana.

AVR Source Code. La estuvimos viendo desde el principio. Si hay varios microcontroladores en el
diseo, se presentar una ventana de cdigo para cada uno. En realidad, eso pasar con todas las
ventanas AVR.... Como se ve en la siguiente figura su men de botn derecho ofrece varias opciones
como configurar los colores, el tipo de fuente, etc. Si haces clic en Dissassembly, veras el cdigo
fuente con su equivalente en ensamblador. Es una vista muy prctica.

AVR Variables. Es para m una de las ms indispensables. Aqu se visualizan las variables que procesa
el programa actualmente. Por ejemplo, en la siguiente figura est mostrando la variable n de la funcin
delay_ms y el color rojo dice que su valor acaba de cambiar a 300. Yo no s qu hara sin esta ventana
;). (Esta ventana corresponde al debugging del programa del LED parpadeante flashled.)

AVR CPU Registers. Muestra el registro de estado SREG, todos los Registros de Trabajo (R0 a R31)
del AVR, incluyendo los punteros X, Y y Z. Tambin muestra la siguiente instruccin a ejecutarse
junto con el valor del Contador de Programa PC. La S que se ve al fondo representa el Puntero de Pila
o Stack.

AVR Program Memory. Visualiza el cdigo del programa en hexadecimal, que poco o nada se
entiende, por eso raramente se le muestra.

AVR Data Memory. Aqu apreciaremos los valores de todos los datos de la RAM del AVR. Lo malo
es que no es nada fcil distinguirlos. Si de examinar las variables del programa se trata, es preferible
ver la ventana AVR Variables, cierto? Bueno, tal vez sirva para descubrir con cierta facilidad cmo
se van colocando los datos en la Pila, que tambin forma parte de la RAM. Una mejor alternativa
puede ser la ventana Watch Window.

Watch Window. Es como la ventanita homnima del Studio 5. All podemos poner solo los datos que
nos interesen, entre variables de la RAM, Registros de Trabajo, Registros de E/S, datos de la
EEPROM interna e incluso de la misma memoria FLASH del AVR. Solo a modo de ejemplo en la
siguiente figura he puesto en mi Watch Window los registros PORTC, PORTB y PIND. Fjate en que
el pin 0 de PORTB vale 1; debe ser que en ese momento el LED parpadeante est prendido.

AVR I/O Registers. Esta ventana muestra todos los registros de E/S del AVR, es decir, los registros
que controlan los puertos, el USART, el conversor ADC, etc. Como ves, tampoco se puede distinguir
mucho porque est en hexadecimal. As que tambin en este caso es preferible optar por la ventana
Watch Window, si es que el Registro de inters no es visualizado en la ventana AVR Variables.

AVR EEPROM Memory. Muestra el contenido de la memoria EEPROM interna del AVR.

Uso de la Ventana Watch Window


Los archivos de depuracin producidos por los compiladores de alto nivel despliegan en la ventana AVR
Variables las variables y registros que el programa utiliza. Por desgracia, no todos los archivos de depuracin
ofrecen la informacin con el mismo grado de detalle. Los mejores en este sentido son sin duda los
compiladores IAR C.
De haber algunos datos que no se visualicen en AVR Variables una forma (no muy agradable) de resolver este
impase es sacarlas manualmente a la ventana Watch Window.
ste es el men contextual (del botn derecho) de la Watch Window.

Men contextual de la ventana Watch.


Si elegimos la opcin Add Items (By Name)..., veremos una ventana con todos los Registros de E/S del AVR,
los cuales podemos enviar uno a uno a la Watch Window dndoles doble clic.

Figura 1-36 Adicin de registros especiales a la ventana Watch.


En cambio, al tomar la opcin Add Items (By Address)... estaremos frente a una ventana ms completa donde
adems de los Registros de E/S podremos acceder a los Registros de Trabajo, a los datos de la RAM, de la
EEPROM interna y de la misma memoria FLASH.

Adicin de variables a la ventana Watch.


Sabes cul es la diferencia entre AVR SRAM y AVR Data Memory en la figura mostrada?
Haremos un ejercicio mientras respondemos a la pregunta. Teniendo en cuenta el programa en WinAVR del
LED parpadeante flashled mostraremos en la Watch Window la variable n de la funcin delay_ms.
Normalmente el archivo ELF de WinAVR solo muestra en la ventana AVR Variables los datos que se estn
en la memoria RAM y no los que se ubican en los Registros de Trabajo, que es donde precisamente est la n.
Pero supongo que habrs notado que los Registros de Trabajo no aparecen en las opciones de Memory. Y he
aqu la respuesta: en tanto que AVR Data Memory da acceso solo a los datos propios de la RAM (direcciones
0x100 en adelante, segn el AVR), mediante AVR SRAM podemos acceder tanto a los Registros de Trabajo
como a los Registros de E/S (que estn entre las direcciones 0x00 y 0xFF) direccionndolos en modo de
memoria de datos. Parece que la referencia estuviera al revs, verdad? stas son cosas de Proteus ms que de
los AVR.
Sea como fuere, lo que nos toca hacer es poner la configuracin indicada en la siguiente figura y presionar
Add para que la n vaya a la Watch Window.

Adicin de variables a la ventana Watch.


Ahora la explicacin porque en cursomicros las cosas no se hacen as nada ms. En Memory escogemos AVR
SRAM, por lo arriba explicado, en Name escribimos un nombre cualquiera para la variable (en nuestro caso n)
y en Address ponemos su ubicacin, que son los Registros de Trabajo R24 y R25, los cuales vistos como parte
de la RAM empiezan en la direccin 0x18. Las otras dos opciones se deducen del hecho de que n es una
variable unsigned integer, de 2 bytes.
La ubicacin de n se observa en la vista dissassembly de la ventana Source Code. Para esto hay que tener
cierto conocimiento del lenguaje C y del ensamblador de los AVR.

La ventana Source Code en modo Dissassembly.

Los Instrumentos Virtuales


El icono Virtual Instruments
de la barra de herramientas despliega en el Object Selector la lista
disponible, de la cual en este momento solo veremos un par de ellas. No tendra sentido que explicsemos los
depuradores SPI DEBUGGER e I2C DEBUGGER porque, imagino, an no tenemos idea de lo que significan.
Por otro lado, supongo que tampoco te hace falta leer un tutorial para aprender a usar los voltmetros y los
ampermetros. Sobre los dems instrumentos, o son muy poco usados o son igual de sencillos.

Los instrumentos virtuales.

Uso del COUNTER TIMER


Aunque la barra de estado de ISIS va mostrando el tiempo de simulacin transcurrido, no es la mejor forma de
medir los tiempos de ejecucin de los cdigos. El Timer Counter se puede configurar como cronmetro
(Time), frecuencmetro (Frequency) o contador de pulsos (Count) en su ventana de propiedades, pero
tambin se puede hacer al vuelo en plena simulacin.
De sus tres terminales normalmente el nico que se conecta es el CLK. CE puesto a 0 detiene su operacin y
RST reinicia el instrumento.

Counter Timer y su ventana de propiedades.


Cuando la simulacin/depuracin est en ejecucin dale clic izquierdo para que aparezca con ventanita ms
grande, como la siguiente.

Counter Timer maximizado.


Observa los cuatro botones, que tambin presentes en su ventana de propiedades:

MODE establece la operacin del Counter Timer como cronmetro (Time), frecuencmetro o
contador de pulsos (Count).
MANUAL RESET. Es ms cmodo hacer el RESET aqu que desde el pin RST.
RESET POLARITY indica si el RESET ser en el flanco de subida o de bajada del botn anterior.
GATE POLARITY establece si en modo contador el incremento ser con los pulsos positivos o
negativos.

Como prctica, veamos el tiempo exacto que toma una funcin delay_ms(), por ejemplo de nuestro programa
del LED parpadeante flashled.

Luego de colocar un Counter Timer sobre el esquemtico es conveniente regresar a la categora


Component (son cosas de Proteus).
Iniciamos la simulacin/depuracin y clicamos el Counter Timer para agrandarlo y verlo mejor.
Nos ubicamos en cualquiera de las lneas delay_ms() y reseteamos el Counter Timer.

Ejecutamos toda la sentencia delay_ms(500) con Step Over. Como 500ms es un poco grande veremos
desaparecer la ventana de cdigo por ese lapso, pero al final veremos que el cronmetro midi 500377
us, o sea, 500.377 ms.

Ahora mediremos el tiempo que transcurre entre las sentencias de las lneas 32 y 35 de este mismo cdigo,
solo que esta vez lo haremos empleando la opcin Run To Source Line (ejecutar hasta la lnea de cdigo
indicada).

Sea continuando con la simulacin o reinicindola, debemos seguir los tres pasos indicados en la
siguiente figura.

En este punto el triangulito rojo debe sealar la lnea 32 y el cronmetro debe indicar 00.000000, como
en la siguiente figura. Ahora sigue los dos pasos que all se indican.

Y sorpresa! (La sorpresa es para m porque en verdad no esperaba dar este resultado como ejemplo. ;)
Obtuvimos 500.377 ms otra vez. Si ahora se ha ejecutado ms cdigo, cmo es que el cronmetro calcul el
mismo tiempo?

Sucede que entre las lneas 32 y 35, adems del delay que toma 500377us, estn las sentencias PORTB |=
0X01 y PORTB &= ~0X01, las cuales se ejecutan en 0.5us, y el cronmetro no puede visualizar fracciones de
microsegundos. O sea que la diferencia est solo que no se aprecia por ser muy pequea. Lo que yo hice para
medirla fue bajar la frecuencia del AVR a 1MHz.

Otra forma de medir tiempos de ejecucin entre dos puntos es mediante los breakpoints. Es muy sencillo. Solo
hay que seguir los siguientes pasos. No pongo las capturas de pantalla porque creo que ya estoy abusando. ;)

Ponemos los breakpoints en las lneas 32 y 35. Aparte del botn


, tambin se puede hacer con la
tecla F9 o, al estilo del Studio 5, con doble clic en las lneas en cuestin. Aparece un punto rojo por
cada breakpoint.
Simplemente deja que corra el programa (F12). Se detendr por s solo en cada breakpoint que
encuentre. Bueno cuando se detenga en el breakpoint de la lnea 32 reseteas el cronmetro.
Ahora vuelve a presionar Run o F12 y el programa correr hasta detenerse en el siguiente breakpoint,
o sea, en la lnea 35.
Observa el tiempo medido por el cronmetro.

Uso del Osciloscopio


Los primeros Osciloscopios de Proteus solo tenan dos canales y presentaban una interface bastante sencilla.
Ahora son de cuatro canales, a colores y algo ms complejos. Con todo, la compatibilidad con las versiones
anteriores se mantiene, y a veces inclusive prefiero las primeras.

Osciloscopio de cuatro canales.


Se puede notar una clara distincin de seis recuadros, cuatro de los cuales corresponden a los cuatro canales
(Channel) A, B, C y D, con colores distintos.

Los diales Position desplazan las grficas de cada canal hacia arriba o hacia abajo, en el eje Y.
Los diales de tensin fijan la amplitud de voltaje por divisin. Va desde 2mV/div hasta 20V/div.
Con la barra deslizadora de cada canal se marca para que las seales sean tratadas como continuas
(DC), solo sus componentes alternas (AC), el nivel de tierra (GND) o para que no aparezca (OFF).

Las opciones del recuadro Trigger se pueden emplear para muestrear las seales de manera que se tengan
grficas ms fluidas sin desfases. En realidad, se usa raramente porque este osciloscopio es ms bien ideal y
poca falta ese tipo de calibraciones.
En el recuadro Horizontal el dial inferior establece el tiempo base para los canales, va desde 0.5s/div hasta
200ms/div. Mientras ms pequeo sea ms anchas se vern las ondas.
Creo que un buen diseo para probar las funciones de este osciloscopio ser un sencillo circuito amplificador
conformado por un op-amp. Tomaremos las seales de tensin en la entrada y en la salida.
El circuito a implementar es el mostrado abajo. Al menos al inicio no te relajes y rmalo tal como est. Al opamp no se le cambia nada, el alternador tiene 1khz de frecuencia y 100mV de amplitud. Donde veo que
muchos meten la pata es en las fuentes de alimentacin. Si tienes dudas puedes regresar a la seccin Edicin
de las Propiedades de los Objetos.

Circuito de prueba.
Al correr la simulacin notaremos solo una onda.

Formas de onda en la entrada y en la salida del circuito.

Pero luego de una adecuada calibracin de los diales tendremos una imagen as: (Fjate en la posicin de los
diales.)

Seales de entrada y la salida reguladas y centradas.

Grficos de Simulacin
Dada la potencia del osciloscopio de Proteus, el uso de grficos para visualizar datos de la simulacin podra
parecer redundante. Bastar con revisar los ejemplos incluidos en Proteus para darse cuenta de que eso no es
del todo cierto.
Los grficos de Proteus son muy parecidos a los obtenidos por programas como PSPICE o Electronics
Workbench. De hecho, utilizan los mismos modelos de SPICE y el mecanismo para construirlos suele ser
similar, sobre todo en los grficos analgicos. Sucede que casi todos los modelos digitales de Proteus son
nativos.
Vamos previendo por tanto que podemos optar por grficos analgicos o digitales, aunque eso no significa
que un grfico analgico no pueda incluir una seal digital o viceversa.
Continuando con el diseo anterior en este ejercicio vamos a capturar las grficas de las tensiones de entrada y
salida del amplificador.

Circuito amplificador simple.


Primeramente selecciona la categora Graph Mode
de la barra de herramientas. Del Object Selector escoge
ANALOGUE y luego dibuja un rectngulo en el esquemtico con el botn izquierdo del mouse. Quedar
como el que aparece a la derecha de la siguiente figura.

Pasos para colocar un grfico de simulacin.


Ahora vamos a colocar los Probes o sondas de voltaje (tambin los hay de corriente). Para ello seleccionamos
su categora Voltage Probe
y sin darle importancia al Object Selector hacemos clic sobre los puntos del
circuito cuyas tensiones tomaremos. Aparecern esas pequeas flechitas azules que se conocen como Probes
(sondas).
Yo solo he puesto dos probes: uno en la entrada y otro en la salida del circuito. Puedes editar las propiedades
de los probes como cualquier otro objeto. Como ves, yo les cambi de nombre: Vi para el primero y Vo para
el segundo. Es solo para darles algo de identidad.

Colocando sondas en el circuito.


Ahora selecciona un probe y con el botn izquierdo arrstralo y sultalo sobre el rectngulo de la grfica. Si el
probe cay bien, debi haber aparecido uno similar dentro del rectngulo. Al final vers que el probe original
no se ha movido (si lo hizo, es que fallaste ;). Luego haz lo mismo con el otro probe. El rectngulo quedar
como ste.

Rectngulo de grficos con los Probes Vi y Vo.


Ahora desplegamos el men contextual del rectngulo de grficos y escogemos la opcin Maximize (Show
Window) para ver las grficas en una ventana propia. Podemos conseguir el mismo resultado clicando a la
franjita verde superior, all donde dice ANALOGUE ANALYSIS.

Ventana maximizada del rectngulo de grficos.


Lleg el momento esperado. Ejecutamos la generacin de la grfica yendo al men Graph Simulate Graph
ya sea en ISIS o en la nueva ventana de grficos. Momentneamente la barra de estado de ISIS mostrar el
progreso de la simulacin y cuando haya finalizado, veremos la grfica esperada.

Ventana de grficos con los resultados de la simulacin.


Bueno, en realidad no tanto. Esperbamos ver ondas sinusoidales, cierto? Debe ser que estn tan juntas que
no se notan. Si deseas puedes estirar la imagen con los botones de ZOOM disponibles en la parte baja.
Una mejor decisin es establecer la simulacin de un tramo ms corto. Esto se puede configurar en la ventana
de propiedades del rectngulo de grficos en ISIS o aqu mismo yendo al men Graph Edit Graph Con
esto veremos una ventana como la de abajo. Start time y Stop time determinan el margen temporal de la
grfica presentada. Por defecto valen 0 y 1 con lo que la simulacin ser en el primer segundo. Es demasiado
tiempo. Por eso vimos tantas ondas. As que vamos a establecer un margen ms pequeo, digamos desde Start
time=100ms hasta Stop time=1010ms. Con esto tendremos una simulacin entre los tiempos 100ms y 110ms.

Configuracin de los mrgenes de la simulacin para grficos.


Volvemos a correr la simulacin con el men Graph
rojo corriendo. Y...

Simulate Graph o presionando el botn del hombrecito

Generacin de los grficos en el tramo 100ms - 110ms.


Eso s se ve mucho mejor. Nota que los mrgenes de la grfica dicen 100 ms y 110 ms, tal cual lo indicamos.
Los dems detalles de la grfica, como el uso del cursor, las coordenadas de tiempo y valores de seal en cada
punto mostrados en la zona inferior, cambio de colores, etc., los dejo a tu observacin. Solo es cuestin de
echarle ojo.

Depuracin con un Archivo SDI


Actualmente incluso los entornos de los ensambladores pueden generar buenos archivos de depuracin,
aunque no tanto como los generados por los compiladores de alto nivel. Por ejemplo, el MPLAB IDE produce
archivos COFF para sus PICs y Studio 5 genera archivos OBJ para sus AVR.
Sin embargo, a veces an podr resultar til trabajar con los archivos SDI que Proteus sola emplear antes.
Estructuralmente un archivo SDI es muy parecido a un archivo de listado. A diferencia de un COF, que separa
los archivos del programa en diferentes campos, un archivo SDI lo presenta todo junto en un solo cuerpo. Esto
puede resultar algo incmodo, ya que si trabajamos con varios archivos es precisamente para ordenar las
cosas.

En este modo el nico archivo a cargar en el microcontrolador es el HEX; el archivo SDI solo tiene que
acompaarlo en su carpeta de destino. Para crear un archivo SDI Proteus tiene que ensamblar de nuevo todo el
programa por su cuenta. El procedimiento que debemos seguir es:
Vamos al menu Source Add/Remove Source Files... Nos aparecer una ventana como la siguiente. En
Target Processor estar seleccionado el nombre del microcontrolador presente en el diseo, es decir,
asumimos que a hacer esto partimos con al menos un microcontrolador en el circuito. De lo contrario habr
que seleccionarlo manualmente. Por otro lado, si hay ms de un microcontrolador en el circuito, aparecern
todos ellos y nosotros escogemos para cul ser el ensamblado actual (los programas se ensamblan por cada
microcontrolador a la vez).

Ventana Add/Remove Source Code Files.


Del mismo modo en el campo Code Generator Tool aparecer el ensamblador a usar. Proteus lo selecciona
automticamente conociendo el microcontrolador del desplegable de al lado. El campo Flags lo dejamos en
blanco. Como los ensambladores son pequeos, Proteus tiene copias de unos cuantos, como vinco ms o
menos.
Ahora le damos al botn New y en seguida seleccionamos el archivo de cdigo fuente del programa. En caso
de tener un programa comprendido por varios archivos, nicamente debemos seleccionar el principal. El
nombre del archivo seleccionado ir al desplegable de arriba.
Notaremos que al final del men Source ahora se ha agregado un tem con el nombre del archivo ASM.
Seleccionando esa opcin se abrir un editor muy parecido al block de notas con el cdigo del programa.
Siendo un editor muy pobre, lo puedes cambiar por otro (Notepad++, por ejemplo) seleccionando la opcin
Setup External Text Editor... Prubalo cuando tengas tiempo.

Construir el programa main.asm.


Para terminar con esto hacemos clic en Build All (Construir todo). Al final quedar una ventana con el
mensaje de Source code build completed OK. Solo la cerramos.

Ensamblado del programa.


Si nos fijamos en la carpeta del proyecto, veremos que ha aparecido un archivo SDI junto al esperado HEX,
entre otros. Solo djalo all. T sigue cargando el archivo HEX en el microcontrolador. En versiones ms

recientes de Proteus esta carga ocurre automticamente tras el ensamblado.


Ya podemos presionar Ctrl + F12.

Depuracin con el archivo SDI del programa AVRassembler1.


As mismo, cada vez que se edite el archivo principal del cdigo fuente, Proteus detectar la desactualizacin
del archivo SDI y ensamblar todo el programa de nuevo.

Qu es el Studio 5
El Studio 5 es el Entorno de Desarrollo Integrado de Atmel para el desarrollo de proyectos con varios de sus
productos, relacionados con sus microcontroladores. Entre las herramientas que incluye nos deben interesar
las siguientes:

Un editor de cdigos, para editar los programas. Como todo gran editor permite mostrar los cdigos
fuente a colores, con nmeros de lnea, etc.
Un administrador de proyectos, que adems de trabajar con programas en ensamblador, le da un
completo soporte a los compiladores GCC AVR32 y GCC AVR (WinAVR). A diferencia de versiones
anteriores ahora es ms difcil la integracin con compiladores comerciales como CodeVision AVR o
ImageCraft AVR.
El ensamblador AVRASM, para trabajar con programas en ensamblador.
Los compiladores de software libre GCC AVR y GCC AVR32 en su versin para Windows
(WinAVR), para desarrollar programas en C para los AVR de 8 y 32 bits, como los ATtiny, ATmega,
ATxMega y AVR32. En versiones pasadas del Studio 5, este compilador se deba instalar por
separado.
El simulador AVR Simulator, para simular los programas de los AVR tanto si estn escritos en
lenguaje C o ensamblador.
El paquete AVR Software Framework o ASF, que es un conjunto de ms de 400 proyectos de ejemplo
en lenguaje C para los AVR de 8 y de 32 bits, desde el uso de puertos hasta el control del puerto USB.
Un completo sistema de ayuda integrado.

El Studio 5 tambin incluye las siguientes herramientas, las cuales son de uso opcional porque requieren del
hardware correspondiente. De todos modos, si t puedes conseguirlos o deseas saber ms de ellos, puedes
visitar la web de Atmel.

Los softwares de programacin como AVR Dragon, AVRISP MkII o AVR ONE!. stos son
programas que trabajan con dispositivos programadores comerciales del mismo nombre.

Los programadores AVRISP MkII (izquierda) y AVR ONE! (derecha).

Un potente depurador llamado JTAGICE mkII. ICE significa In Circuit Emulator, y hace referencia a
un sistema de depuracin en el mismo circuito y con el mismo microcontrolador. Obviamente debe
trabajar con su propio adapatador hardware, que se conecta a la PC va la interface JTAG, conformada
por los pines TMS, TCK, TDI y TDO del AVR. Esta interface tambin permite la programacin serial
del AVR, es decir, el hardware es un depurador y programador. No todos los AVR tienen soporte
JTAG ICE (ejemplo los ATtiny).

El depurador/programador JTAGICE mkII.

El software JTAGICE mkII puede dirigir el programa del AVR en la misma aplicacin paso a paso,
como en cmara lenta o en tiempo real, e ir visualizando los resultados de las operaciones a medida
que se van ejecutando. De ese modo se puede monitorizar y analizar en vivo y en directo cmo van
cambiando los recursos hardware del AVR, como el contenido de sus memorias y de sus registros
internos. Para quienes conozcan algo, es parecido a la simulacin de Proteus o del mismo Studio 5,
pero esto ser real.
Los elementos hardware de control y programacin del AVR tambin suelen estar disponibles en las
tarjetas de desarrollo que provee ATmel, como las tarjetas STK500 o STK600. Las utilidades software
correspondientes estn incluidas en el Studio 5.

La tarjeta de desarrollo STK600.

Descarga del Studio 5 y WinAVR

Ahora que ya tienes una idea bien formada de lo que es y lo que puede hacer el Studio 5, puedes descargarlo
libremente desde www.atmel.com. Puede pesar hasta ms de 600 MB, as que tendrs que esperar un poquito.
Si ya tienes este programa, pero en su versin 4.x, ser mejor que te actualices. Hay sustanciales diferencias
entre la versin 5.x y las anteriores, en las cuales no perder tiempo citndolas. Solo espero que luego no te
quejes si encuentras cosas que no te salen igual ;).
En la pgina de descarga encontrars los siguientes paquetes.

AVR Studio 5 Installer (includes VSS and .NET 4.0). Como all se indica, esto incluye los paquetes Microsoft
Windows Framework .NET 4.0 y Visual Studio Shell (Isolated Mode) 2010. Ambos son prerrequisitos para la
instalacin del Studio 5 y probablemente ya los tengas instalados en tu PC. Si no ests seguro de ello o si
prefieres perder un poquito ms de tiempo en la descarga antes que averiguarlo, puedes descargar este paquete
completo. El instalador trae incluidos los compiladores GCC AVR y GCC AVR32 (WinAVR).
AVR Studio 5 Installer. Contiene nicamente el instalador del Studio 5 (incluyendo GCC AVR32 y GCC
AVR [WinAVR]). Descarga esta opcin si ya tienes Microsoft VSS y .NET 4.0.
AVR Studio 5 AVR Software Framework Update. Este paquete contiene la actualizacin de los ms de los
ms de 400 proyectos, ejemplos, libreras, etc., que conforman lo que se llama el AVR Software Framework o
simplemente ASF. El instalador del Studio 5 ya lo trae incluido solo que quiz no en su versin ms actual.
Por ejemplo, cuando descargu mi Studio 5.0 inclua el ASF 2.5.1 mientras que el actual era 2.7.0. La verdad
es que la diferencia en el contenido no se percibe. Descarga este archivo exe solo si quieres estar sper
actualizado e instlalo luego del Studio 5.

AVR Studio 5.0 - Part Support Pack for AVR XMEGA with USB. Es una extensin para que el Studio 5
tambin soporte los nuevos AVR ATxMega con USB, en este caso los ATxmega16A4U, ATxmega32A4U,
ATxmega64A3U, ATxmega128A3U, ATxmega192A3U, ATxmega256A3BU y ATxmega256A3U. La lista
debe cambiar con el tiempo. Descarga el instalador si trabajas con estos AVR.

Instalacin del Studio 5 y WinAVR


Requisitos de sistema

Windows XP (x86) con Service Pack 3 en todas las ediciones excepto Starter Edition.
Windows Vista (x86) con Service Pack 1 en todas las ediciones except Starter Edition.
Windows XP (x64) con Service Pack 2.
Windows Vista (x64) con Service Pack 1.
Windows 7 (x86 y x64).
Windows Server 2003 R2 (x86 y x64).
Ordenador con procesador de 1.6GHz o superior.
1 GB de RAM para x86.
2 GB de RAM para x64.
512 MB de RAM adicionales si se trabaja en una PC virtual.
3GB de espacio disponible en el disco duro.
Disco duro de 5400 RPM o superior.
Tarjeta de vdeo con soporte DirectX 9 y resolucin de 1024 x 768 o superiores.

Primero debo hacer una salvedad y es que antes de instalar el Studio 5 versin Release debes desinstalar la
versin Beta, si es que lo tenas. Y si tienes instalado AVR Studio 4 o AVR32 Studio, los puedes conservar
porque el Studio 5 puede trabajar en paralelo con ellos.
El Studio 5 ya no soporta los siguientes dispositivos, que an estn disponibles en el Studio 4, ATtiny11,
ATtiny12, ATtiny15, ATtiny22, AT90S1200, AT90S2313, AT90S2323, AT90S2343, AT90S4433,
AT90S8515, AT90S8535, ATmega323, ATmega161, ATmega163, ATmega103, ATmega165, ATmega169,
ATmega406, ATmega16HVA, ATmega16HVA2, ATmega64HVE, ATmega32U6, AT90PWM2,
AT90PWM3, AT90SCR100, AT86RF401.
Bueno ahora s empezamos la instalacin como cualquier otro programa de Windows, siguiendo las
indicaciones presentadas. Aqu, algunos screenshots:

Lista de los requisitos software.


Antes de instalar el Studio 5 propiamente se instalarn los paquetes listados. Para quienes ya tenan instalados
Microsoft .NET Framework 4.0 (se supone que Windows 7 Ultimate ya lo incluye) o Microsoft Visual Studio
2010 Shell, dichas opciones ya no estarn disponibles y por tanto pasars directamente a la instalacin de
AVR Jungo USB Driver.

Instalacin de Microsoft Visual Studio 2010 Shell


Luego vendr la instalacin de Microsoft Visual Studio 2010 Shell. Si tienes Internet y deseas enviar a
Microsoft informacin sobre tu experiencia con la instalacin de Visual Studio, puedes activar la casilla
indicada.

Terminada esta parte nos dirn que Microsoft Visual Studio 2010 Shell se instal con xito, a pesar de que
pueden aparecer esas X en rojo indicando que an no se ha instalado la documentacin respectiva (no es
necesario para el desempeo del Studio 5 pero si deseas la puedes descargar desde la web de Microsoft).
Tambin me recomienda actualizar mi PC con los parches de seguridad ms recientes de Windows.

Instalacin de AVR Jungo USB


Y ahora viene la instalacin del driver USB de Jungo para los AVR. Studio 5 utiliza estos driver para manejar
sus tarjetas hardware con interface USB. Si ya los tenas instalados, digamos porque trabajaste con versiones
anteriores del AVR Studio, entonces este procedimiento actualizar los drivers con la versin presente. Los
drivers USB de Jungo son compatibles con sus antecesores.

Por supuesto que los drivers de Jungo son confiables, son de los mejores.

Instalacin del Studio 5


Y pensar que recin vamos a instalar el Studio 5 propiamente (creo mi Windows se ha instalado ms rpido ;).

Entorno del Studio 5

Entorno de desarrollo de Studio 5.


sa es la distribucin por defecto de las ventanas del Studio 5. En primer plano tenemos a Start Page o Pgina
de Inicio, a la derecha est la ventana Solution Explorer y abajo, la ventana Output. Hay ms ventanas que
iremos conociendo en el camino pero mientras te familiarizas con el entorno puedes reacomodarlas segn tu
preferencia. Para ello puedes arrastrarlas tomndolas por su barra de ttulos y colocarlas donde indica el gua o
puedes dejarlas flotando. Esto ltimo no suele ser muy til, aunque a m me servir para mostrar las capturas
de pantalla ms adelante

Reubicacin de las ventanas del entorno del Studio 5.


Hacer estas reubicaciones es divertido sobre todo si tenemos en cuenta que podemos regresar a la distribucin
inicial yendo al men Window Reset Window Layout. Y si queremos volver a tener la pgina Start Page
para ver a la mariquita de nuevo vamos al men View Start Page. Nota que esta opcin tambin tiene un
icono en la barra de herramientas. Es el men View donde tambin puedes encontrar las otras ventanas y no
en el men Window.
Antes que mostrar una tpica descripcin de cada uno de los elementos del entorno del Studio 5 los ir
presentando a medida que hagan su aparicin y sean requeridos.

Trabajando con Proyectos y Soluciones en C


A diferencia de los compiladores Basic, donde basta con crear un archivo BAS suelto y luego compilarlo, los
programas en C siempre forman parte de un proyecto.
Actualmente desarrollar proyectos en C con el Studio 5 es ms sencillo que en versiones anteriores debido en
gran parte a que est adaptado para trabajar especialmente con los compiladores libres GCC AVR32 y GCC
AVR (WinAVR).
Una Solucin (Solution) es un conjunto formado por uno o varios proyectos, as que todo proyecto debe
pertenecer a alguna Solucin. El Studio 5 puede trabajar con uno solo o con todos los proyectos de la Solucin
al mismo tiempo, pero solo puede administrar una Solucin a la vez.
Hay ms consideraciones respecto a los Proyectos y las Soluciones, pero creo que ser mejor describirlas
mientras creamos el Proyecto.

Creacin de un Proyecto en C
Solo hay una forma de crear un proyecto, esto para simplificar las cosas.
Abierto el Studio 5 vamos al men File

New Project o hacemos clic en New Project de Start Page.

Creando un proyecto desde Start Page.


De cualquier modo llegaremos al mismo asistente, donde empezamos por seguir lo que indica la siguiente
figura. El nombre y la ubicacin del proyecto pueden ser los que desees.

Eleccin del nombre y ubicacin del proyecto.


Ten en cuenta que el Studio 5 crear una nueva carpeta (con el nombre del proyecto) dentro de la ubicacin
indicada. Observa que el nombre de la Solucin es el mismo que el del proyecto.
Todo proyecto debe pertenecer a alguna Solucin, y como estamos creando el primer proyecto, el Studio 5 le
asignar una Solucin automticamente. Cuando creemos los siguientes proyectos tendremos la opcin de
elegir si pertenecern a sta o a una Solucin nueva.
Si activas la casilla Create directory for Solution, la Solucin (que es un archivo a fin de cuentas) se alojar en
la carpeta que deba ser para el proyecto, y el proyecto junto con sus archivos generados irn a parar a una sub
carpeta con el mismo nombre. Esto puede ser conveniente cuando se tiene una Solucin con varios proyectos.
Yo s que parece enredado pero con un par de prcticas lo entiendes mejor y te acostumbras.
Normalmente prefiero tener un proyecto por cada Solucin, de modo que no tengo que marcar la casilla
indicada y puedo tener todos los archivos del proyecto y de la Solucin dentro de la misma carpeta sin que se
confundan. Es as como estn conformadas todas las prcticas de cursomicros.com.
Le damos clic a OK y tendremos una ventana mucho menos traumtica. Solo seleccionamos el AVR con el
que trabajaremos. En el futuro podremos cambiar este microcontrolador por otro. Tambin puedes aprovechar

esta ventana para reconocer las memorias de cada dispositivo y para ver las herramientas que tienen
disponibles desde el Studio 5.

Eleccin del microcontrolador del proyecto.


En seguida tendremos nuestro proyecto con el Editor de Cdigo mostrndonos el archivo de cdigo fuente
principal listo para que lo editemos. Observa que el Explorador de la Solucin o Solution Explorer muestra los
Proyectos de la Solucin as como los archivos de cada Proyecto. Debajo est el marco Properties que informa
las propiedades de cada archivo seleccionado arriba, como su ubicacin.
De todas las ventanas aqu mostradas o las que puedan surgir en adelante la que no deberas perder de vista es
el Explorador de la Solucin. Desde all accedes a todos los archivos del proyecto (los abres con doble clic) y
tambin puedes construir el proyecto as como establecer su configuracin. Si se te llega a perder esta ventana
la puedes visualizar yendo al men View Solution Explorer.

Esquema del proyecto creado.

Edicin del Cdigo Fuente


El editor de cdigos se llama simplemente Code y es accesible desde el men View. Tambin se muestra
automticamente cada vez que abrimos un archivo. Como lo dijimos al inicio, la podemos cerrar, reubicar o
dejar flotando como se ve en la siguiente figura.
El cdigo mostrado corresponde al programa del LED parpadeante ledflasher3, que es con el que vamos a
trabajar. Todava no lo podemos compilar porque faltan agregar al proyecto los archivos mencionados en las
directivas include. Hablamos de los archivos avr_compiler.h, usart.h y usart.c. Pero eso lo veremos en la
siguiente seccin.

Edicin del programa Ledflasher3.

Adicin de Archivos o Libreras al Proyecto


Los proyectos en AVR GCC o IAR C raras veces constan de un solo archivo. Casi siempre hay archivos de
configuracin o libreras que aadir. De hecho, en cursomicros.com todas las prcticas incluyen al menos el
archivo avr_compiler.h, el cual permite que los cdigos de programa se puedan construir indistintamente para
los compiladores AVR GCC o IAR C. Este archivo forma parte del paquete ASF y lo puedes hallar en el
directorio de instalacin del Studio 5. Tambin puedes encontrar una copia suya (con ligeras modificaciones)
en todos los proyectos de cursomicros.com.

Las libreras en el lenguaje C se suelen dividir en dos archivos: uno (con extensin .c) que suele contener los
cdigos ejecutables de las funciones y otro (con extensin .h) donde se escriben las definiciones y los
prototipos de las funciones, bsicamente. As por ejemplo, en cursomicros.com se usan las libreras para
displays LCD (con archivos lcd.h y lcd.c), para el puerto serie (con archivos usart.h y usart.c) o para el bus
I2C (con archivos i2c.h e i2c.c).
En los cdigos fuente los archivos se invocan mediante la directiva include. Adicionalmente en los proyectos
de WinAVR o IAR C deben incluirse desde sus entornos de desarrollo. En esta ocasin veremos como hacer
esto con los archivos avr_compiler.h, usart.h y usart.c y el procediemiento descrito es el mismo que debes
seguir cuando aadas otros archivos.
Una vez ubicados los archivos avr_compiler.h, usart.h y usart.c, colcalos en la carpeta de tu proyecto. Esto
no es necesario porque que se le podra incluir dondequiera que est desde el Explorador de la Solucin. Sin
embargo habr proyectos en los que se quiera editar el archivo incluido y para que dichos cambios no afecten
a los dems proyectos lo ms recomendable ser tener una copia suya en la carpeta de cada proyecto, como se
ve abajo.

Los archivos avr_compiler.h, usart.h y usart.c deberan estar en la carpeta del proyecto.
Ahora debemos indexar el archivo al proyecto. Para ello seleccionamos el proyecto en el Explorador de la
Solucin y vamos al men Project Add Existing Item Tambin puedes emplear mi camino preferido, que
se ilustra a continuacin.

Indexando los archivos avr_compiler.h, usart.h y usart.c al proyecto.


Observa que ahora el archivo aadido tambin se visualiza en el Explorador de la Solucin.

Archivo avr_compiler.h indexado.

Construccin del Proyecto


Antes de entrar en el proceso de la construccin debemos saber que existen dos modos bsicos de
configuracin en que se generarn los resultados, que son Debug y Release. El modo por defecto es Debug.
Uno de los efectos de cambiar del modo Debug al modo Release es que la optimizacin del cdigo se adaptar
para obtener el archivo HEX de menor tamao. Esta optimizacin es necesaria para que las funciones de delay
del programa queden mejor afinadas.

Eleccin entre los modos Debug y Release.


Tambin debemos considerar que cada modo generar los archivos de salida en sus correspondientes carpetas.
Hasta ahora solo hemos visto la carpeta Debug pero al compilar el proyecto en modo release se crear tambin
una carpeta Release. Una opcin para evitar marearnos entre estos modos es ajustar la optimizacin del cdigo
directamente. Aprendemos eso en una prxima seccin. De momento cambiemos a release y sigamos.
Bueno, para construir un proyecto podemos tomar varios caminos, dos de los cuales se muestran abajo. Un
tercer camino es seleccionar el proyecto en el Explorador de la Solucin, hacer clic derecho y en el men
emergente aparecern las mismas opciones del men Build.

Build Solution (construir la Solucin). Una Solucin puede estar compuesta por varios proyectos. De
ser el caso, con esta opcin se construirn todos los proyectos de la Solucin.
Rebuild Solucin (reconstruir la Solucin). Reconstruye todos los proyectos de la Solucin haciendo
previamente una limpieza de los archivos de salida generados antes.
Clean Solution (Limpiar Solucin). Para todos los proyectos de la Solucin actual, limpia o borra
todos los archivos de salida, como HEX, LSS, MAP, etc., que se crearon durante una construccin
previa.

Build ledflasher (construir ledflasher). En el entorno del lenguaje C construir en realidad involucra
varios procesos, como compilar, enlazar (linkar) y ensamblar. Normalmente nos referimos a esta
accin simplemente como compilar.
Rebuild ledflasher (reconstruir ledflasher). Vuelve a construir el proyecto indicado pero haciendo una
limpieza previa de sus archivos de salida.
Clean ledflasher (limpiar ledflasher). Borra todos los archivos de salida del proyecto indicado.
Compile (compilar). La opcin compilar aparece en el men Build cuando se selecciona un archivo C.
Al compilar un archivo no obtendremos los archivos HEX o ELF, sino solo un archivo objeto, que es
previo al HEX o ELF.

Puesto que nuestra Solucin de ejemplo solo tiene un proyecto, dar lo mismo si elegimos construir el
proyecto o construir la Solucin.
Tras construir el proyecto del LED parpadeante nos aparecer la ventana de salida Output con los resultados
de xito, como se aprecia abajo. Probablemente tu ventana Output aparezca en otra disposicin pero como
aprendimos antes, eso es solo cuestin de reubicacin.

Resultados de la construccin.
Entre los archivos de salida puedes ver que aparecieron los esperados HEX (para grabar el microcontrolador)
y ELF (para la depuracin o simulacin en programas como Proteus). Todos estos archivos se depositan en la
carpeta Release del proyecto porque lo compilamos en ese modo.

Lo archivos de salida van a las carpetas Release o Debug.

Renombrar los Archivos del Proyecto


Por defecto los nombres de la Solucin, del proyecto y del archivo de cdigo fuente principal son el mismo.
Esto es opcional, pero yo prefiero que mi archivo de cdigo principal se llame siempre main.c para hacer una
mejor distincin de los otros archivos de cdigo como las libreras.
Renombrar un archivo es sencillo. Solo hay que seleccionarlo en el Explorador de la Solucin, abrir su men
contextual (clic derecho) y escoger la opcin Rename. Del mismo modo tambin es posible cambiar de
nombre el proyecto y la Solucin.

Renombrando los archivos del proyecto y la Solucin.

Cambiar la Frecuencia del Procesador


Para WinAVR normalmente las nicas rutinas que requieren la definicin de la frecuencia del procesador son
las funciones de delay, que se encuentran en los archivos del mismo nombre disponibles en la carpeta utils del
WinAVR. Quienes utilizan dichos delays suelen editar la constante F_CPU definida en esos archivos.
Pero el archivo avr_compiler.h tambin define la constante F_CPU antes de utilizar las funciones de delay de
WinAVR. De modo que si vamos a trabajar con este archivo, es aqu donde debemos editar la constante
F_CPU, que casi siempre coincidir con la frecuencia del XTAL usado. Gracias a las macros de
avr_compiler.h F_CPU tambin tendr efecto en las funciones de delay del compilador IAR AVR C.
Adems, las libreras de www.cursomicros.com para los mdulos sncronos del AVR como el USART o TWI
(I2C) tambin se basan en F_CPU. Queda claro entonces que no solo la utilizo para calibrar los delays.
En el archivo avr_compiler.h del paquete ASF la constante F_CPU se define a 11059200Hz. Pero en casi
todas las copias de avr_compiler.h disponibles en www.cursomicros.com F_CPU est redefinida a 8 MHz

(como se aprecia en el siguiente extracto de dicho archivo) porque en la gran mayora de mis diseos trabajo
con esa frecuencia.
////////////////////////////////////////////////////////////////
#ifndef F_CPU
/* Define la frecuencia de CPU (en Hertz) por defecto, si no ha
* sido definida. */
#define F_CPU 8000000UL // XTAL de 8 MHz
#endif

Reconfiguracin del Proyecto WinAVR


El compilador AVR GCC naci inicialmente para Linux y con el tiempo se desarroll la versin para
Windows llamada WinAVR. Hasta ahora WinAVR no tiene un entorno propio y de hecho ya no hace falta
gracias al Studio 5.
Pero anteriormente para usar WinAVR haba que invocarlo desde otros entornos como CodeBlocks, Eclipse o
el mismo Studio 4. Esos entornos no eran del todo compatibles con WinAVR y ms temprano que tarde uno
tena que configurar las opciones del proyecto en el mismo archivo Makefile.
Makefile es un archivo de texto lleno opciones para la construccin del proyecto. WinAVR sigue trabajando
en base a l, solo que para nosotros el Studio 5 lo crea y edita automticamente. Cada cambio realizado en la
ventana de propiedades del proyecto es reflejado en el archivo Makefile. Si te interesa echarle un vistazo,
puedes encontrar Makefile entre los archivos de salida en las carpetas Debug y/o Release.
Para acceder a la ventana de propiedades del proyecto, lo seleccionamos y vamos al men Project
Properties. Yo, como siempre, prefiero ir por el Explorador de la Solucin, como se ve abajo.

Hay varias categoras desde Build hasta Advance y en muchas de ellas podremos elegir si la configuracin se
aplica al modo Debug o Release. Recuerda que cada modo hace que los archivos de salida (incluyendo el
Makefile) vayan a sus respectivas carpetas.

Ventana de propiedades del proyecto.

Optimizacin del Cdigo


Comnmente los compiladores C ofrecen varios niveles de optimizacin del cdigo. Con WinAVR tenemos
los siguientes niveles:

No optimizar (nivel O0). El cdigo no se optimiza nada. Por ejemplo, el programa del LED
parpadeante compilado con este nivel resulta en un cdigo de 3714 bytes y compilado en nivel Os
resulta en 118 bytes. S que hay diferencia, verdad? Sin embargo, muchas veces se usa este modo con
fines de depuracin porque genera el mejor archivo ELF. No por nada es el nivel por defecto del modo
Debug. Adems, esa diferencia se desvanece cuando se trabaja con programas grandes. Por ejemplo un
programa que utiliza funciones matemticas con nmeros de punto flotante y la funcin Printf en su
mxima performance a m me result en 3366 bytes sin optimizacin y 3038 con optimizacin Os.
Optimizar (nivel O1). Optimiza regularmente; bastante, comparado con el nivel O0. Con este nivel mi
programa de prueba result en 3066 bytes.
Optimizar ms (nivel O2). Con este nivel mi programa de prueba result en 3060 bytes.
Optimizar al mximo (nivel O3). 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. Siguiendo con el ejemplo, mi programa result en 3102 bytes.

Optimizar para tamao (nivel Os). Optimiza para que la construccin genere el menor cdigo mquina
posible. En general el tamao del cdigo maquina se contrapone a su velocidad de ejecucin. No se
puede conseguir un cdigo mnimo y que al mismo tiempo sea el ms veloz. El manual de WinAVR
recomienda utilizar este nivel combinado con la directiva -mcall-prologues, excepto cuando se quiera
usar el nivel O3 para ganar algo de velocidad. El nivel Os es la opcin por defecto del modo Release.

Ahora abramos la ventana de propiedades del proyecto y ubiqumonos en Toolchain


Compiler Optimization.

AVR/GNU C

Recuerda que puedes elegir si la configuracin se aplica al modo Debug o Release y que cada modo hace que
los archivos de salida vayan a sus respectivas carpetas.

Cambio de la optimizacin del cdigo.


Haba sealado anteriormente que para mejorar la optimizacin de cdigo en tamao la ayuda de WinAVR
recomendaba el uso de la opcin -mcall-prologues. Dice que con esto se usarn subrutinas especiales para
guardar y restaurar los registros en las entradas y salidas de las funciones que usen muchos registros, a costa
de un pequeo incremento en el tiempo de ejecucin. (Es de suponer que se trata de bucles, cierto?)

En fin, sea como fuere, para habilitar la opcin -mcall-prologues en el Studio 5 ni siquiera tenemos que
escribirla, simplemente activamos la casilla Use subroutines for function prologues and epilogues, como de
ilustra en la siguiente figura.

Cuando un programa se construye exitosamente no siempre significa que lo hizo de la mejor forma. A veces
puede haber algunos mensajes o advertencias que no se muestran en la ventana de salida Output. Para ver en
detalle estos errores, advertencias y mensajes debemos inspeccionar la ventana Error List, que est disponible
desde el men View Error List.

Lista de errores, advertencias y mensajes de la construccin.


Aqu muestro la ventana flotante pero recuerda que la puedes anclar donde desees. El caso es que la
advertencia mostrada es muy frecuente. Seala que las optimizaciones del compilador estn inhabilitadas y
que las funciones del archivo delay.h no funcionarn adecuadamente. Imagino que con todo lo visto aqu a ti

nunca se te debera presentar. Nosotros no invocamos al archivo delay.h directamente, sino a travs de
avr_compiler.h.

Cambiar de Microcontrolador AVR


Observa que ni en WinAVR ni en otros compiladores como IAR AVR C o ImageCraft AVR se debera
seleccionar el AVR del proyecto desde el cdigo fuente, como se suele hacer en Basic.
Si queremos conocer el microcontrolador AVR para el que est hecho un proyecto o si queremos cambiarlo,
debemos ir a la categora Device.

Reeleccin del microcontrolador del proyecto.


Nos aparecer una ventana muy parecida a la que tenamos cuando creamos el proyecto. La diferencia es que
ya no estn los AVR de 32 bits, as que el cambio solo es factible por otro AVR de 8 bits. Lo mismo ocurre
cuando administramos un proyecto con un AVR de 32 bits. Entendemos que para cambiar de
microcontrolador primero tendramos que cambiar de compilador, de AVR GCC a AVR32 GCC o viceversa,
pero eso tampoco es posible desde el Studio 5. La nica forma sera creando un nuevo proyecto.

Cambio del microcontrolador del proyecto.

Uso de un Archivo Makefile Externo


Explicamos anteriormente lo que significa el archivo Makefile para el compilador WinAVR y que en estos
das sera muy raro editarlo manualmente. Sin embargo, dada la gran flexibilidad de WinAVR, a veces incluso
administrar la ventana de propiedades del proyecto en el Studio 5 puede resultar confuso.
Has de saber que a diferencia de otros compiladores C, la configuracin de compilacin de un proyecto en
WinAVR reside ntegramente en su archivo Makefile. Los archivos de Proyecto y de la Solucin que crea el
Studio 5 se reducen a simples cascarones en comparacin con Makefile.
Si alguna vez tienes el cdigo fuente de un gran proyecto ajeno pero sin su Makefile, probablemente no logres
compilarlo como el autor lo dise, si es que no utilizas la misma configuracin. Tendras que acertar con la
configuracin exacta o conseguir el archivo Makefile original.
Bueno, ya sea que quieras trabar con un archivo Makefile o que simplemente seas uno de los antiguos
nostlgicos que extraan editarlo a mano (como yo ;), la forma de usar un archivo Makefile diferente del
creado por el Studio 5 es:

Uso de Makefile externo en Studio 5.

Configurar el Uso de Printf


Quieres visualizar nmeros de punto flotante y solo obtienes el signo ? ? Imagino que esto debe ser frustrante
para los usuarios de los compiladores fciles como CCS C.
Espera un momento. Ni el lenguaje C ni el compilador GCC fueron diseados para los microcontroladores.
Son muy tiles las familias de funciones printf y scanf pero al mismo tiempo bastante pesadas para la
arquitectura de los microcontroladores, sobre todo si son de 8 bits y no tienen chip matemtico.
Por las limitaciones conocidas hasta los mejores compiladores como IAR C o WinAVR tienen que subdividir
a printf y scanf y sus derivados en tres versiones, para que el usuario escoja la que ms se ajuste a su demanda
y a las restricciones de su microcontrolador.

La versin minimizada de printf restringe los especificadores de conversin al uso de enteros de 16


bits (entre octales, hexadecimales y decimales positivos o negativos), de caracteres y de cadenas de
caracteres.
No estn disponibles los nmeros de punto flotante ni los nmeros enteros de 32 bits. No se permite
especificar el ancho de los nmeros y por ende tampoco se disponen de las opciones de justificacin ni
de relleno con blancos o ceros.

Segn el manual de avr-libc, la versin minimizada de printf se consigue estableciendo las siguientes
opciones de linkador:

-Wl,-u,vfprintf -lprintf_min

La versin predeterminada de printf ofrece todas las caractersticas de la versin completa excepto las
relacionadas con los nmeros de punto flotante. En los megaAVR demanda cerca de 350 bytes ms
que la versin minimizada.
Es redundante decir que sta es la versin por defecto y no requiere de ninguna instruccin al linkador.
Pero si previamente se hubo establecido alguna de las otras versiones, entonces bastara con quitar las
siguientes opciones para regresar a la versin por defecto de printf.

-Wl,-u,vfprintf
La versin completa de printf brinda todo su potencial, incluyendo las conversiones de nmeros de
punto flotante. En los megaAVR requiere cerca de 380 bytes de memoria FLASH ms que la versin
predeterminada.
La versin completa de printf viene con las siguientes opciones de linkador:

-Wl,-u,vfprintf -lprintf_flt -lm

Las preguntas son: y dnde se escriben esas cosas?, o hay que escribirlas? Por qu no se arregla
simplemente con un clic?, o por qu no es como en CCS C, o aunque sea como en CodeVisionAVR?
Y las respuestas son: porque WinAVR sigue siendo un husped del Studio 5 y todava no tienen una mejor
interaccin. Tal vez en el futuro. Por otro lado, de hecho CCS C tambin utiliza varias plantillas de su funcin
printf con diferente alcance cada una, solo que antes de compilar el programa detecta las caractersticas
mnimas necesarias segn el cdigo fuente y selecciona automticamente la plantilla ms adecuada. Genial
idea, verdad? Me quito el sombrero. Sospecho que CodeVisionAVR est cerca de conseguir esa
funcionalidad. Por su parte, las recientes versiones de IAR C ya cuentan con una opcin Auto para este
propsito..
Retomando el tema, lo que hacen esas cosas son dos cosas (como en El gato ;): primero, le dicen al linkador
que no utilice la versin de printf por defecto, as:
-Wl,-u,vfprintf
-Wl indica que la directiva va para el linkador y u,vfprintf le dice que desconozca el smbolo vfprintf (que
representa la funcin printf y similares) que se encuentra entre las libreras que usa por defecto y que ms bien
lo busque en otras libreras. Dnde? Para la versin minimizada, en la librera indicada por:
-lprintf_min
Y para la versin completa, en las libreras indicadas por:
-lprintf_flt -lm
Segn el manual de GNU GCC (que tambin se instal con tu WinAVR), la opcin genrica llibrary se
refiere a la librera liblibrary.a, por lo que en realidad las libreras mencionadas son libprintf_min.a ,
libprintf_flt.a y libm.a.

Si todava sigues ah, selecciona tu proyecto en el Studio 5, muestra su ventana de propiedades, ve a la


categora Toolchain AVR/GNU C Compiler Miscellaneous y al lado de Other Linker Flags escribe -Wl,u,vfprintf, tal como aparece abajo.

Indicar a WinAVR que no use el printf por defecto.


Ahora nos ubicamos en Toolchain
libprintf_min.a, as:

AVR/GNU C Compiler

Libraries y aadimos la librera

Aadiendo libreras para el linkador en el Studio 5.


Del mismo modo, agrega las libreras libprintf_flt.a y libm.a. Te debera quedar as:

Indicar a WinAVR que primero busque [printf] en libprintf_flt.a (versin completa).


Ahora ya podemos usar printf en sus versiones minimizada o completa. Fjate bien en el orden de las libreras
aadidas. Si en la lista aparece libprintf_flt.a antes que libprintf_min.a, entonces se utilizar la versin
completa de printf y similares, como se ve arriba.
Pero si la librera libprintf_min.a tiene mejor nivel que libprintf_flt.a, entonces se usar la versin minimizada
de printf. Esto se ve abajo. Para no confundirte en cualquiera de los casos puedes quitar la librera que no
utilices. Puse este esquema solo para no estar aadiendo libreras a cada rato y para mostrar sus prioridades de
llamada. La posicin relativa de libm.a no importa, pues solo contiene rutinas matemticas para printf en su
versin completa.

Indicar a WinAVR que primero busque [printf] en libprintf_min.a (versin minimizada).

Configurar el Uso de Scanf


La familia de funciones scanf es mucho menos recurrente que printf. A veces su empleo es inclusive
contraindicado debido a que no ofrece buenos filtros.
Scanf tambin viene en tres versiones:

La versin minimizada admite el ingreso de caracteres, cadenas de hasta 256 caracteres y nmeros
enteros de 16 bits positivos o negativos. No maneja nmeros de punto flotante, no admite el
especificador [, usado para filtrar el ingreso segn un conjunto de caracteres indicado por el usuario.
Tampoco permite usar el flag de conversin %.
Para su uso se requieren las siguientes opciones de linkador. All se indican inhabilitar la versin
predeterminada e incluir las libreras libscanf_min.a y libm.a.
-Wl,-u,vfscanf -lscanf_min -lm
La versin predeterminada provee casi todas caractersticas de scanf en versin completa. No ofrece
conversiones de nmeros de punto flotante y el ingreso de caracteres se limita a 256.
No hacen falta opciones de linkador. Si se estableci antes otra versin, habra que remover al menos
las opciones:
-Wl,-u,vfscanf
La versin completa s permite las conversiones de nmeros de punto flotante.

Para su uso debemos usar las siguientes opciones de linkador. All se indican inhabilitar la versin
predeterminada e incluir las libreras libscanf_flt.a y libm.a.
-Wl,-u,vfscanf -lscanf_flt -lm

Ya vimos que agregar libreras y poner las opciones Wl, u, vfscanf es muy sencillo. El punto ser cmo
combinar las opciones de linkador si se usan al mismo tiempo ambas familias de printf y scanf en versiones no
predeterminadas. Cualquiera de las siguientes dos formas vale (no importa el orden entre vfscanf y vfprintf):
-Wl,-u,vfscanf -Wl,-u,vfprintf
-Wl,-u,vfscanf -u,vfprintf

Indicar a WinAVR que no use el scanf por defecto.

Indicar a WinAVR que no use ni el printf ni el scanf por defecto.


No es necesario que estn presentes las dos libreras libscanf_flt.a y libscanf_min.a al mismo tiempo, pero al
estar presentes las dos, se deber tener en cuenta el orden en que aparecen, igual que para printf. Por ejemplo,
de acuerdo con la siguiente figura, scanf funcionar en su versin completa porque su librera est primero, y
printf tambin pero porque no est la librera de la versin minimizada.

Indicar a WinAVR que primero busque [scanf] en libscanf_flt.a (versin completa).

Simulacin del Programa


El simulador AVR Simulator permite monitorizar el curso y evolucin del programa paso a paso, es decir,
ejecutar el programa instruccin por instruccin si fuera necesario y visualizar el contenido de los puertos de
E/S, las variables de programa, el valor de las locaciones internas de la RAM incluyendo los registros de E/S,
los Registros de Trabajo, el estado de la Pila y valor actual del Contador de Programa. Tambin se puede
medir el tiempo transcurrido entre distintos puntos del programa o simular la respuesta del programa ante
ciertos eventos detectados en los puertos de E/S del AVR.
Se ve genial, verdad? Sin embargo, no es mucho comparado con un simulador del calibre de Proteus VSM.
Proteus puede hacer todo lo mencionado arriba y que veremos en seguida, pero mucho mejor. Proteus no solo
simula el programa del AVR, sino que nos permite examinar con mayor acercamiento su interaccin con el
circuito de aplicacin, y muchas veces en tiempo real.
Lo cierto es que Proteus VSM no es un programa gratuito, aunque debemos reconocer que bien vale su precio,
siendo, de lejos, el mejor simulador de circuitos con microcontroladores que existe al menos que yo
conozca. As que si ests entre los afortunados que pueden conseguir Proteus, puedes leer el captulo que se
le dedica en vez de las siguientes pginas. A propsito, la versin demo disponible en su web
www.labcenter.co.uk no permite simular ms que los ejemplos que trae incluidos y algunos circuitos muy
sencillos creados por el usuario; de poco nos servira.

Para esta seccin practicaremos con el pequeo secuenciador de LEDs ledflasher3. Todas las prcticas de
cursomicros.com son proyectos con Solucin separada, as que da lo mismo si abres el archivo del proyecto o
de la solucin. Puedes abrir el proyecto o solucin desde el Studio 5 o con doble clic en cualquiera de sus
archivos, *.avrgccproj o *.avrsln, respectivamente.

Entorno del Studio 5 con el programa de ledflasher3.


Para iniciar el AVR Simulator vamos al men Debug Start Debugging and Break o presionamos Alt + F5 o
mediante su icono, como se ve abajo. Se puede incluso usar cualquiera de los comandos de depuracin como
Step into, Step over, etc. Cualquier camino vale.

Con la accin indicada estamos iniciando el depurador, que ms que un simulador tambin involucra a los
verdaderos depuradores como JTAGICE3. Si en este momento hubiera conectada a la PC alguna de las
tarjetas hardware como la STK600 o STK500, que incorporan interfaces de depuracin, entonces el Studio 5
las detectara y nos dara a elegir con cul depurador deseamos trabajar. Pero como no es el caso, la nica
opcin que nos presenta es el AVR Simulator. Para algunos AVR ni siquiera esta herramienta estar
disponible. As que la seleccionamos, y hacemos clic en OK. En el futuro ya no veremos esta ventana y la
simulacin correr directamente.

Herramientas de depuracin disponibles actualmente para el ATmega88P.


Ahora el Studio 5 nos presentar un entorno con grandes cambios que describiremos.

Los Comandos de Depuracin


La barra de herramientas de depuracin contiene atajos de varias de las opciones del men Debug y es la que
usaremos con mayor recurrencia. Si se llegara a perder se le puede volver sacar del men View Toolbars
Debug.

Barra de herramientas Debug con los principales comandos de depuracin.


Estos botones son casi estndar en todos los softwares simuladores, emuladores y depuradores. De seguro te
acordars para siempre de muchos de ellos. Lo que s vara un poco respecto de otros programas son las teclas
de atajo que los identifican.
Start Debugging and Break. Inicia el modo de Simulacin/Depuracin.
Stop Debugging. Detiene el modo de Simulacin/Depuracin.
Step Into. Ejecuta una instruccin o sentencia del programa, pero si se trata de una llamada a una
subrutina o funcin, se ingresar en el interior de su cdigo.
Step Over. Con este botn se ejecuta una instruccin o sentencia del programa. Si esta
instruccin/sentencia es una llamada a subrutina o funcin, se ejecutar toda la subrutina/funcin de
golpe.
Step Out. Si en el momento actual el flujo del programa se encuentra dentro de una subrutina o funcin,
se puede usar esta opcin para salir de ella.
Run to Cursor. Ejecuta el programa de corrido y se detiene en la lnea donde se ubica el cursor. Para que
se habilite, primero debemos colocar el cursor en un punto diferente del indicado por la flecha amarilla.
Reset. Regresa la ejecucin del programa al inicio del cdigo, o mejor dicho, a la primera sentencia o
instruccin disponible.
Continue. Ejecuta el cdigo del programa tan rpido como se pueda; aunque sigue estando lejos del
tiempo real. A diferencia de las opciones anteriores, en este modo las ventanas no se actualizan sino
hasta que paremos con un Break All o en un breakpoint. Se emplea bastante junto con los breakpoints.
Break All. Detiene la evolucin del programa si estamos en modo Continue. En los otros modos esta
opcin permanece inactiva.
Show Next Statement. Es la misma flechita que dirige el flujo del programa sealando la siguiente
instruccin o sentencia que se ejecutar.

Depuracin en marcha del programa ledflasher3.

Las Ventanas de Depuracin


Estas ventanas aparecen en el men Debug Windows solo cuando se ingresa en modo de Depuracin. A
continuacin descubriremos la utilidad de algunas de ellas.

Ventanas del men Debug

Windows.

La ventana Breakpoints muestra todos los breakpoints del programa, si es que existen. La forma ms cmoda
de poner breakpoints es haciendo doble clic en el margen izquierdo de la lnea deseada. Con ello aparecern
las bolitas rojas que los representan.
Los breakpoints son puntos de parada que no se dejan percibir cuando se recorre el programa paso a paso con
comandos como Step in o Step over. Pero s frenan la ejecucin de la depuracin cuando est en modo Run,
que es iniciado por el comando Continue..

La ventana Processor presenta informacin de los recursos asociados con el procesador del AVR, como el
Contador de Programa, el Puntero de Pila (Stack Pointer), el Registro de Estado, los Registros de Trabajo
(R00 a R31) y los Punteros de RAM (X, Y y Z). Adicionalmente brinda herramientas tiles como el Contador
de Ciclos y el cronmetro Stop Watch.

La ventana IO View muestra los Perifricos del AVR junto con todos sus Registros de E/S, organizados
correspondientemente en dos paneles. Por ejemplo, en la siguiente figura el panel superior selecciona el
mdulo del puerto D y el panel inferior lista sus registros relacionados, en este caso PIND, DDRD y PORTD.
Esta ventana tambin est disponible y ayuda muchsimo cuando se trabaja en modo de diseo. La diferencia
es que en modo de depuracin es posible modificar el valor de algunos de los Registros de E/S. Se podra
cambiar, por ejemplo, el valor de los bits de PIND para simular el efecto de un switch o pulsador conectado a
dicho pin del AVR.

Las ventanas Locals y Autos visualizan las variables de programa. Son muy parecidas. La ventana Locals
muestra todas las variables de mbito local de funcin actual. Por ejemplo en la siguiente figura Locals
presenta las variables i, j, k, b, bi, ba, c y Effect porque actualmente se est ejecutando la funcin main.
Por otro lado, la ventana Autos es ms selectiva an. Autos solo muestra las variables locales involucradas en
la operacin actual. Por ejemplo, en la figura mostrada Autos contiene a Effect porque est cerca la ejecucin
de la sentencia Effect = '1'; segn lo seala la flecha amarilla. El campo type seala el tipo de dato de la
variable, con el signo arroba @ indicando la localidad de RAM donde se ubican. El hecho de que en la figura
se muestre el mensaje de "unimplemented location" significa que las variables indicadas no se encuentran en
la RAM; Al examinar la ventana Regitstry (al final de esta pgina) comprobaremos que se hallan
implementadas en los Registros de trabajo.

Las ventanas Watch (hay 4 en total) pueden desplegar cualesquiera variables del programa, sin importar a qu
mbito pertenezcan (local o global). Inicialmente Watch aparece vaca y la forma ms fcil de colocar las
variables en ella es seleccionar la variable en el cdigo y arrastrarla con el mouse hasta la ventana Watch, ms
o menos como se ilustra en la siguiente imagen.

Las ventanas de Memoria no solo despliegan el contenido de las memorias RAM, FLASH o EEPROM del
AVR, sino tambin de los Registros de Trabajo (R0 a R31) y de los Registros de E/S vindolos mapeados en
la RAM.

La ventana Dissassembly muestra el programa en su correspondiente cdigo ensamblador. Es interesante ver


cmo evoluciona la ejecucin del programa en lenguaje C y en ensamblador en paralelo.

La ventana Registry muestra exclusivamente todos los 32 Registros de Trabajo del AVR (R00 a R31). Vemos
en la siguiente figura que despus de ejecutarse la sentencia Effect = '1'; aparece resaltado de rojo el valor del
registro R18. Dado que los campos as resaltados corresponden a los datos que acaban de actualizarse
deducimos fcilmente que la variable local Effect se almacena en el registro R18.

Medicin de Tiempos con Stop Watch


El Stop Watch y Cycle Counter se encuentran en la ventana Processor.
Cycle Counter cuenta los ciclos de reloj transcurridos y es independiente de la frecuencia del procesador.
Cada ciclo equivale a un pulso del oscilador del sistema del AVR. Cerca de la mitad de las instrucciones (las
ms usadas) de los AVR se ejecutan en un solo ciclo, casi todas las instrucciones que acceden a la RAM se
ejecutan en dos ciclos y algunas otras como las instrucciones de salto se ejecutan en ms de dos ciclos.
El Stop Watch mide el tiempo de simulacin transcurrido en microsegundos (us) o en milisegundos (ms). Se
basa en el valor del Contador de Ciclos y la frecuencia establecida para el procesador del AVR.

Ventana Stopwatch.
Observa que la frecuencia del procesador es por defecto de 1 MHz y no se actualiza al valor establecido en
algn archivo de configuracin. Lo primero que debemos hacer por tanto es modificarlo (si es necesario
claro). Y como en casi todas las prcticas de cursomicros.com se trabaja a 8 MHz, el cambio es obligado para
calibrar los tiempos correctamente. Solo selecciona el valor de Frecuency y edtalo.
Ahora hagamos un ejercicio midiendo el tiempo que toma la ejecucin de las sentencias puts, esto es, desde la
lnea 70 hasta la lnea 75 del programa secuenciador de LEDs ledflasher3. Para ello sigue los siguientes pasos.
Establece el flujo del programa en la primera sentencia puts (lnea 70); para esto puedes colocar el cursor en
esa lnea y luego presionar el botn Run to Cursor

Ahora resetea al valor del Stop Watch y si deseas medir los ciclos transcurridos tambin resetea el Contador
de Ciclos. Debera lucir ms o menos as.

Ventana Stopwatch reseteada.


Ahora aplica varios comandos Step Over
para pasar todas las funciones puts; sin entrar en ellas, hasta
llegar a la sentencia Effect = '1';. Otra forma de llegar a este punto es colocar all el prompt del cursor y volver
a ejecutar el comando Run to Cursor
. Demorar un poco porque el simulador no es de tiempo real. Espera
si desaparece la flecha amarilla antes de aplicar un comando. Pero al final nos quedar algo as:

El Stop Watch ha medido 107.144ms y el Contador de ciclos comput 857 152 ciclos.

Arquitectura Interna de los AVR ATmega

Introduccin
Aprender a programar microcontroladores significa aprender a usar todos sus recursos para luego aplicarlos en
el diseo deseado. Es un proceso continuo, sistemtico y que demanda algo de paciencia.
En esta clase empezaremos por conocer el hardware interno de los AVR enfocndonos en los ATmega de las
series 8yy y 4yy, que son en la actualidad los mejores microcontroladores de 8 bits de Atmel disponibles en
encapsulados DIP de 28 y 40 pines respectivamente. Entre los miembros de estas familias estn el
ATmega48yy, ATmega88yy, ATmega168yy, ATmega328yy, ATmega164yy, ATmega324yy, ATmega644yy
y ATmega1284yy. Las letras yy pueden ser P, V, PA.
Existe mucha compatibilidad entre los microcontroladores de Atmel, por lo que la teora expuesta es aplicable
en gran parte a otras series de AVR como los ATtiny, los ATmega con USB o incluso a los AVR antiguos
como los clsicos ATmega8535, ATmega8515, ATmega16, ATmega32, etc. A decir verdad, los AVR que
estudiaremos son como las versiones mejoradas de los "clsicos" ATmega citados anteriormente.
En esta clase nos familiarizaremos con la nomenclatura de los AVR, aprenderemos a reconocer sus
capacidades de memoria FLASH y RAM a partir de sus nombres, y echaremos un vistazo a sus principales
recursos hardware. As que si no sabas con cul microcontrolador AVR empezar a trabajar, ste es un buen
punto de partida.

Caractersticas Comunes de los ATmega


Citaremos las caractersticas ms notables de los ATmegaNN8YY y ATmegaNN4YY. Quiz muchas de ellas
no las comprendas de plano. Puedes tomar eso como referencia para medir tu avance en el dominio del AVR.

Tienen un repertorio de 131 instrucciones. Estn optimizadas para generar un mejor cdigo con los
compiladores de alto nivel, en especial el C.
Poseen 32 Registros de Trabajo de 8 bits cada uno. Se denominan desde R0 a R31. Esto en realidad es
aplicable a todas las familias de los AVR de bits, incluyendo los ATxmega.
Tienen una velocidad de ejecucin de hasta 20 MIPS (20 Millones de Instrucciones Por Segundo), que
se alcanzar cuando el reloj del sistema (XTAL) sea de 20 MHz. Aunque como en cualquier otro
microcontrolador, en la prctica no ser una velocidad sostenida, porque en el programa habr
instrucciones que se demoran 2 ms ciclos de instruccin. Sin embargo, siguen siendo bastante
rpidos si los comparamos por ejemplo con sus contrapartes de Microchip, los PIC18, los cuales tienen
un lmite de 10 o 12 MIPS.
Tienen un Timer0 de 8 bits que puede trabajar como Contador o Temporizador o Generador de ondas
PWM de 8 bits de resolucin.
Tienen un Timer1 de 16 bits que opera en modo Contador, Temporizador o como Generador de ondas
PWM con resolucin configurable de hasta 16 bits.
Los ATmega1284yy tienen adicionalmente un Timer3 idntico al Timer1.
Tienen un Timer2 de 8 bits, parecido al Timer0 pero con soporte para operar asncronamente con un
XTAL externo.
Tienen un Comparador Analgico.
Tienen un mdulo TWI (Two Wire Interface) para comunicaciones con el protocolo I2C en modos
Maestro y Esclavo.
Tienen un mdulo SPI programable. Soporta los modos Maestro y Esclavo.

Tienen un Conversor ADC de 10 bits, con hasta 8 canales de entrada.


Tienen un USART0: Puerto serie Transmisor Receptor Sncrono Asncrono Universal.
Los ATmegaNN4YY tienen adicionalmente un USART1, con caractersticas idnticas a las del
USART0.
Operan con voltajes de alimentacin entre 1.8V y 5.5V. Mienrtras ms alta sea la frecuencia de
operacin del AVR ms alto ser el nivel de alimentacin requerido, por ejemplo, para trabajar a la
mxima frecuencia de 20 MHz, Vcc debe tener un valor muy estable entre 4.5V y 5V.
Tienen un Oscilador RC interno configurable como oscilador principal del sistema.
Tienen 6 modos Sleep, para una mejor administracin del consumo de la energa.
Tienen un circuito BOD o detector de bajo voltaje de alimentacin.
Tienen un temporizador Watchdog, para vigilar que el programa no quede colgado.
Los ATmegaNN8YY tienen 3 puertos de E/S (con 23 pines en total) y los ATmegaNN4YY tienen 4
puertos de E/S (con 32 pines en total).
Oscilador del sistema seleccionable, desde el Oscilador RC interno hasta cristales de cuarzo.
Tienen un modo de programacin paralela de alto voltaje (14V) y un modo de programacin serial en
bajo voltaje (5V).

Empaques de los ATmega

Diagrama de pines de los ATmegaNN8yy en encapsulado PDIP.

Diagrama de pines de los ATmegaNN4yy en encapsulado PDIP.

Diagrama de bloques de los ATmega


El siguiente diagrama muestra los principales elementos de un AVR y que tarde o temprano los tendrs que
memorizar.

Diagrama de bloques simplificado de los ATmegaNN4yy / ATmegaNN8yy.


Ahora una somera descripcin de lo que representan estos bloques.

El CPU es el circuito encargado de leer, decodificar y ejecutar las instrucciones del programa. Dispone
de 32 registros de trabajo y un ALU (Unidad Aritmtico Lgica) con el que realiza las operaciones de
suma, resta, AND lgica, OR lgica, etc.
La Memoria FLASH, de Programa almacena las instrucciones del programa del AVR. Es una memoria
permanente pero que se puede reprogramar para cambiar de tarea.
La Memoria RAM, de Datos aloja las variables que procesa el CPU.
El Contador de Programa es un registro que evoluciona para indicar cul ser la siguiente instruccin
que debe ejecutar el CPU.
La Pila o Stack es un segmento de la memoria RAM para guardar el valor del Contador de Programa
y tambin variables temporales del programa cuando sea necesario.
Los perifricos del AVR son elementos que se pueden usar para una determinada tarea; por ejemplo, el
Timer0 sirve para temporizaciones. El USART para comunicaciones seriales RS232, etc. Casi todos
ellos sern estudiados en un captulo aparte.
Los puertos de E/S PORTA,..., PORTD son las lneas hacia/desde el exterior donde se pueden conectar
los dispositivos a controlar, como LEDs, transistores, LCDs, etc. Los ATmega de 40 pines tienen los 4
puertos completos, mientras que a los ATmega de 28 pines les falta PORTA y algunos pines en los
otros puertos.

Hay ms recursos presentes dentro de un AVR que tambin son imprescindibles pero cuyo trabajo queda en
segundo plano. Algunos de ellos sern abordados en otro momento.

La Memoria de Programa

Es de tipo FLASH. Aqu es donde se aloja el programa que el CPU ejecutar. Se puede modificar por
completo mediante un dispositivo programador por hasta 10 000 veces. Pero tampoco deberamos tanto. No
conozco a nadie que haya llegado a ese lmite con un solo microcontrolador. Lo ms probable es que, por ms
cuidado que tengas, llegues a frer tu AVR antes de tiempo en algn accidente. Eso es algo muy normal.
En los AVR las instrucciones de programa son de 16 de 32 bits. Pero siendo la gran mayora de 16 bits,
podemos decir que un AVR de N bytes de memoria FLASH puede almacenar hasta N/2 instrucciones de
cdigo ensamblador.
Antiguamente pareca sencillo reconocer la cantidad de memoria FLASH que tenan algunos AVR. Por
ejemplo, un ATmega32 tiene 32 k-bytes, un ATmega8L tiene 8 k-bytes. Los ATmega de ahora todava
conservan esa correspondencia entre el nmero que aparece en su nombre y la cantidad de k-bytes de FLASH,
solo que las nuevas series tambin llevan un nmero que puede entrar a confundir un poco.
En la siguiente tabla apreciamos los ATmega de las series 8yy, 4yy, 5yy y 50yy. (Las letras yy pueden ser P,
V, A o PA.) Personalmente, creo que al haber varias series, es ms fcil separar primero los nmeros que
representan la capacidad de FLASH porque deben ser nmeros redondos (digitalmente hablando), es decir,
deben ser potencias de 2, como 4, 8, 16, 32, 64 128. Por ejemplo, cunta memoria FLASH tendr un
ATmega3290P?, 3290 kbytes, 329 kbytes, 32 kbytes o 3 kbytes? Como el nico nmero redondo es 32, la
respuesta es 32 kbytes y deducimos que este ATmega es de la serie 90P. Se llega ms rpido a la respuesta si
leemos las opciones empezando por la izquierda.
AVR
ATmega48yy
ATmega88yy
ATmega168yy
ATmega328yy
ATmega164yy
ATmega324yy
ATmega644yy
ATmega1284yy
ATmega165yy
ATmega325yy
ATmega3250yy
ATmega645yy
ATmega6450yy

Memoria
FLASH
4K
8K
16 K
32 K
16 K
32 K
64 K
128 K
16 K
32 K
32 K
64 K
64 K

Memoria
RAM
512
1K
1K
2K
1K
2K
4K
16 K
1K
2K
2K
4K
4K

Memoria
EEPROM
256
512
512
1K
512
1K
2K
4K
512
1K
1K
2K
2K

Pines de E/S
23
23
23
23
32
32
32
32
54/69
54/69
54/69
54/69
54/69

Secciones de Aplicacin y de Boot Loader


Los AVR ofrecen la posibilidad de escribir en su memoria de programa FLASH incluso en tiempo de
ejecucin. Esta funcin puede ser aprovechada para almacenar datos procesados por el usuario o para permitir
la auto-programacin del AVR.
Para facilitar y robustecer el proceso de auto-programacin los AVR dividen su memoria FLASH en dos
segmentos lgicos, de Aplicacin y de Boot loader.
La Seccin de Aplicacin est destinada a almacenar el programa que el AVR ejecuta habitualmente, como
leer sensores, controlar motores, etc.

La Seccin de Boot loader est diseada para almacenar el cdigo del Boot loader, que es un pequeo
programa para cargar el programa del AVR en la Seccin de Aplicacin, as como Windows carga en la RAM
de la PC el programa que vamos a utilizar. Un Boot loader no es precisamente un mini S.O. porque ya hay
S.O. para microcontroladores conocidos como RTOS (Real Time Operating System).
Adems, a diferencia de un SO para PC, un Boot loader debe ser el programa ms pequeo posible y se debe
ubicar al final de la memoria FLASH. No todos los ATmega tienen Seccin de Boot Loader, como el
ATmega48yy.

Secciones de Aplicacin y de Boot Loader de la memoria de programa.


La Seccin de Boot loader siempre se ubica al final de la memoria FLAH pero su tamao vara de acuerdo
con el AVR y con la configuracin establecida por los fuses BOOTSZ1 y BOOTSZ0. Por ejemplo, el
ATmega644PA puede tener una Seccin de Boot loader entre 512 palabras y 4096 palabras, segn la siguiente
tabla.
BOOTSZ1 BOOTSZ0
1
1
0
0

1
0
1
0

Tamao del Boot Loader


512 palabras
1024 palabras
2048 palabras
1096 palabras

Direccin del Boot


Loader
0x7E00 - 0x7FFF
0x7C00 - 0x7FFF
0x7800 - 0x7FFF
0x7000 - 0x7FFF

Los fuses BOOTSZ1 y BOOTSZ0 solo se pueden modificar al grabar el AVR. Su configuracin por defecto
siempre establece el tamao mayor elegible de la Seccin de Boot loader.
El hecho de que la Seccin de Boot Loader est diseada para alojar el programa cargador no significa que
est limitada a esa tarea. Si no la vamos a usar para su propsito primigenio, podemos emplearla como si fuera
parte de la Seccin de Aplicacin, o sea, como si no existiera la divisin entre estas dos Secciones y por ende
no tendr importancia el valor que pongamos en los fuses BOOTSZ1 y BOOTSZ0.

Configuracin de los Fuses de Boot loader en el programa grabador.


A propsito, la configuracin de los fuses BOOTSZ1 y BOOTSZ0 en Proteus no tiene ningn efecto porque
Proteus an no soporta simulaciones con Boot Loader. As que estn como decorativos.

Configuracin de los Fuses de Boot loader en Proteus.

La Memoria de Datos SRAM


Actualmente suena redundante especificar memoria SRAM (Static RAM) porque casi todas las RAM de los
microcontroladores son estticas. Decimos simplemente RAM, a la memoria cuya funcin tradicional es
alojar temporalmente los datos que se procesan en el programa.
La cantidad de RAM disponible internamente depende del modelo de AVR y, a diferencia de la memoria
FLASH, no tiene una directa correspondencia con el nombre del dispositivo. Aun as, podemos observar en la
siguiente tabla que en muchos modelos existe una relacin que se repite (los ATmega128nn rompen la
relacin, y pueden no ser los nicos). Los ATmega con 4K de FLASH tienen 512 bytes de RAM, los ATmega
con 8 K y 16 K de FLASH tienen 1 K de RAM, y as, tal como se ve en la tabla.
AVR
ATmegaNNN

Memoria
FLASH
4K

Memoria RAM

Algunos Modelos

512

ATmega48A, ATmega48PA,
ATmega48P/V, ATmega48/V

AVR

Memoria
FLASH

ATmegaNNN

8K

ATmegaNNN

16 K

ATmegaNNN

32 K

ATmegaNNN

64 K

ATmegaNNN

128 K

Memoria RAM

Algunos Modelos

1K

ATmega88A, ATmega88PA,
ATmega88P/V, ATmega88/V
ATmega168A, ATmega168PA,
ATmega168P/V, ATmega168/V
ATmega164A, ATmega164PA
ATmega165A, ATmega165PA
ATmega169, ATmega169P
ATmega328, ATmega328P
ATmega324A, ATmega324PA
ATmega325A, ATmega325PA
ATmega3250A, ATmega3250PA
ATmega329P, ATmega3290P
ATmega644A, ATmega644PA
ATmega645A, ATmega645P
ATmega6450A, ATmega6450P
ATmega128, ATmega1281
ATmega1284, ATmega1284P

1K

2K

4K

4K, 8K o 16K

En los modelos listados y en general en todos los ATmega de las series ms recientes el espacio de la
memoria RAM (entendida en su concepto tradicional) empieza en la direccin 0x0100 y termina en 0x02FF,
0x04FF, 0x08FF, 0x10FF, 0x20FF o 0x40FF, segn el modelo. La verdad, no importa mucho saber dnde
termina como la cantidad misma. La direccin de inicio s es de consideracin pero cuando se programa en
lenguaje ensamblador.
Quiz alguien pudiera preguntar por qu la direccin de inicio no es 0x0000, como en otros
microcontroladores. Porque las primeras direcciones, desde 0x0000 hasta 0x00FF, estn reservadas para
acceder a los Registros de Trabajo y a los Registros de E/S del AVR en modo de memoria.
Normalmente los 32 Registros de Trabajo se acceden directamente con instrucciones como LDI o MOV. Pero
tambin se les puede acceder direccionndolos como si fueran parte de la memoria RAM, con instrucciones
como LD o ST y con las direcciones presentadas. En ocasiones esto facilitar mover bloques de datos entre la
RAM y los registros de trabajo aprovechando la potencia de los punteros.
Del mismo modo, los Registros de E/S, que tampoco ocupan posiciones reales en la RAM, pueden ser
accedidos como si en verdad fueran RAM con las instrucciones como LD y ST, para lo cual emplean las
direcciones mostradas en la figura.
Los Registros de E/S y los Registros de E/S extendidos son hermanos, por decirlo de algn modo, y tienen
funciones anlogas. Estn separados solo por tener diferente modo de acceso, pero eso se explicar mejor en
su seccin respectiva.
El direccionamiento y distincin de los espacios de la RAM solo son de preocupacin al trabajar en
ensamblador. Al programar en lenguajes de alto nivel los compiladores se encargan de toda la administracin
de la RAM, salvo que reciban directivas avanzadas.

Espacio de la Memoria RAM.

El Contador de Programa, PC
El PC es un registro que indica la siguiente instruccin que debe ejecutar el CPU. Si vale 0x0000, ejecutar la
primera instruccin de la memoria; si vale 0x0002 ejecutar la tercera instruccin, y as... Al arrancar
microcontrolador, el PC vale 0x0000 y se va incrementando automticamente, con lo que el AVR debera
ejecutar una a una desde la primera hasta la ltima instruccin del programa. En realidad, en el cdigo habr
instrucciones que modifiquen el valor del PC de modo que el programa nunca termine.

La Pila y el Puntero de Pila


La Pila o STACK es una memoria que almacena temporalmente el valor del PC (Program Counter) cuando el
programa llama a una subrutina o cuando salta a un Vector de Interrupcin. Tambin sirve para guardar datos
temporalmente cuando los 32 Registros de Trabajo no sean suficientes.
Al igual que en una PC, la Pila forma parte de RAM Interna. No es un pedazo de RAM con caractersticas
especiales, es una simple rea cuya direccin de inicio la puede establecer el usuario y cuyo tamao es
indefinido porque crece y decrece en tiempo de ejecucin.

Las que s son especiales son las instrucciones que trabajan con la Pila, como PUSH y POP. Estas
instrucciones no necesitan conocer la locacin en la Pila a/de donde guardarn/recuperarn los datos.
Aprovechan, en cambio, su acceso de tipo LIFO (Last In First Out), que significa el ltimo dato en entrar
ser el primero en Salir
Es por eso que siempre se recurre a la analoga con una pila de platos de donde no podemos tomar un plato
que se encuentra en el fondo o en la mitad. Para llegar a l primero tendramos que quitar los platos que estn
encima.
Pero hasta ah llega la analoga porque a diferencia de las pilas de platos, las pilas en RAM crecen de arriba
abajo, es decir, cada dato que se va depositando en la Pila ocupa una direccin inferior a la anterior. Esta
direccin la va marcando el Puntero de Pila, el cual se decrementa cada vez que se coloca un dato en la Pila y
se incrementa cada vez que se toma un dato de ella.
La Pila trabaja de cabeza para evitar que sus datos colisionen (se solapen) con las variables accedidas
aleatoriamente, las cuales se van mapeando en la RAM normalmente de abajo arriba.
SPHSP15SP14SP13SP12SP11SP10SP9SP8
SPLSP7SP6SP5SP4SP3SP2SP1SP0
El Puntero de Pila est representado por los registros de E/S SPH y SPL, que concatenados actan como un
registro de 16 bits. Como se dijo anteriormente, este registro tiene un comportamiento de auto-incremento y
auto-decremento. La nica intervencin por parte del usuario debera ser su inicializacin, esto es, cargarle la
ltima direccin de la RAM. Pero esta operacin solo es indispensable al programar en ensamblador. Los
compiladores como el C inicializan la Pila automticamente. Sin embargo, incluso en C es importante conocer
estos conceptos para entender por ejemplo por qu un programa para un ATmega328P nunca funcionar en un
ATmega168P, incluso si el tamao del cdigo es pequeo y le puede caber sobradamente.

Los Registros de Trabajo y los Punteros X, Y y Z


Todos los AVR de 8 bits, desde los ATtiny hasta los ATxmega cuentan con 32 Registros de Trabajo
nombrados desde R0 hasta R31. Los Registros de Trabajo tienen la funcin de alojar los datos ms inmediatos
que el CPU procesa. Acaso sa no era tarea de la RAM?.
Bueno, sucede que en todos los microcontroladores inspirados en la arquitectura de los procesadores de Intel
(como los AVR, ARM y Freescale entre otros) el acceso a la memoria RAM toma ms ciclos que el acceso a
los Registros de Trabajo.
En los AVR de 8 bits, por ejemplo, se puede acceder a los Registros de Trabajo en un solo ciclo, puesto que
todos estn directamente conectados al CPU, o mejor dicho, son parte del CPU. En cambio, la mayora de las
instrucciones ensamblador que acceden a la RAM consumen 2 ciclos de instruccin.
No es posible cargar datos en la RAM directamente ni moverlos entre locaciones diferentes de la RAM (a
menos que tengan DMA, como los AVR32). Para esas operaciones los Registros de Trabajo actan como
intermediarios.

Pero quiz la participacin ms notable de Los Registros de Trabajo sea en el ALU (Unidad Aritmtico
Lgica) para computar las operaciones aritmticas y lgicas. Por ejemplo imaginemos que deseamos obtener
la raz cuadrada de un nmero de punto flotante ubicado en la RAM y almacenar el resultado de nuevo en la
RAM. En lenguaje C bastara con escribir una sola sentencia, pero el cdigo mquina generado involucra una
tarea ms compleja: al inicio el CPU mueve los 4 bytes del nmero desde la RAM a los Registros de Trabajo,
luego viene el trabajo pesado, que implica el procesamiento de varios datos intermedios. En lo posible todos
estos datos tambin estarn en los registros de trabajo para aprovechar su velocidad y eficacia. Solo al
terminar el cmputo el CPU depositar el resultado en la RAM.
Los compiladores de alto nivel tambin suelen emplear los Registros de Trabajo para pasar los argumentos de
sus funciones. Creo que con esos ejemplos debe quedar clara la razn de ser de los Registros de Trabajo.
En la siguiente figura podemos notar que los Registros de Trabajo se parten por la mitad. La diferencia est en
que los primeros 16 registros (R0 a R15) no admiten la instruccin LDI, que sirve para cargar constantes al
registro (otro aspecto primordial de la programacin en ensamblador9. Los registros R26 a R31 tienen la
capacidad adicional de funcionar como punteros de 16 bits cada uno.

Los Registros de Trabajo de los AVR.


El par de registros R27-R26 forma el Puntero X, el par R29-R28 forma el Puntero Y, y el par R31-R30 forma
el Puntero Z.

Los punteros pueden apuntar a (contener la direccin de) cualquier locacin del espacio de RAM. Esto junto
con las instrucciones adecuadas conforman el direccionamiento indirecto ms potente, muy til por ejemplo
para mover grandes bloques de datos.

Terminamos esta seccin explicando las direcciones que figuran en el mapa de los Registros de Trabajo. En
principio a los Registros de Trabajo no les debera hacer falta tener direcciones porque estn directamente
unidos al CPU. Hay instrucciones adecuadas como LDI y MOV para acceder a ellos. Sin embargo, los AVR
les brindan direcciones para adicionalmente poder ser accedidos como si fueran parte de la RAM, es decir,
con instrucciones que estn diseadas para la RAM, como LD y ST. De esta manera se hacen ms flexibles las
operaciones de transferencias de datos entre los diferentes espacios de memoria.
Cuando manipulamos los Registros de Trabajo utilizando sus direcciones podemos decir que los estamos
accediendo en modo RAM, pero sin perder de vista que los Registros de Trabajo no pertenecen a la RAM
porque no estn implementadas fsicamente all.

Los Registros de E/S


Anteriormente se dijo que para programar el AVR primero haba que conocer sus recursos. Pues bien, todos
ellos se pueden controlar mediante los Registros de E/S. por ejemplo, si queremos manejar el puerto B,
debemos conocer los registros PORTB, DDRB y PINB. Si queremos programar el USART0, debemos
conocer los registros UDR0, UCSR0A, UCSR0B, UCSR0C, UBRR0L y UBRR0H. Si queremos
En tiempo de ejecucin los registros de E/S (Entrada Salida) lo controlan todo, no solo las operaciones de los
mdulos perifricos, como se podra inferir a partir de la denominacin E/S, sino que tambin controlan la
performance del mismo CPU. En este caso los registros a conocer seran MCUCR, MCUSR o SMCR.
Entenders que no tiene caso seguir mencionando las funciones de otros Registros de E/S. es por eso que cada
mdulo se trata por separado estudiando con detenimiento cada registro y cada uno de los bits que lo
componen.

Espero que los mapas de memoria de los Registros de E/S que presento no te hagan pensar que estn divididos
en una suerte de bancos, como en los PICmicro. No, seor, nada de eso. Todos los espacios de memoria en los
AVR son lineales. Yo los tuve que subdividir para no presentar un culebrn.

Cada registro es de 8 bits y en total se cuentan 224 registros de E/S, aunque ni siquiera la mitad estn
implementados fsicamente. Las locaciones que aparecen sin nombre estn RESERVADAS y no nunca
deberan ser accedidas.
De modo similar, hay registros con algunos bits sin nombre ni funcionalidad que tambin se consideran
reservados. Un ejemplo es el registro SMCR, mostrado abajo. No est prohibido acceder a dichos bits pero se
recomienda dejarlos en 0 por compatibilidad con futuros AVR. Estas aclaraciones aparecen por doquier en los
datasheets. Yo prefiero decirlas ahora para no tener que estar repitindolo a cada rato despus.
SMCR------------SM2SM1SM0SE
Los antiguos microcontroladores AVR solo tenan el espacio de los 64 primeros Registros de E/S. A ellos les
alcanzaba ese espacio aunque a veces a duras penas porque todos los registros estaban all apretujados. Los
Registros de E/S tenan sus propias instrucciones de acceso, IN y OUT, que de hecho todava se usan. Las
instrucciones de ensamblador IN y OUT utilizan el rango de direcciones 0x00 a 0x3F para acceder a los
Registros de E/S. En el esquema mostrado estas direcciones aparecen fuera de los parntesis.
Con la aparicin de nuevos perifricos en los AVR, aumentaron los registros de E/S y se sobrepasaba el
alcance de las instrucciones IN y OUT. As fue necesario disear los Registros de E/S para que tambin
pudieran ser direccionados como si fueran parte de la RAM, o sea, con instrucciones como LD o ST, las
cuales tienen mayor cobertura y permiten repotenciar las transferencias de datos. Para este tipo de acceso,
llamado en modo RAM, las instrucciones como LD o ST utilizan el rango de direcciones 0x20 a 0xFF, que
en los mapas de memoria aparecen dentro de los parntesis.
A juzgar por sus direcciones, ya podrs deducir que la nica diferencia entre los Registros de E/S (estndar) y
los Registros de E/S Extendidos es que estos ltimos se acceden exclusivamente en modo de memoria con
instrucciones como LD y ST, y los primeros todava admiten el uso de las instrucciones clsicas como IN y
OUT.
El direccionamiento y distincin de los Registros de E/S estndar o extendidos son de especial preocupacin
al trabajar en ensamblador. Al programar en lenguajes de alto nivel los compiladores son quienes escogen el
modo de acceso y las instrucciones que consideren ms convenientes, salvo que reciban directivas contrarias.

Registros Generales del Microcontrolador

Estos son registros de E/S que no estn relacionados con una parte exclusiva del microcontrolador. Por tanto
los volveremos a ver al mencionar en otras clases, aunque sea para referirnos a uno solo de sus bits.

El Registro de Estado SREG


Todo microprocesador tiene un registro para reflejar el estado de las operaciones lgicas y aritmticas del
mdulo ALU. SREG tambin incluye dos bits con funciones dismiles.
El registro SREG es bastante utilizado en los programas en ensamblador. Cuando se trabaja con compiladores
de alto nivel su presencia solo sera justificable por el uso del bit I.
SREGITHSVNZC
I Global Interrupt Enable
1. Habilitar las interrupciones individuales habilitadas. Las interrupciones
individuales sern habilitadas en otros registros de control.
0. No se habilita ninguna de las interrupciones sin importar sin importar sus
configuraciones individuales.

El bit I se limpia por hardware despus de ocurrir una interrupcin, y se setea por la
instruccin para habilitar posteriores interrupciones. El bit I tambin se puede setear
o limpiar por la aplicacin con las instrucciones SEI y CLI.
Bit Copy Storage

Las instrucciones para copiar bits (Bit LoaD) y BST (Bit STore) usan el bit T como
inicio o destino para el bit de la operacin. Con la instruccin BST se puede copiar
un bit de un registro de trabajo al bit T y con la instruccin BLD se puede copiar el
bit T a un bit de un registro de trabajo.
Half Carry Flag

El flag H indica un medio acarreo en algunas operaciones aritmticas. El bit H es


muy til en aritmtica BCD.
Sign Bit, S = N V

El bit S es siempre un or exclusivo entre los flags N y V. Leer abajo.


Twos Complement Overflow Flag

El flag V soporta aritmtica de complemento a dos.


Negative Flag

El flag N indica un resultado negativo en una operacin lgica o aritmtica.


Zero Flag

El flag Z indica un resultado cero en una operacin lgica o aritmtica.


Carry Flag
El flag C indica un acarreo en una operacin lgica o aritmtica.

El Registro MCUCR

MCUCR = MCU Control Register. MCU a su vez significa Micro Controller Unit.
Aunque probablemente de este registro solo vayamos a usar el bit PUD, es bueno conocerlo ahora
aprovechando que hace referencia a muchas de las caractersticas del microcontrolador que se estudiaron en
esta clase. Ampliaremos la funcionalidad del bit PUD en la clase de puertos y entrada y salida generales y los
bits IVSEL junto con IVCE sern mejor expuestos en la clase de interrupciones.
MCUCRJTDBODSBODSEPUD------IVSELIVCE
JTD
JTAG Interface Disable
0. La interface JTAG estar habilitada si est programado el fuse JTAGEN.
1. la interface JTAG est deshabilitada.

BODS

Para evitar deshabilitaciones no intencionadas de la interface JTAG, se debe seguir


una secuencia especial para cambiar este bit: el software de aplicacin debe escribir
este bit dos veces el valor deseado dentro de cuatro ciclos de reloj. Este bit no se
debe alterar cuando se est usando el sistema OCD (On Chip Debug)
BOD Sleep
Sirve para deshabilitar el circuito del BOD durante el modo Sleep, si estaba activo.
Recuerda que se activa por los fuses BODLEVEL2-0.
Para poner 1 en el bit BODS se requiere seguir una secuencia especial que involucra
el bit BODSE. Primero se escribe 1 en los bits BODS y BODSE. Luego se debe
escribir 1 en el bit BODS y 0 en el bit BODSE dentro de los siguientes cuatro ciclos
de reloj.

BODSE

El bit BODS estar activo durante tres ciclos de reloj despus de haberse seteado.
Para apagar el circuito BOD para el modo Sleep actual se debe ejecutar la instruccin
Sleep mientras el bit BODS est activo. El bit BODS se limpia automticamente
despus de tres ciclos de reloj.
BOD Sleep Enable

PUD

El bit BODSE habilita la configuracin del bit de control BODS, como se explic en
la descripcin del bit BODS.
Pull-up Disable
1. Se deshabilitan las pull-up de todos los pines de los puertos, sin importar el valor
de los registros DDRx y PORTx.

IVSEL

0. Las pull-up se habilitaran por los registros DDRx y PORTx.


Interrupt Vector Select
0. Los Vectores de Interrupcin se ubican en el inicio de la memoria FLASH.
1. Los Vectores de Interrupcin se mueven al inicio de la Seccin de Boot Loader de
la memoria FLASH.
Para evitar cambios no intencionados en este bit se debe seguir una secuencia

especial:
a. Escribir 1 en el bit IVCE.
b. Dentro de los cuatro ciclos de reloj siguiente, escribir el valor deseado en el bit
IVSEL mientras se escribe un 0 en el bit IVCE.
Las interrupciones se deshabilitarn automticamente durante la ejecucin de esta
secuencia. Las interrupciones se deshabilitan en el ciclo en que se setea el bit IVCE y
permanecen deshabilitadas hasta despus de la instruccin que escribe el bit IVSEL.
Si no se escribe el bit IVSEL, las interrupciones permanecen deshabilitadas por
cuatro ciclos de reloj. El bit I en el registro de estado SREG no se afecta por la
deshabilitacin automtica.

IVCE

Nota: si los Vectores de Interrupcin estn colocados en la Seccin de Boot Loader,


y el bit de candado BLB02 est programado, las interrupciones se deshabilitan
durante la ejecucin del programa desde la Seccin de Boot Loader.
Interrupt Vector Change Enable
Se debe escribir 1 en el bit IVCE para habilitar el cambio del bit IVSEL. El bit IVCE
se limpia por hardware cuatro ciclos despus de que se haya escrito o cuando se
escribe el bit IVSEL. Setear el bit IVCE deshabilitar las interrupciones, como se
explic en la descripcin del bit IVSEL.

El Registro MCUSR
MCUSR = MCU Status Register. Es el registro de estado del microcontrolador, MCU.
MCUSR est conformado por bits de Flag que sirven para identificar la causa del reset del microcontrolador.
Para averiguar la fuente de reset el programa debe leer el registro MCUSR tan pronto como sea posible y
luego limpiar sus Flags.
Observa que a diferencia de los Flags de las Interrupciones, los Flags de MCUSR se limpian escribiendo un
cero y no un uno.
MCUSR---------JTRFWDRFBORFEXTRFPORF
JTRF
JTAG Reset Flag

WDRF

Este bit se pone a uno si se produce un reset por un uno lgico en el Registro JTAG
Reset seleccionado por la instruccin de JTAG AVR_RESET. Este bit se pone a cero
por un Reset POR, o escribiendo un cero lgico en el flag.
Watchdog Reset Flag

BORF

Este bit se pone a uno cuando se produce un Reset por el Watchdog. Este bit se pone
a cero por un reset Power-on o escribiendo un cero lgico en el flag.
Brown-out Reset Flag
Este bit se pone a uno cuando se produce un Reset Brown-out. Este bit se pone a cero
por un reset Power-on o escribiendo un cero lgico en el flag.

EXTRF

PORF

External Reset Flag


Este bit se pone a uno cuando se produce un Reset Externo. Este bit se pone a cero
por un reset Power-on o escribiendo un cero lgico en el flag.
Power-on Reset Flag
Este bit se pone a uno cuando se produce un Reset Power-on. Este bit se pone a cero
escribiendo un cero lgico en el flag.

Los Fuses de los AVR


Los fuses del microcontrolador establecen una caracterstica importante en su operacin, tanto que solo se
puede modificar en el momento de programarlo. Por ejemplo, no se podra escoger el tipo de oscilador a usar
despus de haber iniciado el programa. Sera como cambiarle los neumticos a un automvil en marcha. Cada
aplicacin puede requerir una configuracin particular y si no se establecen los fuses correctos, el programa
puede funcionar mal, suponiendo que funcione. :)
A diferencia de los PICmicro, en los AVR los fuses no pueden formar parte del cdigo HEX, as que siempre
ser necesario configurarlos directamente en el entorno del programa grabador de AVR. Los fuses s pueden
incluirse en los archivos de depuracin como ELF, para simulaciones o depuraciones.
Por otra parte, y esto es para bien, hay muy poca variacin en los fuses de los AVR, inclusive de distintas
series. Por eso sern de fcil recordacin, en contraste con los fuses de los PICmicro, donde son casi inmemorizables. La comparacin es siempre con los PIC18, por supuesto, porque los PIC16 en general no estn
a la altura.
Los fuses estn contenidos en los denominados Bytes de Fuses, que son registros (de 8 bits obviamente)
implementados en EEPROM (no en la EEPROM de datos de AVR). Por eso un 1 es el valor por defecto
cuando un bit no est programado.
Los ATmega ms recientes tienen 3 Bytes de Fuses llamados Byte de Fuses Bajo, Alto y Extendido. El Byte
de Fuses Bajo es el mismo en todos ellos. Los otros empiezan a cambiar un poco, en orden ms que en
funciones, segn cada modelo en particular. Mientras ms reciente sea la serie, se encontrarn menos
divergencias.
El conjunto de Bytes de Fuses mostrado a continuacin pertenece a los AVR de la serie ATmegaNN4YY. En
otras series pueden aparecer, desaparecer o cambiar algunos fuses, por ejemplo los AVR de la serie
ATmegaNN5YY tienen adicionalmente el fuse RSTDISBL. En lo sucesivo se describirn las funciones de los
fuses ms conocidos, no solo los presentados aqu.
Byte de Fuses Bajo
CKDIV8
CKOUT
SUT1
SUT0
CKSEL3
CKSEL2
CKSEL1

Bit
7
6
5
4
3
2
1

Descripcin
Divide clock by 8
Clock output
Select start-up time
Select start-up time
Select Clock source
Select Clock source
Select Clock source

Valor por Defecto


0 (programado)
1 (sin programar)
1 (sin programar)
0 (programado)
0 (programado)
0 (programado)
1 (sin programar)

Byte de Fuses Bajo


CKSEL0
Byte de Fuses Alto
OCDEN
JTAGEN
SPIEN
WDTON
EESAVE
BOOTSZ1
BOOTSZ0
BOOTRST
Byte de Fuses

Bit
0
Bit
7
6
5
4
3
2
1
0
Bit

Extendido

BODLEVEL2
BODLEVEL1
BODLEVEL0

7
6
5
4
3
2
1
0

Descripcin
Select Clock source
Descripcin
Enable OCD
Enable JTAG
Enable Serial Programming
Watchdog Timer always on
EEPROM memory is preserved
Select Boot Size
Select Boot Size
Select Reset Vector
Descripcin

Brown-out Detector trigger level


Brown-out Detector trigger level
Brown-out Detector trigger level

Valor por Defecto


0 (programado)
Valor por Defecto
1 (sin programar)
0 (programado)
0 (programado)
1 (sin programar)
1 (sin programar)
0 (programado)
0 (programado)
1 (sin programar)
Valor por Defecto
1
1
1
1
1
1 (sin programar)
1 (sin programar)
1 (sin programar)

Pongo en relieve estos detalles porque hay muchos programadores, como el famoso WinPic800, en los que los
fuses se configuran bit a bit, como se ve abajo. Cmo haras si tuvieras que configurar el uso de un oscilador
de XTAL de 16 MHz en un ATmega3250P? Primero tendras que conocer los fuses correspondientes, cierto?
En este caso son CKSEL0, CKSEL1, CKSEL2 y CKSEL3. Luego tendras que descubrir cul es la
combinacin correcta para un XTAL de 16MHz. No te preocupes, todo esto lo estudiaremos ampliamente.

Configuracin de los fuses en WinPic800.


WinPic800 es bueno pero sus engredos son los PICmicro y a los AVR no les da tanta consideracin como a
ellos (debe ser por su nombre ;).
Tambin existen programadores con software de mejor interface como el AVRprog2 con su software
AVRFLASH, cuyo entorno se muestra en abajo. A propsito, la toma indica que hay opciones para usar un
XTAL mayor de 8MHz. Podras elegir cualquiera, a menos que sepas lo que significan arranque rpido o
lento, y ests seguro de que necesitas uno de ellos. Bueno, mejor vamos a la descripcin de los fuses de una
vez.

Configuracin de los fuses en AVRFLASH.

CKSEL3-0. Seleccin del Reloj


Este fuse se representa por los bits CKSEL3, CKSEL2, CKSEL1 y CKSEL0. Sirve para adaptar el circuito
interno del oscilador segn el componente externo o interno que se usar como fuente del reloj del sistema.

Reloj Externo. En este caso la fuente de reloj ser una seal de onda cuadrada externa aplicada al pin
XTAL1 del AVR. Su frecuencia podr estar en todo el rango posible, desde 0 hasta 20 MHz.
Se establece con los bits CKSEL3-0 = 0000, respectivamente.
Oscilador RC Interno Calibrado. Con esta opcin no se necesitarn aadir componentes externos. El
reloj ser el oscilador RC Interno, el cual tiene una frecuencia cercana a 8MHz, y adicionalmente
ofrece al diseador la posibilidad de ajustar por software dicha calibracin hasta en 1%. Es til para
sistemas de bajo costo aunque de menor nivel de estabilidad. Es la opcin por defecto.
Se establece poniendo los bits CKSEL3-0 = 0010, respectivamente.

XTAL Externo de Baja Frecuencia. Para utilizar un XTAL externo de 32.768 kHz con capacitores de
estabilizacin opcionales. Es una configuracin diseada para aplicaciones de reloj.
En los ATmega 8xx y 4xx se establece poniendo los bits CKSEL3-0 = 0100 o 0101. En otros ATmega
puede variar.
Oscilador RC Interno de 128kHz. Este oscilador es parecido al RC de 8MHz, solo que ofrece menor
precisin y no se puede calibrar.
En los ATmega 8xx y 4xx se establece con los bits CKSEL3-0 iguales a 0011. En otros ATmega
puede no estar disponible.
XTAL Externo de Baja Potencia. Esta configuracin soporta cristales de 0.9 MHz hasta 16 MHz.
Estos cristales ahorran energa pero su uso es recomendado solo en ambientes libres de ruido. El
XTAL se coloca entre los pines XTAL1 y XTAL2 del AVR y debe usar capacitores externos de entre
12pf y 22pf, similar al circuito con XTAL estndar de alta frecuencia.
En los ATmega 8xx y 4xx los valores de los bits CKSEL3-0 dependern del rango de frecuencia del
XTAL, segn la siguiente tabla.
Rango del XTAL en
MHz
0.9 - 3.0
3.0 - 8.0
8.0 - 16.0

CKSEL3-0
1011 o 1010
1101 o 1100
1111 o 1110

XTAL Externo Estndar. Con esta configuracin el XTAL usado estar entre 0.4 MHz y 20 MHz (el
mximo admisible). El XTAL se coloca entre los pines XTAL1 y XTAL2 del AVR y debe tener
capacitores externos de entre 12pF y 22pF.
En los ATmega 8xx y 4xx se establece con los bits CKSEL3-0 = 0111 o 0110, dependiendo de la
frecuencia del cristal usado, segun la siguiente tabla..
Rango del XTAL en
MHz
0.4 - 16
0.4 - 20

CKSEL3-0
0111 o 0110
0111

Observa que con los 4 bits CKSEL3-0 es posible formar hasta 16 combinaciones. Entre las no citadas, algunas
estn reservadas y no deberan usarse, y otras corresponden a configuraciones relacionadas con el uso de un
Resonador Cermico en lugar del XTAL externo estndar o de baja potencia. Exclu las combinaciones del
resonador cermico para evitar una sobrecarga de nmeros innecesaria.
En la mayora de las prcticas de cursomicros.com se usa un XTAL de 8 MHz. Puede ser estndar o de baja
potencia. El XTAL de cuarzo brinda el reloj ms preciso y estable, lo que es ideal para aplicaciones que usan
los mdulos sncronos como el USART o TWI (I2C).
Debemos recordar que un 0 es un bit programado y un 1 es un bit sin programar. En los entornos de los
programadores 0 suele ser una casilla marcada. Por ejemplo, en WinPic800 la combinacin CKSEL3-0 =
0111 se vera as.

SUT1-0. Retardos de Reset y de Arranque


Se representa por los bits SUT1 y SUT0. En realidad se trata de dos temporizadores.
El Retardo de Reset funciona con el circuito RC del Watchdog y se encarga de mantener el AVR en estado de
reset por 4.1 ms o 65 ms despus de un RESET, por ejemplo, despus de conectar la alimentacin Vcc del
AVR. Esto servira para que se estabilice el nivel de Vcc antes de que el AVR empiece a trabajar. Se
representa por
.

Pero ah no termina. Para asegurarse de que el oscilador del sistema tambin se haya estabilizado, habr un
Retardo de Arranque hasta que transcurran 1K o 16K ciclos de reloj CK. Durante ese lapso el AVR tambin
se mantiene en estado de reset y luego recin el procesador empezar a ejecutar el programa. El Retardo de
Arranque no solo se activa despus de un reset sino tambin despus de que el AVR salga de los estados
Power-save o Power-down, que son dos de los seis modos SLEEP que tienen los ATmega.
La configuraciones de los retardos de reset y de arranque varan de acuerdo con la fuente de reloj del sistema.
La siguiente tabla muestra solo las opciones admisibles para un XTAL estndar o de baja potencia.
CKSEL0
0
0
1
1
1
1

SUT1 SUT0
10
11
00
01
10
11

Retardo de
arranque
14CK 1K CK
4.1ms + 14CK 1K CK
65ms + 14CK 1K CK
14CK 16K CK
4.1ms + 14CK 16K CK
65ms + 14CK 16K CK

Retardo de reset

Fuente de Oscilador, Condiciones de


alimentacin
XTAL baja frecuencia, BOD enabled
XTAL baja frecuencia, fast rising power
XTAL baja frecuencia, slowly rising power
XTAL, BOD enabled
XTAL, fast rising power
XTAL, slowly rising power

En esta tabla XTAL baja frecuencia no se refiere al XTAL de reloj de 32.768 kHz, sino a un XTAL estndar o
de baja potencia cuya frecuencia no est cercana a la mxima admisible por el AVR, que para los ATmega
8xx o 4xx es de 20MHz. Esta condicin concuerda con el valor del bit CKSEL0 dado en las tablas de los bits
CKSEL3-0 vistas anteriormente.
Como se ve algo confuso, vamos a poner un ejemplo de diseo. Supongamos que nuestra aplicacin utilizar
un ATmega644P a una frecuencia muy estable de 20 MHz. En primer lugar debemos utilizar un XTAL
estndar de 20MHz, para lo cual debemos programar los bits CKSEL3-0 con 0111 (ver las tablas de arriba).
Como el bit CKSEL0 en este caso es 1, descartamos las configuraciones de la tabla donde CKSEL0 es 0. As
mismo, como 20MHz no es una frecuencia baja, tambin descartamos la configuracin de XTAL baja
frecuencia, slowly rising power. Ahora todava tenemos las tres opciones resaltadas en la siguiente tabla.
CKSEL0
0
0
1
1
1
1

SUT1 SUT0
10
11
00
01
10
11

Retardo de
arranque
14CK 1K CK
4.1ms + 14CK 1K CK
65ms + 14CK 1K CK
14CK 16K CK
4.1ms + 14CK 16K CK
65ms + 14CK 16K CK

Retardo de reset

Fuente de Oscilador, Condiciones de


alimentacin
XTAL baja frecuencia, BOD enabled
XTAL baja frecuencia, fast rising power
XTAL baja frecuencia, slowly rising power
XTAL, BOD enabled
XTAL, fast rising power
XTAL, slowly rising power

Cul elegir? Vemos que los ciclos de reloj son todos iguales a 16K CK. Entonces nuestra decisin depender
del retardo de reset requerido. Si nuestro circuito tiene una alimentacin Vcc que se eleva lentamente (slowly
rising power), debemos elegir un retardo de 65ms con SUT1-0 = 11.
Pero si en nuestro circuito el nivel de Vcc se eleva rpidamente (fast rising power), podemos optar por el
retardo de 4.1ms con SUT1-0 = 10. Por supuesto que tambin en este caso podemos optar por el retardo de
65ms.
Por ltimo, explicamos lo que significa XTAL, BOD enabled. BOD es un circuito interno que detecta cuando
la tensin de Vcc cae debajo de ciertos niveles. Tambin sirve para que el AVR no est trabajando con una
alimentacin defectuosa. As que configurar el retardo de 14 CK (lo mismo que nada) equivale a no utilizar

retardo de reset y solo debe utilizarse cuando nuestra aplicacin trabaje con un circuito BOD externo o
interno. El BOD interno del AVR se configura con los bits BODLEVEL2-0.

CKDIV8. Prescaler del Reloj del Sistema


Los ATmega recientes, como los tratados en esta clase, tienen un prescaler en el sistema de reloj, que dividir
la frecuencia del reloj cualquiera que sea su fuente (XTAL, RC Interno etc.).
El Prescaler puede dividir la frecuencia del reloj entre 256, entre 128, entre 64, hasta 1, dependiendo del
valor que se cargue en el Registro de E/S CLKPR, el cual se puede modificar en cualquier momento en
tiempo de ejecucin.
En este momento basta con saber que el bit CKDIV8 configura el registro CLKPR para que el reloj se divida
inicialmente entre 8 o entre 1.
CKDIV8 Factor del Prescaler del Reloj
0
8
1
1
El valor por defecto del bit CKDIV8 es 0 = programado = reloj dividido entre 8. Si lo dejamos as, nuestro
ATmega operar 8 veces ms lento. Es ms sencillo desmarcar esa casilla que escribir el cdigo que
reconfigura el Prescaler. A propsito, el Wizard de CodeVision AVR genera ese cdigo automticamente.

BODLEVEL2-0. Voltaje de Disparo del BOD


Es un reset por baja tensin. Esta caracterstica le permite al AVR auto resetearse cada vez que detecte una
cada de tensin en la alimentacin Vcc, por debajo de un nivel establecido por los bits BODLEVEL2,
BODLEVEL1 y BODLEVEL0.
En la figura subsiguiente la seal de RESET INTERNO se activa cuando el nivel de Vcc cae por debajo de
VBOT-. Luego para que el procesador del AVR vuelva a ejecutar el programa no solo bastar que Vcc supere
el valor de VBOT+, sino que transcurra el RETARDO DE RESET . La diferencia entre VBOT+ y VBOTconstituye el mecanismo de histresis.
El AVR tiene un filtro pasa-bajas interno para evadir el ruido y evitar un reset ante los microvalles de tensin
en Vcc. La cada de tensin tiene que ser mayor a 2us (valor tpico).
BODLEVEL2-0
111
110
101
100

Voltaje de disparo de BOD


(valor tpico)
BOD Desactivado
1.8
2.7
4.3

WDTON. Habilitacin del Watchdog


El Watchdog o WDT es un temporizador que puede monitorizar el funcionamiento fluido del
microcontrolador. El WDT lo estudiaremos al final porque no es imprescindible. De momento diremos que se
trata de un temporizador que una vez habilitado debemos resetear peridicamente en el programa. Si no lo
hacemos, causar un reset en el AVR y el programa se volver a ejecutar desde cero.
Cuando el WDT no est habilitado por su fuse WDTON (hardware) todava es posible activarlo por software.
Pero una vez activado por su fuse no habr rutina software que lo apague.
En la mayora de las prcticas no querremos estar preocupndonos del estado del WDT, as que la mejor
decisin ser tenerlo inhabilitarlo, que felizmente es el valor por defecto del bit WDTON, as que ni siquiera
tendramos que tocarlo.

CKOUT. Salida de Reloj


Cuando este fuse est programado la seal de reloj del sistema saldr por el pin CLKO de los ATmega (ver
ms abajo). Esta seal incluye el efecto del Prescaler de reloj.
Puede servir para sincronizar el microcontrolador con otros dispositivos de la aplicacin, pero se usa muy
raramente, as que lo habitual es dejar CKOUT con su valor por defecto 1, o sea sin programar. Si se
programa accidentalmente y el diseo utiliza el pin CLKO para una funcin de E/S, se puede producir un
corto circuito que dae el pin o hasta el ATmega completo.

OCDEN y JTAGEN. Depuracin OCD y JTAG


Con los mdulos OCD o JTAG habilitados, los ATmega que los tienen ponen en accin la circuitera interna
que monitoriza el estado del CPU, de los perifricos internos, de todos los registros del AVR y de las
memorias RAM, FLASH y EEPROM. Los resultados sern enviados a un ordenador a travs de la interface
conformada por los pines TMS, TCK, TDI y TDO (ver ms abajo). Del lado del ordenador estar corriendo un
programa como el Studio 5 en modo JTAGICE para recibir todos los datos e ir visualizndolos en la pantalla.
Tambin es posible enviar desde el ordenador comandos de ejecucin del programa como Step into, Step
over, etc. En otras palabras, es como correr el simulador del Studio 5 o VSM Proteus, pero esto ser real y a
veces en tiempo real. Adems la interface JTAG tambin permite reprogramar el AVR.
El valor predeterminado de OCDEN es desactivado pero el de JTAGEN es activado. Si no vamos a usar el
sistema OCD ni la interface JTAG debemos desactivarlos para que sus circuitos no consuman recursos del
sistema innecesariamente y para no renunciar a los pines TMS, TCK, TDI y TDO como puertos de E/S
generales.

DWEN. Habilitar Lnea de Depuracin


No todos los ATmega tienen un sistema de depuracin sofisticado con interface JTAG. Otros tienen un
sistema OCD con una interface sencilla conformada por una sola lnea, en este caso por el pin DW, que es el
mismsimo pin de RESET del ATmega, aunque normalmente no se grafica as en sus diagramas de pines.
Su configuracin predeterminada es deshabilitada y debemos dejarla as si no lo vamos a usar. De lo contrario
el circuito de monitoreo del OCD interno tambin estar funcionando activamente, consumiendo energa
innecesaria.

EESAVE. Preservar Contenido de la EEPROM


Cada vez que grabamos nuestro AVR con un nuevo programa se ejecutar previamente un borrado completo
de la memoria FLASH y de la EEPROM interna. Si queremos que la EEPROM no se borre en este proceso
debemos programar este fuse. Su configuracin predeterminada es sin programar, como se muestra abajo.

SPIEN. Habilitacin de Programacin Serial


Los ATmega tienen dos modos de programacin. Programacin paralela de alto voltaje (a 14 V) y
programacin serial de bajo voltaje (a 5 V). La programacin paralela siempre estar disponible pero la
programacin serial se puede deshabilitar mediante el fuse SPIEN. La modificacin del bit SPIEN solo es
posible desde la programacin paralela.
Los ATmega vienen con su programacin serial activada de fbrica, como se aprecia en la siguiente imagen.

BOOTSZ1-0. Tamao de la Seccin de Boot Loader


Este fuse configura el tamao que tendr la Seccin de Boot Loader, estudiada previamente. Si no vamos a
usar esta caracterstica del ATmega, no interesan los valores que tengan los bits BOOSZ1 y BOOTSZ0.

BOOTRST. Ubicacin del Vector de Reset


El Vector de Reset es la direccin de la memoria FLASH por donde se empezar a ejecutar el programa.
Normalmente es 0x0000 porque el cdigo del programa empieza a mapearse desde la primera direccin. La
nica situacin en que esto debe cambiar es cuando se usa un programa de Boot Loader.
Cuando el fuse BOOTRST est activado el vector de reset ser la primera direccin de la Seccin de Boot
Loader.
Como es de esperar, la configuracin por defecto de este fuse es sin programar, como se ve abajo, y no
debera modificarse a menos que se sepa bien lo que se hace.

RSTDISBL. Deshabilitar Reset Externo


Est disponible en muchos ATmega de 28 pines. Por defecto este fuse no est programado y el pin de RESET
cumple su funcin habitual de reiniciar la ejecucin del programa cuando se pone a nivel bajo.

Pero si programamos el bit RSTDISBL, el pin reset trabajar como PC6, o sea, como el sptimo pin de E/S
del puerto C. Yo no suelo programar este bit porque me gusta resetear el AVR a cada rato para asegurarme de
que programa siempre inicia bien.

SELFPRGEN. Habilitar Autoprogramacin

Este fuse solo est disponible en los ATmega que no dividen su memoria en secciones de Aplicacin y de
Boot Loader, es decir, en los AVR que no soportan el Boot Loader convencional. Con el fuse programado se
podr utilizar la instruccin de ensamblador SPM en cualquier parte del programa. En caso contrario SPM no
tendr efecto. Por defecto el fuse SELFPRGEN est sin programar.
Ya sea que se trabaje en C o en ensamblador, el programador normalmente sabe si va a acceder a la memoria
FLASH para escritura. En ese caso se deber activar este fuse. Y si no tienes idea de lo que estoy hablando,
casi te puedo asegurar que no interesa el valor que le des a este fuse y hasta te recomendara que lo actives.

Lock Bits o Bits de Candado


Last, not least. De hecho, los Bits de Candado son ms importantes que los fuses. Por eso aparecen en
primera posicin en los softwares de grabacin. Al menos los fuses se pueden reprogramar, en cambio los Bits
de Candado podran establecer una configuracin definitiva que deje al AVR inutilizable.
Todos los ATmega disponen de los Bits de candado generales LB1 y LB2 y, obviamente, los bits de candado
de Boot Loader solo estn presentes en los ATmega con soporte de Boot Loader.

Byte de
Bits de Lock

BLB12
BLB11
BLB02
BLB01
LB2
LB1

Bit
7
6
5
4
3
2
1
0

Descripcin

Boot Lock bit (Bit de candado de Boot Loader)


Boot Lock bit (Bit de candado de Boot Loader)
Boot Lock bit (Bit de candado de Boot Loader)
Boot Lock bit (Bit de candado de Boot Loader)
Lock bit (Bit de candado general)
Lock Bit (Bit de candado general)

Valor por defecto


1 (sin programar)
1 (sin programar)
1 (sin programar)
1 (sin programar)
1 (sin programar)
1 (sin programar)
1 (sin programar)
1 (sin programar)

Los bits de candado generales LB1 y LB2 establecen tres modos de proteccin que se describen en la tabla de
abajo. Debes tener especial cuidado con los modos 2 y 3. Si eliges el modo 3 tu AVR se quedar con su
programa actual y no podrs volver a programarlo. Con esta opcin tampoco se podr leer el cdigo de
programa del AVR desde un programador. Esto podra servir como una proteccin de cdigo, pero sin
marcha atrs.
En el modo 2 todava ser posible reprogramar el AVR, pero solo mediante la interface JTAG. Con esta
interface tambin se pueden programar los bits de fuses y de candado.
Modo de LB
1
2

LB2 LB1 Tipo de proteccin


11
No se habilita ninguna caracterstica de Candado.
Se deshabilitan las posteriores programaciones de las memorias FLASH y
10
EEPROM tanto en modo de programacin Serial y Paralela. Tambin se
bloquean los bits de los fuses.
Se deshabilitan las posteriores programaciones y verificaciones de las
memorias FLASH y EEPROM tanto en modo de programacin Serial, Paralela
00
y JTAG (si se tiene). Tambin se bloquean los bits de los Fuses y los bits de
candado de Boot Loader en los modos de programacin Serial y Paralela.

Para comprender el uso de los 4 bits de candado de Boot Loader debemos conocer primero la funcin de las
Secciones de Aplicacin y de Boot Loader, adems de las instrucciones de ensamblador LPM y SPM, que
participan activamente en la auto-programacin del AVR.
LPM (Load from Program Memory) sirve para leer un byte de dato de la memoria FLASH.
SPM (Store to Program Memory) sirve para escribir un byte de dato en la memoria FLASH.
Ambas instrucciones, LPM y SPM, trabajan con el puntero Z para direccionar la memoria FLASH.
Modo de BLB0
1
2

BLB02 BLB01 Tipo de proteccin


No habr restricciones para SPM o LPM en su acceso a la Seccin de
11
Aplicacin.
10
No se permite el uso de SPM para escribir en la Seccin de Aplicacin.
No se permite el uso de SPM para escribir en la Seccin de Aplicacin,
ni el uso de LPM para leer la Seccin de Aplicacin desde la Seccin
de Boot Loader.
00
Si los Vectores de Interrupcin estn ubicados en la Seccin de Boot
Loader, las interrupciones se deshabilitan durante la ejecucin desde la
Seccin de Aplicacin.

Modo de BLB0

Modo de BLB1
1
2

BLB02 BLB01 Tipo de proteccin


No se permite el uso de LPM para leer la Seccin de Aplicacin desde
la Seccin de Boot Loader.
01
Si los Vectores de Interrupcin estn ubicados en la Seccin de Boot
Loader, las interrupciones se deshabilitan durante la ejecucin desde la
Seccin de Aplicacin.
BLB12 BLB11 Tipo de proteccin
No habr restricciones para SPM o LPM en su acceso a la Seccin de
11
Boot Loader.
No se permite el uso de SPM para escribir en la Seccin de Boot
10
Loader.
No se permite el uso de SPM para escribir en la Seccin de Boot
Loader, ni el uso de LPM para leer la Seccin de Boot Loader desde la
Seccin de Aplicacin.
00
Si los Vectores de Interrupcin estn ubicados en la Seccin de
Aplicacin, las interrupciones se deshabilitan durante la ejecucin
desde la Seccin de Boot Loader.
No se permite el uso de LPM para leer la Seccin de Boot Loader
desde la Seccin de Aplicacin.
01
Si los Vectores de Interrupcin estn ubicados en la Seccin de
Aplicacin, las interrupciones se deshabilitan durante la ejecucin
desde la Seccin de Boot Loader.

El valor predeterminado de todos bits de candado es 1 sin programar. En los softwares de programacin
como WinPic800 significa que las casillas respectivas estn desmarcadas y as se deberan dejar. En otros
software como AVRFLASH se indican las configuraciones por sus modos (1, 2, 3 4) y siendo los modos 1
los predeterminados, tambin deberan quedar as, a menos que estemos seguros de lo que hacemos.

Entrada y Salida Generales

Los Puertos de los AVR


Los puertos se conforman por las lneas del microcontrolador donde se pueden conectar los dispositivos de
Entrada/Salida a controlar, por ejemplo LEDs, displays, transistores, otros ICs o, mediante rels u
optoacopladores, cargas de 110V/220V como medianos motores.
Los ATmegaNN4YY tienen 4 puertos, llamados PORTA, PORTB, PORTC y PORTD. Todos ellos estn
completos, as que disponen de 4 x 8 = 32 pines en total.
Los ATmegaNN8YY tienen 3 puertos, PORTB, PORTC y PORTD. En total suman 20 pines con capacidad de
ampliarse a 23 pines.
Los pines de los puertos tienen nombres compuestos, como PC4 ADC4/SDA/PCINT12. Los nombres
compuestos implican que tienen funciones multiplexadas. Por ejemplo, este pin PC4, adems de pin digital
convencional puede funcionar como el canal 4 del conversor analgico digital ADC4, como lnea serial de
datos SDA del mdulo TWI (I2C) o como pin de interrupcin por cambio de estado PCINT12.
Cuando los pines no son interface de algn perifrico, como el USART, el ADC, los Timers, etc., es decir,
cuando son manual y directamente controlados por sus registros PORTx, PINx y DDRx se dice que actan
como Entradas y Salidas Generales, y es a lo que nos dedicaremos en esta clase.
En principio todos los pines son bidireccionales cuando actan como lneas de Entrada y Salida Generales. La
direccin es configurable por software. Algunos pines pierden esa funcin cuando su control es asumido por
algn mdulo relacionado, por ejemplo, una vez configurado el puerto serie, el USART asumir el control de
los pines TXD y RXD.

Puertos de los ATmegaNN4YY.

Puertos de los ATmegaNN8YY.

Capacidades de Voltaje y Corriente

Cuando actan como salidas, los pines pueden entregar tensiones de hasta Vcc. Cuando actan como entradas
pueden manejar niveles de hasta 0.5V por encima de Vcc. El diseo de los pines incluye diodos internos de
sujecin que les permiten soportar tensiones mucho mayores que Vcc o inferiores que GND, siempre que la
corriente no sobrepase del orden de los micro Amperios.
Cada pin de E/S puede soportar picos de corriente de hasta 40 mA, pero en estado estable cada pin de puerto
puede suministrar o recibir hasta 20 mA de corriente cuando Vcc = 5V y hasta 10 mA cuando Vcc = 3V. Sin
embargo, esta capacidad no puede estar presente en todos los pines al mismo tiempo. En seguida tenemos los
lmites de corriente total que soportan los puertos en los ATmegaNN4YY:

La suma de las corrientes suministradas por lo pines PB0 - PB7, PD0 - PD7 y XTAL2 no debera
exceder los 100 mA.
La suma de las corrientes suministradas por los pines PA0 - PA3 y PC0 - PC7 no debera exceder los
100 mA.
La suma de las corrientes recibidas por los pines PB0 - PB7, PD0 - PD7 y XTAL2 no debera exceder
los 100 mA.
La suma de las corrientes recibidas por los pines PA0 - PA3 y PC0 - PC7 no debera exceder los 100
mA.

Para los ATmegaNN8YY la distribucin de lmites es un poco diferente. (Los pines ADC7 y ADC6 no estn
presentes en encapsulados DIP.)

La suma de las corrientes suministradas por lo pines PC0 - PC5, ADC7, ADC6 no debera exceder los
100 mA.
La suma de las corrientes suministradas por los pines PB0 - PB5, PD5 - PD7, XTAL1 y XTAL2 no
debera exceder los 100 mA.
La suma de las corrientes suministradas por los pines PD0 - PD4 y RESET no debera exceder los 100
mA.
La suma de las corrientes recibidas por los pines PC0 - PC5, PD0 - PD4, ADC7 y RESET no debera
exceder los 150 mA.
La suma de las corrientes recibidas por los pines PB0 - PB5, PD5 - PD7, ADC6, XTAL1 y XTAL2 no
debera exceder los 150 mA.

Las Resistencias de Pull-up


Una de las cualidades que distinguen a los microcontroladores de los microprocesadores es que encierran en
un solo chip todos los elementos posibles de un sistema de control. Con este fin los AVR incorporan en todos
sus puertos transistores a manera de fuente de corriente que en la prctica funcionan como resistencias de pullup.
Estas pull-ups nos pueden ahorrar el uso resistencias de sujecin externas en los pines de los puertos
configurados como entradas. Las pull-ups se podran equiparar con resistencias de entre 20 K y 50 K. a partir
de dichos valores podemos calcular la corriente que puede fluir por ellas si estn activadas.
Las pull-ups se pueden habilitar pin por pin independientemente escribiendo un 1 en su registro de salida
PORT. Las-pull ups solo sern efectivas en los pines que actan como entradas; en los pines configurados
como salidas las pull-ups quedan automticamente deshabilitadas.

Existe un bit llamado PUD en el registro MCUCR cuya funcin es deshabilitar todas las pull-ups de todos los
puertos si su valor es 1. El bit PUD (Pull-Ups Disable) inicializa a 0 y un posible inters por setearlo puede ser
eliminar la pequea corriente que puede fluir por las pull-ps cuando los pines en cuestin se conectan a 0
lgico.
La siguiente figura muestra la conexin de un pulsador al AVR aprovechando la pull-up de un pin de E/S.
Fjate en que las pull-ups no se pueden usar como resistencias para excitar dispositivos como LEDs, rels, etc.

Ejemplo de uso de las resistencias de pull-up.


La figura de ejemplo muestra la pull-up de un solo pin pero estn presentes en todos los pines de E/S del
AVR.

Configuracin y Manejo de los Puertos


Cuando los pines trabajan como entradas y salidas generales su control descansa principalmente en los
Registros de E/S MCUCR, DDRx, PORTx y PINx, donde x puede ser A, B, C o D.
Del registro MCUCR solo interviene el pin PUD, cuya funcin es deshabilitar las pull-ups de todos los pines
cuando PUD = 1. Pero si PUD = 0, la habilitacin de las pull-ups todava requiere de cierta configuracin por
parte de los registros DDRx, PORTx y PINx.
MCUCRJTDBODSBODSEPUD------IVSELIVCE
Cada puerto tiene sus correspondientes registros DDRx, PORTx y PINx, as por ejemplo el puerto A tiene sus
registros DDRA, PORTA y PINA. Lo mismo es aplicable a los otros puertos.
PORTAPORTA7PORTA6PORTA5PORTA4PORTA3PORTA2PORTA1PORTA0
PINAPINA7PINA6PINA5PINA4PINA3PINA2PINA1PINA0
DDRADDA7DDA6DDA5DDA4DDA3DDA2DDA1DDA0

El registro PORTx es para escribir datos en los pines del puerto x que estn configurados como salida.
se es su uso habitual. Sin embargo, escribir en los bits de PORTx cuyos pines estn configurados
como entradas significa activar o desactivar las pull-ups de dichos pines (ver la tabla de abajo). sta es
la segunda funcin de PORTx. Leer el registro PORTx solo significa obtener el ltimo valor que se
escribi en este registro.
El registro PINx es para leer el estado de los pines del puerto x, sin importar si los pines estn
configurados como entradas o como salidas. La funcin alternativa de este registro es para conmutar el
estado de los pines configurados como salidas cuando se escribe un 1 en su bit correspondiente de
PINx.
El registro DDRx es para configurar la direccin del puerto x, es decir, para establecer cules pines
sern entradas y cules sern salidas. (Data Direction Register = Registro de Direccin de Datos).
Despus de un reset todos los puertos inician con sus pines configurados como entradas, pero se
pueden reconfigurar en cualquier punto del programa.

Si se escribe un 0 en un bit de DDRx, entonces el pin correspondiente en el puerto x ser de entrada y si se


escribe un 1, el pin ser de salida. Detesto mencionar a los PICmicro, pero creo que te puede servir saber que
la implicancia del 1 y el 0 en los PICmicros es al revs.

0 entrada
1 salida

Por ejemplo, si escribimos el valor 11110000 en DDRB, entonces los cuatro pines de menor peso del puerto B
sern entradas digitales y los cuatro pines superiores sern de salida.
Si escribimos 11111111 en DDRA, todos los pines del puerto A sern de salida, y si escribimos 00000000 en
DDRB todo el puerto B ser de entrada. La codificacin de lo expuesto sera as:
DDRA = 0xFF; // 0xFF = 0b11111111 DDRB = 0x00; // 0x00 = 0b00000000
Luego podremos leer y escribir en los puertos mediante PORTA y PINB, as.
unsigned char regval;
PORTA = 0x73; // Escribir 0b01110011 en el puerto A
regval = PINB; // Leer puerto B

Hasta aqu estuvo todo muy fcil porque los puertos completos estaban configurados para entrada o salida.
Ahora veremos casos de configuracin mixta y lo que sucede, por ejemplo, si escribimos en PINx o si leemos
de PORTx. Si adems trabajamos con el pin PUD para habilitar las resistencias de pull-up, tendremos que
valernos de una tabla para no enredarnos.
Caso
1
2
3
4
5

Bit en
DDRx
0
0
0
1
1

Bit en
PORTx
0
1
1
0
1

Bit PUD
(en MCUCR)
X
0
1
X
X

Direccin de
Pin
Entrada
Entrada
Entrada
Salida
Salida

Pull-up

Estado de Pin

No
S
No
No
No

Tri-Estado (Alta impedancia)


Alto, debido a la pull-up.
Tri-Estado (Alta impedancia)
Bajo (0 lgico)
Alto (1 lgico)

Casos 4 y 5. Son los ms simples. Si un pin est configurado como salida, de ningn modo tendr pullup y su estado de salida ser 1 0 lgico, dependiendo del valor escrito en su respectivo bit de
PORTx.

Caso 1. Si un pin est configurado como entrada y su bit respectivo en PORTx vale 0, el pin tampoco
tendr pull-up y su estado ser de alta impedancia.
Caso 2. Si un pin est configurado como entrada y su bit respectivo en PORTx vale 1, el pin tendr
pull-up y su estado se leer como 1 lgico (por la pull-up), siempre que el bit PUD del registro
MCUCR valga 0.
Caso 3. Raramente se suele poner PUD a 1, pero si se hace, se deshabilitarn las pull-ups de todos los
pines. Por tanto los pines de entrada sern siempre de alta impedancia.

Ms adelante est una prctica de interface con teclado matricial donde se utiliza.
La notacin de nmeros binarios empleando el prefijo 0b no es parte del C Estndar y aunque AVR GCC la
admita, IAR C no la reconoce. Por tanto solo es recomendable el uso de la notacin hexadecimal.

Control de Dispositivos Bsicos


Secuenciador de 3 Efectos
Este programa est inspirado en el led flasher 2 de Seiichi Inoue y lo puedes ubicar en el sitio web
http://www.piclist.com/images/www/hobby_elec/e_pic6_b.htm. Estos son los tres efectos que se pueden
escoger.

Cada efecto se podr seleccionar ingresando su correspondiente nmero mediante el teclado.


El reto es que el primer LED alumbra a su plenitud, el segundo LED, un poquito menos y el tercero, como la
colita de un cometa, brilla menos an.
Cmo conseguiremos variar la intensidad de brillo de un LED? Regulando la cantidad de corriente que fluye
por l. Y cmo variamos su corriente sin emplear un potencimetro o algo por el estilo? Bueno, las dos
formas ms habituales se conocen como modulaciones PWM y PRM, a la segunda de las cuales se apega la
onda generada por el patrn de efecto de nuestro secuencial. PRM es la sigla de Modulacin por Frecuencia
de Pulsos, en ingls.

Ondas de corriente en los LEDs y su valor promedio.

Dado que los LEDs que parpadean dan miles de centelleos por segundo, nuestros ojos no lo podrn percibir
as, solo veremos una disminucin en su luminosidad.
Es el valor promedio de la corriente lo que cuenta para el brillo del LED. No obstante, tampoco hay una
proporcin directa entre estos dos parmetros. As un LED aparezca prendido la sexta parte del tiempo que
otro, no significa que vaya a brillar 6 veces menos. Por otro lado, este hecho es aprovechable por otras
aplicaciones como el control de varios displays de 7 segmentos o los letreros matriciales de LEDs, que
veremos ms adelante.

Circuito de la prctica.
Para generar los efectos de desplazamiento de los LEDs y las ondas PRM de cada LED se emplea una matriz
llamada Pattern. Es una gran matriz pero el software emplea ndices relativos para dividirla en varios bloques.
La separacin de los bloques en tres grupos, uno para cada efecto, tambin es una divisin lgica.
Cada uno de los bloques de la matriz Pattern es una posicin en el desplazamiento de los LEDs. Por ejemplo,
en la posicin 2 del primer efecto se ejecutar el bloque 2 por 100 veces cclicamente. El bloque 2 es el
tercero porque en el mundo digital se empieza desde 0 y su patrn es el conjunto de datos

0xe7,0x24,0x24,0x66,0x24,0x24. No dice mucho porque est en hexadecimal, pero si lo pasamos a binario,


que es como originalmente lo elabor, y lo ordenamos de arriba abajo, tenemos esto
11100111
00100100
00100100
01100110
00100100
00100100
Como se ve, este bloque indica que en este lapso los bits 2 y 5 siempre estarn activos, los bits 1 y 6 se
prenden en dos de seis partes, y los bits 0 y 7 solo se prenden la sexta parte del tiempo. Si no tenemos un
osciloscopio de 8 canales, una grfica de Proteus nos puede mostrar las ondas correspondientes. En la seccin
Grficos de Simulacin de la clase de Proteus se describe paso a paso cmo obtener este resultado. La nica
diferencia es que en esta ocasin la grfica es de tipo DIGITAL en vez de ANALGICO.

/******************************************************************************
* FileName:
main.c
* Purpose:
LED Flasher 3. Secuenciador de 3 efectos seleccionables va USART
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
Basado en el led flasher 2 de Seiichi Inoue localizado en
*
http://hobby_elec.piclist.com/index.htm.
*

* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.


*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"
/******************************************************************************
Patrn que contiene el patrn de efecto del secuencial
led1
111111111111111111111111111111...
Prendido 6 de 6 partes
led2
100100100100100100100100100100...
Prendido 2 de 6 partes
led3
100000100000100000100000100000...
Prendido 1 de 6 partes
- Cada bloque tiene 6 items
- Cada bloque se repite 100 veces
- Hay una pausa de 150 s entre los tems
- Hay 12/11 bloques en total
-> Cada barrido dura 6 * 100 * 150 * 12 = 1.08 segundos aprox.
******************************************************************************/
PROGMEM char Pattern[] =
{
// Efecto 1. 12 bloques de 6 items
0x81,0x81,0x81,0x81,0x81,0x81, 0xc3,0x42,0x42,0xc3,0x42,0x42,
0xe7,0x24,0x24,0x66,0x24,0x24, 0x7e,0x18,0x18,0x3c,0x18,0x18,
0x3c,0x18,0x18,0x18,0x18,0x18, 0x18,0x18,0x18,0x18,0x18,0x18,
0x3c,0x24,0x24,0x3c,0x24,0x24, 0x7e,0x42,0x42,0x66,0x42,0x42,
0xe7,0x81,0x81,0xc3,0x81,0x81, 0xc3,0x00,0x00,0x81,0x00,0x00,
0x81,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,
// Efecto 2. 11 bloques de 6 items
0x80,0x80,0x80,0x80,0x80,0x80, 0xc0,0x40,0x40,0xc0,0x40,0x40,
0xe0,0x20,0x20,0x60,0x20,0x20, 0x70,0x10,0x10,0x30,0x10,0x10,
0x38,0x08,0x08,0x18,0x08,0x08, 0x1c,0x04,0x04,0x0c,0x04,0x04,
0x0e,0x02,0x02,0x06,0x02,0x02, 0x07,0x01,0x01,0x03,0x01,0x01,
0x03,0x00,0x00,0x01,0x00,0x00, 0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,
// Efecto 3. 11 bloques de 6 items
0x01,0x01,0x01,0x01,0x01,0x01, 0x03,0x02,0x02,0x03,0x02,0x02,
0x07,0x04,0x04,0x06,0x04,0x04, 0x0e,0x08,0x08,0x0c,0x08,0x08,
0x1c,0x10,0x10,0x18,0x10,0x10, 0x38,0x20,0x20,0x30,0x20,0x20,
0x70,0x40,0x40,0x30,0x40,0x40, 0xe0,0x80,0x80,0xc0,0x80,0x80,
0xc0,0x00,0x00,0x80,0x00,0x00, 0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00
};
/*****************************************************************************
* Main function
****************************************************************************/
int main(void)
{
unsigned int i, j, k, b, bi, ba;
char c, Effect;
DDRB = 0xFF;

// Setup PORTB for output

usart_init();

// Initialize USART @ 9600 N 1

puts("\n\r Escoja un efecto ");


puts("\n\r (1) Barrido simtrico");

puts("\n\r (2) Barrido a la derecha");


puts("\n\r (3) Barrido a la izquierda");
Effect = '1';

// Por defecto iniciar con efecto 1

while(1)
{
start:
/* Establecer parmetros de barrido del efecto actual */
switch(Effect)
{
case '1': ba = 0; b=12; break;
case '2': ba = 6*12; b=11; break;
case '3': ba = 6*(12+11); b=11; break;
}
/* Iniciar barrido del efecto actual */
for(i=0; i<b; i++)
// Para barrer b bloques
{
for(j=0; j<100; j++)
// Cada bloque se repite 100 veces
{
bi = ba + 6*i;
for(k=0; k<6; k++)
// Cada bloque tiene 6 items
{
// PORTB = Pattern[bi+k];
PORTB = pgm_read_byte(&(Pattern[bi+k]));
/* Este bloque sondea el puerto
* opcin vlida para cambiar de
*/
if(kbhit())
//
{
c = getchar();
//
if((c<='3') && (c>='1')) //
{
Effect = c;
//
goto start;
//
}
}
delay_us(150);

serie esperando recibir una


efecto
Si hay datos en el USART,..
Leer dato
Si es una opcin vlida,...
Tomar opcin
y reiniciar

}
}
}
}
}

Delays Antirrebote
Cuando apretamos un pulsador o movemos un switch, la seal de tensin relacionada no cambiar su valor
establemente, sino que se darn pequeos rebotes en el contacto que generarn ondas irregulares. A veces se
puede implementar un sencillo filtro pasa-bajas para evadir estos rebotes. Como este circuito puede resultar
algo incmodo de armar, casi siempre se prefiere aadir una rutina antirrebote en el programa.

Sencillo filtro antirrebote para un pulsador.


De los tantos mecanismos realizables poner un delay es el ms simple. Una vez detectado el cambio de
tensin se espera un lapso de tiempo hasta que la seal se estabilice y luego se vuelve testear la entrada. Una
variante es, luego de responder al primer pulso esperar un tiempo para que se estabilice la seal.
En cuanto al enunciado del programa: cada vez que presionemos un botn (pulsador) se prender un LED ms
del puerto B y con otro botn se har lo mismo para apagar un LED ms. De modo que aprovecharemos el
circuito de la prctica anterior. Para comprobar la utilidad de la aparente irrelevante rutina antirrebote, puedes
probar lo que sucede si le quitas los Delays al programa.

Circuito de la prctica.

/******************************************************************************
* FileName:
main.c
* Purpose:
Delays antirrebote
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
int main(void)
{
char port = 0;
DDRD = 0x00;
PORTD = 0x0C;

// PORTD como entrada


// Activar pull-ups de pines PD2 y PD3

DDRB = 0xFF;
PORTB = 0x00;

// PORTB como salida


// Limpiar PORTB

while(1)
// Bucle infinito
{
if((PIND & 0x04)==0)
// Si PD2 vale 0 (pulsador presionado)
{
port <<= 1;
// Desplazar port a la izquierda
port |= 0x01;
// Setear bit 0 de port
PORTB = port;
// Colocar en PORTB
delay_us(30000);
// Delay antirrebote, 30ms
while((PIND & 0x04)==0)
// Mientras PD2 valga 0, esperar
continue;
}
if((PIND & 0x08)==0)
// Si
{
port >>= 1;
port &= 0x7F;
PORTB = port;
delay_us(30000);
while((PIND & 0x08)==0)
continue;
}

PD3 vale 0 (pulsador presionado)


//
//
//
//
//

Desplazar port a la derecha


Limpiar bit 7 de port
Colocar en PORTB
Delay antirrebote, 30ms
Mientras PD3 valga 0, esperar

}
}

Control de Displays 7 segmentos


Un display 7 segmentos no es ms que una matriz de 7 diodos LEDs dispuestos de forma que encendindolos
apropiadamente se pueden formar los nmeros del 0 al 9 y algunas letras del alfabeto. Se dividen en dos
grupos: de nodo comn y de ctodo comn. El que vamos a emplear en esta prctica pertenece al segundo
grupo y se muestra abajo.

Display 7 segmentos de ctodo comn.


Como ves arriba, cada LED del display est identificado por una letra entre a y g. Algunos displays tambin
cuentan con un led en la parte inferior llamado dp (decimal point), pensado para representar un punto decimal.
Yendo a la prctica. Este programa controla 4 displays de 7 segmentos multiplexados. Esta tcnica es muy
conocida y consiste en prender los displays uno a uno en tiempos sucesivos pero con la suficiente frecuencia
como para que parezcan que estn prendidos los cuatro al mismo tiempo.
Los datos que visualizarn los displays sern ingresados por el puerto serie de la PC.

Circuito de la prctica.
En el programa se emplea el array bcdcodes para almacenar los cdigos 7 segmentos de los nmeros del 0 al
9.
// Matriz de cdigos hexadeximales. Formato: xgfedcba , x = don't care
const char bcdcodes[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

Aqu tampoco se distingue mucho por estar en hexadecimal, pero convertido en binario y ordenado de arriba
abajo lo notaremos mejor.
Hexa
----0x3f
0x06
0x5b
0x4f

=
=
=
=

binario
----------00111111
00000110
01011011
01001111

//
//
//
//

7-segment
7-segment
7-segment
7-segment

code
code
code
code

of
of
of
of

0
1
2
3

0x66
0x6d
0x7d
0x07
0x07
0x7f

=
=
=
=
=
=

01100110
01101101
01111101
00000111
01111111
01101111

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

7-segment
7-segment
7-segment
7-segment
7-segment
7-segment

code
code
code
code
code
code

of
of
of
of
of
of

4
5
6
7
8
9

De acuerdo con el circuito, el display est conectado a los 7 primeros pines de PORTB del AVR (a con 0, b
con 1... y g con 6). El pin 7 queda libre, por eso el bit 7 de cada dato no interesa, dont care.
De acuerdo con el circuito, el display est conectado a los 7 primeros pines de PORTB del AVR (a con 0, b
con 1... y g con 6). El pin 7 queda libre, por eso el bit 7 de cada dato no interesa, dont care.
/******************************************************************************
* FileName:
main.c
* Overview:
Control de 4 displays 7 segmentos con interface USART
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"
char GetNumStr(char * buffer, unsigned char len);

// Prototipo de funcin

int main(void)
{
// Matriz de cdigos hexadeximales. Formato: xgfedcba , x = don't care
const char bcdcodes[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned int d, digit;
signed char i;
char buff[10] = "1234";
char buff2[10];
DDRB = 0xFF;
DDRD = 0xF0;

// Iniciar con 1234


// Todo PORTB para salida
// Nible alto de PORTD para salida

usart_init();
puts("\r\n Escribe un nmero para el display 7 segmentos \r\n");
while(1)
{
for(d=0; d<4; d++)
// Para pasar por los 4 displays
{
if(GetNumStr(buff2, 4))
// Si se ley una cadena de 4 nmeroa
strcpy(buff, buff2);
// Actualizar cadena para visualizar
i = strlen(buff)-d-1;
if(i >= 0)
digit = buff[i];
else
digit = '0';

// Calcular posicin de dgito


// Obtener dgito
// Rellenar con ceros a la izquierda

PORTD = 0xf0;
PORTB = bcdcodes[digit-'0'];
PORTD = PORTD & (~(0x10<<d));
delay_us(100);

// Apagar displays temporalmente


// Poner nuevo dato en display
// Activar nuevo display

}
}
}
//****************************************************************************
// Lee una cadena de texto de 'len' nmeros
// El tamao de 'buffer' debe ser mayor que 'len'.
//****************************************************************************
char GetNumStr(char * buffer, unsigned char len)
{
char c;
static unsigned char i=0;
if(kbhit())
{
c = getchar();
if((c<='9'&&c>='0')&&(i<len))
// Si c est entre 0 y 9
{
buffer[i++] = c;
// Guardar en buffer
putchar(c);
// Eco
}
else if((c=='\b')&&(i))
// Si c es RETROCESO y si i>0
{
i--;
//
putchar(c);
// Eco
}
else if((c=='\r')&&(i))
// Si c es ENTER y si i>0
{
buffer[i] = '\0';
// Poner un 0x00 (fin de cadena)
putchar(c);
// Eco
i = 0;
// Resetear contador
return 1;
// Retornar con 1
}
}
return 0;
// Retornar con 0
}

Retraso y Frecuencia de Repeticin


Antes, en los tiempos analgicos, gran parte de la interface de usuario de los aparatos electrnicos implicaba
la presencia de algn tipo de potencimetros y switches. Actualmente, una caracterstica de los sistemas
digitales es su manipulacin basada en botones. Un botn para esto, otro para aquello... Inclusive a un mnimo
de ellos se le puede dotar de mltiples funciones para el completo control de algn proceso.
El programa de esta prctica implementa el concepto que tantas veces leemos como retraso de repeticin frecuencia de repeticin. De hecho, podemos encontrar este mecanismo en casi cualquier artefacto digital.
Aprovechando que ya tenemos armado nuestro circuito de 4 displays 7 segmentos, haremos un contador que
se incremente cada vez que se pulse un botn y se decremente cuando se pulse otro. Si un botn permanece
apretado por ms de cierto tiempo (retraso de repeticin), se iniciar un incremento/decremento continuo,
segn la razn establecida (frecuencia de repeticin).

Pero aqu no termina la cosa. Con criterio de buen diseador deberamos prever lo que pasara si alguien
presionara un botn sin antes haber soltado el anterior. Eso depender de la aplicacin. En este programa yo
decid dejar sin efecto al segundo botn mientras el primero est pulsado. Est de ms decir que los rebotes
deben ser filtrados.

Circuito de la prctica.
La tarea de la funcin CheckButtons la puedes leer en su respectivo encabezado. All dice que si se llama
peridicamente cada 100us el retraso de repeticin ser de 600ms y la frecuencia de repeticin de 1/100ms =
10 incrementos/decrementos por segundo. Estos parmetros se pueden modificar editando los lmites de
conteo de las variables a y b.
Tras ser detectada la seal negativa (botn pulsado), el programa seguir testeando el pin continuamente
durante 25 ms. Solo se reconocer como vlido el pulso que permaneci estable en este lapso. Eso servir para
filtrar los pequeos pulsos de rebote.

Una caracterstica notable de este mecanismo, comparable solo con las interrupciones, es que permite atender
a los botones sin necesidad de que el programa se trabe esperando a que se pulsen o liberen.
Sin embargo, debo aclarar que el tiempo de los 100us no se cumple estrictamente y puede variar bastante de
acuerdo con la aplicacin. En esta prctica ni se nota, pero si realmente llegara a interesar se puede ajustar o
superar con las interrupciones de algn timer.
/******************************************************************************
* FileName:
main.c
* Purpose:
Retraso de repeticin y velocidad de repeticin
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
/* Prototipos de funcin */
void CheckButtons(void);
void IncCNT(void);
void DecCNT(void);
/* Definiciones */
#define MaxCNT
5000
#define ButtonI
( PIND & (1<<2) )
#define ButtonD
( PIND & (1<<3) )
/* Variables globales */
volatile unsigned int CNT;

// Mximo Valor de CNT (arbitrario)


// PD2 Botn de incremento
// PD3 Botn de decremento

// Contador

/*****************************************************************************
* Main function
****************************************************************************/
int main(void)
{
// Matriz de cdigos hexadeximales. Formato: xgfedcba , x = don't care
const char bcdcodes[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
unsigned int i, j, digit;
signed char idx;
char buff[10];
DDRB = 0x7F;
PORTB = 0x80;
DDRD = 0xF0;
PORTD = 0x0C;

//
//
//
//

Los 7 bits altos de PORTB para salida


0 a pines de salida, sin Pull-up en pin de entrada
Nible alto de PORTD para salida
Activar Pull-ups de pines PD2 y PD3

CNT = 123;
while(1)
{
for(j=0; j<100; j++)
{
for(i=0; i<4; i++)

// Inicializar contador

// Para pasar por los 4 displays

{
CheckButtons();
itoa(CNT, buff, 10);
idx = strlen(buff)-i-1;
if(idx >= 0)
digit = buff[idx];
else
digit = '0';
PORTD |= 0xf0;
PORTB = bcdcodes[digit-'0'];
PORTD &= (~(0x10<<i));

// Convertir nmero en cadena


// Calcular posicin de carcter
// Obtener carcter
//
//
//
//

Poner 0 en displays sin nmero


Apagar displays temporalmente
Poner nuevo dato en display
Activar nuevo display

}
}
}
}
/*****************************************************************************
* Mide el tiempo que los botones I y D estn pulsados. Si dicho tiempo
* llega a 25ms llamar a IncCNT (o DecCNT) y luego de 600ms la llamar
* continuamente tras cada 100ms. Un botn no responder mientras el
* anterior no sea liberado.
* Para cumplir los tiempos citados debera ser llamada cada 100us.
****************************************************************************/
void CheckButtons(void)
{
static unsigned int a;
// Cuenta el tiempo que ButtonI est pulsado
static unsigned int b;
// Cuenta el tiempo que ButtonD est pulsado
if((ButtonI==0)&&(b==0)) // Si ButtonI pulsado y ButtonD libre
{
a++;
// Incrementar contador
if(a==250)
// Si lleg a 25 ms
{
IncCNT();
// Procesar por primera vez
}
else if (a>6250)
// Si pasaron ms de 600 ms
{
IncCNT();
// Procesar por segunda y dems veces
a -= 1000;
// Siguientes incrementos sern cada 100 ms
}
}
else
a=0;
// Resetear contador
if((ButtonD==0)&&(a==0)) // Si ButtonD pulsado y ButtonI libre
{
b++;
// Incrementar contador
if(b==250)
// Si lleg a 25 ms
{
DecCNT();
// Procesar por primera vez
}
else if (b>6250)
// Si pasaron ms de 600 ms
{
DecCNT();
// Procesar por segunda y dems veces
b -= 1000;
// Siguientes incrementos sern cada 100 ms
}
}
else
b=0;
// Resetear contador
}

/*****************************************************************************
* Incrementan o decrementan CNT.
* El valor de CNT est limitado al rango [0 : MaxCNT]
****************************************************************************/
void IncCNT(void)
{
if(CNT<MaxCNT)
// Si CNT < MaxCNT
CNT++;
// Incrementar CNT
}
void DecCNT(void)
{
if(CNT>0)
CNT--;
}

// Si Duty > 0
// Decrementar CNT

Control de Motor Paso a Paso


A diferencia de un motor DC, que solo tiene una bobina y que empieza a girar apenas se le conecta la
alimentacin, con una velocidad que vara de acuerdo con el voltaje aplicado; los motores de pasos tienen
cuatro bobinas y avanzan o retroceden solo un pequeo ngulo de giro, llamado ngulo de paso, por cada
combinacin de voltaje aplicada en sus boninas. Para mantener la marcha del motor es necesario cambiar
peridicamente la combinacin de voltajes en sus terminales.
Con 4 bobinas un motor PAP (Paso A Paso) puede presentar hasta 8 terminales, 2 por cada bobina. Los
terminales se pueden unir interna o externamente dando lugar a dos tipos de motores PAP: los unipolares y los
bipolares. Puedes ver la siguiente figura para identificar el origen de tu motor PAP. Observa que si tienes un
motor unipolar de 8 6 terminales, lo puedes convertir en un motor bipolar; pero no es posible arreglar un
motor bipolar para que trabaje como unipolar, a menos que separes sus bobinas internamente.

Motores de pasos Unipolares y Bipolares.


Aunque tengan ms terminales, los motores PAP Unipolares son ms fciles de manejar por el hardware
requerido. En estos motores las bobinas se polarizan en una sola direccin, por lo que basta con un switch o
transistor por cada bobina para energizarla.
En cambio en los motores PAP bipolares cada bobina debe polarizarse en ambas direcciones. Esto es
equivalente a hacer girar un motor DC en ambas direcciones; y t sabes que para eso se requiere de un circuito
llamado Puente-H.
Para controlar un motor PAP Unipolar las bobinas deben ser polarizadas secuencialmente de acuerdo con la
orientacin que se le quiera dar al rotor. Para comprender esto debes recordar la ley de atraccin y repulsin
entre polos magnticos, as como la ley de Lens, que explica la orientacin del campo magntico generado por
la corriente que fluye por una bobina.
En el motor PAP unipolar no deben polarizarse las 4 bobinas al mismo tiempo porque generaran un campo
magntico simtrico y el rotor no sabra a dnde girar. A partir de esto se deduce que existen hasta tres modos
de hacer girar un motor PAP unipolar, pero los dos principales son:

Modo Full Step o de paso completo. Es cuando las bobinas se polarizan de dos en dos. En la siguiente
animacin los terminales medios estn unidos interna o externamente al crculo que representa la
alimentacin positiva. Entonces para polarizar las bobinas se ponen a nivel bajo (azul) sus terminales

largos. He resaltado en amarillo cada bobina polarizada. Eso ayuda a distinguir la secuencia de
polarizacin y a descubrir que no existen ms que 4 combinaciones de polarizacin aplicables, las
cuales deben repetirse cclicamente.
Los nmeros hexadecimales al lado de la tabla se obtienen asumiendo que los puntos azules son ceros
y los rojos unos. De hecho esa suposicin no importa mucho, como tampoco importa que los
terminales medios estn conectados a la alimentacin positiva o a GND, ni que la secuencia de pasos
inicie en 0x09. Puedes invertir las polarizaciones y al final te sorprender que el motor siga girando en
la misma direccin. Lo nico que har que cambie de direccin es que reciba la secuencia de pasos en
orden invertido.

Operacin de un motor de pasos unipolar en modo Full step.

Modo Half Step o de paso medio. Es cuando las bobinas se polarizan de a una y de a dos
intercaladamente. Si te fijas bien en la tabla de pasos, vers que tambin incluye los 4 pasos del modo
full step. Obviamente esos son los momentos en que hay dos bobinas polarizadas, en los otros 4 pasos
solo se polariza una bobina. La ventaja de este mecanismo respecto del modo Full step es que se
pueden realizar movimientos de giro ms finos. La desventaja es que puede disminuir el torque, o
fuerza de giro del rotor.

Operacin de un motor de pasos unipolar en modo Half step.

El programa de esta prctica soporta ambos modos de control, los cuales se establecen desde la consola
mediante la tecla *. Por cada vez que se pulse la tecla + el motor avanzar 1 medio paso, dependiendo
del modo actual seleccionado y por cada vez que se pulse la tecla - el motor retroceder 1 medio paso.

Circuito de la prctica.
Puesto que la tabla de pasos del modo Half Step tambin incluye los pasos del modo Full Step, no fue
necesario crear dos tablas separadas. Los 8 pasos del motor se encuentran en el array steps.
/* Matriz de pasos */
const char steps[] = {0x09,0x0D,0x0C,0x0E,0x06,0x07,0x03,0x0B};

/******************************************************************************
* FileName:
main.c
* Overview:
Control de motor PAP en modos Full step y Half step, va USART
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*

* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"
int main(void)
{
/* Matriz de pasos */
const char steps[] = {0x09,0x0D,0x0C,0x0E,0x06,0x07,0x03,0x0B};
signed char step;
char modo, c;

// Marca el paso actual

DDRB = 0x0F;
PORTB = 0x00;

// Configurar Nibble bajo para salida


// Limpiar nibble bajo. Sin Pull-ups en nibble alto

step = 1;

// Iniciar en cualquier paso

modo = 0xff;

// Esto ser Full step (0x00 para half step)

usart_init();

// Inicializar USART0 @ 9600 N 1

puts("\r\n
puts("\r\n
puts("\r\n
puts("\r\n

Control de Motor PAP en Full step y Half step\r\n");


(*): Conmutar entre Full step y Half step");
(+): Avanzar 1 o 1/2 paso");
(-): Retroceder 1 o 1/2 paso");

while(1)
// bucle infinito
{
if(kbhit())
// Esperar a que llegue un dato del puerto serie
{
c = getchar();
// Leer dato recibido
switch(c)
{
case '*': modo = ~modo;
// Conmutar modo
if (modo)
puts("\r\n Full step");
else
puts("\r\n Half step");
break;
case '+':

step++;
if( modo && (step&0x01) )

// Medio paso adelante


// En modo Full step y si es

step++;
if(step>7)
step = 0;
PORTB = steps[step];
break;

//

step--;
if( modo && (step&0x01) )

// Medio paso atrs


// En modo Full step y si es

necesario...

case '-':

... medio paso ms

// Dar la vuelta
// Ejecutar el paso

necesario...
step--;
if(step<0)
{
step = 7;

//

... medio paso ms

// Dar la vuelta

if(modo)
step--;
}
PORTB = steps[step];
break;

// En full step empezar desde 6


// Ejecutar el paso

}
}
}
}

Control de Teclado Matricial


Un teclado matricial es un conjunto de botones (switches) dispuestos en forma de malla, de modo que no se
requieran de muchas lneas para su interface. De hecho, la mayora de los teclados (incluyendo quiz el de tu
ordenador) funciona con una estructura similar. En esta prctica trabajaremos con un teclado de 44.
Como se aprecia en la siguiente imagen, cada botn del teclado est conectado a alguna de las filas Row, por
un lado; y por el otro, a alguna de las columnas Col.

Aspecto fsico y estructura interna de un teclado.


La siguiente figura esboza la conexin entre un microcontrolador y un teclado de 44. Obviamente, no se
puede leer el estado de una tecla como un pulsador cualquiera. Pero es fcil darse cuenta de que una tecla
pulsada establece la conexin entre una de las filas Row y una de las columnas Col.

Conexin de un teclado a un microcontrolador.


Por ejemplo, al presionar la tecla 6 se unen las lneas Row 1 y Col 2. O sea, si sacamos un 1 ( 0) por el pin
de Row 1, tambin deberamos leer un 1 ( 0) en el pin de Col 2, o viceversa. Generalizando, solo hay un par
Row-Col que identifica cada tecla.
En consecuencia, para saber cul fue la tecla pulsada debemos sondear una a una todas las combinaciones
Row-Col. Una vez detectada la condicin de circuito cerrado, se usa el par Row-Col para deducir la posicin
de la tecla pulsada.
Luego de expuesta la relativa sencillez de este teclado podemos sentirnos ansiosos empezar a codificar el
programa de control. Solo hay que poner especial cuidado en la direccin de los pines y su conexin. Un
mnimo descuido causara un cortocircuito que daara el AVR.

La funcionalidad del programa no puede ser ms sencilla. El valor de cada tecla pulsada ser enviado a la
consola de puerto serie de la PC.

Circuito de la prctica.
La funcin de bajo nivel para el teclado es keypad_scan. En el interior de esta funcin el nibble bajo de
PORTB se configura como salida y el nibble alto, como entrada, con sus correspondientes pull-ups. Al salir,
todo PORTB queda como entrada para facilitar su posible posterior uso para otras rutinas.
Segn mi cdigo, el valor ledo en las columnas cuando no hay teclas pulsadas debera ser 1 lgico, y 0
cuando si las hay. Para eso es necesario que dichas lneas estn sujetas a Vcc por medio de resistencias, de
pull-ups. Si se deshabilitan las pull-ups, el programa no funcionar a menos que se las sustituyan por
resistencias externas con la misma funcin.
/******************************************************************************
* FileName:
main.c
* Purpose:
Control de teclado matricial de 4x4
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.

*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"
#define
#define
#define

kpd_PORT
kpd_PIN
kpd_DDR

PORTB
PINB
DDRB

// Port write
// Port read
// Direccin de puerto

char keypad_read(void);
void keypad_released(void);
char keypad_scan(void);
int main(void)
{
char tecla;
usart_init();

// Inicializar USART0 @ 9600 N 1

puts("\r\n Control de Teclado Matricial de 4x4 \r\n");


while(1)
{
nop();
tecla = keypad_read();
if(tecla)
{
putchar(tecla);
keypad_released();
}
}

// Alguna otra tarea


// Leer teclado
// S hubo Tecla pulsada (si es diferente de 0)
// Esperar teclado libre

}
//****************************************************************************
// Escanea el teclado y retorna el valor ASCII de la tecla presionada por
// al menos 25ms. En otro caso retorna 0x00.
//****************************************************************************
char keypad_read(void)
{
char c1, c2;
c1 = keypad_scan();
// Escanear teclado
if(c1)
// Si hubo alguna tecla pulsada
{
delay_us(25000);
// Delay antirrebote
c2 = keypad_scan();
// Escanear otra vez
if( c1==c2 )
// Si Ambas teclas ledas son iguales
return c2;
// entonces aceptarla
}
return 0x00;
}
//****************************************************************************
// Espera hasta que el teclado quede libre.
//****************************************************************************
void keypad_released(void)
{
delay_us(10);
//

while(keypad_scan())
continue;

// Mientras se detecte alguna tecla pulsada


// seguir escaneando.

}
//****************************************************************************
// Escanea el teclado y retorna el valor ASCII de la primera tecla que
// encuentre pulsada. De otro modo retorna 0x00.
//****************************************************************************
char keypad_scan(void)
{
unsigned char Col, Row;
char RowMask, ColMask;
// Col0 Col1 Col2 Col3
const static char keys[] = {'7', '8', '9', 'A',
// Row 0
'4', '5', '6', 'B',
// Row 1
'1', '2', '3', 'C',
// Row 2
'.', '0', '#', 'D'}; // Row 3
kpd_DDR = 0x0F;
kpd_PORT = 0xF0;
RowMask = 0xFE;
for(Row=0; Row<4; Row++)
{
kpd_PORT = RowMask;
delay_us(10);
ColMask = 0x10;

// Nibble alto entrada, nibble bajo salida


// Habilitar pull-ups del nibble alto
// Inicializar RowMask a 11111110

//
// Para que se estabilice la seal
// Inicializar ColMask a 00010000

for(Col=0; Col<4; Col++)


{
if((kpd_PIN&ColMask)==0)
// Si hubo tecla pulsada
{
kpd_DDR = 0x00;
// Todo puerto entrada otra vez
return keys[4*Row+Col]; // Retornar tecla pulsada
}
}

ColMask <<= 1;

// Desplazar ColMask para escanear


// siguiente columna

RowMask <<= 1;
RowMask |= 0x01;

// Desplazar RowMask para escanear


// siguiente fila

}
// Se llega aqu si no se hall ninguna tecla pulsada
kpd_DDR = 0x00;
// Todo puerto entrada otra vez
return 0x00;
// Retornar Cdigo de no tecla pulsada
}

Letrero Matricial de LEDs


Un panel matricial es uno de los proyectos ms atractivos en el mundo de la electrnica. Su elaboracin puede
ser sencilla por su funcionamiento, aunque algo complicada por la implementacin del hardware.
En esta oportunidad aprenderemos a disear un panel de 8 filas y de 64 columnas, es decir, de 512 LEDs, pero
vers que ampliar o reducir el tamao ser tan simple como aadir o quitar registros en el hardware o cambiar
un solo nmero en el software.

El hardware

Sabemos que para encender un LED necesitamos de una seal de control, aparte de la alimentacin (Vcc o
GND), cierto? Con esto en mente deberamos suponer que para un letrero de 515 LEDs se necesitaran de
512 seales saliendo del microcontrolador, ms o menos como en la siguiente figura:
Pero no es as. Podemos resolver parte de este problema multiplicando las seales del microcontrolador con
ayuda de dispositivos como multiplexores, decodificadores o registros serie-paralelo como el 74164, 74595 o
el CD4094. Los dos ltimos son muy parecidos y son a la vez mejores que los primeros porque cuentan con
doble buffer. Uno para almacenar internamente los datos seriales que van ingresando al registro y otro que se
conecta al exterior. Ms adelante veremos los beneficios de esta arquitectura.
Todos estos registros son de 8 bits pero tienen la caracterstica de poder ser montados en cascada para
multiplicar sus salidas. Por ejemplo, en la siguiente figura se muestra cmo conectar varios registros 74595 en
cascada. Se pueden ir aadiendo tantos registros como salidas paralelas se desee.
Por otro lado, si nos basamos solo en este mecanismo para ampliar nuestras seales, para controlar los 512
LEDs tendramos que usar 512/8 = 64 registros de 8 bits, lo cual nos llevara a un circuito muy difcil de
implementar adems de bastante costoso. La tcnica para salvar este segundo inconveniente es un artificio que
consiste en encender grupos de LEDs en tiempos diferentes pero con la suficiente frecuencia como para dar la
impresin de que estuvieran encendidos todos al mismo tiempo.
Obviamente, en un letrero matricial los LEDs quedan mejor agrupados en filas y/o columnas. En la siguiente
figura los nodos de los LEDs se unen formando las columnas y los ctodos se unen formando las filas (rows).
Tambin se puede armar una configuracin alternativa invirtiendo la polaridad de todos los LEDs. En ese caso
los transistores sern de tipo PNP.
Los valores de las resistencias R1 a R64 dependen de la corriente que tiene que fluir por los LEDs, la cual a su
vez depende de los mismos LEDs. Hay que tener en cuenta que los LEDs no estarn prendidos al 100 % sino
la octava parte (por las ocho filas) y tambin que la corriente promedio no siempre es proporcional al brillo
del LED prendido, es decir, que un LED est prendido la octava parte no significa que vaya a brillar ocho
veces menos.
Por otro lado, los transistores deben tener la capacidad de controlar la corriente proveniente de todos los LEDs
de cada fila. En algunos casos bastar con usar el ULN2803.
Los barridos

Una vez estructurado el hardware de la matriz de LEDs nos damos cuenta de que podemos encender los LEDs
que queramos de cualquier fila o de cualquier columna simplemente activando las coordenadas de dichos
LEDs. Por ejemplo, si queremos prender los LEDs de las columnas 0, 3, 4, 7 y 63 de la fila 5 se vera as:
Sin embargo, no es posible encender varios LEDs que pertenezcan a diferentes filas y diferentes columnas al
mismo tiempo. Es aqu donde entra a jugar el software.
Por ejemplo en la siguiente animacin se muestra como para visualizar la letra G se encienden los LEDs
correspondientes pero en tiempos diferentes. La primera figura muestra la secuencia del barrido en cmara
lenta pero en la prctica los barridos sern tan rpidos que los LEDs se vern como en la segunda figura.

Los caracteres

De lo visto anteriormente queda claro que encendiendo los LEDs convenientemente podemos formar en el
letrero la figura que deseemos. Ser el microcontrolador quien de acuerdo con su programa se encargue de
generar los barridos activando las filas y columnas adecuadamente segn un patrn establecido. Este patrn
corresponde a letras, figuras o nmeros que queramos y se puede estructurar de diversas formas.
Vamos a representar el patrn con una matriz de datos, donde cada dato represente una columna del panel de
LEDs. De esta forma, si asignamos un 0 a un LED apagado y un 1 a un LED prendido, podemos establecer a
partir de cada columna un byte de dato. Luego podremos agrupar ordenadamente todos estos datos y
escribirlos en un formato conocido.
Por ejemplo, para el pequeo texto de arriba la matriz escrita en lenguaje C quedara algo as:
const char matrix[] = {0x00, 0xFF, 0x10, 0x28, 0x44, 0x82, 0x00, 0xFF, 0x11, 0x31, 0x51, 0x8E, 0x00, 0x00,
0x00};
Esta matriz puede tener cualquier nombre pero de aqu en adelante me referir a ella como matrix.
Generacin automtica de matrix

Ya vimos que para generar nuestra matrix que se visualizar en el panel de LEDs hace falta conocer el sistema
binario y/o hexadecimal. Pero para quienes no tengan la paciencia de componerla manualmente sobre todo si
quieren experimentar con textos grandes, les presento una de varias herramientas que encontr en Internet. Se
llama LCD font maker y, aunque fue diseado para componer patrones de caracteres o grficos para displays
LCD o GLCD, tambin nos servir para nuestro panel de LEDs. Su uso es bastante fcil de descubrir, as que
no entrar en muchos detalles. Los pasos que debemos seguir son:

Presiona el botn Choose font y escoge la fuente que desees. Yo escog Verdana-Negrita-11 porque he visto
que produce un tipo de letra que se ajusta bien a la altura del letrero. Puedes probar por tu cuenta para ver
otros resultados.
En Char input ingresa el texto que mostrar tu letrero; Yo dej unos espacios al principio para que el texto
empiece a aparecer desde el costado derecho. Con "Offset" puedes centrar y ajustar vertical y
horizontalmente el patrn del texto. Hay que establecer "Height" (altura) a 8 y "Width" (ancho) lo ajustamos
hasta que cubra todo el texto, en mi caso fue de 230.
Ahora presiona el botn "Step 2: open the fonts dialog parameters" y en la ventana que se abre escoge los
parmetros que indica la siguiente figura.
Presiona el botn "Step 3: Making a single fonts with the current graphics" para generar la matriz
hexadecimal. El resultado aparecer como se muestra en la siguiente figura. Puedes seleccionarlo y copiarlo
manualmente o mediante el botn "Copy all".
Guarda la matriz generada para usarla en el cdigo fuente. Esta matriz est por defecto declarada como
"unsigned char code Bmp001" pero lo cambiaremos luego.

El cdigo fuente

La elaboracin del cdigo depender de varios factores, como el tamao del panel, la forma cmo se presenta
el texto (efecto), la longitud de los mensajes, de si los mensajes son estticos o si se programan en tiempo de
ejecucin, etc. En esta prctica el panel solo muestra un mensaje en desplazamiento. Por tanto el cdigo
fuente ser muy simple.

/******************************************************************************
* FileName:
main.c
* Overview:
LED sign. Letrero Matricial de LEDs.
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y la nota de autor de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#define SDATA_HIGH()
#define SDATA_LOW()

PORTD |= (1<<5)
PORTD &= ~(1<<5)

#define SCLOCK_HIGH() PORTD |= (1<<6)


#define SCLOCK_LOW() PORTD &= ~(1<<6)
#define SOEN_HIGH()
#define SOEN_LOW()

PORTD |= (1<<7)
PORTD &= ~(1<<7)

#define WIDTH 64
PROGMEM char matrix[] =
{
/*---------------------------------------------------------------------------Source file / text :
www.cursomicros.com
Width x Height (pixels) :230X8
----------------------------------------------------------------------------*/
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x1F,0xFC,0xE0,
0x7C,0x0F,0x0F,0x7C,0xE0,0xFC,0x1F,0x03,0x00,0x03,0x1F,0xFC,0xE0,0x7C,0x0F,0x0F,
0x7C,0xE0,0xFC,0x1F,0x03,0x00,0x03,0x1F,0xFC,0xE0,0x7C,0x0F,0x0F,0x7C,0xE0,0xFC,
0x1F,0x03,0x00,0x00,0xE0,0xE0,0x00,0x00,0x3C,0x7E,0xC3,0x81,0x81,0x81,0x42,0x00,
0x7F,0xFF,0x80,0x80,0x80,0x40,0xFF,0xFF,0x00,0xFF,0xFF,0x02,0x03,0x03,0x03,0x00,
0x4E,0x9F,0x99,0x99,0x99,0xF9,0x72,0x00,0x3C,0x7E,0xC3,0x81,0x81,0xC3,0x7E,0x3C,
0x00,0xFF,0xFF,0x02,0x01,0x01,0xFF,0xFE,0x02,0x01,0x01,0xFF,0xFE,0x00,0x00,0xFF,
0xFF,0x00,0x00,0x3C,0x7E,0xC3,0x81,0x81,0x81,0x42,0x00,0xFF,0xFF,0x02,0x03,0x03,
0x03,0x00,0x3C,0x7E,0xC3,0x81,0x81,0xC3,0x7E,0x3C,0x00,0x4E,0x9F,0x99,0x99,0x99,
0xF9,0x72,0x00,0x00,0xE0,0xE0,0x00,0x00,0x3C,0x7E,0xC3,0x81,0x81,0x81,0x42,0x00,
0x3C,0x7E,0xC3,0x81,0x81,0xC3,0x7E,0x3C,0x00,0xFF,0xFF,0x02,0x01,0x01,0xFF,0xFE,
0x02,0x01,0x01,0xFF,0xFE,0x00
};
const unsigned int LEN = sizeof(matrix);
/*****************************************************************************
* Main function
****************************************************************************/
int main(void)
{
unsigned int bad;
// Base index
unsigned int idx;
// Index
unsigned char dato; // dato

unsigned char row;


unsigned char col;
unsigned char i;

// Fila
// Columna

DDRD = 0xE0;
DDRB = 0xFF;
PORTD = 0x00;
PORTB = 0x00;
while(1)
{
for(bad=0; bad<LEN; bad++)
// Bucle para LEN "frames"
{
for(i=0; i<6; i++)
// Bucle de 6 barridos por "frame"
{
for(row=1; row; row<<=1) // Cada barrido pasa por las 8 filas (23288
ciclos)
{
for(col=WIDTH; col; col--) // Cada fila iene WIDTH columnas (2911
ciclos)
{
idx = bad + col - 1;
// Calcular ndice de elemento en matrix
if(idx < LEN)
// Si est dentro del rango
dato = pgm_read_byte(&matrix[idx]);
// Extraer dato
else
dato = 0x00;
// si no, asumir 0x00
if(dato & row)
SDATA_HIGH();
else
SDATA_LOW();
nop();nop();

// Si el bit de row es 1
// colocar 1 en DS

SCLOCK_HIGH();
nop();nop();
SCLOCK_LOW();

// Pulso de reloj para


// validar el dato colocado

}
PORTB = 0x00

// o, colocar 0

// Desactiva todas las filas

temporalmente

}
}
}
}
}

descargar

SOEN_HIGH();
nop();nop();
SOEN_LOW();

// Pulso para sacar todos


// los datos cargados

PORTB = row;

// Activar la fila actual

La simulacin

Dudo que todos los lectores consigan implementar en la prctica real un letrero matricial completo debido a la
relativa complejidad del hardware, pero creo que al menos podrn ver su diseo en una buena simulacin
gracias a Proteus.
Debemos notar que para la simulacin en Proteus no es necesario armar el circuito completamente. Para este
diseo por ejemplo he ignorado las resistencias y los transistores de las columnas y filas del panel.
Se puede (o debe) editar el parmetro Minimum Trigger Time de las matrices de LEDs para mejorar la
visualizacin de los LEDs sobre todo si se cambia la frecuencia del XTAL.
El realismo de simulacin tambin depender de la potencia de ordenador. En ordenadores lentos el contenido
del panel se desplaza ms lentamente, aunque se puede mejorar la animacin modificando algunos
parmetros, como la cantidad de barridos por frame en el cdigo fuente, solo para fines de la simulacin.

Los Displays LCD

Introduccin
Este captulo est dedicado a los LCDs alfanumricos con controlador Hitachi HD44780 o compatible, es
decir, la mayora. Hay diversas firmas, como Optrex, Sharp, Crystalfontz America, Tianma, etc., que
producen muchsimos LCDs de este tipo. Los hay desde 1 a 4 lneas, desde 8 a 40 letras por lnea, algunos con
iluminacin de fondo, con diferente tecnologa de fabricacin, etc. Dada la compatibilidad en el control de
todos ellos, la eleccin de un modelo en particular queda a tu cargo. El LCD utilizado en este curso es de 2
lneas, de 16 letras cada una.

Un display LCD de 2 lneas, de 16 caracteres cada una.


Si bien es necesario conocer un dispositivo para sacarle el mximo provecho, en primera instancia a la
mayora de los aficionados solo le interesa ponerlo en prctica aunque sea de forma limitada. Si eres uno de
ellos, y por el momento quieres ahorrarte algo de tiempo, puedes saltar a la seccin Interface de un Display
LCD.

Pines del LCD

Nmero
de Pin
1
2
3
4
5
6
7...14

Smbolo
Vss
Vcc o Vdd
Vee o Vo
RS
R/W
E
DB0...DB7

Pines del LCD.


Algunos LCDs con iluminacin disponen de dos pines adicionales para encenderla. Aun as, los 14 pines aqu
citados siempre deberan coincidir.
Nombre
de seal
DB0-DB7
E

Funcin
8 lneas de bus de datos. Para transferencia bidireccional de datos entre el C y el mdulo LCD.
DB7 tambin se puede usar como bit busy flag. En operacin de 4 bits solo se usa el nibble alto.
Enable Seal de inicio de operacin de lectura/escritura.
Seal para seleccionar operacin de lectura o escritura.

R/W

0 : Escribir en LCD
1 : Leer de LCD
Register Select

RS

0 : Registro de comandos (escritura).


: Busy flag + puntero de RAM (lectura).
1 : Registro de datos (escritura, lectura). Acceso a DDRAM o CGRAM.
Ajuste de contraste del LCD. Vee = GND es mximo contraste.
Alimentacin = +5 V tpicamente.
Alimentacin = 0 V (GND).

Vee o Vo
Vdd o Vcc
Vss

Memorias del LCD


CGROM - Character Generator ROM
Es la zona de memoria donde se encuentran grabados los patrones de todos los caracteres que puede visualizar
el LCD de fbrica. Tiene grabados cerca de 200 (vara mucho) tipos de caracteres de 57 puntos (lo ms
comn) o 32 caracteres de 510 puntos. Este ltimo modo es raramente usado porque no todos los modelos lo
soportan.

Tabla estndar de caracteres de la CGROM.

DDRAM - Display Data RAM


La DDRAM almacena los cdigos de las letras que se visualizan en la pantalla del LCD. Tiene capacidad de
80 bytes, un byte por carcter si la fuente es de 57 puntos. Observa que no siempre se podrn visualizar los
80 caracteres.
Por ejemplo, si quisiramos mostrar el mensaje Hello en la pantalla, deberamos enviar a la DDRAM los
cdigos ascii de cada letra de esa palabra. El controlador interno del LCD tomar esos cdigos para buscar en
la CGROM sus correspondientes patrones de visualizacin y luego los mostrar en la pantalla.
La siguiente figura muestra la correspondencia entre las locaciones de la DDRAM y las posiciones de las
letras que vemos en la pantalla de un LCD de 2 lneas, particularmente de uno de 216. Fjate en que los 80
bytes de la DDRAM se dividen en dos sectores de 40 bytes, un sector por lnea, as:

Lnea 1, con sector de DDRAM desde 0x00 hasta 0x27.


Lnea 2, con sector de DDRAM desde 0x40 hasta 0x67.

Por lo tanto, podemos entender que siempre tenemos un LCD virtual de 240; aunque solo podamos ver 8, 16
20 letras por cada lnea. Los otros datos escritos en la DDRAM permanecen all aunque no se visualicen.

Posiciones en DDRAM de las letras de la pantalla (nmeros en hexadecimal).

CGRAM - Character Generator RAM


La CGRAM es una RAM de 64 bytes donde el usuario puede programar los patrones de nuevos caracteres
grficos, ya sean de 57 puntos (hasta 8 caracteres) o de 510 puntos (hasta 4 caracteres). Este tema lo
detallar en la prctica final.

El Puntero de RAM
Llamado tambin Address Counter, es un registro que sirve para acceder a las memorias RAM del LCD. Por
ejemplo, si el Puntero de RAM vale 0x00, accedemos a la locacin de DDRAM (o CGRAM) de esa direccin.
Ahora bien, solo hay un puntero de RAM que trabaja con las dos RAMs del LCD, y para saber a cul de ellas
accede actualmente debemos ver la instruccin enviada ms recientemente.
Las instrucciones Clear Display, Return Home y Set DDRAM Address designan el Puntero de RAM a la
DDRAM, mientras que Set CGRAM Address lo designa a la CGRAM.
Afortunadamente, en la gran mayora de los casos, el Puntero de RAM estar apuntando a la DDRAM.
Adems, en este caso viene a representar la posicin del cursor (visible o no) del LCD en la pantalla.

Set Instrucciones del Display LCD


Es el controlador interno HD44780 (u otro) del LCD quien ejecutar las operaciones de mostrar las letras en la
pantalla, mover el cursor, desplazar el contenido de la pantalla, etc. Lo que nos toca a nosotros es enviarle los
cdigos de esas operaciones. A continuacin, un resumen.
Instrucciones

Cdigo

Instrucciones
de comando

RS R/W DB7 DB6 DB5

DB4

DB3

DB2

DB1

DB0

Clear Display
Return Home

0 0
0 0

0
0

0
0

0
0

0
0

0
0

0
0

0
1

Entry Mode Set

0 0

I/D

0 0

0 0

0 0

0 0
0 0

0
1

1
Puntero de RAM (CGRAM)
Puntero de RAM (DDRAM)

Display ON/OFF
Control
Cursor or Display
Shift
Function Set

Set CGRAM Address


Set DDRAM Address
Read Busy Flag
& RAM Pointer
Write to CGRAM
Instrucciones
or DDRAM
de datos
Read from CGRAM
or DDRAM

0 1

BF

DL

S/C

R/L

Puntero de RAM (DDRAM o CGRAM)

1 0

Escribir dato

1 1

Leer dato

Conviene saber que las instrucciones Clear Display y Return Home tienen un tiempo de ejecucin de cerca de
1.52 ms. Las dems toman algo de 40 s.
El LCD cuenta con dos registros internos principales, que dividen, grosso modo, las instrucciones en de datos
y de comando.
Poniendo el pin RS = 1 accedemos al registro de datos y mediante l a cualquier locacin de la DDRAM o
CGRAM, para operaciones de lectura y escritura de datos.
Con RS = 0 accedemos al registro de comandos para escribir instrucciones de control del LCD (Clear
Display, Function Set, etc.). En el caso de una lectura, obtenemos un dato particular que contiene el valor del
puntero de RAM junto con el bit Busy flag.
Clear display: 0 0 0 0 0 0 0 1

Limpia toda la pantalla del LCD. Tambin retorna el cursor a su posicin inicial (cima izquierda), esto es,
designa el puntero de RAM a la direccin 0x00 de la DDRAM.
Return home: 0 0 0 0 0 0 1 x

Regresa el cursor a su posicin inicial pero sin alterar el texto del display, es decir, solo designa el puntero de
RAM a la direccin 0x00 de la DDRAM.
Entry mode set: 0 0 0 0 0 1 I/D S

Establece el modo de incremento o decremento y modo de desplazamiento del LCD.

I/D = 1: El puntero de RAM se incrementa en 1 despus de leer o escribir un dato. As accedemos


automticamente a la siguiente locacin de DDRAM o CGRAM. Si es DDRAM, este puntero representa la
posicin del cursor en la pantalla y el incremento significa su avance a la derecha.
I/D = 0: El puntero de RAM se decrementa en 1 despus de leer o escribir un dato.
S = 1: Si se escribe un nuevo dato de carcter en el LCD, entonces el display entero se desplaza a la derecha
cuando I/D = 0 o a la izquierda cuando I/D = 1.
S = 0: El display no se desplaza luego de escribir en la DDRAM. Esto es lo usual.

Display on/off control: 0 0 0 0 1 D C B

Prende o apaga el Display, el Cursor y la funcin Blink del cursor.

D = 1: El display se prende.
D = 0: Apaga el display. (No significa que los datos de las RAMs se vayan a borrar.)
C = 1: Despliega el cursor.
C = 0: No despliega el cursor
B = 1: La letra indicada por el cursor parpadea.
B = 0: La letra no parpadea.

Cursor or display shift: 0 0 0 1 S/C R/L x x

Desplaza el cursor o el display a la derecha o la izquierda sin escribir o leer datos.


S/CR/LOperacin
0 0 Mueve el cursor a la izquierda (puntero de RAM se decrementa en 1)
0 1 Mueve el cursor a la derecha (puntero de RAM se incrementa en 1)
1 0 El Cursor y el display entero se desplazan a la izquierda
1 1 El Cursor y el display entero se desplazan a la derecha
Function set: 0 0 1 DL N F x x

Configura la longitud del bus de datos, el nmero de lneas y el tipo de fuente.

DL = 1 : La interface con el LCD es mediante un bus de datos de 8 bits.


DL = 0 : La interface con el LCD es mediante un bus de datos de 4 bits.
N = 1: Configura un display de 2 lneas.
N = 0: Configura un display de 1 lnea.
F = 0: Fuente de carcter de 57 puntos.
F = 1: Fuente de carcter de 510 puntos.

Set DDRAM address: 1AAAAAAA

Designa el puntero de RAM a lanueva direccin AAAAAAA de la DDRAM. Digamos que sirve para
controlar la posicin del cursor del LCD.
Ejemplo, para escribir un texto en la segunda lnea del display (que tiene direccin inicial 0x40), primero
habra que enviar el comando Set DDRAM Address con el nmero 0x40 en el parmetro AAAAAAA.

Set CGRAM address: 01AAAAAA

Designa el puntero de RAM a la nueva direccin AAAAAAA de la CGRAM.


Read Busy Flag & RAM Pointer: BF AAAAAAA

Leer bit Busy Flag (BF) y el valor del puntero de RAM. BF = 1 indica que una operacin interna est en
progreso. El LCD no aceptar una nueva instruccin hasta que BF sea 0.
El valor de AAAAAAA ledo representa el valor del puntero de RAM.
Es posible prescindir del bit BF. Para ello debemos esperar el tiempo adecuado antes de enviar la siguiente
instruccin.
Write data to CGRAM / DDRAM: DDDDDDDD

Escribe el dato de 8 bits DDDDDDDD en la DDRAM o CGRAM, dependiendo de cul de las dos est siendo
direccionada actualmente. Despus de la escritura el puntero de RAM se incrementa o decrementa, segn se
haya configurado el display. Ver instruccin Entry Mode Set.
Read data from CGRAM / DDRAM: DDDDDDDD

Lee un dato de 8 bits de la DDRAM o CGRAM, dependiendo de cul de ellas est siendo direccionada
actualmente. Despus de la lectura el puntero de RAM se incrementa o decrementa en uno, segn la
configuracin del display. Ver instruccin Entry Mode Set.

Inicializacin del LCD


Los LCDs tienen un circuito interno de reset que lo inicializa automticamente tras alimentar el LCD. Lo
cierto es que la auto-inicializacin no siempre es fiable. Por eso existe la inicializacin por software, que
permite una completa configuracin de los parmetros del LCD. Su defecto es que es bastante extico (Un
poco ms y nos piden que bailemos tap:). Se constituye de una serie de pasos que, por si fueran poco, varan
de acuerdo con la interface de 4 u 8 bits a usar y con el empleo o no del bit Busy Flag.
Los siguientes flowcharts corresponden a las inicializaciones del LCD para operar con interface de 4 y 8 bits y
usando el bit busy flag.

Inicializacin por software del LCD con interface de 4 bits.

Inicializacin por software del LCD con interface de 4 bits.

Interface de un Display LCD

Aunque los LCDs parezcan simples de usar, para bien o para mal sus caractersticas abren puertas a diversos
modos de interface. Aqu, algunos puntos de consideracin.

Bus de datos. Estos LCDs ofrecen la posibilidad de ser controlados utilizando los 8 bits de su bus de
datos o solo 4.
Un modo de operacin del LCD (con ventajas y desventajas) le permite trabajar sin conectar el pin
R/W al microcontrolador. En ese modo pin R/W siempre debe plantarse a tierra y el programa deber
esperar los tiempos adecuados para que se ejecuten las instrucciones.
Los LCDs estn fabricados con tecnologa CMOS, lo que deriva en la sugerencia de conectar los pines
de entrada no usados a alguna seal estable para evitar que por ellos se filtre algn ruido que pueda
perturbar la operacin del LCD.
LCDs con iluminacin de fondo. Esta caracterstica se basa en diferentes tecnologas, siendo la ms
habitual el empleo de una matriz de LEDs colocados detrs de la pantalla. T sabes que hay todo tipo
de LEDs: algunos prenden a penas, mientras que otros, con la misma corriente, pueden servir de faros
(bueno, casi :). Creo que eso da cuenta de lo mucho que puede variar el uso de la iluminacin de un
modelo a otro.
La iluminacin suele activarse con los pines 15 y 16, pero su polaridad tambin vara entre modelos.
Sobra decir, por tanto, que sera mejor que chequees el datasheet de tu LCD si es uno de estos.
Como sea, los pines que activan la iluminacin suelen ser independientes de los 14 estndares y las
prcticas de este curso deberan funcionar con iluminacin o sin ella.

Librera Para Display LCD


Tenemos a continuacin una librera para controlar un LCD con una interface de 4 bits y usando el bit BF
(Busy Flag). Si tuviste la paciencia de leer las pginas anteriores, vers que es un claro reflejo de todo lo
expuesto. Y si no, de todos modos en seguida citar ligeramente cmo utilizarla.
La librera trabaja para los compiladores IAR C y AVR GCC y consta de dos archivos lcd.h y lcd.c. Ambos
debern ser indexados al proyecto en el entorno de IAR C o Studio 5 para WinAVR (ante alguna duda puedes
ir a la seccin Adicin de Archivos o Libreras al Proyecto). En el cdigo fuente, sin embargo, solo se debe
indicar el archivo de cabecera i2c.h mediante la directiva #include.
#include "lcd.h"

La librera utiliza por defecto el puerto B del AVR tanto para el bus de datos del LCD como para las lneas de
control E, RS y R/W. Esta interface se puede modificar en los #defines del archivo i2c.h. Nota que por cada
puerto se deben cambiar los tres registros, PORT, DDR y PIN. Esa podra ser una configuracin de cierta
recurrencia, en cambio, no deberamos tocar lcd.c, salvo que por alguna razn deseemos editar su cdigo.

Funciones Bsicas Disponibles

lcd_init(). Obviamente es la primera funcin a llamar. Tras ejecutarse el LCD debe quedar inicializado, con la
pantalla limpia y con el cursor en el primer casillero.
lcd_gotorc(r,c). El LCD tiene un cursor que, si bien puede mostrarse en pantalla, suele configurarse para que
permanezca oculto. Bien, visible o no, el cursor avanza automticamente tras cada letra que se escribe. Con
lcd_gotorc(r,c) podemos mover el cursor manualmente a la fila r y columna c. El parmetro r puede valer entre
1 y 2, y el valor de c va de 1 en adelante.

lcd_puts(s). Visualiza la cadena s en el LCD empezando en la posicin actual del cursor. La cadena s es
almacenada en RAM. No se suelen mostrar grandes datos en un LCD, as que no implemente una funcin
anloga que opere sobre la memoria FLASH.
lcd_clear(). Limpia la pantalla del LCD y coloca el cursor al inicio, en la fila 1, columna 1.
lcd_data(c). Escribe una sola letra en el LCD, en la posicin actual del cursor. lcd_data() tambin permite crear
caracteres grficos, como se muestra en una prctica ms adelante.
lcd_cmd(com). Ocasionalmente tambin usaremos esta funcin para enviar comandos al LCD, por ejemplo:
lcd_cmd(LCD_LINE2);
// Mover cursor al inicio de lnea 2
lcd_cmd(LCD_CLEAR);
// Limpiar pantalla
lcd_cmd(LCD_CURBLK);
// Mostrar Cursor + Blink
lcd_cmd(LCD_CURSOR);
// Mostrar solo Cursor
lcd_cmd(LCD_CGRAM+7); // Mover Puntero de RAM a direccin 7 de la CGRAM

Las constantes LCD_CLEAR y algunas ms se hallan definidas en el archivo lcd.h. Por supuesto que tambin
se pueden formar cualesquiera cdigos de instrucciones de los estudiados antes.
/******************************************************************************
* FileName:
lcd.h
* Purpose:
Librera de funciones para controlar un display LCD con chip
*
Hitachi HD44780 o compatible. La interface es de 4 bits.
* Processor: ATmel AVR
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
//****************************************************************************
//
CONFIGURACIN DE LOS PINES DE INTERFACE
//****************************************************************************
/* Define el puerto a donde se conectar el bus de datos del LCD
* Se utilizar el nible alto del puerto escogido (ejem. PB4-DB4,...,PB7-DB7)
*/
#define lcd_DATAout
PORTB
// Registro PORT del puerto
#define lcd_DATAin
PINB
// Registro PIN del puerto
#define lcd_DATAddr
DDRB
// Registro DDR del puerto
/* Define el puerto a donde se conectarn las lneas de control del LCD
* E, RW y RS. Puede ser el mismo puerto del bus de datos.
*/
#define lcd_CTRLout
PORTB
// Registro PORT del puerto
#define lcd_CTRLin
PINB
// Registro PIN del puerto
#define lcd_CTRLddr
DDRB
// Registro DDR del puerto
/* Define los nmeros de los pines del puerto anterior que correspondern a
* las lneas E, RW y RS del LCD.
*/
#define lcd_E
3
// Pin Enable
#define lcd_RW
2
// Pin Read/Write
#define lcd_RS
1
// Pin Register Select

//****************************************************************************
//
CDIGOS DE COMANDO USUALES
//****************************************************************************
#define LCD_CLEAR
0x01
// Limpiar Display
#define LCD_RETHOM
0x02
// Cursor a inicio de lnea 1
#define LCD_LINE1
0x80
// Lnea 1 posicin 0
#define LCD_LINE2
0xC0
// Lnea 2 posicin 0
#define LCD_DDRAM
0x80
// Direccin 0x00 de DDRAM
#define LCD_CGRAM
0x40
// Direccin 0x00 de CGRAM
#define LCD_CURSOR
0x0E
// Mostrar solo Cursor
#define LCD_BLINK
0x0D
// Mostrar solo Blink
#define LCD_CURBLK
0x0F
// Mostrar Cursor + Blink
#define LCD_NOCURBLK 0x0C
// No mostrar ni Cursor ni Blink
//****************************************************************************
//
PROTOTIPOS DE FUNCIONES
//****************************************************************************
void lcd_init(void);
// Inicializa el LCD
void lcd_puts(char * s);
// Enva una cadena ram al LCD
void lcd_gotorc(char r, char c);
// Cursor a fila r, columna c
void lcd_clear(void);
// Limpia el LCD y regresa el cursor al inicio
void lcd_data(char dat);
// Enva una instruccin de dato al LCD
void lcd_cmd(char com);
// Enva una instruccin de comando al LCD
char lcd_read(char RS);
// Lee un dato del LCD
void lcd_write(char inst, char RS); // Escribe una instruccin en el LCD
void lcd_nibble(char nibble);
void ldelay_ms(unsigned char );
/******************************************************************************
* FileName:
lcd.c
* Purpose:
Librera de funciones para controlar un display LCD con chip
*
Hitachi HD44780 o compatible. La interface es de 4 bits.
* Processor: ATmel AVR
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "lcd.h"
//****************************************************************************
// Ejecuta la inicializacin software completa del LCD. La configuracin es
// de: interface de 4 bits, despliegue de 2 lneas y caracteres de 5x7 puntos.
//****************************************************************************
void lcd_init(void)
{
/* Configurar las direcciones de los pines de interface del LCD */
lcd_DATAddr |= 0xF0;
lcd_CTRLddr |= (1<<lcd_E)|(1<<lcd_RW)|(1<<lcd_RS);
/* Secuencia de inicializacin del LCD en modo de 4 bits*/
lcd_CTRLout &= ~((1<<lcd_E)|(1<<lcd_RW)|(1<<lcd_RS));
ldelay_ms(45);
// > 40 ms
lcd_nibble(0x30);
// Function Set: 8-bit
ldelay_ms(5);
// > 4.1 ms

lcd_nibble(0x30);
// Function Set: 8-bit
ldelay_ms(1);
// > 100 s
lcd_nibble(0x30);
// Function Set: 8-bit
ldelay_ms(1);
// > 40 s
lcd_nibble(0x20);
// Function Set: 4-bit
ldelay_ms(1);
// > 40 s
lcd_nibble(0x20);
// Function Set: 4-bit, 2lines, 47font
lcd_nibble(0x80);
//
lcd_write(0x0C, 0); // Display ON/OFF Control: Display on, Cursor off, Blink off
lcd_write(0x01, 0);
// Clear Display
lcd_write(0x06, 0);
// Entry Mode Set
}
//****************************************************************************
// Escribe una instruccin en el LCD:
// Si RS = 0 la instruccin es de comando (Function Set, Entry Mode set, etc).
// Si RS = 1 la instruccin es de dato y va a la DDRAM o CGRAM.
//****************************************************************************
void lcd_write(char inst, char RS)
{
while(lcd_read(0)&0x80);
// Esperar mientras LCD est ocupado
if(RS)
lcd_CTRLout |= (1<<lcd_RS); // Para escribir en DDRAM o CGRAM
else
lcd_CTRLout &= ~(1<<lcd_RS); // Para escribir en Registro de Comandos
delay_us(5);
// Permite actualizar el Puntero de RAM
lcd_nibble(inst);
// Enviar nibble alto
lcd_nibble(inst<<4);
// Enviar nibble bajo
}
//****************************************************************************
// Enva el nibble alto de 'nibble' al LCD.
//****************************************************************************
void lcd_nibble(char nibble)
{
lcd_CTRLout &= ~(1<<lcd_RW);
// Establecer Modo de escritura
lcd_DATAddr |= 0xF0;
// Hacer nibble alto de bus de datos salida
lcd_DATAout = (nibble&0xF0)|(lcd_DATAout&0x0F); // Colocar dato
delay_us(2);
// tAS, set-up time > 140 ns
lcd_CTRLout |= (1<<lcd_E);
// Pulso de Enable
delay_us(2);
// Enable pulse width > 450 ns
lcd_CTRLout &= ~(1<<lcd_E);
//
lcd_DATAddr &= 0x0F;
// Hacer nibble alto entrada
}
//****************************************************************************
// Lee un byte de dato del LCD.
// Si RS = 1 se lee la locacin de DDRAM o CGRAM direccionada actualmente.
// Si RS = 0 se lee el 'bit de Busy Flag' + el 'Puntero de RAM'.
//****************************************************************************
char lcd_read(char RS)
{
char high, low;
if(RS)
lcd_CTRLout |= (1<<lcd_RS); // Para leer de DDRAM o CGRAM
else
lcd_CTRLout &= ~(1<<lcd_RS); // Para leer Busy Flag + Puntero de RAM
lcd_CTRLout |= (1<<lcd_RW);
// Establecer Modo Lectura
lcd_DATAddr &= 0x0F;
// Hacer nibble alto entrada
delay_us(2);
// tAS, set-up time > 140 ns
lcd_CTRLout |= (1<<lcd_E);
// Habilitar LCD

delay_us(2);
high = lcd_DATAin;
lcd_CTRLout &= ~(1<<lcd_E);
delay_us(2);
lcd_CTRLout |= (1<<lcd_E);
delay_us(2);
low = lcd_DATAin;
lcd_CTRLout &= ~(1<<lcd_E);
return (high&0xF0)|(low>>4);

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

Data Delay Time > 1320 ns


Leer nibble alto
Para que el LCD prepare el nibble bajo
Enable cycle time > 1200 ns
Habilitar LCD
Data Delay Time > 1320 ns
Leer nibble bajo
Juntar nibbles ledos

}
//****************************************************************************
// Envan cadenas RAM terminadas en nulo al LCD.
//****************************************************************************
void lcd_puts(char * s)
{
unsigned char c, i=0;
while(c = s[i++])
lcd_write(c, 1);
// Instruccin 'Write Data to DDRAM/CGRAM'
}
//****************************************************************************
// Ubica el cursor del LCD en la columna c de la lnea r.
//****************************************************************************
void lcd_gotorc(char r, char c)
{
if(r==1)
r = LCD_LINE1;
else
r = LCD_LINE2;
lcd_write(r+c-1, 0);
// Instruccin 'Set DDRAM Address'
}
//****************************************************************************
// Limpia la pantalla del LCD y regresa el cursor a la primera posicin
// de la lnea 1.
//****************************************************************************
void lcd_clear(void)
{
lcd_write(LCD_CLEAR, 0);
// Instruccin 'Clear Display'
}
//****************************************************************************
// Envan instrucciones de comando y de datos al LCD.
//****************************************************************************
void lcd_cmd(char com)
{
lcd_write(com, 0);
// Cualquier instruccin de comando
}
void lcd_data(char dat)
{
lcd_write(dat, 1);
// Instruccin 'Write Data to DDRAM/CGRAM'
}
//****************************************************************************
// Genera un delay de n milisegundos
//****************************************************************************
void ldelay_ms(unsigned char n)
{
while(n--)
delay_us(1000);
}

Prcticas con LCD


Hello World
Mostrar un mensaje de Hello World en el LCD es un programa casi tan trillado como hacer parpadear un
LED.
Segn la configuracin por defecto de la librera para el LCD, debemos usar la conexin mostrada en el
esquema de abajo. La configuracin de puertos y de pines a usar se puede cambiar en archivo lcd.h.
El pin VEE (o Vo) del LCD establece el contraste de la pantalla. Muchas veces se prefiere quitar el
potencimetro y conectar VEE a tierra para fijar el mximo contraste. En los siguientes circuitos haremos algo
parecido.

Circuito de la prctica.
/******************************************************************************
* FileName:
main.c
* Purpose:
LCD - Visualizacin de texto
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*

* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.


*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "lcd.h"
void delay_ms(unsigned int t)
{
while(t--)
delay_us(1000);
}
int main(void)
{
lcd_init();
while(1)
{
lcd_gotorc(1,7);
lcd_puts("Hello");
lcd_gotorc(2,7);
lcd_puts("World");
delay_ms(600);
lcd_clear();
delay_ms(400);

// Inicializar LCD

//
//
//
//

Cursor a fila 1 posicin 7


Escribir Hello
Cursor a fila 2 posicin 7
...

// Pausa de 600 ms
// Limpiar pantalla
// ...

}
}

Visualizacin de Nmeros
Los LCDs solo entienden de caracteres alfanumricos y algunos otros, pero no saben reconocer nmeros. En
esta prctica veremos cmo hacerlo implementando un sencillo reloj. No ser el ms preciso, pero servir de
buen ejemplo parar formatear nmeros.
Para el circuito, de ahora en adelante, en vez del potencimetro, colocaremos un diodo 1N4148 en el pin VEE
para fijar la tensin (VDD-VEE) a cerca de 4.3 V. En la mayora de los LCDs este valor brinda un muy
aceptable nivel de contraste de la pantalla.

Circuito de la prctica.
La funcin lcd_puts recibe como parmetro un array de tipo char, que en su forma ms usada sera una cadena
texto.
Para visualizar nmeros en el LCD primero debemos convertirlos en cadenas de texto. La conocida funcin
sprintf (acrnimo de string print formatted) puede formatear cadenas y nmeros en diferentes bases y
colocarlas en el array que recibe como primer parmetro. Sprintf est basada en printf, as que tiene las
mismas caractersticas y limitaciones. En este programa solo se convierten nmeros enteros. Pero si deseas
utilizar nmeros de punto flotante tendrs que habilitar el uso de la librera que contiene printf en versin
completa. Para ms informacin puedes revisar la seccin Configuracin de printf de la clase de Studio5 y
WinAVR.
Sprintf soporta varios formatos de nmeros e incluso en su modo bsico requiere de cierta memoria que a
veces podra ser de consideracin. Para ese caso tambin se pueden usar otras funciones de la librera C
estndar stdlib.h, como itoa, por ejemplo. Normalmente no las uso porque tienen variaciones entre los
compiladores y al menos para las prcticas como sta prefiero no tocar esas divergencias.
/******************************************************************************
* FileName:
main.c
* Purpose:
LCD - Visualizacin de nmeros
* Processor: ATmega164P

* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "lcd.h"
void delay_ms(unsigned int t)
{
while(t--)
delay_us(1000);
}
int main(void)
{
char buff[17];
unsigned seg, min, hor;
seg = min = 0;
hor = 12;
lcd_init();
lcd_gotorc(1,4);
lcd_puts("easy clock");

// Array de 17 elementos tipo char

// Inicializar LCD
// Cursor a fila 1 posicin 4

for(;;)
{
sprintf(buff, "%2d:%02d:%02d ", hor, min, seg); // Formatear
lcd_gotorc(2,5);
// Cursor a fila 2 posicin 5
lcd_puts(buff);
// Enviar buffer a LCD
if(++seg > 59)
{
seg = 0;
if(++min > 59)
{
min = 0;
if(++hor > 12)
hor = 1;
}
}
delay_ms(998);
}
}

Caracteres grficos en LCD


La creacin de caracteres grficos puede ser un tema superfluo. Aun as, suponiendo que no faltarn algunas
personas obsesivas como yo, que siempre quieren probarlo todo, he preparado esta prctica para ir cerrando la
clase.

Hagamos un poco de memoria. Cuando enviamos el cdigo de un carcter alfanumrico a la DDRAM del
LCD, su chip interno buscar en la CGROM el patrn correspondiente y luego lo visualizar en la pantalla.
As se escriben todos textos (y as hemos trabajado hasta ahora).
Ahora bien, si el cdigo enviado vale entre 0x00 y 0x07 (o 0x08 y 0x0F), el chip interno buscar su patrn de
visualizacin en la CGRAM. Siendo sta una RAM de lectura/escritura, podemos programar en ella los
diseos que se nos ocurran.

Mapa de memoria para la creacin de nuevos caracteres.


La CGRAM (Character Generator RAM) consta de 64 bytes en los que se pueden escribir los patrones de 8
nuevos caracteres de 57 puntos 4 caracteres de 510 puntos. Aqu veremos el primer caso.
Cuando los caracteres son de 57 puntos los 64 bytes se dividen en 8 bloques de 8 bytes cada uno, y cada
bloque almacena el patrn de un nuevo carcter. El esquema mostrado arriba indica que:

El primer bloque de CGRAM, con direcciones desde 0b00000000 hasta 0b00000111, corresponde al cdigo
0x00 ( 0x80) de la DDRAM.

El segundo bloque CGRAM, con direcciones desde 0b00001000 hasta 0b00001111, corresponde al cdigo 0x01
( 0x88) de la DDRAM; y as sucesivamente.

Por ejemplo, la figura de arriba indica que se han rellenado los dos primeros bloques con los patrones de dos
pacman. Hasta ah solo se han creado dos nuevos caracteres. Para mostrarlos en el LCD habra que escribir un
cdigo as:
lcd_data(0x00);
lcd_data(0x01);

// Visualizar primer pacman


// Visualizar segundo pacman

Sobre la prctica en s, como parte de su funcionalidad, el LCD tiene instrucciones para desplazar lo mostrado
en la pantalla hacia un lado u otro. Puede parecer interesante, pero sus limitaciones llevan a muchos a realizar
esos efectos mediante rutinas software. Pues es lo que haremos en esta prctica, mostrar por el LCD un
mensaje que pasa como una marquesina, y como nuevo carcter pondremos a un pacman glotn que en la
esquina inferior izquierda.

Circuito de la prctica.
Despus de iniciado el LCD, los datos que se le enven irn a la DDRAM (para mostrar caracteres en la
pantalla). Como los patrones de los pacman deben ir en la CGRAM necesitamos establecerla como destino.
Para eso enviamos el comando Set CGRAM Address con la direccin de CGRAM que queremos acceder.

La otra sentencia lcd_cmd(LCD_CGRAM+8) permitir que los siguientes datos vayan al segundo bloque (de
8 bytes) de la CGRAM. Fjate en que no era necesario porque el Puntero de RAM ya estaba apuntando a esta
direccin.
Como hemos creado los dos pacman en los dos primeros bloques (de 8 bytes) de la CGRAM, los cdigos para
accederlos sern 0 (PacOpen) y 1 (PacShut), respectivamente.
A continuacin se encuentra la sentencia lcd_clear(). Con ella no solo limpiamos la pantalla del LCD (que,
por cierto, ya estaba limpia) sino que volvemos a cambiar a la DDRAM.
Por si no qued claro cmo se forman los patrones de los dos pacman, aqu los tenemos solitos. (Los bits no
importan, pueden ser 1s o 0s.)

const char PattOpen [] = {0x0F,0x1C,0x18,0x10,0x18,0x1C,0x0F,0x00}; //Pattern


const char PattShut [] = {0x00,0x0E,0x1F,0x10,0x1F,0x0E,0x00,0x00}; //Pattern

Pasando a otros temas: el texto de la pantalla se desplaza una posicin cada 400 ms. Si te parece que avanza
muy lento, puedes disminuir esta pausa. No obstante, podras empezar a ver como si hubiera dos letras por
casillero de la pantalla. Ello se debe a que el carcter enviado al LCD no se muestra ni se borra de inmediato.
Es lo que sus datasheets llaman tiempo de respuesta de visualizacin.
En general, a diferencia del Basic, en C es muy mal visto el uso de un goto, salvo un caso extremo. goto
funciona como en el ensamblador: salta a otro punto del programa, identificado con una etiqueta. Mi goto
salta a la etiqueta start para salir de dos bucles al mismo tiempo. Dicen que se es uno de los pocos casos
considerados extremos: salir intempestivamente de varios bucles anidados. A decir verdad, siempre hay
algoritmos alternativos para evitar el goto.
/******************************************************************************
* FileName:
main.c
* Purpose:
LCD - Creacin de caracteres grficos personalizados
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"

#include "lcd.h"
#define
#define
#define

LCD_LEN
PacOpen
PacShut

16
0x00
0x01

// Para LCD de 216


// Indentificador de carcter nuevo
// Indentificador de carcter nuevo

PROGMEM char Taine[] = "


\"EL HAMBRE PRODUCE POEMAS INMORTALES. \
LA ABUNDANCIA, SOLAMENTE INDIGESTIONES Y TORPEZAS\"
";
void delay_ms(unsigned int t)
{
while(t--)
delay_us(1000);
}
int main(void)
{
unsigned char j;
// ndice relativo
unsigned char i;
// ndice base
char c;
const char PattOpen[] = {0x0F,0x1C,0x18,0x10,0x18,0x1C,0x0F,0x00};
const char PattShut[] = {0x00,0x0E,0x1F,0x10,0x1F,0x0E,0x00,0x00};
lcd_init();
/* Crear dos nuevos caracteres (los pacman's) en la CGRAM */
lcd_cmd(LCD_CGRAM);
// Instruccin Set CGRAM Address
for (i=0; i<8; i++)
// Volcar patrn de pacman 1
lcd_data(PattOpen[i]);
//
lcd_cmd(LCD_CGRAM + 8);
for (i=0; i<8; i++)
lcd_data(PattShut[i]);

// Instruccin Set CGRAM Address


// Volcar patrn de pacman 2
//

lcd_clear();
lcd_puts(" Hungry Pacman ");

// Limpiar pantalla y regresar a DDRAM


// Escribir "Hungry Pacman" en LCD

while(1)
{
start:
i = 0;
for(;;)
{
lcd_cmd(LCD_LINE2);
if(i & 0x01)
lcd_data(PacOpen);
else
lcd_data(PacShut);

//
//
//
//
//

Cursor
Si bit
enviar
Si no,
enviar

a inicio de lnea 2
0 de i es 1,
pacman abierto
pacman cerrado

for(j=0; j<LCD_LEN-1; j++)


{
c = pgm_read_byte(&Taine[i+j]); // Obtener dato de matriz
if(c)
// Si es dato vlido,
lcd_data(c);
// enviarlo a LCD
else
// Si no (c = 0x00 = fin),
goto start;
// salir de los dos bucles for
}
delay_ms(400);
i++;
}
}

Comunicacin PC AVR LCD


Aqu tenemos un programa clich en los ejemplos de interface entre un microcontrolador y un ordenador
mediante el puerto serie. El programa terminal enva por el puerto serie las letras que presionemos en el
teclado. El AVR los recibir, los reflejar al PC y tambin los visualizar en el display LCD. Haremos que un
par de teclas generen instrucciones especiales:

La tecla Escape, de cdigo 27, sirve pare limpiar la pantalla del LCD.
La tecla Retroceso o Backspace, de cdigo 0x08 = \b, lleva el cursor del LCD una posicin atrs.

En esta ocasin ser de utilidad tener el cursor a la vista.

Circuito de la prctica.
/******************************************************************************
* FileName:
main.c
* Purpose:
LCD - Acceso a LCD desde PC mediante AVR
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "lcd.h"
#include "usart.h"
int main(void)
{
lcd_init();
lcd_cmd(LCD_CURBLK);

// Ver interface en "lcd.h"


// Mostrar Cursor + Blink

usart_init();
// 9600 - 8N1
puts("\n\r Acceso a LCD desde PC \n\r");
puts("\n\r Escribe en el LCD... \n\r");
while(1)
{
if(kbhit())
// Si hay algn dato en el buffer de recepcin
{
char c = getchar();
// Obtener un dato
putchar(c);
// Devolver dato
switch(c)
{
case 0x1B: lcd_clear();
// Limpiar LCD
puts("\n\r");
break;
case 0x08: lcd_cmd(0x10);
// Cursor atrs
lcd_data(' ');
// Escribir espacio blanco
lcd_cmd(0x10);
// Cursor atrs
break;
default:
lcd_data(c);
// Escribir c en LCD
}
}
}
}

Las Interrupciones en los AVR

Introduccin

Hay una analoga que siempre recuerdo desde que la le en un buen libro de Turbo Pascal cuando aprenda a
programar en dicho lenguaje. Cuando vamos a recibir una visita en nuestra casa podemos ir a la puerta a cada
momento para ver si ya lleg y atenderla apropiadamente, o podemos quedarnos haciendo nuestras labores
cotidianas esperando a que sea la visita quien llame a la puerta para ir a recibirla.
Ir a la puerta constantemente se compara por ejemplo con testear los puertos del AVR para ver si se presion
algn pulsador o algn teclado y actuar en consecuencia. Eso se conoce como tcnica Polling o de sondeo e
implica el desperdicio de recursos y ciclos de CPU.
En este captulo aprenderemos a atender nuestras visitas justo cuando llamen a la puerta para que el AVR no
se canse en vano y que se ponga a dormir, si fuera posible. sta es solo una pequea muestra de lo que se
puede conseguir con las interrupciones.

Qu son las Interrupciones?


Una interrupcin es una llamada inesperada, urgente e inmediata a una funcin especial denominada
Interrupt Service Routine (ISR).
El mecanismo funciona as: sin importar lo que est haciendo en main o cualquier funcin relacionada con
main, cuando ocurra la interrupcin el CPU har una pausa y pasar a ejecutar el cdigo de ISR. Tras
terminarlo, el CPU regresar a la tarea que estaba realizando antes de la interrupcin, justo donde la haba
suspendido.
Aunque es posible provocar interrupciones desde el programa ejecutndolas como si fueran funciones
ordinarias, las interrupciones son disparadas (llamadas) por eventos del hardware del microcontrolador. El
evento puede ser algn cambio en cierto pin de E/S, el desbordamiento de un Timer, la llegada de un dato
serial, etc. Se puede deducir por tanto que las fuentes de interrupcin estn relacionadas con la cantidad de
recursos del microcontrolador.

Los Vectores de Interrupcin


Cada interrupcin est identificada por un Vector de Interrupcin, que no es otra cosa que una direccin
particular en la memoria FLASH. Todos los Vectores estn ubicados en posiciones consecutivas de la
memoria FLASH y forman la denominada Tabla de Vectores de Interrupcin. El RESET no es una
interrupcin pero su direccin 0x0000 tambin se conoce como Vector de Reset. Por defecto, la Tabla de
Vectores de Interrupcin est ubicada en las primeras posiciones de la memoria, tal como se ve abajo. Solo
cuando se habilita el uso de la Seccin de Boot Loader toda la tabla se desplazar al inicio de dicha seccin.
Enseguida se presenta una tabla con las 35 interrupciones posibles en los ATmegaNN4YY. Debemos recordar
que solo los ATmega1284yy tienen el Timer3 y por tanto las 4 interrupciones relacionadas con el Timer3 no
estarn disponibles en los otros ATmega de esta serie. Aprenderemos de a poco y para empezar en esta clase
nos ocuparemos de las 7 interrupciones externas, desde INT0 hasta PCINT3. Las restantes sern estudiadas en
sus mdulos respectivos.
Num Direccin de
Nombre de
Fuente de interrupcin
Vector Programa Vector de Interrupcin
1
0x0000 RESET
External Pin, Power-on Reset, Brown-out Reset,

Num Direccin de
Nombre de
Vector Programa Vector de Interrupcin
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

0x0002
0x0004
0x0006
0x0008
0x000A
0x000C
0x000E
0x0010
0x0012
0x0014
0x0016
0x0018
0x001A
0x001C
0x001E
0x0020
0x0022
0x0024
0x0026
0x0028
0x002A
0x002C
0x002E
0x0030
0x0032
0x0034
0x0036
0x0038
0x003A
0x003C
0x003E
0x0040
0x0042
0x0044

INT0
INT1
INT2
PCINT0
PCINT1
PCINT2
PCINT3
WDT
TIMER2_COMPA
TIMER2_COMPB
TIMER2_OVF
TIMER1_CAPT
TIMER1_COMPA
TIMER1_COMPB
TIMER1_OVF
TIMER0_COMPA
TIMER0_COMPB
TIMER0_OVF
SPI_STC
USART0_RX
USART0_UDRE
USART0_TX
ANALOG_COMP
ADC
EE_READY
TWI
SPM_READY
USART1_RX
USART1_UDRE
USART1_TX
TIMER3_CAPT
TIMER3_COMPA
TIMER3_COMPB
TIMER3_OVF

Fuente de interrupcin
Watchdog Reset, and JTAG AVR Reset
External Interrupt Request 0
External Interrupt Request 1
External Interrupt Request 2
Pin Change Interrupt Request 0
Pin Change Interrupt Request 1
Pin Change Interrupt Request 2
Pin Change Interrupt Request 3
Watchdog Time-out Interrupt
Timer/Counter2 Compare Match A
Timer/Counter2 Compare Match B
Timer/Counter2 Overflow
Timer/Counter1 Capture Event
Timer/Counter1 Compare Match A
Timer/Counter1 Compare Match B
Timer/Counter1 Overflow
Timer/Counter0 Compare Match A
Timer/Counter0 Compare match B
Timer/Counter0 Overflow
SPI Serial Transfer Complete
USART0 Rx Complete
USART0 Data Register Empty
USART0 Tx Complete
Analog Comparator
ADC Conversion Complete
EEPROM Ready
2-wire Serial Interface
Store Program Memory Ready
USART1 Rx Complete
USART1 Data Register Empty
USART1 Tx Complete
Timer/Counter3 Capture Event
Timer/Counter3 Compare Match A
Timer/Counter3 Compare Match B
Timer/Counter3 Overflow

Para entender cmo funciona el mecanismo de las interrupciones en bajo nivel, recordemos que el Contador
de Programa es un registro que dirige cada una de las instrucciones que ejecuta el CPU. Pues bien, al
dispararse la interrupcin el hardware guardar en la Pila el valor actual del Contador de Programa y lo
actualizar con el valor del Vector de Interrupcin respectivo, de modo que el CPU pasar a ejecutar el cdigo
que se encuentre a partir de esa direccin. Al final del cdigo de la interrupcin debe haber una instruccin de
retorno que restaure el Contador de Programa con el valor que se haba guardado en la Pila. La instruccin es
reti y la pone el compilador.
Si se llegara a producir el evento excepcional en que se disparen dos o ms interrupciones al mismo tiempo, se
ejecutarn las interrupciones en orden de prioridad. Tiene mayor prioridad la interrupcin cuyo Vector se
ubique ms abajo, es decir, entre todas, la interrupcin INT0 tiene siempre las de ganar.

La estructura y caractersticas de la Tabla de Vectores de Interrupcin pueden variar entre las diferentes
familias de ATmega y a veces entre diferentes partes de una misma serie. Por ejemplo, los ATmega de la serie
8yy no tienen la interrupcin externa INT2 y tampoco las interrupciones PCINT3 (porque les falta el puerto
A). Adems, el ATmega48yy no dispone de la funcionalidad de Boot Loader, as que este AVR no puede
desplazar su Tabla de Vectores de Interrupcin. La ausencia de algunas interrupciones hace que los otros
Vectores cambien de valor.
En cualquier caso, para nosotros, los programadores en C o Basic, es suficiente tener en cuenta los nombres de
los Vectores de Interrupcin, que en la tabla de arriba se resaltan con enlaces en en azul.
Los nombres de los Vectores de Interrupcin presentados corresponden al datasheet y no necesariamente son
idnticos a los que utilizan los compiladores AVR GCC o IAR C. Estos nombres se encuentran definidos en
los archivos de dispositivo de cada AVR, ubicados en la carpeta include de cada compilador. La instalacin
por defecto de WinAVR con Studio 5 en Windows 7 marca la ruta C:\Program Files (x86)\Atmel\AVR Studio
5.0\AVR Toolchain\avr\include\avr. All los puedes ubicar, y de hecho es recomendable examinarlos que de
vez en cuando.
Pero si de momento deseas ahorrarte el trabajo te dir que la nica diferencia es el apndice _vect. Es decir, en
todos los archivos de dispositivo de IAR C y de AVR GCC (en sus versiones actuales) los nombres de los
Vectores de Interrupcin son los mismos que aparecen en el datasheet pero con el aadido _vect, como se
muestra en la siguiente tabla de ejemplo. Est de ms decir que en nuestros programas debemos usar la forma
con _vect.
Nombre de
Nombre de
Vector de Interrupcin Vector de Interrupcin
en datasheet
en archivo de dispositivo
INT0
INT0_vect
INT1
INT1_vect
INT2
INT2_vect
PCINT0
PCINT0_vect
PCINT1
PCINT1_vect
PCINT2
PCINT2_vect
PCINT3
PCINT3_vect
TIMER0_COMPA TIMER0_COMPA_vect
TIMER0_COMPB
TIMER0_COMPB_vect
TIMER0_OVF
TIMER0_OVF_vect
USART0_RX
USART0_RX_vect
USART0_UDRE
USART0_UDRE_vect
USART0_TX
USART0_TX_vect
Lamentablemente para quienes programan en CodeVisionAVR, Pavel Haiduc decidi no s por qu usar
otros nombres para los Vectores de Interrupcin. No solo son diferentes de los indicados en los datasheets
sino que la Tabla de Vectores empieza en 2 y no en 1 (sin incluir el Vector de reset, claro est). As que ellos
no tendrn ms remedio que recurrir a los archivos de dispositivo de sus AVR, los cuales se hallan en la
fcilmente ubicable carpeta inc creada por CodeVisionAVR en su directorio de instalacin.

Las Funciones de Interrupcin

La Funcin de Interrupcin o ISR va siempre identificada por su Vector de Interrupcin, y su esquema vara
ligeramente entre un compilador y otro, puesto que no existe en el lenguaje C un formato estndar. Lo nico
seguro es que es una funcin que no puede recibir ni devolver ningn parmetro.
En el compilador AVR GCC (WinAVR) la funcin de interrupcin se escribe utilizando la palabra reservada
ISR. Recordemos que el Vector_de_Interrupcion debe tener la terminacin _vect, como se indic
anteriormente, y si tienes dudas puedes buscar en la carpeta include del directorio de instalacin de WinAVR.
ISR (Vector_de_Interrupcion)
{
// Cdigo de la funcin de interrupcin.
// No requiere limpiar el flag respectivo. El flag se limpia por hardware
}

Por otro lado, en IAR C y CodeVisionAVR la construccin es un poquito ms elaborada. Requiere el empleo
de la directiva #pragma vector y la palabra reservada __interrupt. El Nombre_de_Interrupcion queda a la
libertad del programador.
#pragma vector = Vector_de_interrupcion
__interrupt void Nombre_de_Interrupcion (void)
{
// Cdigo de la funcin de interrupcin.
// No requiere limpiar el flag respectivo. El flag se limpia por hardware
}

Por fortuna, estas divergencias entre IAR C y WinAVR solo se presentan en el encabezado de la funcin. La
implementacin del cuerpo de la funcin es idntica en ambos compiladores. Adems existe la posibilidad de
utilizar macros que adapten el esquema de la funcin de interrupcin de IAR C al formato de WinAVR. Estas
macros estn escritas en el archivo avr_compiler.h del Atmel Software Framework o ASF y que siempre se
usa en los programas de cursomicros.com.
En realidad, CodeVision AVR ofrece otras formas adicionales de implementar una funcin de interrupcin
pero, por ms que se parezcan en forma, lamentablemente ninguna de ellas es compatible con el cdigo de
IAR C o WinAVR, debido a las diferencias en los nombres de los Vectores de Interrupcin.

Control de las Interrupciones


Hay dos tipos de bits para controlar las interrupciones: los Bits Enable, que habilitan las interrupciones, y los
Bits de Flag, que indican cul interrupcin se ha producido. Bueno, eso para decirlo a grandes rasgos.
Hay un bit enable individual para cada interrupcin y adems hay un bit enable general I (ubicado en el
registro SREG) que afecta a todas las interrupciones.
Para habilitar una interrupcin hay que setear su bit enable individual como el bit enable general I. Tambin se
pueden habilitar varias interrupciones del mismo modo. Ninguna habilitacin individual tendr efecto, es
decir, no disparar una interrupcin si el bit I est en cero.
Por otro lado, cada interrupcin tiene un Bit de Flag nico, que se setea automticamente por hardware
cuando ocurre el evento de dicha interrupcin. Eso pasar independientemente de si la interrupcin est
habilitada o no. Si la interrupcin fue previamente habilitada, por supuesto que se disparar.

Cada interrupcin habilitada y disparada, saltar a su correspondiente Funcin de Interrupcin o ISR, de modo
que a diferencia de algunos otros microcontroladores no ser necesario sondear los flags de interrupcin para
conocer la fuente de interrupcin.
Los AVR van ms lejos y tienen un hardware que limpia automticamente el bit de Flag apenas se empiece a
ejecutar la funcin de interrupcin. Pero puesto que los flags se habilitan independientemente de si las
interrupciones estn habilitadas o no, en ocasiones ser necesario limpiarlos por software y en ese caso
debemos tener la especial consideracin de hacerlo escribiendo un uno y no un cero en su bit respectivo. S,
seor, dije, uno.
Al ejecutarse la funcin de interrupcin tambin se limpia por hardware el bit enable general I para evitar que
se disparen otras interrupciones cuando se est ejecutando la interrupcin actual. Sin embargo, la arquitectura
de los AVR le permite soportar ese tipo de interrupciones, llamadas recurrentes o anidadas, y si as lo
deseamos podemos setear en el bit I dentro de la ISR actual.
A propsito, el ya famoso bit enable general I se puede escribir como cualquier otro bit de un registro de E/S.
Pero dada su especial importancia, existen dos exclusivas instrucciones de ensamblador llamadas sei (para
setear I) y cli (para limpiar I). El archivo avr_compiler.h ofrece las macros sei() y cli() para llegar a esas
instrucciones.

Las Interrupciones INT0, INT1 e INT2


Los ATmega de 40 pines tienen las tres interrupciones y los ATmega de 28 pines solo tienen las dos primeras.
Como las tres interrupciones tienen caractersticas idnticas y se controlan igual, la descripcin ser general
citando una x que representa el 0, 1 2.
El evento que puede disparar la interrupcin INTx es un flanco (de subida y/o de bajada) o un nivel bajo
detectado en el pin INTx del AVR, sin importar si ese pin est configurado como entrada o como salida.
Los bits de enable y flag de estas interrupciones se encuentran en los registros

EIMSK = External interrupts Mask Register. Contiene los bits enable.


EIFR = External Interrupts Flags Register. Contiene los bits de flag.
EICRA = External Interrupts Control Register A. Configura la seal externa que va a generar la interrupcin. Es A
porque hay AVRs ms grandes con ms interrupciones INTx donde adems existe el registro EICRB.

EIMSK---------------INT2INT1INT0
EIFR---------------INTF2INTF1INTF0
EICRA------ISC21ISC20ISC11ISC10ISC01ISC00
Para habilitar la interrupcin INTx hay que setear el bit INTx, del registro EIMSK, adems del bit enable
general I, del registro SREG.
Una vez producido el evento, el hardware setear el flag INTFx, del registro EIFR, y luego se disparar la
interrupcin. Este evento se debe configurar previamente en el registro EICRA y hay cuatro opciones
posibles.
Modo

ISCx1

ISCx0

Evento de la Interrupcin

Modo
0
1
2
3

ISCx1

ISCx0

0
1
1

Evento de la Interrupcin
Nivel bajo en el pin INTx.

Cualquier flanco (de subida y/o de bajada) detectado en el pin INTx.

Flanco de bajada detectado en el pin INTx.

Flanco de subida detectado en el pin INTx.

EICRA------ISC21ISC20ISC11ISC10ISC01ISC00

Como la mayora de los registros, EICRA inicia con todos sus bits a cero lo que significa que por defecto la
interrupcin INTx habilitada se disparar cuando dicho pin est a nivel bajo.
La interrupcin externa INTx tiene la capacidad de despertar al AVR, es decir, de sacarlo del modo sleep.
sta es una caracterstica muy notable que veremos luego.

Prctica: Uso de la interrupcin INTx


En estas prcticas de ejemplo evitaremos programas sofisticados con cdigos grandes que desven la atencin
hacia una breve aplicacin de la teora expuesta. Por eso no nos vendr mal volver a los socorridos LEDs
parpadeantes.
El programa tendr dos tareas: la rutina principal se encargar de parpadear un LED y la funcin de
interrupcin har bascular otro LED cada vez que presionemos un pulsador. Esto ser como fusionar dos
programas que alguna vez hicimos. Correr dos programas a la vez Dicen que algo as le paso por la
cabeza a Bill Gates cuando pens en MS Windows.
De las seales que se generan al presionar el botn escogeremos el flanco de bajada para disparar la
interrupcin INT0.

Circuito de la prctica.
El cdigo fuente

Cada aplicacin puede tener sus propias especificaciones, pero, en general, un buen hbito de programacin es
poner la sentencia sie(); que setea el bit I del registro SREG cuando ya todo est listo para atender a la
interrupcin.
Al analizar la estructura del programa, notamos que la funcin ISR es totalmente independiente de main, es
decir, no es referenciada desde ningn punto de main.
Una vez habilitada, la interrupcin se disparar cuando alguien presione el botn (en el flanco de bajada). En
ese preciso instante (quiz cuando se est ejecutando PINC = 0x02 o quiz en algn punto dentro de
delay_ms(600)) el CPU pasar a ejecutar la funcin ISR. Al salir de ISR, el CPU regresar a continuar la tarea
que estaba ejecutando antes de la interrupcin.
/******************************************************************************
* FileName:
main.c
* Purpose:
Uso de la interrupcin INTx
* Processor: ATmega AVR
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
void delay_ms(unsigned int t)
{

while(t--)
delay_us(1000);
}
//****************************************************************************
// Interrupt Service Routine, ISR
// Esta funcin se ejecuta cuando se detecta un flanco de bajada en el pin INT0
//****************************************************************************
ISR (INT0_vect)
{
PINC = 0x01;
// Conmutar pin PC0
delay_ms(40);
// Para pasar los rebotes
}
//****************************************************************************
// Funcin principal
//****************************************************************************
int main(void)
{
DDRC = 0x03;
// Pines PC0 y PC1 para salida (LEDs)
PORTD = 0x04;
// Habilitar pull-up de pin PD2/INT0 (pulsador)
/* Habilitar y configurar la interrupcin INT0 para que se dispare con
* cada flanco de bajada detectado en el pin INT0 (PD2)
*/
EIMSK = (1<<INT0);
// Habilitar INT0
EICRA = (2<<INT0*2); // Elegir flanco de bajada (modo 2)
sei();

// Habilitacin general de interrupciones

while(1)
{
PINC = 0x02;
delay_ms(600);
}

// Bucle infinito
// Conmutar pin PC1
// Pausa de 600ms

El Modo Sleep
El modo Sleep es un estado en que se detiene el oscilador del sistema y, por tanto, dejan de funcionar todas las
partes del microcontrolador que dependen de l, incluyendo en algunos casos el mismo procesador, es decir,
se congela la ejecucin del programa. Sin embargo los valores de todos los registros y puertos del
microcontrolador permanecern inalterables. En este estado se dice que el microcontrolador est durmiendo,
por el trmino sleep = sueo, en ingls.
La pregunta es para qu sirve un microcontrolador con su hardware congelado? Pues hay aplicaciones donde
el microcontrolador debe atender ciertas tareas solo cuando ocurre un evento externo como por ejemplo la
pulsada de un botn. El resto del tiempo no hace nada til.
Al hacer que el microcontrolador se ponga a dormir y que despierte solo cuando cierto evento se lo demande,
se consigue ahorrar muchsima energa que se perdera con el CPU y dems perifricos estando activos en
vano. Esto es clave sobre todo en circuitos alimentados por bateras.
Los microcontroladores AVR tienen un sistema oscilador sofisticado que divide el reloj en varias
ramificaciones que van a los diferentes mdulos del AVR. De esa forma, su modo sleep tiene hasta 6

diferentes niveles dependiendo de las ramificaciones del reloj que se pongan a congelar. Detallar cada una de
ellas en este momento extendera tanto el tema que movera el enfoque de las interrupciones. Solo dir que el
mayor nivel, es decir, donde el hardware del AVR se congela por completo se denomina Power-down.
El modo Power-down se configura escribiendo el valor 0x02 en el registro SMCR y luego solo bastar con
ejecutar la instruccin de ensamblador sleep para que el AVR cierre sus ojos.
El modo sleep es muy propio de los microcontroladores y no existe en el lenguaje C una sentencia para la
instruccin sleep. Cada compilador la implementa a su modo. En el archivo avr_compiler.h original se utiliza
la macro sleep_enter(), pero yo le aad otra definida como sleep(), a secas.
Tocamos el modo sleep ahora porque el evento por excelencia que puede despertar al microcontrolador es el
disparo de una interrupcin proveniente de una parte del microcontrolador que no est congelado. Puesto que
las interrupciones INTx y PCINTx son externas, ellas pueden sacar al AVR incluso del sueo ms profundo, o
sea del modo Power-down.
Cuando se dispare una interrupcin lo primero que har el CPU al despertar es ejecutar la primera instruccin
de ensamblador que sigue a sleep, e inmediatamente despus pasar a ejecutar la funcin de interrupcin ISR.
Si an recuerdas el retardo de arranque, te dir que es aqu donde entra en accin: esto es, despus de
descongelarse el oscilador del sistema, habr un tiempo llamado retardo de arranque en que el AVR espera en
estado de RESET hasta que el oscilador se haya estabilizado por completo. Luego, recin, el CPU reiniciar
su trabajo. De los 6 modos sleep del AVR, el retardo de arranque solo acta en los niveles Power- down y
Power-save porque en los dems niveles, el oscilador del AVR no est del todo detenido.

Prctica: Interrupciones Mltiples + Modo Sleep


Si al programa anterior le quitsemos la tarea de la rutina principal, el AVR ya no tendra nada que hacer all.
ste puede ser un buen momento para tomar una siesta.
Por otro lado, en esta ocasin experimentaremos con las tres interrupciones, INT0, INT1 e INT2, al mismo
tiempo como ejemplo de uso de interrupciones mltiples.

Circuito de la prctica.
El cdigo fuente
/******************************************************************************
* FileName:
main.c
* Purpose:
Uso de las interrupciones INTx + Modo Sleep
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
void delay_ms(unsigned int t)
{
while(t--)
delay_us(1000);
}
//****************************************************************************
// Gestor de Interrupcin INT0
// Esta funcin se ejecuta cuando se detecta un flanco de bajada o de subida
// en el pin INT0
//****************************************************************************
ISR (INT0_vect)
{
PINC = 0x01;
// Conmutar pin PC0
delay_ms(40);
// Para pasar los rebotes
}

//****************************************************************************
// Gestor de Interrupcin INT1
// Esta funcin se ejecuta cuando se detectan flancos de subida en el pin INT1
//****************************************************************************
ISR (INT1_vect)
{
PINC = 0x02;
// Conmutar pin PC1
delay_ms(40);
// Para pasar los rebotes
}
//****************************************************************************
// Gestor de Interrupcin INT2
// Esta funcin se ejecuta cuando el pin INT2 se encuentra en nivel bajo
//****************************************************************************
ISR (INT2_vect)
{
PINC = 0x04;
// Conmutar pin PC2
delay_ms(40);
// Para pasar los rebotes
}
//****************************************************************************
// Funcin principal
//****************************************************************************
int main(void)
{
DDRC = 0x07;
// Pines PC0, PC1 y PC2 para salida (LEDs)
/* Habilitar pull-ups de pines INT0/PD2, INT1/PD3 e INT2/PB2 para los
* pulsadores. Se asume que estos pines estn configurados como entradas
*/
PORTD = 0x0C;
//
PORTB = 0x04;
//
/* Habilitar las interrupciones INT0, INT1 e INT2 y configurarlas para que
* se disparen:
* INT0 con cada flanco (de bajada o subida) en el pin INT0/PD2 (modo 1)
* INT1 con cada flanco de subida en el pin INT1/PD3
(modo 3)
* INT2 mientras haya un nivel bajo en el pin INT2/PB2
(modo 0)
*/
EIMSK = (1<<INT0)|(1<<INT1)|(1<<INT2);
// Habilitar INT0, INT1 e INT2
EICRA = (1<<INT0*2)|(3<<INT1*2)|(0<<INT2*2); // Elegir flancos
sei();

// Habilitacin general de interrupciones

while(1)
// Bucle infinito
{
/* Entrar en modo sleep (Power-Down mode) */
SMCR = (1<<SM1)|(1<<SE);
sleep();
nop();
}
}

El AVR despertar con el disparo de la interrupcin, ejecutar nop() (que tambin equivale a una instruccin
de ensamblador) y luego llamar a la funcin ISR respectiva. Lo dems es historia conocida.
Aunque en algunos casos el nop() es recomendable, en esta ocasin lo puse solo para esta explicacin.

Interrupciones de Cambio de Pin, PCINTx


Esta interrupcin se dispara cada vez que se detecta un cambio de nivel lgico 1 a 0 o viceversa en
cualquiera de los pines de los puertos del AVR, sin importar si estn configurados como entrada o como
salida. Aunque no es propiamente reconocido, tambin se podra decir que se dispara con los flancos de
subida y de bajada en los pines de puertos. En ese sentido, se parece bastante a las interrupciones INTx. La
interrupcin de Cambio de Pin tambin puede sacar al AVR del modo Sleep.
Esta interrupcin no est presente en los AVR antiguos como los ATmega32, ATmega16, ATmega8535, etc.
Aparte del bit enable general I, del registro SREG, las interrupciones de cambio de pin se habilitan pasando
por las dos siguientes etapas, no necesariamente en el orden citado.
Primero, se debe setear el bit que identifica el puerto donde se encuentran los pines que generarn las
interrupciones. Estos son bits de enable ubicados en el registro PCICR (Pin Change Interrupt Control
Register). Para los ATmega de 4 puertos la correspondencia es la siguiente.
PCICR------------PCIE3PCIE2PCIE1PCIE0

Y para los ATmega de 3 puertos como los de la serie 8xx, la correspondencia entre los bits PCIEx (Pin
Change Interrupt Enable) y los puertos del AVR es
PCICR---------------PCIE2PCIE1PCIE0

Luego se deben setear los bits enable que identifican individualmente los pines de los puertos. Estos bits se
encuentran en los registros de mscara PCMSK (Pin Change Mask Register). Hay un registro de mscara por
cada puerto del AVR aunque la relacin vara segn el nmero de puertos del AVR, como se indica en la
siguiente tabla.
ATmega de 4 puertos
Registro de mscara
Puerto
PCMSK0
PORTA
PCMSK1
PORTB
PCMSK2
PORTC
PCMSK3
PORTD

ATmega de 3 puertos
Registro de mscara
Puerto
PCMSK0
PORTB
PCMSK1
PORTC
PCMSK2
PORTD

Cada bit del registro de mscara PCMSK corresponde a su respectivo pin de PORT. Por ejemplo, si en un
AVR de 4 puertos seteamos los bits 4 y 7 de PCMSK2, estaremos habilitando las interrupciones de los pines 4
y 7 de PORTC. Esta correspondencia se cumple incluso en los AVR cuyos puertos no tengan los 8 pines
completos.
Otra forma de seleccionar los pines de interrupcin es ubicndolos directamente por sus nombres PCINTx.
Para esto tambin debes estar revisando el diagrama de pines del AVR.
PCMSK0PCINT7PCINT6PCINT5PCINT4PCINT3PCINT2PCINT1PCINT0
PCMSK1PCINT15PCINT14PCINT13PCINT12PCINT11PCINT10PCINT9PCINT8
PCMSK2PCINT23PCINT22PCINT21PCINT20PCINT19PCINT18PCINT17PCINT16
PCMSK3PCINT31PCINT30PCINT29PCINT28PCINT27PCINT26PCINT25PCINT24
Observa que cada bit PCINTx corresponde a un pin del AVR con el mismo nombre.

Una vez producido el cambio de nivel en uno o varios de los pines habilitados para interrupcin, se activar el
flag respectivo PCIF (Pin Change Interrupt Flag) del registro PCIFR y luego se llamar a la funcin de
interrupcin ISR. As como hay un bit enable para cada puerto, en este nivel tambin hay un bit de flag
correspondiente. Es de prever que el siguiente esquema pertenece a los AVR de 4 puertos. All, por ejemplo,
si un pin de PORTB cambia de nivel, entonces se activar el flag PCIF1.
PCIFR------------PCIF3PCIF2PCIF1PCIF0

Por supuesto, en los AVR de 3 puertos hay variacin en el mapa del registro PCIFR (Pin Change Interrupt
Flag Register).
PCIFR---------------PCIF2PCIF1PCIF0

Como de costumbre, el flag PCIF ser limpiado por el hardware al ejecutarse el gestor de interrupcin ISR.
Sin embargo, como este flag puede activarse sin necesidad de que est seteado el bit enable general I (del
registro SREG), a veces se tendr que limpiar por software. En ese caso se limpia escribiendo un 1. Para evitar
llegar a esta situacin es recomendable habilitar la Interrupcin de Cambio de Pin despus de realizar en los
puertos todas las operaciones necesarias que pudieran ocasionar cambios de nivel en sus pines, por ejemplo,
activar las pull-ups.
Finalmente, debemos tener en cuenta que hay un Vector de Interrupcin [de Cambio de Pin] por cada puerto
de AVR, llamados PCINT0_vect, PCINT1_vect, PCINT2_vect y PCINT3_vect. La activacin del flag PCIFx
conducir a la ejecucin de la funcin ISR identificada por el vector PCINTx_vect.

Prctica: Interrupciones de Cambio de Pin


En el programa el AVR permanece en estado sleep (Power-down) y despierta cada vez que se presionen los
pulsadores conectados a los pines PD5, PD6 y PD7. (Puede haber ms o menos pulsadores y se pueden elegir
cualesquiera ortos) Cada pulsador har conmutar un LED conectado al puerto B. Los LEDs deben conmutar
solo al presionar los pulsadores y no al soltarlos.

Circuito de la prctica.
El cdigo fuente
/******************************************************************************
* FileName:
main.c
* Purpose:
Uso de las Interrupciones de Cambio de Pin, PCINTx
* Processor: ATmega AVR
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
//****************************************************************************
// Interrupt Service Routine
// Esta funcin se ejecuta cuando se detectan cambios de nivel (flancos de
// subida o de bajada) en los pines PD5, PD6 o PD7.
//****************************************************************************
ISR (PCINT3_vect)
{
char dato = ~(PIND|0x1F);
// Enmascarar bits PD5, PD6 y PD7
switch (dato)
{
case (1<<5): PINC = 0x01; break;
// Cambio de pin PD5
case (1<<6): PINC = 0x02; break;
// Cambio de pin PD6
case (1<<7): PINC = 0x04; break;
// Cambio de pin PD7
default: nop();
// Cambio de ms de un pin
}
delay_us(40000);
// 40ms para pasar los rebotes

}
//****************************************************************************
// Funcin principal
//****************************************************************************
int main(void)
{
DDRC = 0x07;
// Pines PC0, PC1 y PC2 para salida (LEDs)
/* Habilitar pull-up de pines PD5, PD6 y PD7 (pulsadores) */
PORTD = (1<<5)|(1<<6)|(1<<7);
/* Habilitar interrupciones PCINT de PORTD en los pines PD5, PD6 y PD7 */
PCICR = (1<<PCIE3);
PCMSK3 = (1<<5)|(1<<6)|(1<<7);
sei();

// Habilitacin general de interrupciones

while(1)
// Bucle infinito
{
/* Entrar en modo sleep (Power-Down mode) */
SMCR = (1<<SM1)|(1<<SE);
sleep();
}
}

Prctica: Control de Teclado por Interrupciones


Alguna vez le en uno de los documentos de Microchip que la interrupcin de Cambio de PORTB de sus
PICmicros fue pensada para controlar los pequeos teclados matriciales. En ese entonces los ATmega an no
tenan una caracterstica similar.
Pero como sabemos ahora, los ATmega como los que estamos estudiando llegaron ms lejos y nos permiten
manejar no solo uno sino varios teclados matriciales con el mnimo hardware, en estado sleep y desde
cualquier puerto del microcontrolador.
En este programa el AVR debe permanecer durmiendo (modo Power-down) y despertar solo cuando se pulsa
una tecla para leerla y mostrarla en el terminal serial.

Circuito de la prctica.
El cdigo fuente
/******************************************************************************
* FileName:
main.c
* Purpose:
Control de Teclado mediante Interrupciones PCINTx
* Processor: ATmega164P
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"

#include "keypad.h"
void SetupInt(void);
//****************************************************************************
// Interrupt Service Routine
// Esta funcin se ejecuta cuando se detectan cambios de nivel (flancos de
// subida o de bajada) en los pines PB4...PB7.
//****************************************************************************
ISR (PCINT1_vect)
{
char dato = keypad_read(); // Leer teclado
if(dato)
// Si fue tecla vlida
{
/* Esperar a que haya espacio en el buffer de transmisin */
while ((UCSR0A & (1<<UDRE0)) == 0 );
/* Colocar dato en el buffer de transmisin */
UDR0 = dato;
/* Esperar a que el teclado quede libre */
keypad_released();
}
SetupInt();
}
//****************************************************************************
// Funcin principal
//****************************************************************************
int main(void)
{
usart_init();
// Setup USART0 @ 9600-8N1
puts("\r\n Control de Teclado mediante Interrupciones PCINTx \r\r\n");
SetupInt();
/* Habilitar las interrupciones PCINT de PORTB en los pines PB4...PH7 */
PCICR = (1<<PCIE1);
PCMSK1 = 0xF0;
sei();

// Habilitacin general de interrupciones

while(1)
// Bucle infinito
{
/* Entrar en modo sleep (Power-Down mode) */
SMCR = (1<<SM1)|(1<<SE);
sleep();
}
}
//****************************************************************************
// Prepara el puerto B para que detecte un cambio de tensin producido al
// presionar una tecla.
//****************************************************************************
void SetupInt(void)
{
/* Configurar PORTB: Nibble de Rows salida, Nibble de Rows bajo
* nible alto entrada y habilitar pull-ups de nibble alto */
PORTB = 0xF0;
DDRB = 0x0F;
}

Segn el circuito y la librera del teclado, cuando no hay teclas pulsadas las lneas Col (nibble alto de PORTB)
se leen como 1 lgico (gracias a las pull-ups), y as deberan permanecer mientras el AVR est soando.
Por tanto, para que haya un cambio de nivel al pulsar una tecla, las lneas de Row (nibble bajo de PORTB)
deberan sacar 0 lgico. De esto se encarga la funcin SetupInt.
//****************************************************************************
// Prepara el puerto B para que detecte un cambio de tensin producido al
// presionar una tecla.
//****************************************************************************
void SetupInt(void)
{
/* Configurar PORTB: Nibble de Rows salida, Nibble de Rows bajo
* nible alto entrada y habilitar pull-ups de nibble alto */
PORTB = 0xF0;
DDRB = 0x0F;
}

El USART y la Interface RS232

Introduccin
La interface con un ordenador se puede realizar por cualquiera de sus puertos externos ms conocidos: serie,
paralelo o el USB. El paralelo casi ni se encuentra en los ordenadores de hoy y de momento el puerto USB
nos queda fuera de alcance por la complejidad del desarrollo del firmware (programa del microcontrolador).
As nos quedamos con el puerto serie.
Aprenderemos a volcar datos desde nuestro microcontrolador a la pantalla del ordenador (data logging), as
como a enviar datos mediante el teclado del PC hacia el microcontrolador.

El Estndar RS-232
Toda comunicacin elaborada entre dos dispositivos requiere conocer el protocolo que la gobierna a nivel
hardware y software. Para el puerto serie se trata del Estndar RS-232, o ms bien EIA/TIA-232 por las siglas
de Electronics Industry Association y Telecommunications Industry Association, sus desarrolladores.
El RS-232 fue originariamente pensado para regir las comunicaciones entre ordenadores y equipos de mdem
de la poca (hace ms de 40 aos). Con el tiempo han surgido otras versiones como RS-232-C, RS-232-D,
RS-232-E, etc., una ms reciente que la otra, pero con variaciones inapreciables por ser uno de los estndares
menos estrictos. Despus de todo, es solo un Estndar Recomendado o Recommended Standard; de ah la
RS.
En la literatura tcnica se acostumbra mucho utilizar los trminos DTE y DCE para referir a los dispositivos
que se comunican segn el Estndar RS-232. DTE (Data Terminal Equipment) suele representar al ordenador
y DCE (Data Circuit-terminating Equipment) designa a cualquier dispositivo conectado al ordenador (un
mdem se sobrentenda antes).
Sin embargo, estos conceptos no quedan del todo claros en redes del tipo ordenador-ordenador o
microcontrolador-microcontrolador usando el puerto serie. As que por comodidad en adelante hablaremos de

ordenador y mdem, viendo como mdem hasta donde quepa a cualquier dispositivo conectable al puerto
serie (el circuito de nuestro microcontrolador).
Ahora pasemos a describir los principales aspectos que nos recomienda el estndar.

Voltajes de los Niveles Lgicos RS-232


En las comunicaciones seriales RS-232 los valores para representar los 1s y 0s lgicos son muy diferentes de
los que estamos acostumbrados a usar en el mundo TTL. All no existen los 5V (para el 1) y 0V (para el 0).
Para entenderlo ms fcilmente veamos la siguiente figura, donde se compara la forma de onda de una seal
RS-232 con la forma de onda de una seal digital convencional.

Niveles de tensin para los 1s y 0s lgicos.


Puedes notar la enorme diferencia: los 1 lgicos se representan con voltajes negativos y los 0 lgicos, por
voltajes positivos; adems del amplio rango de los voltajes.

Un 1 lgico se expresa por una tensin de 5V a 15V. Este estado se llama spacing.
Un 0 lgico se da cuando la tensin en cualquiera de las lneas es de +5V hasta +15V. Este estado se
conoce como marking

Formato de Transferencia de Datos


Como en toda comunicacin serial, los datos viajan en grupos de bits. En este caso cada grupo o carcter
consta de un bit Start, los bits de Datos (8 por lo general), un bit de Paridad (opcional) y finaliza con uno o dos
bits de Stop.

Formato de un byte de dato en el Estndar RS-232.

Bit Start. Es la transicin de 1 a 0 e indica el inicio de una transferencia. En la lgica RS-232 podra
significar una transicin de -15V a +15V y en lgica TTL es una transicin de 5V a 0V.
Bits de Datos. Forman los datos en s que se desean transmitir. Cada dato puede ser de 5, 6, 7 u 8 bits.
Por supuesto, siempre preferimos trabajar con 8 bits (1 byte). El primer bit a transmitir es el menos
significativo o LSbit (Least Significant Bit).
Bit de Paridad. Este bit es opcional y se puede enviar despus de los bits de datos. Sirve para ayudar a
detectar posibles errores en las transferencias de datos. Es muy raramente usado, primero, porque es
poco efectivo (solo podra detectar errores, no corregirlos) y, segundo, porque hay mejores formas de
tratamiento de errores.
Bits Stop. Los bits de Stop son estados de 1 lgico. El Estndar dice que puede haber 1, 1.5 2 bits de
Stop al final de los datos (o del bit de paridad si lo hubiera).

Velocidad de Transmisin (Baud Rate)


El Baud Rate es el nmero de bits que se transmiten por segundo.
Debido a que estamos hablando de un tipo de transmisin asncrona, no existe una seal de reloj que
sincronice los bits de datos. Para que los dispositivos transmisor y receptor se entiendan correctamente
tambin es necesario que operen con el mismo baud rate. Los valores ms comunes que fija el Estndar RS232 son: 1200, 2400, 4800, 9600, 19200, 38400, 56000, 57600, 115200, 128000, 256000. Aunque las
versiones ms recientes del Estndar ponen un lmite de 20 kbits, es comn emplear los valores altos como
115200 (siempre que sea posible).
Sabemos que no es lo mismo la interface entre un ordenador y un microcontrolador usando un cable de 2 m de
longitud que conectarlo a un PLC a 8 m de distancia: la longitud del cable y la interferencia presente en el
ambiente son factores a considerar a la hora de escoger el baud rate.

Seales del Puerto Serie


Internamente el puerto serial de un ordenador es controlado por un circuito integrado (por ejemplo el 16750,
de 40 pines). De esas lneas solo 9 salen al exterior y desembocan en un conector DB9 macho (el que nosotros
vemos y donde conectbamos nuestro programador serial). Raras veces se ve que salen ms lneas para llegar
a un conector DB25.
El uso de las 9 seales tiene ms sentido cuando se trabaja con un mdem. Por eso vamos a seguir hablando
de mdem, pese a que bien puede ser reemplazado por otro dispositivo.

Pines del conector DB9 (macho) del puerto serie.


En la figura mostrada las direcciones de las flechas sealan si los pines son de entrada o de salida. Del mismo
modo, los colores ayudan a asociar los pines con funciones anlogas o complementarias, as:

TD y RD se encargan de transmitir y recibir los datos, respectivamente.


RTS y CTS sirven para controlar el Control del Flujo de Datos (Handshaking) hardware.
DTR, DSR y DCD intervienen en el establecimiento de la comunicacin.
Adems de ellos, estn la infaltable tierra (SG) y RI, usada exclusivamente en conexiones con un
mdem.

Ahora bien, cuando vamos a conectar el ordenador a un microcontrolador nuestro inters se puede reducir a
tres lneas: TD, RD y SG. Las dems: o pueden ignorarse, o pueden conectarse al mismo puerto serie
artificiosamente para evitar problemas de comunicacin, o pueden usarse para implementar un Control del
Flujo de Datos (Handshaking) hardware, con el microcontrolador emulando algunas funciones de mdem. En
cualquiera de los tres casos, eso depender del software de ordenador usado para controlar el puerto serie.
En el conector hembra la posicin de los pines se puede ver diferente a fin de establecer una conexin
cruzada, es decir, para que el TD de un dispositivo se una con el RD del otro y viceversa. Lo mismo debe
pasar con los pares RTS-CTS y DTR-DSR.

Control del Flujo de Datos (Handshaking)


Generalmente el ordenador superar a su interlocutor (mdem u otro) tanto en velocidad de transferencia
como en los buffers de recepcin de datos. Para que el mdem no empiece a perder los datos llegados el
Estndar contempla mecanismos de control de flujo de datos ya sea va hardware o software.
El control de flujo de datos por software se identifica por el uso de los caracteres Xon (ascii 0x11) y Xoff
(ascii 0x13). El dilogo es as: cuando, por alguna razn, el mdem ya no desea recibir ms datos del
ordenador entonces le enva el carcter Xoff dicindole que suspenda la transmisin al menos temporalmente.
Cuando el mdem est nuevamente dispuesto a aceptar ms datos le enviar el carcter Xon y el ordenador
reanudar la transferencia.
Lo bueno del mtodo es que el hardware requerido es el mnimo (ver la siguiente figura) y lo malo es que
ambos dispositivos deben soportar las transferencias full duplex que exige el estndar RS232 para este caso.

Conexin bsica para el Handshaking software.


El control de flujo por hardware hace participar activamente a las lneas RTS y CTS.
En un tipo de comunicacin simplex el protocolo es ste: cuando el ordenador quiere enviar datos al mdem
pone un 1 en RTS. Si el mdem est dispuesto a recibir esos datos, le responder con un 1 por la lnea CTS y
el ordenador empezar a transmitir los datos; de otro modo, le responder con un 0 y el ordenador tendr que
posponer el envo de datos.
Cuando la comunicacin es half duplex o full duplex el protocolo vara, pero eso ya no lo tocaremos aqu para
no seguir enredndolo. Solo vemos lo suficiente del estndar, en este caso para entender las conexiones
alternativas entre un ordenador y un microcontrolador que se usan en algunas ocasiones y que veremos en la
siguiente seccin.

Cableado para el handshaking hardware entre dos dispositivos.


En el diagrama se han sumado las lneas DTR, DSR y DCD (de color marrn), por las que los dispositivos se
informan el uno al otro si estn listos o no para iniciar la comunicacin.

Interface Serial Microcontrolador-Ordenador


Como es de esperar, el enfoque se divide en dos partes:

Requerimientos Hardware

Nos vamos a enfocar en dos aspectos.


Primero veamos el tema del transceiver. Dado que los niveles de tensin en el Estndar RS-232 (de 12V, 0V
y +12V en el ordenador) no son compatibles con los niveles habituales de los microcontroladores (de 0 y 5V),
se requiere de un transceiver que convierta estas tensiones de unos niveles a otros y viceversa.
Sin duda, el MAX232 es el ms famoso de todos. Como se ve en su esquema, mostrado abajo, el MAX232
puede trabajar con una fuente de alimentacin de 5V y provee dos canales de transmisin y dos de recepcin,
aunque solo se suele usar un par. A su gran tamao se suma como desventaja el uso de condensadores
externos, para bombear la carga necesaria en los circuitos doblador e inversor de voltaje.

Interface entre un microcontrolador y un computador mediante el transceiver MAX232.


El mismo fabricante del MAX232, Dallas Semiconductors, ofrece sus versiones mejoradas como el MAX203,
que no requiere de capacitores externos, o el MAX202, que brinda proteccin contra cargas electrostticas.
Mejor aun para pequeos circuitos sera el DS275 (de 8 pines), el cual tampoco requiere de capacitores
externos y cuenta con el par justo de drivers de transmisin y recepcin de datos. Su principal inconveniente
es que est diseado para operar solo en transferencias half duplex.
Para conocer ms del funcionamiento interno de los transceivers es recomendable que consultes sus
respectivos datasheets.

Interface entre un microcontrolador y un computador mediante el transceiver DS275.


El segundo aspecto hardware que interesa es el relacionado con el Control del Flujo de Datos (Handshaking):
en los dos esquemas presentados anteriormente las retro-conexiones en el conector DB9 (de color violeta) son
opcionales. Solo harn falta cuando el programa terminal del ordenador est configurado para utilizar los
pines indicados, as:
RTS (7) se conecta a CTS (8) para que siempre que el ordenador desee enviar datos al microcontrolador, se
responda a s mismo con un permiso concedido.
Anlogamente, DTR (4) se une a DSR (6) para que cuando el ordenador informe un estoy listo para la
comunicacin, su eco (hacindose pasar por el microcontrolador) le responda con un yo tambin lo estoy.
A veces DTR tambin se dirige a DCD (1).

Requerimientos Software
Por un lado necesitamos unas rutinas para el microcontrolador que gestionen las funciones del Estndar RS232. stas pueden implementarse tranquilamente a nivel software debido a su simplicidad o mediante el
mdulo USART, el cual por supuesto ofrecer mucha ms eficiencia y flexibilidad.
Por otro lado, necesitaremos un programa de ordenador que se encargue de controlar su puerto serie. A su vez,
este programa puede ser uno desarrollado por nosotros mismos, que nos permitira tener el control total del
puerto serie y podramos transmitir y recibir todo tipo de datos (binarios o de texto). Tambin podramos
implementar tcnicas alternativas de control de flujo de datos (aparte de los descritos arriba), o sofisticados
mecanismos para el control de errores en la transferencias de datos. Como ves, se ve muy atractivo, pero
tambin requiere de conocimientos a mediano nivel sobre programacin en lenguajes como Visual C++,
Delphi o Visual Basic.
Como alternativa prctica, podemos usar softwares como el Hyperterminal de Windows, Serial Port Monitor,
Putty o Tera Term. Estos son programas de tipo consola que nos permiten visualizar los datos que se
transfieren hacia/desde el puerto serie. Por no ofrecer tanta flexibilidad nos limitaremos a trabajar con datos de
texto. Conforme vamos escribiendo los caracteres en la consola, se irn enviando hacia nuestro
microcontrolador. As mismo, los caracteres enviados desde el microcontrolador se irn mostrando en la
consola, todo en tiempo real.

Interface del programa Tera Term

Uso del Programa Tera Term


Ya tenemos todo listo para el microcontrolador. Ahora nos falta ejecutar algn programa terminal en nuestro
ordenador para empezar el intercambio de datos. De los muchos que hay vamos a elegir el Tera Term, que es
lo mejor que he podido encontrar y no lo digo precisamente porque sea de licencia GPL. Lo puedes bajar
libremente desde su web http://ttssh2.sourceforge.jp, o haciendo clic aqu.
Tera Term es un emulador de terminal (programa de comunicaciones) que soporta:

Conexiones con el puerto serie.


Conexiones TCP/IP (telnet, SSH1, SSH2).
Comunicaciones IPv6
Emulacin de VT100 y de VT200/300.
Emulacin de TEK4010.
Protocolos de Transferencia de Archivos (Kermit, XMODEM, YMODEM, ZMODEM, B-PLUS y Quick-VAN).
Scripts usando el Lenguaje Tera Term.
Sets de caracteres Japons, Ingls, Ruso y Coreano.
Codificacin de caracteres UTF-8.

Bien, una vez descargado, los instalas aceptando lo que se te pida hasta llegar a

Si no los vas a utilizar o si no tienes idea de lo que significan, te recomiendo desmarcar las casillas de TTSSH,
CygTerm+, LogMeTT, TTEdit y TTProxy. De lo contario, la instalacin te pedir aceptar los trminos y la
configuracin de cada aplicacin por separado.
Suponiendo que seguiste mi recomendacin, la instalacin terminar enseguida y solo se crear un icono de
acceso en el escritorio.
Cada vez que iniciemos Tera Term se nos presentar la siguiente ventana, donde debemos escoger la opcin
Serial y en Port debemos seleccionar el puerto serie COM a usar. Normalmente los ordenadores actuales solo
tienen un puerto COM disponible, el COM1. Despus de hacer clic en OK se abrir el puerto serial. Tera
Term es muy potente y ser capaz de quitarle el control del puerto a alguna otra aplicacin que lo est
utilizando.

Y as de rpido estaremos al frente de la siguiente ventana, que ya podemos utilizar para nuestras
comunicaciones seriales, ya que su configuracin por defecto suele ser la ms habitual. La barra de ttulo
indica que el baud rate usado es de 9600 y que se tiene seleccionado el puerto COM1.

De vez en cuando ser necesario cambiar la configuracin de comunicacin que usa Tera Term por defecto.
Para ello debemos ir al men Setup Serial Port El parmetro Transmit delay es el retardo que habr entre
cada uno de los datos que se enven y/o entre cada lnea de caracteres. Quiz interese incrementar este retardo
cuando el microcontrolador no etnga la capacidad de recibir los datos con la prestancia necesaria. En cuando a
los otros parmetros, ya los discutimos de sobra en el Estndar RS232.

Otra ventana donde encontrar algunas opciones tiles est en men Setup Terminal New-line establece si
el cursor pasar a la siguiente lnea con los caracteres CR (Carriage Return = 0x0D = \r) y/o LF (LineFeed =

Newline= 0x0A = \n). Por lo general estos caracteres se manejan a nivel firmware (desde el programa del
microcontrolador), del mismo modo el eco lo hace e microcontrolador y raras veces ser recomendable marcar
la casilla de Local echo.

Ahora vamos a modificar el aspecto del programa. Puede que sea un tema superfluo pero al menos para m
esta apariencia vale mucho. Primero cambiaremos el tipo de fuente yendo al men Setup Font Y bueno,
qu te puedo decir, escoge la fuente de tu preferencia.

Para quienes deseen que la consola luzca como el terminal simulado de Proteus pueden ir al men Setup
Window. All escogemos la forma del cursor (Cursor Shape) como Horizontal line. Luego en el marco Color
empezamos por cambiar el color de fondo (Background) a negro y despus el color de fuente (Text) a verde
como se indica en la figura.

Ya te habrs dado cuenta de que la ventana del terminal se limpia cada vez que cambias su tamao. Para evitar
que lo haga debemos ir al men Setup Aditional settings y desmarcar la opcin Clear display when
window resized, como se ve abajo.

Para limpiar la ventana podemos usar las opciones Clear screen y Clear buffer del men Edit. La primera
opcin simplemente recorre el contenido de la ventana, por eso es preferible usar Clear buffer, que resetea
todo el contenido de la ventana. Las otras opciones de este men hablan por s solas.

Para terminar configuraremos el uso del teclado numrico. Esto no es un aspecto decorativo, es muy
importante y lo deje para el final solo por ser algo engorroso. Por defecto, Tera Term emula el teclado VT100,
por lo que algunas teclas como + o - no funcionarn como en un teclado convencional.
Bueno, para ir directo al grano, seguimos el directorio de instalacin de Tera Term, que en Windows 7 suele
ser C:\Program Files (x86)\teraterm/ y utilizamos un editor de texto como el bloc de notas o Notepad++ para
abrir el archivo KEYBOARD.CNF. Este archivo tiene varias secciones; nosotros nos ubicamos en [VT
numeric keypad], que empieza en la lnea 29 como se ve abajo.

Las lneas precedidas de punto y coma (;) son comentarios y no cuentan. Son los nmeros mostrados en
naranja los que debemos editar: debemos cambiarlos todos por la palabra off, desde Num0 hasta PF4, 18 en
total, y debe quedar como en la siguiente figura.

Ahora guarda el archivo con los cambios realizados y reinicia el programa Tera Term. O tambin puedes ir al
men Setup Load key map y recargar manualmente el archivo KEYBOARD.CNF.
Finalmente, puesto que usaremos este programa con tanta frecuencia y no querremos estar configurndolo
cada vez que lo ejecutemos, debemos guardar la configuracin realizada yendo al men Setup Save setup
en la ventana presentada simplemente hacemos clic en Guardar y con eso bastar para que Tera Term se abra
siempre con la configuracin actual.

El USART, USART0 y USART1 de los AVR

USART es la sigla de Universal Synchronous Asynchronous Receiver Transmitter. Es el perifrico que


incorporan muchos microcontroladores para comunicarse con dispositivos que soportan el estndar RS-232.
De su nombre se deprende que puede trabajar en modo Sncrono o Asncrono. En esta presentacin nos
enfocaremos exclusivamente al modo asncrono.
Algunos AVR (como los viejos ATmega32, ATmega8535, etc.) poseen un solo mdulo USART llamado
simplemente USART. Los AVR mejorados como los que utilizamos en cursomicros tienen uno o dos
mdulos, llamados USART0 y USART1, con caractersticas de control idnticas. Por ejemplo, los ATmega de
la serie 8xx tienen USART0 y los ATmega de la familia 4xx tienen adicionalmente el USART1.
El USART0 y el USART1 son gemelos. Entre ellos y los viejos USART de los AVR hay una mnima
diferencia que muchas veces se pasar por alto sin notarlo. De hecho, aparte de los nombres de los registros de
relacionados, el control del USART es compatible en todos los AVR que lo tienen; incluso hay un gran
parecido con los de otros microcontroladores. As que creo que valdr la pena demorarnos un poco en esta
teora.
Las caractersticas comunes del USART en los AVR son:

Asncronamente pueden trabajar en modo full-duplex, esto es transmitir y recibir datos, al mismo tiempo
inclusive.
Pueden generar interrupciones al recibir datos o despus de enviarlos.
Puede operar en modo SPI maestro. Esta opcin no est disponible en los viejos AVR.
Operan en background (detrs del escenario) para que las transferencias de datos se lleven a cabo mientras el
CPU realiza otras tareas.
El baud rate es configurable por el usuario.
Los datos pueden ser de 5, 6, 7, 8 9 bits. Puede trabajar con uno o dos bits de Stop. Y adems de aceptar el
bit paridad puede computar a nivel hardware su valor para el dato actual.

Los Registros del USART


Adems de estos registros todava faltan por citar los que controlan las interrupciones del USART. Aunque ya
los conocemos bastante bien los veremos por separado.
Los nombres de los registros y de sus bits varan de acuerdo con el nmero de USART. En toda mi exposicin
tomo como referencia el USART0 y por ello vers el nmero 0 acompaando a cada registro. Si utiliza el
USART1 se debe cambiar el 0 por el 1 en cada registro y en cada bit; y si se utiliza el USART viejo
simplemente se quita el 0. Por ejemplo, en el USART1 el registro UCSR0A se debe suprimir por UCSR1A.
Lo mismo debe aplicarse a los nombres de cada bit.

UDR0. El USART tiene dos buffers para las transferencias de datos: un buffer de transmisin (donde se cargan
los datos a trasmitir) y un buffer de recepcin (donde se almacenan los datos recibidos). Mediante el registro
UDR0 se accede a ambos buffers. Esto es, se accede al buffer de transmisin (en las operaciones de escritura)
y al buffer de recepcin (en las operaciones de lectura). UDR0 significa USART Data Register 0. Su nombre en
los USART1 es UDR1 y en los USART viejos se llama simplemente UDR.
UCSR0A, UCSR0B y UCSR0C. (USART Control and Status Register A, B y C). Son los Registros de Control y Estado
del USART0. Creo que los nombres lo dicen todo. Para los que an usan los viejos AVR, deben saber que los
registros UCSRC y UBRRH comparten la misma locacin en espacio de los registros de E/S. Ellos deben setear el
bit 7 para escribir en el registro UCSRC y limpiarlo para escribir en UBRRH. Las operaciones de lectura son
inusuales.
UBRR0L y UBRR0H. Son los registros generadores de Baud Rate del USART0. Juntos forman un registro de 16
bits cuyo valor establece la velocidad de transferencia de datos.

Todos estos registros son de 8 bits. UDR0 y el ltimo par no tienen formatos preestablecidos y podran aceptar
cualesquiera valores. Los registros de control y estado tienen los siguientes mapas de bits:
UCSR0ARXC0
TXC0
UDRE0
UCSR0BRXCIE0
TXCIE0
UDRIE0
UCSR0CUMSEL01 UMSEL00 UPM01

FE0
RXEN0
UPM00

DOR0
TXEN0
USBS0

UPE0
UCSZ02
UCSZ01

U2X0
RXB80
UCSZ00

MPCM0
TXB80
UCPOL0

En lo sucesivo iremos describiendo las funciones de estos dos registros y de cada uno de sus bits. Algunos bits
estn relacionados con la operacin del USART en modo sncrono, tema que de momento no nos compete, y
sern ignorados.

Inicializacin del USART


Lo primero que debemos hacer con el USART es configurar su operacin: esto es, establecer el modo de
operacin, fijar el formato de los datos, poner la velocidad de las transferencias y habilitar los mdulos
Receptor y/o Transmisor. Los bits de los registros de Control y Estado relacionados con la configuracin de la
librera [usart.h y usart.c] usada en cursomicros.com son los siguientes. Los bits no mencionados los
trataremos de cerca al estudiar los modos de operacin Sncrono y SPI Master.
MPCM0. Recordemos que inicialmente el estndar RS232 permite comunicaciones solo entre dos dispositivos
(DTE y DCE). Actualmente los USART tambin soportan comunicaciones con otros protocolos como del
RS485, donde se pueden engarzar al bus varios dispositivos en relaciones maestro-esclavo y con direcciones
que los identifican (parecido al bus I2C). Para que el USART del AVR pueda integrarse a esas redes debemos
setear el bit MPCM0 (Multiprocessor Communication Mode). Para comunicaciones RS232 este bit se debe
mantener en cero.
TXEN0 y RXEN0. Son los bits para habilitar los mdulos Transmisor y Receptor del USART. No es
necesario habilitar los dos mdulos al mismo tiempo. Al habilitar un mdulo (seteando el bit TXEN0 o
RXEN0), ste asumir el control del pin respectivo (TXD0 para el transmisor y RXD0 para el receptor) y por
tanto dichos pines dejarn de funcionar como entrada y salida generales, sin importar el valor de los bits
correspondientes en los registros DDR, PORT y PIN.
UMSEL01 y UMSEL00. Estos bits establecen uno de los tres modos en que puede trabajar el USART0: Modo
Asncrono, modo Sncrono y modo SPI Maestro. La configuracin por defecto es Asncrono, con ambos bits
iguales a cero, as que de momento no tendremos que tocarlos.
USBS0. Si este bit vale 0, el mdulo transmisor enviar un bit Stop al final de cada dato. Si vale 1, enviar 2
bits Stop. El mdulo receptor solo evala el primer bit Stop recibido, as que no le interesa esta configuracin.
Tampoco tocaremos este bit puesto que trabajaremos con un solo bit Stop porque es el valor preestablecido en
todos sistemas RS232, y as nos ahorramos settings adicionales ;).
UCSZ02, UCSZ01 y UCSZ00. Estos bits establecen el tamao de los datos que se utilizarn en las
transferencias, los cuales pueden ser desde 5 hasta 9 bits. El formato seleccionado ser empleado por ambos
mdulos, transmisor y receptor. Si los datos son de 8 bits se trabajar con el valor completo del registro
UDR0; si los datos son de menos bits se obviarn los bits de mayor peso del registro UDR0; y si los datos son
de 9 bits, los 8 bits de UDR0 se complementarn con el bit TXB80 (para las transmisiones) y RXB80 (para las
recepciones).
Por defecto todos estos bits inician a cero, as que observando la siguiente concluimos en que deberemos
cambiarlos a 011.

UCSZ02UCSZ01UCSZ00Tamao del Carcter


0
0
0
5 bits
0
0
1
6 bits
0
1
0
7 bits
0
1
1
8 bits
1
0
0
Reservado
1
0
1
Reservado
1
1
0
Reservado
1
1
1
9 bits
U2X0. Como habamos estudiado, el baud rate es la velocidad de transferencias de datos, se mide en
bits/segundo y hay valores estndar que debemos respetar (9600, 19200, 115200, etc.).
Seteando el bit U2X0 se divide el prescaler generador de baud rate de modo que la velocidad de transferencia
de datos se multiplique por dos. Este bit solo tiene efecto en el modo Asncrono y dependiendo de su valor se
disponen de dos frmulas para calcular el valor del baud rate final. F_CPU es la frecuencia del procesador y
recuerda que es una constante definida en el archivo avr_compiler.h.
U2X0 = 1 (Velocidad Doble)

U2X0 = 0 (Velocidad Normal)

UBRR0 est conformado por la unin de dos registros de E/S: UBRR0H y UBRR0L. Con su capacidad de 16
bits y la frmula de U2X0 = 1 se pueden obtener los suficientes valores de baud rates como para hacernos
olvidar de la frmula con U2X0 = 0. De hecho, analizando las frmulas se deduce que cualquier valor de baud
con U2X = 0 tambin se puede conseguir con la frmula de U2X0 = 1, aunque a veces esto devendr en una
menor performance en el muestreo de la seal de recepcin.
En la prctica no interesa tanto el hecho de que U2X0 = 1 incremente la velocidad de las transferencias,
sino que se pueden obtener baud rates ms precisos.
En consecuencia, la mejor decisin en la mayora de los casos ser escoger la primera frmula, con U2X = 1.
De all debemos despejar UBRR0 para calcular su valor. Esto nos da siguiente expresin, que escrita en forma
lineal sera UBRR0 = F_CPU/(8*BAUD) 1.
El valor generado para UBRR0 raras veces ser exacto y en tales casos se deber escoger el ms cercano y
recalcular el valor del baud rate. Debemos tener cuidado con los resultados obtenidos puesto que hay valores
impuestos por el estndar RS232 y que debemos respetar. En todo caso, puedes revisar las tablas de baud rate
presentes en los datasheets.
Con todo lo expuesto ya podemos implementar la siguiente funcin de inicializacin del USART0. Es irnico
que tanta teora se condense en tan poco cdigo, pero es ms gracioso saber que la configuracin establecida
se puede resumir con la notacin BAUD 8N1, que ledo en orden significa Baud rate = BAUD, formato de
datos = 8 bits, No (sin) bit de paridad y 1 bit de Stop. Deberemos recordar esos parmetros a la hora de
configurar el software terminal del lado del ordenador.
//****************************************************************************
// Inicializa el USART0.
//****************************************************************************
void usart_init(void)

{
/* Configurar baud rate
*/
UCSR0A |= (1<<U2X0);
UBRR0 = F_CPU/(8*USART_BAUD)-1;
/* Configurar modo de operacin Asncrono y formato de frame a
* 8 bits de datos, 1 bit de stop y sin bit de paridad. */
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
/* Habilitar mdulos Receptor y Transmisor
UCSR0B = (1<<RXEN0)|(1<<TXEN0);

*/

#if defined( __GNUC__ )


/* Asociar las funciones 'putchar' y 'getchar' con las funciones de entrada
* y salida (como printf, scanf, etc.) de la librera 'stdio' de AVR-GCC */
fdevopen((int (*)(char, FILE*))putchar, (int (*)(FILE*))getchar);
#endif
}

Una aclaracin para quienes usen los viejos AVR: en dichos AVR los registros UBRRH y UBRRL se
encuentran bastante lejos uno del otro en el espacio de los registros de E/S. Como consecuencia la sentencia
de asignacin UBRR = F_CPU/(8*USART_BAUD)-1; no ser vlida. Tendrn que escribirlos por separado

Transmisin de Datos
El dato que el USART0 transmitir debe ser previamente cargado en el registro UDR0. Con ello el dato pasar
al Registro de Desplazamiento de Transmisin si es que est vaco. Luego el dato saldr serialmente bit a bit
por el pin TXD0. Pero si el Registro de deslazamiento se encuentra transmitiendo un dato previo, el nuevo
dato permanecer en UDR0 hasta que el registro de desplazamiento quede disponible. En ese lapso de tiempo
el registro UDR0 no podr aceptar un nuevo dato.

Mdulo de Transmisin del USART0.


Mientras el registro UDR0 contenga algn dato, el bit UDRE0 (del registro UCSR0A) valdr 0. Cuando
UDR0 est nuevamente libre (cuando haya descargado su contenido en el registro de desplazamiento), el flag
UDRE0 se setear automticamente por hardware. Por tanto, ser necesario comprobar que el bit UDRE0
valga 1 antes de intentar escribir un nuevo dato en UDR0. La activacin del flag UDRE0 puede generar una
interrupcin si es que est habilitada. Detallaremos las interrupciones del USART en otra seccin ms
adelante.

Una conclusin de lo descrito es que es posible escribir en UDR0 hasta dos datos sin pausa intermedia, el
primero ir al registro de desplazamiento y el segundo se quedar en UDR0. As podemos decir que el
USART0 tiene un buffer FIFO de transmisin de dos niveles.
Con lo expuesto ya se puede codificar la siguiente funcin putchar. El encabezado de esta funcin est de
acuerdo con su definicin en la librera stdio.h del compilador C. All se indica que putchar debe tener un
parmetro de entrada y uno de salida, ambos de tipo int, aunque no en la prctica no parezca que sea
necesario.
//****************************************************************************
// Transmite el byte bajo de 'dato' por el USART
//****************************************************************************
int putchar(int dato)
{
/* Esperar a que haya espacio en el buffer de transmisin */
while ((UCSR0A & (1<<UDRE0)) == 0 );
/* Colocar dato en el buffer de transmisin */
UDR0 = dato;
return dato;
}

Existe un flag adicional llamado TXDC (en el registro UCSR0A) que se setea cuando se haya terminado de
transmitir el dato del registro de desplazamiento y al mismo tiempo no exista otro dato presente en el registro
UDR0. Este flag tambin tiene la capacidad de disparar una interrupcin ante dicho evento. En ese caso el flag
TXDC se limpiar automticamente al ejecutarse la funcin ISR; pero siendo de lectura/escritura tambin se
puede limpiar por software escribindole uno sobre l.

Recepcin de Datos
Los datos seriales que llegan ingresan bit a bit por el pin RXD0 y se van depositando en el Registro de
Desplazamiento de Recepcin. Cuando el dato est completo (cuando llegue su bit Stop) pasar paralelamente
al registro UDR0. Luego podremos leer el dato del registro UDR0. Pero si el registro UDR0 est ocupado con
datos previos que no han sido ledos por el procesador, el nuevo dato permanecer en el registro de
desplazamiento hasta que haya espacio en UDR0.

Mdulo de Recepcin del USART0.

Apenas haya un dato en UDR0 se setear el flag RXC0 (del registro UCSR0A). As que para saber si hay un
dato all pendiente de ser ledo debemos comprobar el bit RXC0. Est flag es de solo lectura y se limpiar
automticamente cuando hayamos terminado de leer todos los datos del buffer UDR0. La activacin del flag
RXC0 tambin puede generar una interrupcin si est previamente habilitada.
No debemos confundir el UDR0 de recepcin con el UDR0 de transmisin. Aunque tengan el mismo nombre,
corresponden a dos registros diferentes. Es ms, el UDR0 de recepcin en realidad es un buffer de dos niveles
(puede almacenar hasta dos datos).
Ahora podemos decir que el USART0 tiene un buffer FIFO capaz de almacenar hasta tres datos al mismo
tiempo: dos en el buffer UDR0 y uno en el registro de desplazamiento. Si en este momento se detectara la
llegada de un nuevo dato por el pin RXD0, dicho dato se perdera y como seal se activara el flag de
desbordamiento DOR0 (Data OverRun).
Como no querremos que ocurra dicho evento trgico ser recomendable que leamos de inmediato cada nuevo
que llegue al USART. Siguiendo esta recomendacin la funcin de recepcin tendr el siguiente aspecto. Esta
implementacin tambin toma como referencia la definicin de getchar presente en el archivo stdio.h del
compilador C.
//****************************************************************************
// Recibe un byte de dato del USART
//****************************************************************************
int getchar(void)
{
/* Esperar a que haya al menos un dato en el buffer de recepcin */
while ((UCSR0A & (1<<RXC0)) == 0 );
/* Leer y retornar el dato menos reciente del buffer de recepcin */
return UDR0;
}

Registros del USART0


El Registro UCSR0A
UCSR0ARXC0
TXC0
UDRE0
RXC0
USART Receive Complete

TXC0

FE0

DOR0

UPE0

U2X0

Este bit de flag vale uno cuando hay datos no ledos en el buffer de recepcin y vale cero
cuando el buffer de recepcin est vaco (esto es, no contiene datos por leer). Si el
mdulo receptor est deshabilitado, el buffer de recepcin ser liberado y
consecuentemente el bit RXC0 se pondr a cero. E flag RXC0 se puede usar para generar
una Interrupcin de Recepcin Completada (ver la descripcin del bit RXCIE0).
USART Transmit Complete
Este bit de flag se pone a uno cuando un dato completo ha terminado de salir del
Registro de Desplazamiento de Transmisin y no hay ningn dato presente en el registro
UDR0. El bit TXC0 se limpia automticamente cuando se ejecuta la funcin de
interrupcin, o se puede limpiar por software escribiendo uno sobre l. El flag TXC0
puede generar la Interrupcin de Transmisin Completada (ver la descripcin del bit
TXCIE0).

MPCM0

UCSR0ARXC0
TXC0
UDRE0
UDRE0
USART Data Register Empty

FE0

DOR0

UPE0

U2X0

FE0

El flag UDRE0 indica si el buffer de transmisin (UDR0) est listo para recibir un nuevo
dato. Si el bit UDRE0 vale uno, el buffer est vaco, y por tanto est listo para ser escrito.
El flag UDRE0 puede generar una Interrupcin de Registro de Dato Vaco (ver
descripcin del bit UDRIE0). El bit UDRE0 se pone a uno despus de un reset para
indicar que el Transmisor est listo.
Frame Error

DOR0

Este bit se pone a uno si el siguiente carcter en el buffer de recepcin tuvo un error de
frame en la recepcin del dato. Esto es, cuando el primer bit Stop del siguiente carcter
en el buffer de recepcin es cero. Este bit ser vlido hasta que se lea el buffer de
recepcin UDR0. El bit FE0 vale cero cuando el bit Stop del dato recibido es uno.
Siempre que se escriba en el registro UCSR0A este bit se debe mantener en cero.
Data OverRun

UPE0

Este bit se pone a uno cuando se detecta una condicin de desbordamiento de dato.
Ocurre un desbordamiento de dato (Data OverRun) cuando el buffer de recepcin est
lleno (con dos caracteres), contiene un nuevo carcter esperando en su Registro de
Desplazamiento, y se detecta un nuevo bit Start. Este bit es vlido hasta que se lea el
buffer UDR0. Siempre que se escriba en el registro UCSR0A este bit se debe mantener
en cero.
USART Parity Error

U2X0

Este se pone a uno al detectarse un Error de Paridad en el buffer de recepcin cuando


estn habilitados la recepcin del bit de Paridad y su comprobacin (UPM01 = 1). Este
bit ser vlido hasta que se lea el buffer UDR0. Siempre que se escriba en el registro
UCSR0A este bit se debe mantener en cero.
Double the USART Transmission Speed

MPCM0

Este bit solo tiene efecto en el modo de operacin asncrono. Se debe escribir cero en
este bit cuando se usa el modo de operacin sncrono.

MPCM0

En las comunicaciones asncronas, si se escribe uno en este bit se reducir el divisor del
generador de baud rate de 16 a 8, dando como resultado la multiplicacin por dos de la
velocidad de transferencia de datos.
Multi-processor Communication Mode
Este bit habilita el modo de Comunicacin Multiprocesador. Cuando se escribe uno en el
bit MPCM0, sern ignorados todos los frames recibidos por el USART que no contengan
informacin de direccin. El mdulo Transmisor no queda afectado por la configuracin
de los bits MPCM0.

El Registro UCSR0B
UCSR0BRXCIE0
TXCIE0
UDRIE0
RXCIE0 RX Complete Interrupt Enable

RXEN0

TXEN0

UCSZ02

RXB80

Al escribir uno en este bit se habilita la Interrupcin de Recepcin Completada cada vez

TXB80

UCSR0BRXCIE0
TXCIE0
UDRIE0
RXEN0
TXEN0
UCSZ02
RXB80
TXB80
que se active el flag RXC0. Para que se genere la interrupcin ser necesario que tambin
el bit enable general I de SREG valga uno.
TXCIE0
TX Complete Interrupt Enable

UDRIE0

Al escribir uno en este bit se habilita la Interrupcin de Transmisin Completada cada vez
que se active el flag TXC0. Para que se genere la interrupcin ser necesario que tambin
el bit enable general I de SREG valga uno.
USART Data Register Empty Interrupt Enable
Al escribir uno en este bit se habilita la interrupcin al activarse el flag UDRE0. Se generar una
Interrupcin de Dato de Registro Vaco solo si valen uno el bit UDRIE0, el flag de Global de
Interrupciones en el registro SREG y el bit UDRE0 en el registro SREG.

RXEN0

Receiver Enable

TXEN0

Al escribir uno en este bit se habilita el mdulo Receptor del USART. El receptor tomar
el control del pin RXD0. Al deshabilitar el receptor se liberar el buffer de recepcin
invalidando los flags FE0, DOR0 y UPE0.
Transmitter Enable

UCSZ02

Al escribir uno en este bit se habilita el mdulo Transmisor del USART. El transmisor
tomar el control del pin TXD0. La des-habilitacin del transmisor (escribiendo 0 en
TXEN0) no se har efectiva hasta que se completen las transmisiones en marcha y las
pendientes, esto es, cuando el registro UDR0 y el registro de desplazamiento estn vacos.
Cuando se deshabilite el Transmisor el pin TXD0 quedar en libertad.
Character Size

RXB80

El bit UCSZ02 combinado con los bits UCSZ01 y UCSZ00 del registro UCSRC establece
el nmero de los bits de datos (tamao del carcter) en los frames que usarn el
Transmisor y el Receptor.
Receive Data Bit 8

TXB80

RXB80 es el noveno bit del dato recibido cuando se trabaja con datos seriales de 9 bits.
Se debe leer antes de leer los bits bajos del registro UDR0.
Transmit Data Bit 8
TXB80 es el novena bit del dato a transmitir cuando se trabaja con datos de 9 bits. Se
debe escribir antes de escribir los bits bajos del registro UDR0.

El Registro UCSR0C
UCSR0C UMSEL01 UMSEL00
UPM01
UPM00
USBS0
UCSZ01
UCSZ00
UCPOL0
UMSEL01
USART Mode Select
UMSEL00
Estos bits seleccionan el modo de operacin del USART como se muestra en
la siguiente tabla.
UMSEL01 UMSEL00
Mode
0
0
USART Asncrono

UCSR0C UMSEL01 UMSEL00


UPM01
UPM00
USBS0
UCSZ01
UCSZ00
UCPOL0
0
1
USART Sncrono
1
0
Reservado
1
1
Master SPI (MSPIM)
UPM01:
Parity Mode
UPM00
Estos bits habilitan y configuran la generacin y comprobacin del bit de
paridad. Si est habilitado, el Transmisor generar y enviar automticamente
el bit de paridad de cada dato. El Receptor calcular el bit de paridad de cada
dato recibido y lo comparar con la configuracin del bit UPM00. Si se
detecta una discordancia, se setear el flag UPE0 del registro UCSR0A.

USBS0

UPM01 UPM00
0
0
0
1
1
0
1
1
Stop Bit Select

Modo de Paridad
Deshabilitado
Reservado
Habilitado, Paridad Par
Habilitado, Paridad Impar

Este bit selecciona el nmero de bits Stop que utilizar el Transmisor. El


Receptor ignora esta configuracin.

UCSZ01:
UCSZ00

USBS0
0
1
Character Size

Bits Stop
1 bit
2 bits

Estos bits se combinan con el bit UCSZ02 del registro UCSR0B para
establecer el nmero de bits de los datos (tamao de carcter) que utilizarn el
Transmisor y el Receptor.

UCPOL0

UCSZ02 UCSZ01 UCSZ00


Tamao del Carcter
0
0
0
5 bits
0
0
1
6 bits
0
1
0
7 bits
0
1
1
8 bits
1
0
0
Reservado
1
0
1
Reservado
1
1
0
Reservado
1
1
1
9 bits
Clock Polarity
Este bit solo se usa en el modo de operacin Sncrono. En el modo Asncrono
debe permanecer en cero. El bit UCPOL0 establece la relacin entre el cambio
de los datos de salida y la seal de reloj XCK0, y la relacin entre el muestreo
de los datos de entrada y la seal de reloj XCK0.
UCPOL0
0

El Cambio de Datos ocurre


(A la salida del pin TxD0)
En el flanco de Subida del pin

El Muestreo de Datos ocurre


(A la entrada del pin RxD0)
En el flanco de Bajada del pin

UCSR0C UMSEL01 UMSEL00


UPM01
UPM00
USBS0
UCSZ01
UCSZ00
XCK0
XCK0
En el flanco de Bajada del pin
En el flanco de Subida del pin
1
XCK0
XCK0

UCPOL0

Los Registros UBRR0L y UBRR0H


UBRR0H--UBRR0L Bit 7
Bit 15:12 Reserved

--Bit 6

--Bit 5

--Bit 4

Bit 11
Bit 3

Bit 10
Bit 9
Bit 8
Bit 2
Bit 1

Bit 0

Estos bits estn reservados para usos futuros. Cuando se escriba en UBRR0H este bit debe
permanecer en cero por compatibilidad con futuros dispositivos.
Bit 11:0 UBRR11:0: USART Baud Rate Register
Este es un registro de 12 bits que contiene el baud rate del USART. El registro UCRR0H
contiene los 4 bits ms significativos, y UBRR0L contiene los 8 bits menos significativos del
baud rate del USART. Si se cambia el baud rate cuando haya transmisiones o recepciones de
datos en curso, estas operaciones sern estropeadas. La escritura en UBRR0L actualizar de
inmediato el prescaler del baud rate.

Tablas de Baud Rate


Algunos compiladores como CodeVisionAVR pueden calcular automticamente el valor de los registros
UBRR para generar el baud rate que le indiquemos, pueden incluso avisar el error producido. En otros casos
ser recomendable ver por nosotros mismos si el error es aceptable para nuestras aplicaciones.
Si es una cuestin crucial, tambin se puede optar por cambiar de XTAL por otro cuya frecuencia sea mltiplo
del baud rate deseado; por ejemplo, para los baud rates mltiplos de 9600 (que son la gran mayora) los
XTALes de 3.6884 MHz, 11.0592 MHz 18.4320 MHz derivan en errores de 0.00%.

Prctica: Hello

El programa maneja el ingreso y salida de cadenas de texto.

Circuito de la prctica.
El cdigo fuente
/******************************************************************************
* FileName:
main.c
* Purpose:
Comunicacin bsica por USART0
* Processor: ATmel AVR con mdulo TWI
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/

#include "avr_compiler.h"
#include "usart.h"
int main(void)
{
char name[20];
usart_init();

// Inicializar USART0 @ 9600-8N1

puts("\n\r www.cursomicros.com ");


puts("\n\r =================== \n\r");
puts("\n\r - Cul es tu nombre?... \n\r - ");
scanf("%s", name);
printf(" - Gusto en conocerte, %s", name);
while(1);
}

Para que el programa responda como se muestra en la siguiente imagen ser necesario que configurar el uso
del eco ya sea en el entorno de Tera Term, como se indic en la seccin Uso de Tera Term, o en el archivo
usart.h, como se indic en la seccin Libreras para el USART. De lo contrario la funcin scanf no mostrar el
texto que vayas ingresando por el teclado.

Las Interrupciones del USART

Seguiremos considerando que todo lo expuesto es igualmente vlido para el USART1 e incluso para el
USART de los viejos AVR. Con eso aclarado El USART0 puede generar tres interrupciones, a citar:

Interrupcin de Recepcin Completada. Cuando se acaba de recibir un nuevo dato, esto es, cuando un dato
recin llegado acaba de pasar del Registro de desplazamiento al buffer del registro UDR0 se activar el flag
RXC0 el cual podr disparar la interrupcin, si es que est habilitada.
Esta interrupcin se habilita seteando el bit RXCIE0, aparte del bit enable general I, claro est.
El flag RXC0 es algo especial porque es de solo lectura y no se limpia automticamente al ejecutarse la funcin
de interrupcin ISR, sino nicamente cuando el buffer de recepcin est vaco. Esto significa que si la
interrupcin est habilitada se disparar sucesivamente mientras haya uno o ms datos por leer.

UCSR0ARXC0
UCSR0BRXCIE0

TXC0
TXCIE0

UDRE0
UDRIE0

FE0
RXEN0

DOR0
TXEN0

UPE0
UCSZ02

U2X0
RXB80

MPCM0
TXB80

Interrupcin de Registro de Datos Vaco. Se refiere al registro de datos UDR0 del mdulo transmisor. Esta
interrupcin se habilita seteando el bit UDRIE0 (aparte de I), y se disparar cuando se active a uno el flag
UDRE0. Este evento ocurre cuando el registro UDR0 est vaco, sea despus de un reset o despus de
depositar su contenido en el Registro de desplazamiento de modo que est dispuesto a aceptar un nuevo
dato.
El flag UDRE0 se limpia automticamente al ejecutarse la funcin de interrupcin ISR. Tambin se puede
limpiar escribindole un uno.
Interrupcin de Transmisin Completada. Esta interrupcin se habilita seteando el bit TXEN0 (adems de I), y
se disparar cuando se active el flag TXC0, es decir, cuando un dato se haya terminado de transmitir, o dicho
de otro modo, cuando el dato haya salido por completo del Registro de desplazamiento. Creo que con eso
queda clara su diferencia respecto de la Interrupcin de Registro de Dato Vaco. En la prctica significa que los
datos se envan ms rpido usando la Interrupcin de Registro de Datos Vaco porque los espacios entre datos
se reducen al mnimo, de modo que la Interrupcin de Transmisin completada ser raramente usada.
El flag TXC0 se limpia automticamente al ejecutarse la funcin de interrupcin ISR o puede limpiarse por
software escribindole un uno.

Prctica: Interrupciones del USART


Cada dato que llegue al USART es vuelto a enviar al terminal serial. Por otro lado, se enva todo un buffer
(una cadena de texto) pero usando solo interrupciones. Usaremos el mismo circuito de la prctica anterior.

Circuito de la prctica.
El cdigo fuente
/******************************************************************************
* FileName:
main.c
* Purpose:
Uso de las interrupciones del USART
* Processor: ATmel AVR con mdulo TWI
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"

volatile char buf[] = "\n\r Uso de interrupciones del USART \n\r\r";


//****************************************************************************
// Gestor de interrupciones (Interrupcin de Recepcin Completada).
// La interrupcin de recepcin se dispara cuando haya llegado algn dato.
//****************************************************************************
ISR (USART0_RX_vect)
{
char c = UDR0;
// Leer dato
UDR0 = c;
// Devolver dato
}
//****************************************************************************
// Gestor de interrupciones (Interrupcin de Registro de Datos Vaco).
// La interrupcin se dispara cuando se pueda enviar un nuevo dato.
// La ISR deposita en el registro de transmisin el siguiente dato a enviar.
//****************************************************************************
ISR (USART0_UDRE_vect)
{
static unsigned char i = 0;
char c = buf[i];
if(c != 0)
// Fin de buffer ?
{
UDR0 = c;
i++;
}
}
int main(void)
{
usart_init();

// Inicializar USART0 @ 9600-8N1

/* Habilitar las interrupciones de 'Recepcin Completada' y


* de 'Registro de Datos Vaco'.
*/
UCSR0B |= (1<<RXCIE0)|(1<<UDRIE0);
sei();
// Setear bit I de SREG
while(1)
{
/* Entrar en Modo sleep (Idle mode).
* Es el nico modo sleep en que el USART todava puede
* generar interrupciones */
SMCR = (1<<SE);
sleep();
}
}

Observa que en la funcin principal main no se transfiere ningn dato. Todos viajan por interrupciones. La
interrupcin de recepcin se usa con mucha frecuencia y es la ms fcil de captar.
//****************************************************************************
// Gestor de interrupciones (Interrupcin de Recepcin Completada).
// La interrupcin de recepcin se dispara cuando haya llegado algn dato.
//****************************************************************************
ISR (USART0_RX_vect)
{
char c = UDR0;
// Leer dato

UDR0 = c;

// Devolver dato

Eso es todo: cuando haya algn dato llegado lo recibimos y lo devolvemos :)


La interrupcin de transmisin no es tan usual como la anterior. Es una tcnica algo sofisticada y muy
eficiente pero que raras veces resulta realmente necesaria. En el programa funciona as: una vez habilitada, la
interrupcin se disparar de inmediato ya que el registro de transmisin UDR0 estar vaco. As se empieza a
enviar el primer dato de buf. La interrupcin se volver a disparar dada vez que el USART termine de enviar
el dato anterior y parar en el ltimo dato, cuando su flag se limpie por el solo hecho de empezar a ejecutarse
la ISR, aunque en esa ocasin ya no se toque el registro UDR0. Fin de la historia.
//****************************************************************************
// Gestor de interrupciones (Interrupcin de Registro de Datos Vaco).
// La interrupcin se dispara cuando se pueda enviar un nuevo dato.
// La ISR deposita en el registro de transmisin el siguiente dato a enviar.
//****************************************************************************
ISR (USART0_UDRE_vect)
{
static unsigned char i = 0;
char c = buf[i];
if(c != 0)
// Fin de buffer ?
{
UDR0 = c;
i++;
}
}

El buffer circular o buffer de anillo


Recordemos que no podemos depositar un dato en el registro de transmisin del USART hasta que se termine
de enviar el dato anterior. Mientras se espera a que eso pase el CPU podra perder tiempo valioso. La solucin
es guardar los datos en un buffer y que se vayan transmitiendo a su momento utilizando interrupciones,
parecido a lo que se vio en la prctica pasada.
Por otro lado, el registro de recepcin, al ser un buffer de dos niveles, s puede recibir un segundo dato antes
de que se haya ledo el dato previo. Pero si siguen llegando ms datos sin que sean recogidos, no solo se
perderan, sino que bloquearan el USART. Incluso si los datos se leyeran a tiempo utilizando interrupciones,
qu se hara con ellos si el CPU an est ocupado procesando los datos previos? Tienes razn! Podran ir
guardndose en un buffer.
Pues bien, en ambos casos las cosas saldrn mejor si se usa un buffer de tipo circular.
Un buffer circular es un recurso de programacin tan viejo como til en el intercambio de todo tipo de datos,
no solo en comunicaciones del USART. No es ms que un buffer o array ordinario que adopta su nombre por
la forma en que se ponen y sacan sus elementos.
Un buffer circular trabaja bsicamente con dos ndices, que aqu llamaremos Inpointer y Outpointer. No son
como los punteros que define el lenguaje C, son simples variables que operan como ndices para acceder a los
elementos del buffer. Ambos ndices tienen avance incremental y cclico, es decir, se incrementan de uno en
uno y luego de apuntar al ltimo elemento del buffer vuelven a apuntar al primero. Eso explica su nombre.

Estructura de un buffer circular de N elementos.


Al inicio los dos ndices apuntan al primer elemento del buffer. La pregunta es cundo y cmo se
incrementan?

Cada nuevo dato a guardar en el buffer ser depositado en la casilla actualmente apuntada por
Inpointer. A continuacin Inpointer se incrementa en 1. (Inpointer para datos In = entrada.)
Por otro lado, cada dato que salga del buffer ser el de la casilla actualmente apuntada por Outpointer.
A continuacin Outpointer se incrementa en 1. (Outpointer para datos Out = salida.)

Con todo lo expuesto ya puedes ensayar cmo funciona el buffer circular. Descubrirs que tiene un
comportamiento FIFO: los primeros datos en entrar sern los primeros en salir; que en tanto haya espacio en
el buffer siempre se podrn meter ms datos sin importar en qu posiciones vayan, evitando el riesgo de
sobrescribir posiciones ya ocupadas. Podras hacer eso con un buffer lineal? Muy difcil, verdad? Ahora
pasemos de los ensayos a la prctica real.
Para saber si en el buffer hay espacio para meter ms datos o si hay al menos un dato que sacar, se debe usar
la diferencia entre las posiciones de los punteros. Por lo confuso que se ve eso, es preferible emplear una
variable adicional que se incremente con cada dato ingresado y se decremente con cada dato extrado.

Prctica: Buffer circular con Interrupciones


El uso de un buffer circular en las recepciones de datos es una tcnica robusta que se convierte en una
necesidad de facto por razones ya explicadas. En las transmisiones, en cambio, brinda una eficiencia superflua
y hasta innecesaria, salvo que la aplicacin realmente la requiera.
No quiero poner programas de esos aqu porque son demasiado grandes como ejemplos. Superficialmente esta
prctica se ve igual que la primera de este captulo. Solo que ahora todos los datos son transferidos por
interrupciones pasando por buffers circulares.

Circuito de la prctica.
El cdigo fuente
/******************************************************************************
* FileName:
main.c
* Purpose:
Uso de buffers circulares con las interrupciones del USART
* Processor: ATmel AVR con mdulo TWI
* Compiler:
IAR-C y AVR-GCC (WinAVR)
* Author:
Shawn Johnson. http://www.cursomicros.com.
*
* Copyright (C) 2008 - 2012 Shawn Johnson. All rights reserved.
*
* License:
Se permiten el uso y la redistribucin de este cdigo con
*
modificaciones o sin ellas, siempre que se mantengan esta
*
licencia y las notas de autor y copyright de arriba.
*****************************************************************************/
#include "avr_compiler.h"
#include "usart.h"
#include "lcd.h"

char
char
void
void

GetFromTXBuffer(void);
GetFromRXBuffer(void);
PutToTXBuffer(char data);
PutToRXBuffer(char data);

#define
#define

TXBufferSize
RXBufferSize

50
50

// Tamao de buffer circular de Tx


// Tamao de buffer circular de Rx

volatile
volatile
volatile
volatile
volatile
volatile
volatile
volatile
volatile
volatile

char TXBuffer[TXBufferSize];
// Buffer circular de Tx
char RXBuffer[TXBufferSize];
// Buffer circular de Rx
unsigned char TXInpointer = 0;
unsigned char RXInpointer = 0;
unsigned char TXOutpointer = 0;
unsigned char RXOutpointer = 0;
unsigned char TXBufferData = 0;
unsigned char RXBufferData = 0;
unsigned char TXBufferSpace = TXBufferSize;
unsigned char RXBufferSpace = RXBufferSize;

//****************************************************************************
// Gestor de interrupciones (Interrupcin de Recepcin Completada).
// La interrupcin de recepcin se dispara cuando haya llegado algn dato.
//****************************************************************************
ISR (USART0_RX_vect)
{
char c = UDR0;
// Leer dato
if(RXBufferSpace)
PutToRXBuffer(c);
else
nop();

// Si hay espacio en RXBuffer


// RXBuffer est lleno
// Cdigo para TXBuffer lleno

}
//****************************************************************************
// Gestor de interrupciones (Interrupcin de Registro de Datos Vaco).
// La interrupcin se dispara cuando se pueda enviar un nuevo dato.
//****************************************************************************
ISR (USART0_UDRE_vect)
{
char c;
if(TXBufferData)
{
c = GetFromTXBuffer();
UDR0 = c;
}

// Si hay datos en TXBuffer


// Estraer dato
// Enviarlo

}
int main(void)
{
char c;
unsigned char i=0;
const char *text = "\n\r Escribe en el LCD... \n\r";
usart_init();

// Inicializar USART0 @ 9600-8N1

lcd_init();
lcd_cmd(LCD_CURBLK);

// Inicializat LCD. Ver interface en "lcd.h"


// Mostrar Cursor + Blink

while(c = text[i++])

// Cargar TXBuffer

{
if(TXBufferSpace)
PutToTXBuffer(c);
else
nop();

// Si hay espacio en TXBuffer


// Meter c
// Cdigo para TXBuffer lleno

}
/* Habilitar las interrupciones de 'Recepcin Completada' y
* de 'Registro de Datos Vaco'.
*/
UCSR0B |= (1<<RXCIE0)|(1<<UDRIE0);
sei();
// Setear bit I de SREG
while(1)
{
if(RXBufferData)
// Si hay datos en RXbuffer
{
c = GetFromRXBuffer();
// Obtener un dato
switch(c)
{
case 0x1B: lcd_clear();
// Limpiar LCD
break;
case 0x08: lcd_cmd(0x10);
// Cursor atrs
lcd_data(' ');
// Escribir espacio blanco
lcd_cmd(0x10);
// Cursor atrs
break;
default:
lcd_data(c);
// Escribir c
}
}
// Algunas otras tareas...
nop();
}
}
//****************************************************************************
// Extrae un dato de TXBuffer.
// Antes de llamar se debe comprobar si hay algn dato con if(TXBufferData)
//****************************************************************************
char GetFromTXBuffer(void)
{
char c = TXBuffer[TXOutpointer];
// Extraer dato
if(++TXOutpointer >= TXBufferSize) // Al pasar el lmite
TXOutpointer = 0;
// Dar la vuelta
TXBufferData--;
// Un dato menos
TXBufferSpace++;
// Un espacio ms
return c;
//
}
//****************************************************************************
// Extrae un dato de RXBuffer.
// Antes de llamar se debe comprobar si hay algn dato con if(RXBufferData)
//****************************************************************************
char GetFromRXBuffer(void)
{
char c = RXBuffer[RXOutpointer];
// Extraer dato
if(++RXOutpointer >= RXBufferSize) // Al pasar el lmite
RXOutpointer = 0;
// Dar la vuelta
RXBufferData--;
// Un dato menos
RXBufferSpace++;
// Un espacio ms
return c;
//
}

//****************************************************************************
// Ingresa un dato en TXBuffer
// Antes de llamar se debe comprobar si hay espacio con if(TXBufferSpace)
//****************************************************************************
void PutToTXBuffer(char data)
{
TXBuffer[TXInpointer] = data;
// Ingresar dato
if(++TXInpointer >= TXBufferSize)
// Al pasar el lmite
TXInpointer = 0;
// Dar la vuelta
TXBufferData++;
// Un dato ms
TXBufferSpace--;
// Un espacio menos
}
//****************************************************************************
// Ingresa un dato en RXBuffer
// Antes de llamar se debe comprobar si hay espacio con if(RXBufferSpace)
//****************************************************************************
void PutToRXBuffer(char data)
{
RXBuffer[RXInpointer] = data;
// Ingresar dato
if(++RXInpointer >= RXBufferSize)
// Al pasar pasar el lmite
RXInpointer = 0;
// Dar la vuelta
RXBufferData++;
// Un dato ms
RXBufferSpace--;
// Un espacio menos
}

Descripcin del programa

Como ves, hay dos buffers circulares, uno para transmisiones y otro para recepciones. Una vez implementados
su uso es bastante simple. Solo compara esto: para enviar y recibir datos en un programa rstico se utilizan
funciones como putchar para depositar un dato en el registro de transmisin, y getchar para leer del minibuffer
de recepcin de 2 datos.
En cambio, con los buffers circulares podemos usar las funciones como PutToTXBuffer o GetFromRXBuffer
para depositar/leer en/de sus megabuffers de transmisin y recepcin. Se usan las variables como
RXBufferData o TXBufferSpace para comprobar si hay datos o espacios para ellos en los buffers.

Potrebbero piacerti anche