Você está na página 1de 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

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

// Llave de cierre del bloque de 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; //... b = a; b = 150; a = 60; a = b; // Declarar constante a // Declarar variable b // Vlido // Vlido // Error! a es constante // Error! a es constante

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

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

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

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 ) { sentenciaB; sentenciaC; // ... }; sentenciaX; // ... // Este ; es opcional // Mientras expression sea verdadera, ejecutar el // siguiente bloque

Nota que en este caso primero se evala expression. Por lo tanto, si desde el principio 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 Ejemplos:


int a, b, c;

Accin

// 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; a = b++; a = ++b; if (a++ < 10) { // algn cdigo } if (++a < 10) { // algn cdigo } // Declarar variables enteras a y b // Lo mismo que a = b; y luego b = b + 1; // Lo mismo que b = b + 1; y luego a = b; // Primero comprueba si a < 10 y luego // incrementa a en 1

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

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; // 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

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

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

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

Desplazamientos producidos por los operadores << y >>.

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

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 } if( (a<b) && (a>c) ) { // sentencias } // Si a igual 0 sea falso

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

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

// 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 y b<c, ...

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

return SomeData; }

// Necesario solo si la funcin retorna algn valor

Donde:

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

Funciones sin parmetros


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

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


function_name();

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

Funciones con parmetros (por valor)


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; if ( arg3 < min ) min = arg3; return min; } void main (void) { int a, b, c, d; // Si arg2 es menor que min // Cambiar a arg2 // Si arg3 es menor que min // Cambiar a arg3 // Retornar valor de min

// Declarar variables a, b, c y d

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

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

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

Parmetros por referencia


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

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

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

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

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

Para terminar, dado que los nombres de las variables en los parmetros de entrada son opcionales, los prototipos de func1 y func2 tambin se pueden escribir 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++; vari = 0;

// Acceso a variable global speed // Esto dar ERROR porque vari solo pertenece // a la funcin foo. No compilar.

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

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

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

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

// Declarar variable a // Incrementar a

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

Una variable se hace esttica anteponiendo a su declaracin el especificador static. Por defecto las variables estticas se autoinicializan a 0, pero se le puede dar otro valor en la misma declaracin (dicha inicializacin solo se ejecuta la primera vez que se llama a la funcin), as:
static int var1; static int var2 = 50; // 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 = &height; ip = &width; cp = &a; cp = &c; cp = &a; fp = &max; fp = &height; //... } // ip es un puntero a variable tipo int // cp es un puntero a variable tipo char // Puntero a variable tipo float // Con esto ip tendr la direccin de height // Ahora ip apunta a width // cp apunta a a // Ahora cp apunta a c // Ahora cp apunta a a otra vez // fp apunta a max // Error! height no es una variable float

Asignaciones indirectas mediante punteros


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

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

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

//... }

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; n = *p; p++; n = *p; p++; n = *p; *p = 10; p--; *p = 100; p = mat; p = NULL; // ... } // p apunta a mat (a su primer elemento) // // // // // Esto da n = Incrementar Esto da n = Incrementar Esto da n = 78 p para apuntar a siguiente elemento 98 p para apuntar a siguiente elemento 26

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

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]; a = str2[0]; a = str2[3]; str1 += 2; str2 += 2; str1++; str2++; a = *str2; // } ... // Esto da a = 'A' // Esto da a = 'a' // Esto da a = 'P' // Esto da a = 'n' // Error! Str1 es esttico // Correcto. Ahora str2 apunta a 'i' // Error otra vez! Str1 es esttico // Correcto. Ahora str2 apunta a 'n' // Esto da a = 'n'

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 = /* 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 texto */ 9 15 13

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 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. de

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. AVR/GNU C

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

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
512

Algunos Modelos ATmega48A, ATmega48PA, ATmega48P/V, ATmega48/V

AVR ATmegaNNN

Memoria FLASH 8K

Memoria RAM
1K

Algunos Modelos 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

ATmegaNNN

16 K

1K

ATmegaNNN

32 K

2K

ATmegaNNN ATmegaNNN

64 K 128 K

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. 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. 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 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. 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

BODS

BODSE

PUD

IVSEL

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. 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.

IVCE

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 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 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.

WDRF

BORF

EXTRF

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.

PORF

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 Extendido BODLEVEL2 BODLEVEL1 BODLEVEL0

Bit 0 Bit 7 6 5 4 3 2 1 0 Bit 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 reset 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 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 reset 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 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

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)

BLB12 BLB11 BLB02 BLB01 LB2 LB1

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 No S No No No

Estado de Pin 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; usart_init(); // Setup PORTB for output // 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; DDRB = 0xFF; PORTB = 0x00; // PORTD como entrada // Activar pull-ups de pines PD2 y PD3 // 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; CNT = 123; while(1) { for(j=0; j<100; j++) { for(i=0; i<4; i++) // // // // 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 // 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)); } } } } /***************************************************************************** * 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 } // 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

