Cronógrafo de AVR desde el concepto de PCB (6 / 13 paso)

Paso 6: El código

Ahora a los cerebros del proyecto! El código.

Te pega las instrucciones aquí una sección en un momento y explicar brevemente lo que hacen.  Recuerde "Programación en C para microcontroladores" un tutorial más detallado explicando el uso de operadores y tal.  La hoja de datos de ATMega328P también es una buena referencia para aprender el propósito de un registro o bit.  Voy a intentar mantener esta parte breve, pero informativo.  C el aprendizaje es un proyecto por sí mismo, por lo que no se desanime si te pierdes totalmente en primera!  Sé que hice.

El código real en esta página estará en negrita para que sea más fácil discernir del texto... Comentarios a Itálica.

/*
* Speed_measurement.c
*
* Creado: 15/09/2012 20:50:23
* Autor: Thomas L
*/

#define F_CPU 14.7456E6

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

Estas primeras líneas dan sólo el compilador de información sobre qué archivos necesita y la velocidad de reloj de CPU.  Necesitamos utilizar algunos retrasos más adelante para hacer el AVR solo esperar, así que tenemos que incluir el archivo util/delay.h.  Este archivo contiene las funciones de retardo real que llamaremos en el programa principal.  Las funciones de retardo ofrecen un retraso de tiempo, por lo que nos obligan a la velocidad de reloj de CPU.  #define F_CPU indica al compilador la velocidad del oscilador de cristal, y este número se proporciona entonces para todas las funciones que deban utilizarlo.

/************************************************************************/
/ * declarar variables globales * /
/************************************************************************/

unsigned int resultado = 0;
int interruptcount = 0;
int largo = 0;
resetcount int largo = 0;

Estas son declaraciones de variables globales.  Si queremos asignar un valor a una variable, tenemos que asignar a la variable de un tipo primero.  Por ejemplo 'int' es de entero y es un número entero representado por 16 bits (bits número 15 y un bit de signo).  'Unsigned int' es también un entero, pero con ningún bit de signo.  Esto significa que sólo puede ser positiva, pero puede ser dos veces tan grande debido al poco extra número.  Estas variables son necesarias en la función main(), así como las rutinas de interrupción, por lo que declaró aquí antes de que comience la función main().  Esto hace que estas variables globales, lo que significa que pueden ser accedidos y modificados por cualquier parte del programa.

También, en este punto es importante tener en cuenta, cada expresión debe finalizar con un punto y coma!!! Estos olvido todo el tiempo, y el compilador me da errores... todo el tiempo.

