Cintillo Institucional

wiki comunidad

Proyecto Hardware Libre Cenditel

Página Cenditel

wiki Cenditel

Plataforma de Desarrollo Colaborativo

Seminario AVR



logohl.png logohl.png logohl.png

Primera práctica: Makefile y Fuse

Makefile

El Makefile es un archivo utilizado principalmente para la compilación del código escrito para un microcontrolador determinado, además se puede especificar los comandos para la estimación de recursos, programación y depuración del código en línea en el caso de que aplique.

Para configurar el makefile, se parte desde una plantilla prediseñada, luego se realizan unos pequeños cambios para adecuarlo al microcontrolador del que se disponga y del programador. Esta plantilla se puede descargar desde acá makefile. Otra opción sería utilizar una plantilla autogenerable.

Antes de editar el makefile, se deben conocer algunas cosas. Primero, el nombre del microcontrolador para el compilador; para el ATmega328p es atmega328p, en minúsculas y el nombre para el programa utilizado para grabar en el microcontrolador (AVRDUDE), acá es m328p. Luego el nombre del código fuente, por ejemplo prueba.c y también el nombre de cualquier dependencia utilizada. A parte de todo se debe conocer el nombre del programador y el puerto que utiliza; para nosotros dragon_isp o usbasp y el puerto es usb.

Antes de continuar, recomendamos crear una carpeta donde se vayan guardando las prácticas realizadas y otras cosas cosas que se puedan utilizar en el transcurso de ellas. Para ello podemos hacer, por ejemplo

$ mkdir /home/tu_usuario/Documentos/AVR

De esa forma se tendrá un poco más ordenado todo.

Para hacer un poco más rapido la generación del makefile podemos, como se dijo antes, utilizar una plantilla autogenerable pero antes se deben instalar algunos paquetes

$ sudo aptitude install tcl-dev tk-dev

Despues de instalar los dos paquetes anteriores, descargamos este archivo en el escritorio, lo descomprimimos y copiamos la carpeta en la que creamos hace poco /home/tu_usuario/Documentos/AVR. Esto sería, después de descargarlo en el escritorio, ejecutamos

$ cd /home/tu_usuario/Escritorio
$ tar -xf generador_makefile.tar
$ cp mfile /home/tu_usuario/Documentos/AVR/

Una vez hecho esto, vamos a la carpeta mfile y editamos el archivo mfile.tlc de la siguiente forma

$ cd /home/tu_usuario/Documentos/AVR/mfile
$ gedit mfile.tlc

Una vez abierto, cambiamos en la línea 20 donde dice: /home/hlpd/Documentos/AVR/mfile por /home/tu_usuario/Documentos/AVR/mfile. Después de hecho lo anterior ya estamos listos para crear nuestro Makefile.

Dentro de la carpeta mfile ejecutamos:

$ wish mfile.tcl

Entonces saldrá una ventana.

En ella podemos configurar el Makefile solo seleccionando las opciones. Cabe destacar que esta aplicación no es de nuestra autoría, los créditos son para Jörg Wunsch, pero la inclusión de los programadores aquí utilizados y la traducción, aunque un poco incompleta, es de nuestra autoría. Si quieren descargar el archivo original lo pueden hacer desde aquí.

Ahora vamos a examinar cada una de las opciones del generador de archivos Make.

Si hacemos clic en la opcion Makefile nos saldrá algo como la imagen siguiente. La primera opción es para colocar el nombre del archivo que se quiere compilar. Si hacemos clic en ella, saldrá una ventana donde se puede colocar el nombre.

En la siguiente opción elegiremos el modelo del microcontrolador: atmega328p.

La siguiente opción es el formato para el archivo de salida. Eso lo dejaremos en ihex.

En la opción del Nivel de optmización, se elegira "s" que es el nivel para la optimización en tamaño, esto hara que el archivo resultante sea lo más pequeño posible.

Ahora para generar el archivo de depuración se debe elegir la opción qe indica el uso de GDB que es el utilizado para depurar el código para los AVR (gdb-avr).