/***************************************************************************** * 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; DDRB = 0x0F; PORTB = 0x00; step = 1; modo = 0xff; usart_init(); puts("\r\n puts("\r\n puts("\r\n puts("\r\n // Marca el paso actual // Configurar Nibble bajo para salida // Limpiar nibble bajo. Sin Pull-ups en nibble alto // Iniciar en cualquier paso // Esto ser Full step (0x00 para half step) // Inicializar USART0 @ 9600 N 1

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 '+': necesario... step++; if(step>7) step = 0; PORTB = steps[step]; break; case '-': necesario... step--; if(step<0) { step = 7; // ... medio paso ms step--; if( modo && (step&0x01) ) // ... medio paso ms // Dar la vuelta // Ejecutar el paso // Medio paso atrs // En modo Full step y si es step++; if( modo && (step&0x01) ) // Medio paso adelante // En modo Full step y si es

// 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(); } } } //**************************************************************************** // 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); //

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

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; } RowMask <<= 1; RowMask |= 0x01; } // 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 } // Desplazar ColMask para escanear // siguiente columna // Desplazar RowMask para escanear // siguiente fila

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() #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 PORTD |= (1<<7) PORTD &= ~(1<<7)

unsigned char row; unsigned char col; unsigned char i; DDRD = 0xE0; DDRB = 0xFF; PORTD = 0x00; PORTB = 0x00;

// Fila // Columna

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(); SCLOCK_HIGH(); nop();nop(); SCLOCK_LOW(); } PORTB = 0x00 temporalmente SOEN_HIGH(); nop();nop(); SOEN_LOW(); PORTB = row; } } } } } // Pulso para sacar todos // los datos cargados // Activar la fila actual // Si el bit de row es 1 // colocar 1 en DS // o, colocar 0 // Pulso de reloj para // validar el dato colocado // Desactiva todas las filas

descargar

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 R/W 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. 0 : Escribir en LCD 1 : Leer de LCD Register Select 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).

RS

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

RS R/W DB7 DB6 DB5

DB4

DB3

DB2

DB1

DB0

Clear Display Return Home Entry Mode Set


Instrucciones de comando

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1

0 0 0 0 0 0 0 1
BF

0 0 0 0 0 0

0 0 0 0 0 1

0 0 0 0 1
DL

0 0 0 1
S/C N

0 0 1
D R/L F

0 1
I/D C

1
S B

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

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

Escribir dato 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]); lcd_clear(); lcd_puts(" Hungry Pacman "); while(1) { start: i = 0; for(;;) { lcd_cmd(LCD_LINE2); if(i & 0x01) lcd_data(PacOpen); else lcd_data(PacShut); // Instruccin Set CGRAM Address // Volcar patrn de pacman 2 // // Limpiar pantalla y regresar a DDRAM // Escribir "Hungry Pacman" en LCD

// // // // //

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
0 0 1 1

ISCx0
0

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.

1 0 1

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(); while(1) { PINC = 0x02; delay_ms(600); } } // Habilitacin general de interrupciones // 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 FE0 DOR0 UPE0 U2X0 MPCM0

TXC0

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).

UCSR0ARXC0 TXC0 UDRE0 UDRE0 USART Data Register Empty

FE0

DOR0

UPE0

U2X0

MPCM0

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 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 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 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 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. 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.

DOR0

UPE0

U2X0

MPCM0

El Registro UCSR0B
UCSR0BRXCIE0 TXCIE0 UDRIE0 RXCIE0 RX Complete Interrupt Enable RXEN0 TXEN0 UCSZ02 RXB80 TXB80

Al escribir uno en este bit se habilita la Interrupcin de Recepcin Completada cada vez

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 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.

UDRIE0

RXEN0

Receiver Enable 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 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 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 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.

TXEN0

UCSZ02

RXB80

TXB80

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. UPM01 UPM00 0 0 0 1 1 0 1 1 Stop Bit Select Modo de Paridad Deshabilitado Reservado Habilitado, Paridad Par Habilitado, Paridad Impar

USBS0

Este bit selecciona el nmero de bits Stop que utilizar el Transmisor. El Receptor ignora esta configuracin. USBS0 0 1 Character Size Bits Stop 1 bit 2 bits

UCSZ01: UCSZ00

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. 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

UCPOL0

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); TXBufferSize RXBufferSize 50 50 // Tamao de buffer circular de Tx // Tamao de buffer circular de Rx

#define #define 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(); } //**************************************************************************** // 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; } } int main(void) { char c; unsigned char i=0; const char *text = "\n\r Escribe en el LCD... \n\r"; usart_init(); lcd_init(); lcd_cmd(LCD_CURBLK); while(c = text[i++]) // Inicializar USART0 @ 9600-8N1 // Inicializat LCD. Ver interface en "lcd.h" // Mostrar Cursor + Blink // Cargar TXBuffer // Si hay datos en TXBuffer // Estraer dato // Enviarlo // Si hay espacio en RXBuffer // RXBuffer est lleno // Cdigo para TXBuffer lleno

{ if(TXBufferSpace) PutToTXBuffer(c); else nop(); } /* 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; // } // Si hay espacio en TXBuffer // Meter c // Cdigo para TXBuffer lleno

//**************************************************************************** // 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.

Você também pode gostar