int Main
{

DDRD = 0 X 00;     //portd entrada de interrupciones externas
DDRC = 0xDF;   //portc salida de 7 segmentos multiplexado y 1 entrada para la visualización de la distancia
DDRB = 0xFF;   datos de 7 segmentos de fot //portb salida en bcd
PORTD | = 0xFF; //enable portd tire hacia arriba de los resistores
//(int0 and int1 require external pull up resistors or 1 interrupt will be triggered at reset)

Ahora tenemos la función main().  Este es el comienzo del programa real, y comienza por configurar algunas cosas.  Las declaraciones de DDRn anteriores asignan valores a los registros de dirección de datos de los 3 puertos.  Estos registros de 8 bits configuración los pines de I/O como entradas o como salidas.  Un 1 en una posición poco hará el correspondiente pin una salida, y un 0, sería una entrada. Los valores asignados pueden ser en decimal, binario o hexadecimal.  Números decimales se escriben normalmente como 255.  Binario y hexadecimal tienen un prefijo para contar al compilador qué tipo de número es.  Binario sería 0b11111111. Hexagonal sería 0xFF.  Los tres de estos son iguales a 255, y el compilador no le importa que utilices.  Hex es normalmente el sistema de elección porque es más fácil de leer que el binario y más fácil relacionar a binario a decimal... El equivalente binario del número asignado a cualquier DDR es de 8 bits que representa la dirección de datos de los 8 pines del puerto.  El PORTD | = FF; asignación permite que las resistencias de pull-up en los pines de entrada.  Si se configura PORTD como salida, entonces la asignación cambiaría los Estados de salida de los pins

Aprende tu HEX a binario conversiones y comprometan a la memoria! Viene bien...

Un tutorial de HEX: http://www.microbuilder.eu/Tutorials/Fundamentals/Hexadecimal.aspx ***

EICRA | = (1 << ISC11) | (1 << ISC01) ;                   //configure interrupciones externas
EIMSK | = (1 << INT1) | (1 << INT0);
TCCR1B | = (1 << CS12);                                    
escala para timer1 //set 256

Esta la parte donde suelen tener para sacar la hoja de datos para el AVR y hacer una pequeña excavación.  EICRA es el A. externos interrumpir el registro de Control  Este registro tiene 4 bits que podemos fijar para configurar cómo se activan interrupciones por los pines de interrupción externa INT0 y INT1.  Cada uno de los 4 bits tiene un único nombre para identificarla cuando ajuste o compensación pedacitos.  ISC00 y ISC01 son control de sentido interrupción INT0.  Estas definir el tipo de cambio necesario en el pasador de INT0 para activar la interrupción.  ISC10 y ISC11 son el mismo control para lo pin INT1.  Comprobación de la hoja de datos muestra que mediante el establecimiento de ISC01 y ISC11, configuramos los pines de interrupción para activar en el borde negativo que va solo.  La expresión (1 << ISC11) es un poco operador de desplazamiento para fijar la broca ISC11.  Vea "Programación en C para microcontroladores" para aprender todo sobre los operadores y la manipulación de bits; es un poco demasiado para entrar aquí.

El registro EIMSK (máscara de interrupción externa) tiene dos bits para activar o desactivar las interrupciones externas.  Ambos de estos bits se deben establecer o las rutinas de interrupción nunca funcionará.

TCCR1B (Timer/Counter Control registrar 1 B) tiene varios pedacitos que pueden configurar cómo se comporta el temporizador.  Todo lo necesario para preocuparse por aquí es el valor de escala.  Esto significa simplemente, reloj de la CPU cuántos pulsos se contará antes de que el contador se incrementa en 1.   En este caso, elegí un valor prescale de 256, representado por el ajuste de la broca CS12 (consulte la hoja de datos.)  Esto divide el reloj de la CPU por 256 antes de aplicar para el temporizador/contador ayuda a evitar desbordamientos de contador en el marco de tiempo que intentamos medir.

/************************************************************************/
/ * declaramos las variables para el cálculo y la presentación * /
/************************************************************************/


unsigned int unos = 0;
unsigned int decenas = 0;
unsigned int cientos = 0;
unsigned int x = 0;
doble ticsfoot = 0;
doble fps = 0;
doble fph = 0;
doble mph = 0;
doble h = 0;
doble mps = 0;
int distancia = 0;

SEI();        //enable las interrupciones globales

OK, una sección de fácil! Esto es sólo algunas declaraciones más variables y que cosa sei().  "sei()" es un comando conocido por el compilador en el sentido de 'activar interrupciones globales'.  No hay interrupciones se activarán si este bit no está establecido.  "cli()" borra el bit de desactivación de interrupciones globales.  Que se usaremos más adelante para evitar problemas.



while(1)
{

/************************************************************************/
/ * obtener sensor distancia en pies de pind 0,1,4,5 * /
/************************************************************************/

int distanceinput = (~ PIND & 0x33);
hibits int = (distanceinput >> 2);                                
//getting pind bits 0,1 y 4,5 para ser
int lobits = (distanceinput & 0 x 03);                           //distance valor en BCD.  Brocas de 2, 3 son los
distancia = (hibits lobits);                                           //ext pines de interrupción ya en uso.

si (distancia == 0) distancia = 16;

Esta pequeña sección lee el estado de los interruptores dip 4 que permiten al usuario elegir la distancia entre los sensores.  Hay cierta manipulación de bits pasando aquí porque las 4 entradas no son consecutivas.  Las variables 'hibits' y 'lobits' son lugares de almacenamiento temporal a ordenar los bits.  La primera línea aísla los pedacitos que quieras con el & operador.  La segunda línea cambia los bits en 2 lugares la conseguir la Hola de los pedacitos en la posición correcta.  La tercera línea sólo aísla el lo de los pedacitos.  Entonces en la cuarta línea la lo y hi pedacitos se agregan para hacer que un número.  La instrucción 'if' que al final impide que el usuario elegir '0' y 'división por cero' condición más adelante.


/************************************************************************/
/*                                    'ready' indicator LED                                           */
/************************************************************************/


Si (interruptcount == 0)
{
PORTC | = (1 << 3);
}
otra cosa
{
PORTC & = (0 << 3);
}

Esta sección averigua si hay una medida en el progreso y luces LED de listo si no.  La variable interruptcount se incrementa en las rutinas de interrupción para el programa de seguimiento de qué parte de la medida está teniendo lugar actualmente.  Vamos a ver las rutinas de interrupción cerca del final del código.

/************************************************************************/
/ * cálculos para encontrar la velocidad en unidades de 4 * /
/************************************************************************/

si (interruptcount == 2) //Only calcular cuando ambas interrupciones ocurridas
{
CLI();                                                            
//disable las interrupciones globales

ticsfoot = (tiempo / distancia);                    
//distance distancia entre sensores en pies - ticsfoot es contador tics/pie
fps = (57600 / ticsfoot);                           //57600 es contador tics/seg (cpu clk/prescaler)
fph = (fps * 60 * 60);
mph = (fph / 5280);

km/h = (mph * 1.609344);
MPS = (fps * 0.3048);

EIMSK | = (1 << INT1) | (1 << INT0);
SEI();                                                             interrupciones externas //Re-Enable e interrupciones globales
  }

Si la variable interruptcount se ha incrementado dos veces (esto sucede más tarde en las rutinas de interrupción), que significa que se han producido dos interrupciones y el valor medido está listo para ser utilizado en los cálculos.  Aquí vemos la instrucción cli() que deshabilita las interrupciones.  Es una buena práctica hacerlo al leer una variable que una interrupción tiene la habilidad de cambiar.  ¿Por qué?  Las variables normales 'int', por ejemplo, son 16 pedacitos de largo o dos bytes.  El AVR es un sistema de 8 bits, por lo que toma dos instrucciones para leer los 2 bytes de un solo valor.  Es posible, entonces, una interrupción que ocurra después de que el primer byte es leer, pero antes de la segunda. Si intenta leer una variable, pero la interrupción ocurre después de leer el primer byte, la rutina de interrupción puede cambiar el valor de la variable antes de haya terminado se lee!  El programa se reanudará donde se quedó y lee el segundo byte de la variable con datos que no coincide con el primer byte!  Entonces el valor de carga no es lo que se espera y puede causar problemas.

Aquí las matemáticas no es demasiado complicada.  Los comentarios deben ser suficiente para averiguar lo que está sucediendo.  Esto es donde usamos la variable distancia de los interruptores dip.  La variable tiempo viene después de las rutinas de interrupción.

La primera interrupción se inhabilita en su rutina para que puede ejecutar sólo una vez antes de que la medición esté completa.  Es por ello que la instrucción EIMSK existe.  Vuelva a activar ambas interrupciones externas para que cualquiera era primera y consiguió para discapacitados se activa otra vez.

/************************************************************************/
/*                                   choose output options                                         */
/************************************************************************/

   if (!( PIND & (1 << PIND6)) & & (PIND & (1 << PIND7))) //choose pies/seg.
   {
Round(FPS);
resultado = fps;
}
else if (PIND & (1 << PIND6) & &! () PIND & (1 << PIND7)))
//choose metros/seg.
    {
Round(MPS);
resultado = mps;
}
else if (PIND & (1 << PIND6) & & (PIND & (1 << PIND7)))
//choose kilómetros/hora
     {
Round(kph);
resultado = kph;
}
else                                                                                                            
//default miles/hr
    {
Round(mph);
resultado = mph;
}

Si (resultado > = 999) resultado = 999;

Esta sección está leyendo los dos últimos interruptores dip para determinar las unidades de salida deseado.  Entonces cualquier valor es necesario se almacena en la variable de resultado.

La función round() es una manera fácil de obtener valores enteros como nuestra salida sin introducir demasiado error.  Esta función esta incluida en el fichero math.h que ya está incluido en el programa el archivo delay.h que incluimos manualmente.  Si se mira a la ventana de explorador de la solución en el lado derecho en el estudio de ATMEL, verás el proyecto nombre seguido de "Dependencias" (marcadas en la foto).  Después de construir la solución por primera vez (haga clic en crear >> generar solución,) todos los archivos incluidos en el programa se listarán aquí.  Luego puede abrir el archivo delay.h que hemos incluido al principio del código y echar un vistazo.  Si se desplaza a través de él, debe encontrar la instrucción #include < math.h > cerca de la cima.  Cuanto más abajo se encuentra la función _delay_ms() que llamamos crear el 1ms retrasar más adelante.

El 'si' al final previene tratando de mostrar un número que no encaja en nuestra 3 muestra.





/************************************************************************/
/ * retardo detener múltiples "2ª interrupción" activa * /
/ * sin retrasar la ejecución de código principal * /
/************************************************************************/


resetcount ++;

Si ((resetcount > = 0x00FF) & & (interruptcount > = 2))
//resetcount límite determina el retardo de
  {                                                                                                                      //before reset. 0x00FF approx. 3 sec
interruptcount = 0;
resetcount = 0;
}


Esta parte es (especie de) un temporizador hecho fuera del bucle principal while(1).  Cada vez que el programa lazos más allá de este punto, resetcount se incrementa.  Los cálculos sólo se producen si interruptcount es exactamente 2; por lo tanto, si se produce la segunda interrupción otra vez no desordenar las matemáticas.  Si la interruptcount es 2 o más, nada puede ser medido hasta que los incrementos de resetcount hasta 0x00FF dando unos 3 segundos retrasan para que el objeto que el área del sensor.  No podemos usar un retraso normal aquí porque necesitamos la muestra al ser que nos muestra el valor medido.  Cuando interruptcount se restablece a 0, el LED de listo se enciende y la siguiente medición se puede hacer.






/********************************************************************************/
/ * Mostrar int resultado en pantalla de 3 dígitos siete segmentos * /
/ * retardo da siete segmento decodificador tiempo para decodificar y mostrar los dígitos * /
/********************************************************************************/

if (!( PINC & (1 << PINC5))) //to Mostrar configuración de distancia en pantalla
  {                                                                                //only while button is pressed
resultado = distancia;
}
otra cosa

Este 'si' relojes sólo el botón que es una petición para que la distancia de referencia mostrado en los visualizadores.  El más reciente cálculo de velocidad se almacena en el resultado cada vez que la sección de unidades de salida (arriba) se ejecuta en el bucle, por lo que el resultado sólo es igual a distancia mientras se presiona el botón.

cientos = (resultado / 100);                             lugar de //Get cientos dígitos
x = (resultado de 100%);
PORTB = (0x00|hundreds);
PORTC | = (1 << 2);                                                  
/ / escribir dígitos
_delay_ms(1) de ;
PORTC & = (0 << 2);

decenas = (x / 10);                                                         / / obtener lugar de 10 dígitos
x = x % 10;
PORTB = (0x00|tens);
PORTC | = (1 << 1);                                                      
/ / escribir dígitos
_delay_ms(1) de ;
PORTC & = (0 << 1);

unos = x;                                                                     / / obtener 1 lugar dígitos
PORTB = (0x00|ones);
PORTC | = (1 << 0);                                                        
/ / escribir dígitos
_delay_ms(1) de ;
PORTC & = (0 << 0);
}
}