Para el estándar de C que utilizará el compilador utilizaremos el estándar de 1999 (C99) indicado aquí como gnu99. La imagen siguiente indica como seleccionarlo.

En el caso en el que se tenga más de un archivo fuente, por ejemplo cundo se tenga que utilizar una librería para el control de una pantalla LCD, se deberá indicar la ubicación de cada uno de los archivos, la forma más sencilla sería que esos archivos se encontrarán en la misma carpeta que el archivo principal. Para indicar la ubicación de estos archivos en el generador de archivos Make, se hace como sigue:

Como habrán visto, es bastante fácil la edición del Makefile. Las opciones que siguen las dejaremos con la configuración por defecto. Las opciones de la sección AVRdude: Programador y Puerto, se elegirán para el caso de Programaodr: dragon_isp o usbasp, dependiendo del programador que se disponga. Para la opción Puerto se selecciona usb.

Para el caso del archivo Make, esto es todo. Pasemos a la siguiente sección de la práctica.

Fuse

Antes de comenzar la revisión de cualquier microcontrolador, se debe revisar algunos detalles de su estructura interna, específicamente los fuse. Ellos son los encargados de la configuración del microcontrolador. Es muy importante conocer sobre ellos lo básico para no equivocarnos y luego no poder resolver los problemas que esto conllevaría.

Para tener información sobre los fuses nos debemos ubicar en la hoja de datos de los microcontroladores, en nuestro caso el ATmega328p.

Acá vamos a hacer referencia a los más importantes, que son los relacionados con el oscilador y el tipo de programación. De igual forma se indicará como cambiar el valor de ellos utilizando el programador.

Primero se debe saber saber que los fuse son bytes que pueden ser cambiados en la configuracion de los microcontroladores. El número de bytes dependerá del modelo del programador, como ejemplo, el ATmega328p tiene 3 bytes de configuración. Para ayudarnos con los valores de los fuse, utilizaremos esta página. Si la revisan, verán que el microcontrolador se puede seleccionar de una lista y automáticamente aparecen las opciones a configurar. Las que aparecen a la primera son las configuraciones por defecto de cada microcontrolador. Aún con la página anterior es muy recomendable tener a la mano la hoja de datos del micro a utilizar.

Empezaremos primero leyendo los fuse del microcontrolador.

En la parte anterior, donde estudiábamos los programas a utilizar; se examinaron algunas opciones del AVRdude, entre ellas el como detectar mediante la tarjeta Dragon, el microcontrolador que se quiere utilizar. Ahora vamos a repetir ese comando pero con la opción -v (verbose), la cual nos permite obtener una serie de información que es oculta para el usuario. Como referencia, mientras más veces coloquemos esa opción (-vv), más información nos dará. El comando sería el siguiente.

$ avrdude -c dragon_isp -p m328p -P usb -v

Eso si disponen de una Dragon. Para el USBasp sería

$ avrdude -c usbasp -p m328p -P usb -v

Como recordatorio: Si los comandos anteriores no funcionan, prueben ejecutarlos como suoerusuario, esto se hace anteponiendo la palabra sudo a los comandos.

De acá en adelante se especificará los comandos solo para la Dragon. Si trabajan con el USBasp o algún otro prpgramador solo deben cambiar el nombre dragon_isp por el nombre de su programador.

Después de ejecutar alguno de los comandos anteriores obtendremos algo así:

El interés son las últimas lineas, específicamente donde dice: lfuse, hfuse y efuse; con esta designación se hace referencia a los 3 bytes que conforman los fuse en el microcontrolador. Cabe destacar que por facilidad de explicación en lo que sigue los bits se explican mediante 0 y 1, pero en realidad para realizar la programación se utilizan números hexadecimales cuyo valor dependerá de la combinación de los 0 y 1.

Low fuse(lfuse)

Mediante este fuse se cambiarán algunas características relacionadas con el reloj. Las tablas que se utilizarán a continuación fueron tomadas del manual para el Atmega328p.

