||[[http://hl.cenditel.gob.ve/wiki/|wiki comunidad]] ||[[http://wiki.cenditel.gob.ve/wiki/hlpd|Proyecto Hardware Libre Cenditel]] ||[[http://www.cenditel.gob.ve/|Página Cenditel]] ||[[http://wiki.cenditel.gob.ve/wiki|wiki Cenditel]] ||[[http://fsl.cenditel.gob.ve/|Plataforma de Desarrollo Colaborativo]] || = Seminario AVR = <> <
><
> {{attachment:logohl.png||height="132",width="180"}} {{attachment:logohl.png||height="132",width="180"}} {{attachment:logohl.png||height="132",width="180"}} == 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á [[attachment: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 [[attachment:generador_makefile.tar|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. . {{attachment:mfile_principal.png|Principal|width="1080"}} 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 [[http://www.sax.de/~joerg/mfile/|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. . {{attachment:mfile_nombre.png|Nombre del archivo|width="1080"}} En la siguiente opción elegiremos el modelo del microcontrolador: atmega328p. . {{attachment:mfile_micro.png|Modelo del microcontrolador|width="1080"}} La siguiente opción es el formato para el archivo de salida. Eso lo dejaremos en ihex. . {{attachment:mfile_formato.png|Formato de salida|width="1080"}} 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. . {{attachment:mfile_optimizacion.png|Nivel de optimización|width="1080"}} 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). . {{attachment:mfile_depuracion.png|Formato para la depuración|width="1080"}} 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. . {{attachment:mfile_estandar.png|Estándar de C|width="1080"}} 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: . {{attachment:mfile_fuenteC.png|Selección de otras fuentes en C|width="1080"}} 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 [[http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf|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 [[http://www.engbedded.com/fusecalc|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 [[http://hl.cenditel.gob.ve/wiki/avr/prog|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í: {{attachment:avr_fuse.png|Lectura|width="1080"}} 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. ||<:> '''Low fuse Byte''' ||<:> '''Nº de Bit''' ||<:> '''Descripción''' ||<:> '''Valor por defecto''' || ||<:> CKDIV8 ||<:> 7 ||<:> Divide el reloj entre 8 ||<:> 0 || ||<:> CKOUT ||<:> 6 ||<:> Salida del reloj ||<:> 1 || ||<:> SUT0 ||<:> 5 ||<:> Tiempo de comienzo ||<:> 1 || ||<:> SUT1 ||<:> 4 ||<:> Tiempo de comienzo ||<:> 0 || ||<:> CKSEL0 ||<:> 3 ||<:> Selección de la fuente del reloj ||<:> 0 || ||<:> CKSEL1 ||<:> 2 ||<:> Selección de la fuente del reloj ||<:> 0 || ||<:> CKSEL2 ||<:> 1 ||<:> Selección de la fuente del reloj ||<:> 1 || ||<:> CKSEL3 ||<:> 0 ||<:> Selección de la fuente del reloj ||<:> 0 || ===== 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. ||<:> '''High fuse Byte''' ||<:> '''Nº de Bit''' ||<:> '''Descripción''' ||<:> '''Valor por defecto''' || ||<:> RSTDISBL ||<:> 7 ||<:> Deshabilitar el ''reset'' externo ||<:> 1 || ||<:> DWEN ||<:> 6 ||<:> Habilitar del debugWire ||<:> 1 || ||<:> SPIEN ||<:> 5 ||<:> Habilitar la programación serial y la descarga de datos ||<:> 0 || ||<:> WDTON ||<:> 4 ||<:> Temporizador del perro guardián siempre encendido ||<:> 1 || ||<:> EESAVE ||<:> 3 ||<:> Preservación de la información en la EEPROM durante el borrado ||<:> 1 || ||<:> BOOTSZ1 ||<:> 2 ||<|2> Tamaño de la memoria para el BOOT ||<:> 0 || ||<:> BOOTSZ0 ||<:> 1 ||<:> 0 || ||<:> BOOTRST ||<:> 0 ||<:> Selección del vector de reset ||<:> 1 || ===== 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. ||<:> '''Extended fuse Byte''' ||<:> '''Nº de Bit''' ||<:> '''Descripción''' ||<:> '''Valor por defecto''' || ||<:> - ||<:> 7 ||<:> - ||<:> 1 || ||<:> - ||<:> 6 ||<:> - ||<:> 1 || ||<:> - ||<:> 5 ||<:> - ||<:> 1 || ||<:> - ||<:> 4 ||<:> - ||<:> 1 || ||<:> - ||<:> 3 ||<:> - ||<:> 1 || ||<:> BODLEVEL2 ||<:> 2 ||<|3> Nivel de disparo ||<:> 1 || ||<:> BODLEVEL1 ||<:> 1 ||<:> 1 || ||<:> BODLEVEL0 ||<:> 0 ||<:> 1 || ===== 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í: {{attachment:pag_fuse.png|Página para calcular los fuse|}} 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: {{attachment:pag_fuse_conf.png|Opcines de los fuse|}} 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. ||<:> '''Rango de frecuencias (MHz)''' ||<:> '''CLKSEL3''' ||<:> '''CLKSEL2''' ||<:> '''CLKSEL1''' || ||<:> 0.4 - 0.9 ||<:> 1 ||<:> 0 ||<:> 0 || ||<:> 0.9 - 3.0 ||<:> 1 ||<:> 0 ||<:> 1 || ||<:> 3.0 - 8.0 ||<:> 1 ||<:> 1 ||<:> 0 || ||<:> 8.0 - 16.0 ||<:> 1 ||<:> 1 ||<:> 1 || 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: ||<:> '''Oscilador, condiciones de alimentación''' ||<:> '''Ciclos de retardo''' ||<:> '''Retardo adicional (Vcc=5.0 V)''' ||<:> '''CLKSEL0''' ||<:> '''SUT1''' ||<:> '''SUT0''' || ||<:> Resonador cerámico, alimentación estable rápidamente ||<:> 258 CK ||<:> 14 CK + 4.1 ms ||<|4> 0 ||<|2> 0 ||<:> 0 || ||<:> Resonador cerámico, alimentación estable lentamente ||<:> 258 CK ||<:> 14 CK + 65 ms ||<:> 1 || ||<:> Resonador cerámico, ''Brown-out detection'' establecido ||<:> 1k CK ||<:> 14 CK ||<|2> 1 ||<:> 0 || ||<:> Resonador cerámico, alimentación estable rápidamente ||<:> 1k CK ||<:> 14 CK + 4.1 ms ||<:> 1 || ||<:> Resonador cerámico, alimentación estable lentamente ||<:> 1k CK ||<:> 14 CK + 65 ms ||<|4> 1 ||<|2> 0 ||<:> 0 || ||<:> Oscilador de cristal, ''Brown-out detection'' establecido ||<:> 16k CK ||<:> 14 CK ||<:> 1 || ||<:> Oscilador de cristal, alimentación estable rápidamente ||<:> 16k CK ||<:> 14 CK + 4.1 ms ||<|2> 1 ||<:> 0 || ||<:> Oscilador de cristal, alimentación estable rápidamente ||<:> 16k CK ||<:> 14 CK + 65 ms ||<:> 1 || 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: ||<:> '''Rango de frecuencias (MHz)''' ||<:> '''CLKSEL3''' ||<:> '''CLKSEL2''' ||<:> '''CLKSEL1''' ||<:> '''CLKSEL0''' || ||<:> 7.3 - 8.1 ||<:> 0 ||<:> 0 ||<:> 1 ||<:> 0 || Para el retardo sería: ||<:> '''Condiciones de alimentación''' ||<:> '''Ciclos de retardo''' ||<:> '''Retardo adicional (Vcc=5.0 V)''' ||<:> '''SUT1''' ||<:> '''SUT0''' || ||<:> ''Brown-out detector'' establecido ||<:> 6 CK ||<:> 14 CK ||<:> 0 ||<:> 0 || ||<:> Alimentación estable rapidamente ||<:> 6 CK ||<:> 14 CK + 4.1 ms ||<:> 0 ||<:> 1 || ||<:> Alimentación estable lentamente ||<:> 6 CK ||<:> 14 CK + 65ms ||<:> 1 ||<:> 0 || ||<:-3> Reservado ||<:> 1 ||<:> 1 || Para el oscilador externo los bits para CLKSEL son: ||<:> '''Rango de frecuencias (MHz)''' ||<:> '''CLKSEL3''' ||<:> '''CLKSEL2''' ||<:> '''CLKSEL1''' || ||<:> 0.4 - 20 ||<:> 0 ||<:> 1 ||<:> 1 || Y para los retardos. las opciones son: ||<:> '''Oscilador, condiciones de alimentación''' ||<:> '''Ciclos de retardo''' ||<:> '''Retardo adicional (Vcc=5.0 V)''' ||<:> '''CLKSEL0''' ||<:> '''SUT1''' ||<:> '''SUT0''' || ||<:> Resonador cerámico, alimentación estable rápidamente ||<:> 258 CK ||<:> 14 CK + 4.1 ms ||<|4> 0 ||<|2> 0 ||<:> 0 || ||<:> Resonador cerámico, alimentación estable lentamente ||<:> 258 CK ||<:> 14 CK + 65 ms ||<:> 1 || ||<:> Resonador cerámico, ''Brown-out detection'' establecido ||<:> 1k CK ||<:> 14 CK ||<|2> 1 ||<:> 0 || ||<:> Resonador cerámico, alimentación estable rápidamente ||<:> 1k CK ||<:> 14 CK + 4.1 ms ||<:> 1 || ||<:> Resonador cerámico, alimentación estable lentamente ||<:> 1k CK ||<:> 14 CK + 65 ms ||<|4> 1 ||<|2> 0 ||<:> 0 || ||<:> Oscilador de cristal, ''Brown-out detection'' establecido ||<:> 16k CK ||<:> 14 CK ||<:> 1 || ||<:> Oscilador de cristal, alimentación estable rápidamente ||<:> 16k CK ||<:> 14 CK + 4.1 ms ||<|2> 1 ||<:> 0 || ||<:> Oscilador de cristal, alimentación estable rápidamente ||<:> 16k CK ||<:> 14 CK + 65 ms ||<:> 1 || 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: ||<:> '''BODLEVEL2''' ||<:> '''BODLEVEL1''' ||<:> '''BODLEVEL0''' ||<:> '''Vmin''' ||<:> '''Vtip''' ||<:> '''Vmáx''' ||<:>'''Unidad'''|| ||<:|4> 1 ||<:|2> 1 ||<:> 1 ||<:-4> Deshabilitado || ||<:> 0 ||<:> 1.7||<:> 1.8 ||<:> 2.0 ||<:|3> V || ||<:|2> 0 ||<:> 1 ||<:> 2.5 ||<:> 2.7 ||<:> 2.9 || ||<:> 0 ||<:> 4.1 ||<:> 4.3 ||<:> 4.5 || ||<:|4> 0 ||<:|2> 1 ||<:> 1 ||<:-4|4> Reservado || ||<:> 0 || ||<:|2> 0 ||<:> 1 || ||<:> 0 || ==== 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 * Memoria a la que se va a acceder: Esta pude ser flash, eeprom, lfuse, hfuse ó efuse. Hay otras memorias pero por ahora no son de interés * Lo que se va a hacer: Escritura(w), lectura(r), lectura y verificación(v). * Lo que se va a escribir o donde se guardará lo leido: Esto depende directamente de la siguiente opción. En el caso de escritura, puede ser un número hexadecimal, un archivo .bin ó un archivo .hex. Si se va a leer, se puede guardar en un archivo .bin ó .hex. Acá, para que todo sea más sencillo, utilizaremos archivos .hex * Formato: Intel Hex(h), motorola(s), raw(r) y escritura directa(m). Para escribir los fuse utlizaremos escritura directa(m). Para escribir los archivos .hex a la memoria flash, no se indicará 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< #include int main (void) { //Configuramos los puertos DDRD = (1< #include void delay_s () { uint16_t ms; ms=1000; while(ms) { _delay_ms(1); ms--; } } int main (void) { //Configuramos los puertos DDRD = (1< #include int main (void) { //Configuramos los puertos DDRD = (1< #include void delay_s () { uint16_t ms; ms=1000; while(ms) { _delay_ms(1); ms--; } } int main (void) { //Configuramos los puertos DDRD = (1< #include void delay_vs (uint16_t ms) { while(ms) { _delay_ms(1); ms--; } } int main (void) { //Configuramos los puertos DDRD = (1< #include int main (void) { short cont=0; //Configuramos los puertos DDRD = (1<