Finalmente! a la pantalla!  Ahora que 'resultado' es igual a un número decimal de 3 dígitos válido, tenemos que romper en solos dígitos y enviarlas a su vez a las pantallas.  A obtener de cada dígito y almacenarlo en una variable llamada a, decenas o cientos.  Coloque los cientos dígitos es fácil; sólo dividir por 100.  La variable es de tipo 'int', para que nada después del punto decimal se almacena.  'x' es una variable temporal para almacenar el siguiente valor necesario.  El operador '%' hace la división, pero devuelve el resto de 'x'.  Esto nos da el dígito decenas y unidades a tratar.  Dividir por 10 y repetir para los peques.  Es una manera fácil de separar todos sus dígitos para ir a la pantalla y simplemente se puede ampliar para trabajar con números más grandes.

Las instrucciones de PORTB escriben los dígitos a los pines de salida va al decodificador de 7 segmentos.  Casi al mismo tiempo, las instrucciones de PORTC gire en la salida a transistor de tierra de la pantalla correspondiente a la cifra.  Luego el retraso mantiene la pantalla durante 1 MS antes de volver a apagar el transistor y pasar al siguiente dígito.  Muy bien.

Las llaves de cierre fin el bloque de código del bucle de While(1) y terminan el bloque de código de la función main().