Nota: Un valor de "0" significa progrmado, es decir activo; un valor de 1 significa no programado.

CKDIV8

Con este lfuse programado, se obtiene una división entre 8 de la fuente del reloj. Por ejemplo, el reloj interno del microprocesador es de 8 MHz por lo que con este fuse se obtendría una frecuencia de trabajo de 1 Mhz. Esta división puede cambiarse mediante código independientemente de que si el fuse este programado o no pero el hecho de que venga programado por defecto asegura que el chip pueda funcionar con valores grandes de oscilador sin sobrepasar el valor umbral de funcionamiento del microcontrolador.

CKOUT

Si se programa este lfuse se obtendrá por el pin 0 del puerto B (PORTB0) una señal cuedrada con una frecuencia igual a la del reloj, es decir, la frecuencia del oscilador dividida entre el preescalador.

SUT0, SUT1

Esto nos permite la selección del tiempo de retardo antes que el reset interno del microcontrolador sea desactivado. Una mejor explicación: al alimentar el microcontrolador, este no comienza a funcionar inmediatamente. El espera un número determinado de ciclos de reloj más un tiempo en ms, también determinado. Los ciclos de reloj de espera son para asegurar que la señal proveniente del oscilador sea estable. El tiempo de espera en ms es utilizado como tiempo de establecimiento de la alimentación, en otras palabras espera hasta que el voltaje de alimentación, se supone es el adecuado. Digo se supone porque este tiempo es predeterminado por el usuario más no varia mediante el censado de la alimentación, osea que el usuario debe conocer con anterioridad el tiempo aproximado de este establecimiento.

CKSEL0...CKSEL3

Programando estos bits se nos permite seleccionar que tipo de oscilador se va a utilizar. Los bits con los que viene programado por defecto, corresponden a la selección del reloj interno.

High fuse(hfuse)

El hfuse permite la configuración de parámetros de programación del microcontrolador, como por ejemplo la habilitación de la programación ISP o la habilitadion del debugWire.

RSTDISBL

Este bit nos permite especificar si el pin 6 del puerto C servirá como reset externo del microcontrolador o como pin de entrada/salida.

DWEN

Si programamos este bit, el modo de debugWIRE se activará. Hay que tener bastante cuidado con la programación de este bit ya que una vez hecho el modo de programación ISP queda deshabilitado debido a que debugWIRE toma el control del pin de reset. Para desabilitar el modo debugWIRE se requiere el modo de programación High-voltage.

SPIEN

SPIEN permite activar o desctivar la programación serial del microcontrolador. Este bit no puede ser desactivado mediante programación ISP.

WDTON

Con la programción de este bit se fuerza a que el temporizador del perro guardián se active en el modo de reset. En este modo, el temporizador puede utilizarse para evitar que el microcontrolador se cuelgue durante funcionamientos continuos.

EESAVE

Si esta programado, el contenido que se encuentra en la memoria EEPROM se mantiene intacto en el momento de realizar un borrado de la memoria del chip.

BOOTSZ1, BOOTSZ0

Para no entrar en detalles, estos bits permiten seleccionar que cantidad de memoria FLASH es destinada para la aplicación y que cantidad es destinada pra albergar el BOOT LOADER

BOOTRST

En pocas palabras, esto nos permite especificar si después de un reset el micro ejecutará el área de la FLAS donde esta contenida la aplicación o ejecutará el BOOT LOADER

Extended fuse(efuse)

El efuse se encarga de configurar el límite, inferior y superior, de voltaje al cual el microcontrolador desactivará el reset interno. Este fuse configura una especie de comparador el cual censará el voltaje de alimentación del chip.

BODLEVEL2...BODLEVEL0

Con el Brown-out detection se configura un comparador que activará el reset interno si el valor del voltaje de alimentación esta por debajo de ese valor Hay que recordar que estos fuse son para el ATmega328p. Otros microcontroladores presentan cambios en los fuse, ya sea en la posición o en el byte donde se encuentran. De cualquier forma esta es una buena referencia para su manejo.