/************************************************************************/
/*                                     sensor 1 interrupt                                                */
/************************************************************************/

ISR(INT0_vect)
{
Si (interruptcount == 0)
{
TCNT1 = 0 X 0000;                                              
//reset contador a 0
interruptcount ++;                                               //increment interrupción cuenta
EIMSK & = (1 << INT1) | (0 << INT0);                   //disable INT0

}
else if (interruptcount == 1)
{

tiempo = TCNT1;                                     valor del contador //Capture
interruptcount ++;                               //increment interrupción cuenta

}
Else resetcount = 0;

}

/************************************************************************/
/*                                       sensor 2 interrupt                                              */
/************************************************************************/

ISR(INT1_vect)
{
Si (interruptcount == 0)
{
TCNT1 = 0 X 0000;                                                    
//reset contador a 0
interruptcount ++;                                                 //increment interrupción cuenta
EIMSK & = (0 << INT1) | (1 << INT0);                           //disable INT1

}
else if (interruptcount == 1)
{
tiempo = TCNT1;                                                      
//capture valor del contador
interruptcount ++;                                                 //increment interrupción cuenta

}
Else resetcount = 0;
}

Ahora, los dos interrumpen rutinas...  Son exactamente la misma excepto una instrucción, por lo que sólo hablaré uno.

En primer lugar, observe que las rutinas de interrupción están fuera de la función main().  El programa completamente deja de lo que su hacer, deja la función main() y ejecuta la rutina de interrupción.  Después de la interrupción rutina es completa, devuelve a la función main() donde se quedó.

La primera línea, ISR(INT0_vect), es el nombre de la interrupción de la hoja de datos.  Si la interrupción está habilitada en el registro EIMSK y global las interrupciones están habilitadas, el programa saltará a esta línea cuando se activa la interrupción.

Verás que las rutinas de interrupción se componen básicamente de un compuesto ' IF... ELSE IF' declaración que solo hace un par de cosas.  Por lo general, es mejor mantener las rutinas de interrupción muy corto, cambiando solamente lo que debe cambiarse y volver rápidamente a la función main().  Utilizando la variable interruptcount para realizar un seguimiento del número de interrupciones que se han producido nos permite hacer las rutinas idénticas y también permite que los sensores ver objetos de destino procedente de cualquier dirección.

Cualquier interrupción se ejecuta primero restablece el temporizador de TCNT1 (que fijamos el prescaler para el principio) incrementa la variable interruptcount, luego deshabilita a sí mismo para que no se puede dispara más veces por un objeto de forma irregular.

Puesto que la primera interrupción incrementado interruptcount, segundo ejecutará las siguientes líneas de par.  Esta vez captura el valor de temporizador TCNT1 y almacenarlo en la variable 'tiempo' para ser utilizado en los cálculos.  Entonces interruptcount se incrementa otra vez.

Con interruptcount ahora igual a 2, se realizan los cálculos y el resetcount cuenta a 0x00FF antes de que se restablecerá todo para la siguiente medición.  Si la segunda interrupción se activa otra vez por alguna razón, resetcount se establece en 0 y la espera comienza otra vez.  Se trata de cómo impidió varias activaciones de la interrupción 2 para como necesarias para que el objeto que estorbe.

¡ UF, espero que al menos un poco de sentido... El siguiente paso, cargar el programa en nuestro chip y probar el circuito breadboarded!

Artículos Relacionados

Estación de Audio y cronógrafo Steampunk

Estación de Audio y cronógrafo Steampunk

gracias a todos de sus diseños muy creativos y proyectos, todos ustedes me han inspirado a crear mi propio Steampunk Audio y estación de cronógrafo.  Este proyecto me llevó WAAAY más tiempo del previsto y pasó a manera de presupuesto, pero una vez ca
Cronógrafo para cañón de aire

Cronógrafo para cañón de aire

Cronógrafo consiste de un sensor montaje y temporizador de caja que puede utilizarse para medir la velocidad del bozal de un proyectil disparado desde un cañón de aire.Había construido esta configuración para el uso con un cañón de aire grande con un
Paintball/balísticos Cronógrafo

Paintball/balísticos Cronógrafo