Selección

Ya que tenemos alguna referencia delos fuse, comencemos a escribir algunos de ellos con ayuda de la página que anteriormente hicimos referencia. Vale la pena volver a mencionar que los valores de los bytes que componen los fuse estan en notación hexadecimal.

Vamos a dirigirnos a la página: http://www.engbedded.com/fusecalc/ Al entrar nos encontraremos con algo así:

Una vez allí, seleccionamos el modelo del microcontrolador y automáticamente aparece la configuración por defecto de los fuses. A parte de poder configurar manualmente cada uno de los bits, lo primero que se nos presenta son opciones predefinidas; las cuales al ser seleccionadas nos cambian el valor de los bits. Acá nos concentraremos en el cambio bit por bit en la sección que se ve a continuación:

Los valores anteriores corresponden a los que por defecto vienen configurados en el micro. Si vamos al final de la página vemos que se nos indica el valor en hexadecimal de cada uno de los bytes y si vemos hacia la derecha, esta el código que se debe utilizar para escribir estos valores usando el AVRdude. Estos valores son:

lfuse= 0x62 hfuse=0xD9 efuse=0xFF

También se puede apreciar una nota que indica que el valor del efuse cambiará dependiendo del dispositivo podría ser 0xFF ó 0x07. Esto es porque si observamos la tabla de los efuse, se aprecia que hay cinco bits que son indefinidos. En el caso del ATmega328p el efuse por defecto tiene un valor de 0x07.

Por ahora solo haremos cambios en el lfuse y más tarde del efuse. Para ello debemos conocer la combinación en la cual programaremos cada uno de los bits. Para el bit 7 solo tenemos dos opciones: dividir el oscilador entre ocho o no, es decir, si lo colocamos en cero (programado), el oscilador estará dividido. Hay que mencionar que para programar el bit en la página, hay que seleccionar la opción correspondiente.

El estudio de los siete bits restantes, se debe hacer en conjunto ya que la variación de los que conforman el CKSEL permiten opciones diferentes en SUT. Una vez más, para poder conocer todas estas opciones, hay que dirigirse a la hoja de datos del micro.

La primera opción es "Cristal oscilador de baja potencia". Podemos seleccionar diferentes frecuencias para este tipo de cristal cambiando los bits de CLKSEL3, CLKSEL2 y CLKSEL1.

Ahora, para seleccionar el tiempo de retardo y los números de ciclo de reloj antes de la desactivación del reset interno, se sigue:

Así como la anterior, hay varias opciones de las cuales podemos elegir. Mejor nos concentraremos en dos cosas, el oscilador interno calibrado y el oscilador externo mediante cristal. Primero las opciones para el oscilador interno calibrado son las siguientes:

Para el retardo sería:

Para el oscilador externo los bits para CLKSEL son:

Y para los retardos. las opciones son:

Después de todo lo anterior hay que saber unas cosas. La selección por defecto del chip en cuanto al oscilador es CLKSEL3...CLKSEL0=0010 y SUT1,SUT0=10, lo que sería: oscilador interno con un retardo de 6 CK y un retardo adicional de 14 CK + 65 ms.

Otra cosa es que cuando el micro se alimenta o se recupera de un reset el retardo es los "Ciclos de retardo" + "Retardo adicional", mientras que si viene de el estado Power-down o Power-save el retardo solo lo componen los "Ciclos de retardo".

Nota: Para conocer sobre los estados Power-down y Power-save, dirigense a la página 41 de la hoja de datos del micro.

Como recomendación, si no se conoce los tiempos de establecimientos de la alimentación, es mejor seleccionar el retardo total más largo o en su defecto configurar el Brown-out detection como se explicará a continuación.

Conocer los valores del Brown-out detection (BOD), es importante porque mediante el conseguimos protección para el microcontrolador. Con el BOD spodemos establecer un nivel mínimo de alimentación. Este funciona al momento del encendido o si cae el voltaje de alimentación. El BOD reseteará el micro si la alimentación cae por debajo de los niveles establecidos por más de dos microsegundos.