crear un cronógrafo de Paintball bajo costo menos de $ 40!Un cronógrafo balístico es un dispositivo que registra la velocidad de un proyectil. Este cronógrafo particular que he hecho ha sido probado solamente con una pistola de paintball y es relativ
Airsoft o pistola de aire (u otro proyectil lanza dispositivo) Cronógrafo utilizando un micrófono

Airsoft o pistola de aire (u otro proyectil lanza dispositivo) Cronógrafo utilizando un micrófono

mi primera crítica Instructable, constructivo Bienvenido.Si tienes un rifle o cualquier otro dispositivo de disparo del proyectil que usted necesita o quiere Cronógrafo las opciones son bastante limitadas. Puede encontrar una persona que tiene un cro
ATtiny Cronógrafo

ATtiny Cronógrafo

Hice un cañón de aire el verano pasado, y aunque 's diversión para disparar dardos nerf a velocidades inane durante horas, es un poco difícil de cuantificar exactamente a qué velocidad "realmente maldita ' rápido" sin una cámara de alta velocida
Cómo utilizar mi método de cronógrafo a ROF

Cómo utilizar mi método de cronógrafo a ROF

si no lees mi método chronographing leerla { antes de continuar.En este instructable mostrará usted cómo adaptar el método de chronographing para dar aproximado velocidad de disparo de un rifle automático.Se necesita:1. una pistola de airsoft2. una c
Pobre hombre Cronógrafo airsoft.

Pobre hombre Cronógrafo airsoft.

Si usted cada airsoft jugado en un campo, o incluso con cuidado amigos saben la importancia de tener una pistola que dispara a un nivel adecuado de FPS (pies por segundo). Hoy voy a mostrar cómo hacer un FPS áspero de la lectura de una lata de soda.P
Perfiles de forma de cuchillo, desde el concepto hasta la ejecución.

Perfiles de forma de cuchillo, desde el concepto hasta la ejecución.

Un knifemaker, tanto si eres un primero temporizador o un veterano, una de las habilidades más básicas que necesita para desarrollar es hoja de perfiles. Esencialmente, que cantidades para el desarrollo de la hoja básica forma eventualmente será su c
Cacatúa loro Tutorial de máscara de cuero: Desde concepto hasta finalización

Cacatúa loro Tutorial de máscara de cuero: Desde concepto hasta finalización

Hola, mi nombre es Chelsea y este es mi segundo Instructable publicado.En este Instructable, yo se ser caminando a través de los pasos de hacer una máscara de cuero de calidad premium; desde el concepto del diseño y la compra inicial de la piel hasta
¿Una guía completa de arranque AVRs

¿Una guía completa de arranque AVRs

has jugado con Arduino y ahora tienen un gusto por microcontroladores?¿Han intentado ir más allá de Arduino pero tiene parada por la pajina densa?Este es el instructivo para usted!Estaba trabajando en un instructivo para el concurso de epilog que vin
Desde la IDEA hasta la producción - Cryptex

Desde la IDEA hasta la producción - Cryptex

Este Instructable es ilustrar el proceso de tomar un concepto a través de diseño y prototipos hasta producción Inicio en pequeña escala. Hemos elegido un Cryptex para ilustrar este proceso. Ya hay varios diseños de Cryptex en este sitio, pero queremo
Programador universal para de AVR y S51 y ZIF socket!

Programador universal para de AVR y S51 y ZIF socket!

Este programador ha sido diseñado por la empresa y se ha producido al menos 2500PCS de esto y es absolutamente probado y normas y ahora no fabricar más esta versión, aquí es el producto de enlace (lo siento persa sólo estaba disponible)Programador un
Montando el jinete de dragón 500 para uso con el dragón del AVR

Montando el jinete de dragón 500 para uso con el dragón del AVR

no hace mucho tiempo la empresa Atmel salió con una gran herramienta para el uso con la línea AVR de microcontroladores llamado el dragón del AVR. Este pequeño dispositivo USB proporciona a profesionales y aficionados por igual la capacidad para util
Cómo empezar con Eclipse y AVR

Cómo empezar con Eclipse y AVR

programación de AVRs es divertido, pero a veces los entornos de desarrollo de fabricantes hacen mantenimiento de código una tarea. Si buscas un gratis, cruz plataforma, pieza de alta calidad de software para la programación de AVRs Eclipse es una bue