Para configurar el BOD, se sigue la siguiente tabla:

Escritura

Ahora que ya conocemos un poco sobre los fuse, vamos a ver como escribirlos en el microprocesador. Anteriormente una forma de leerlos.

Vamos a leerlos de nuevo

$ avrdude -c dragon_isp -p m328p -P usb -v

Los valores de los fuses leidos, en mi caso son: lfuse= 0xFF hfuse= 0xD9 efuse= 0x07, los cuales corresponden a la notación binaria:lfuse= 11111111 hfuse= 11011001 efuse= 00000111. Como se darán cuenta, la configuración del lfuse que tiene el micro que utilizo no esta contemplada en este tutorial pero para conocimiento esta pertenece a la utilización de un cristal de más de 8 MHz sin división de reloj. Las configuraciones que se explicaron anteriormente, también sirven para este tipo de cristal.

Vamos a escribir un valor de fuse diferente. En esta parte deberían contar con un cristal para utilizarlo como oscilador.

Para escribir cualquier valor en la memoria del micro mediante el AVRdude, se debe utilizar la opción "-U" en conjunto con algunos otros parámetros.

-U la_memoria_se_va_a_acceder:que_se_va_a_hacer:lo_que_se_a_escribir_o_a_donde_se_guardará_lo_leido_:el_formato

Hay que destacar que "-U" puede ser utilizado varias veces en una misma orden.

Para más información acerca de la opción "-U", visiten http://www.nongnu.org/avrdude/user-manual/avrdude_4.html#Option-Descriptions

Escribiremos los valores predeterminados

$ avrdude - c dragon_isp -p m328p -P usb -U lfuse:w:0x62:m -U hfuse:w:0xd9:m -U efuse:w:0x07:m

Ahora vamos escribir los fuse para utlizar un cristal de 10 MHz, para ello nos ayudamos de las tablas anteriores y de la página para configurar los fuse. Solo se va a configurar el lfuse. Primero, desprogramamos el CLKDIV8 escribiendo "1", es decir deseleccionando la opción en la página. En SUT1 escribimos "1" y en SUT0, "1". Luego para CLKSEL3, CLKSEL2, CLKSEL1 y CLKSEL0 escribimos "0111", respectivamente. El resultado sería 1011111. En la página, los check del lfuse deberían estar todos deseleccionados, excepto CLKSEL3. Con esto podemos utilizar un cristal de 10 Mhz, sin división del reloj y retardos 16k CK + 14 CK + 65ms después de un reset ó un retardo de 16k CK despues de un estado Power-down o Power-save. El resultado del lfuse que nos da la página es: 0xF7. Esto lo podemos comprobar convirtiendo 11110111 a hexadecimal

$ avrdude -c dragon_isp -p m3s8p -P usb -U lfuse:w:0xf7:m

Con esto podemos dar por terminado la configuración de los fuse. Si necesitan más información, diríjanse al la hoja de datos del microcontrolador.

Segunda práctica: Manejo de puertos

En el ATmega328p se tienen tres puertos: B, C, D. En ellos se manejan tres registros para cada uno. Estos registros son: Registro de datos(PORTx), Registro de dirección de datos(DDRx) y los Pines del puerto de entrada(PINx). La "x" especifica el puerto y para indicar el número del pin es, por ejemplo, PORTB3. Para la notación que se utilizará sera "n" quien indique el número del pin, PORTxn.

Registros

Los registros estan configuradoes de la siguiente manera:

PORTB

Bit

7

6

5

4

3

2

1

0

Nombre

PORTB7

PORTB6

PORTB5

PORTB4

PORTB3

PORTB2

PORTB1

PORTB0

Valor predeterminado

0

0

0

0

0

0

0

0

DDRB

Bit

7

6

5

4

3

2

1

0

Nombre

DDB7

DDB6

DDB5

DDB4

DDB3

DDB2

DDB1

DDB0

Valor predeterminado

0

0

0

0

0

0

0

0

PINB

Bit

7

6

5

4

3

2

1

0

Nombre

PINB7

PINB6

PINB5

PINB4

PINB3

PINB2

PINB1

PINB0

Valor predeterminado

0

0

0

0

0

0

0

0

PORTC

Bit

7

6

5

4

3

2

1

0

Nombre

-

PORTC6

PORTC5

PORTC4

PORTC3

PORTC2

PORTC1

PORTC0

Valor predeterminado

-

0

0

0

0

0

0

0

DDRC

Bit

7

6

5

4

3

2

1

0

Nombre

-

DDC6

DDC5

DDC4

DDC3

DDC2

DDC1

DDC0

Valor predeterminado

-

0

0

0

0

0

0

0

PINC

Bit

7

6

5

4

3

2

1

0

Nombre

-

PINC6

PINC5

PINC4

PINC3

PINC2

PINC1

PINC0

Valor predeterminado

-

0

0

0

0

0

0

0

PORTD

Bit

7

6

5

4

3

2

1

0

Nombre

PORTD7

PORTD6

PORTD5

PORTD4

PORTD3

PORTD2

PORTD1

PORTD0

Valor predeterminado

0

0

0

0

0

0

0

0

DDRD

Bit

7

6

5

4

3

2

1

0

Nombre

DDD7

DDD6

DDD5

DDD4

DDD3

DDD2

DDD1

DDD0

Valor predeterminado

0

0

0

0

0

0

0

0

PIND

Bit

7

6

5

4

3

2

1

0

Nombre

PIND7

PIND6

PIND5

PIND4

PIND3

PIND2

PIND1

PIND0

Valor predeterminado

0

0

0

0

0

0

0

0

Lectura, escritura...

Configuración

Ya con los registros definidos. Vamos a configurar los puertos como entradas, salidas, con y sin pull-up.

El registro DDRx permite especificar si el puerto es de entrada o salida. Si se escribe un uno en algún DDxn, el pin Pnx se configurará como salida. Por el contrario si se escribe un cero, este se configurará como entrada.

La función del registro PORTx dependerá de lo escrito en el registro DDRx. Si se escribe un uno cuando el pin esta configurado como salida, esto activará el pull-up. Lo contrario sucede si se escribe un cero. Ahora si el pin esta configurado como entrada, si se escribe un uno en PORTxn este pin se pondrá en alto. Cuando se escribe un cero, este se pondrá en bajo.

Por el último el registro PINx, permite leer los datos de entradas en ese pin sin importar la configuración de DDRx. Sin embargo, si se escribe un uno mediante PINxn, este hará que cambie el valor de PORTxn sin importar como este configurado DDRx.

Vamos a ver un ejemplo de configuración de los pines. El siguiente, esta extraido de la hoja de datos del micro, luego haremos los nuestros.

//Configuramos el pull-up y los pines en alto
PORTB=(1<<PORTB7)|(1<<PORTB6)|(1<<PORTB1)|(1<<PORTB0);
//Configuramos algunos pines como salida
DDRB=(1<<DDB3)|(1<<DDB2)|(1<<DDB1)|(1<<DDB0);
//Retardo de un ciclo
__no_operation();
//Leemos los pines
i=PINB;

Si revisan el código anterior verán que hay un comando, "no_operation()". Este permite dar un tiempo entre el seteo de los puertos y la lectura. La explicación del porque hay que hacer esto la encontrarán en la hoja de datos.

Ahora vamos a hacer uno propio

//Configuramos los pull-up. Pueden ser en alto o en bajo
PORTC=(1<<PORTC0)|(1<<PORTC2)|(1<<PORTC1)
//Configuramos algunos como salida y otros como entrada
DDRC=(1<<DDC1)|(0<<DDC2)

Códigos de ejemplo

Encender un led

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
        //Configuramos los puertos
        DDRD = (1<<DDD0);
        while(1)
        {
                //Colocamos un "1" en PORTD0
                PORTD = (1<<PORTD0);                
        }
}

Hacer parpadear un led

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>

void delay_s ()
{       
        uint16_t ms;
        ms=1000;
        while(ms)
        {
                _delay_ms(1);
                ms--;
        }
}

int main (void)
{
        //Configuramos los puertos
        DDRD = (1<<DDD0);
        while(1)
        {
                PORTD = (1<<PORTD0);
                delay_s();
                PORTD = (0<<PORTD0);
                delay_s();
        }
}

La función "delay_s" permite crear un retraso de 1 s haciendo mil retardos de 1 ms

Encender un led presionando un botón

Apagado instantáneo

El led sigue estando en PIND0 y el botón esta en PINC0

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>

int main (void)
{
        //Configuramos los puertos
        DDRD = (1<<DDD0);
        DDRC = (0<<DDC0);
        asm("NOP");
        while(1)
        {
                if((PINC & (1<<PINC0)))
                {
                        PORTD = (1<<PORTD0);
                }
                else
                {
                        PORTD = (0<<PORTD0);
                }
        }
}

Apagado temporizado

Tambien se puede encender el led presionando el boton y mantenerlo encendido por cierto tiempo. En el siguiente ejemplo el tiempo es de 1 s.

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>

void delay_s ()
{       
        uint16_t ms;
        ms=1000;
        while(ms)
        {
                _delay_ms(1);
                ms--;
        }
}

int main (void)
{
        //Configuramos los puertos
        DDRD = (1<<DDD0);
        DDRC = (0<<DDC0);
        asm("NOP");
        while(1)
        {
                if((PINC & (1<<PINC0)))
                {
                        PORTD = (1<<PORTD0);
                        delay_s();
                }
                else
                {
                        PORTD = (0<<PORTD0);
                }
        }
}

O se puede mantener encendido por más tiempo modificando un poco la función de retardo, para poder indicar el tiempo. En este caso el tiempo se indica en ms

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>

void delay_vs (uint16_t ms)
{       
        while(ms)
        {
                _delay_ms(1);
                ms--;
        }
}

int main (void)
{
        //Configuramos los puertos
        DDRD = (1<<DDD0);
        DDRC = (0<<DDC0);
        asm("NOP");
        while(1)
        {
                if((PINC & (1<<PINC0)))
                {
                        PORTD = (1<<PORTD0);
                        delay_vs(5000);
                }
                else
                {
                        PORTD = (0<<PORTD0);
                }
        }
}

Encendido secuencial

Por último haremos el encendido secuencial de tres leds. Cada uno enciende al presionar el botón en PINC0

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>
int main (void)
{
        short cont=0;
        //Configuramos los puertos
        DDRD = (1<<DDD0)|(1<<DDD1)|(1<<DDD2);
        DDRC = (0<<DDC0);
        PORTC = (0<<PORTC0);
        asm("NOP");
        while(1)
        {
                if(bit_is_set(PINC,0))
                {       
                        _delay_ms(300);
                        cont++;
                }
                switch (cont)
                {
                        case 0:
                                PORTD=0x00;
                                break;
                        case 1:
                                PORTD=(1<<PORT0);
                                break;
                        case 2:
                                PORTD=(0<<PORTD0)|(1<<PORTD1);
                                break;  
                        case 3:
                                PORTD=(0<<PORTD0)|(0<<PORTD1)|(1<<PORTD2);
                                break;
                        case 4:
                                PORTD=(1<<PORTD0)|(1<<PORTD1)|(1<<PORTD2);
                                break;
                        case 5:
                                cont=0;
                                break;
                        default:
                                PORTD=0x00;
                }
        }
}

El retardo _delay_ms(300) se realiza previniendo los rebotes en el pulsador. Así se asegura que el pulso realizado ya es estable. Si la entrada no proviniese de un dispositivo mecánico sino más bien eletrónico, esto no sería necesario.

Todos los ejemplos anteriores han sido probados y funcionan a la perfección siguiendo los pasos aquí señalados.

Tercera práctica: Entradas analógicas

avr/pract (última edición 2010-07-06 18:43:11 efectuada por rcelaya)