En esta entrada de Blog, mostraré un ejemplo ilustrativo del uso del comparador analógico de un microcontrolador AVR. La implementación se realiza en un microcontrolador ATMEGA16. En dicho ejemplo se usará el comparador para estimar la frecuencia de una señal sinusoidal.
Funcionamiento de comparador
El comparador análogo compara los valores de entrada en el pin positivo AIN0, y en el pin negativo AIN1. Cuando el voltaje en el pin positivo es mayor que el voltaje en el pin negativo, se activa la bandera del comparador análogo. Adicionalmente, se puede usar la salida de comparador análogo para activar la captura de eventos de la unidad Timer1 y también para activar interrupciones por flanco de subida, bajada o conmutación de la salida del comparador.
Por otra parte, es posible seleccionar cualquiera de los pines de las entradas analógicas (ADC7:0) para reemplazar la entrada negativa del comparador análogo. Así, se puede multiplexar la comparación de varias señales, conectadas a diferentes pines del microcontrolador. Para este caso, se hace uso del multiplexor del convertidor análogo/digital, y por tal motivo, éste se debe apagar mientras se realiza la multiplexación del comparador. Si el bit de habilitación de multiplexación del comparador análogo se encuentra activado (ACME=`1'), y el ADC se encuentra deshabilitado (ADEN=`0'), entonces los bits MUX2:0 en el registro ADMUX controlarán el pin de entrada que reemplazará la entrada negativa del comparador.
Ejemplo
Diseñar un programa que permita medir la frecuencia de una señal sinusoidal entre 0 y 5V, haciendo uso del comparador análogo y el módulo de captura del Timer1. La visualización del resultado debe realizarse en un arreglo de 4 displays de 7 segmentos.
Código solución
1 // Dispositivo = ATMEGA16
2 // Fclk = 1.0MHz
3 // Incluir librería matemática libm.a en el proyecto
4
5 #include<avr/io.h> // Puertos y registros i/o
6 #include<util/delay.h> // Tiempos y retrasos
7 #include<avr/interrupt.h> // Interrupciones
8 #include <util/atomic.h> // Ejecución atómica de bloques
9
10 // Prototipo de funciones
11 void visualizar(int); // Función para visualizar un dato en 4
12 // displays de 7 segmentos
13
14 // Declaración de variables globales
15 volatile unsigned int tiempo1=0, tiempo2=0, diff=0;
16
17 int main(void)
18 {
19 // Variable flotante para calcular frecuencia
20 volatile float freq;
21
22 // Variable entera de contador
23 int i;
24
25 // Asignación de dirección de puertos
26 DDRA=0xFF; // Puerto A salida
27 DDRC=0xFF; // Puerto C salida
28 DDRD=0x0F; // Mitad de puerto D como salida
29
30 // Configuración del comparador análogo
31 // * El comparador activa el módulo de captura del Timer1
32 ACSR|=(1<<ACIC);
33
34 // Configuración de Timer1
35 // * Modo normal
36 // * Filtro cancelador de ruido en captura
37 // * Captura por flanco de subida
38 // * Reloj sin preescalador
39 TCCR1B|=(1<<ICNC1)|(1<<ICES1)|(1<<CS10);
40 TIMSK|=(1<<TICIE1); // Habilitación interrupción de captura
41
42 sei(); // Habilitación de interrupciones globales
43
44 // Ciclo de polling
45 while(1){
46
47 // Mediante este macro es posible ejecutar un
48 // bloque de forma atómica, es decir, sin que la
49 // interrupción interrupa la ejecución de este bloque
50 ATOMIC_BLOCK(ATOMIC_FORCEON)
51 {
52 // Se convierte la diferencia del registro de
53 // comparación en frecuencia
54 freq=1E6/((float)diff);
55 }
56
57 for(i=1;i<50;i++)
58 // Visualización del valor de frecuencia
59 visualizar(freq);
60
61 }
62 }
63
64 // Función para visualizar un número entero de 4 dígitos
65 // como máximo en un arreglo de 4 displays de 7 segmentos
66 // de ánodo común, en los cuales los pines de los segmentos
67 // se conectan al puerto C, y los ánodos se conectan al
68 // nibble menos significativo del puerto D
69 void visualizar(int num)
70 {
71 // Vector que contiene el mapeo de cada dígito para
72 // el display de 7 segmentos
73 const int mapa7Seg[10]={
74 0b11000000, // 0
75 0b11111001, // 1
76 0b10100100, // 2
77 0b10110000, // 3
78 0b10011001, // 4
79 0b10010010, // 5
80 0b10000010, // 6
81 0b11111000, // 7
82 0b10000000, // 8
83 0b10010000};// 9
84
85 // Variable para hacer el recorrido por cada display
86 volatile char i;
87
88 // Ciclo de graficación en cada display
89 for(i=0;i<=3;i++)
90 {
91 // Se apagan los segmentos antes de cambiar de display
92 PORTC=0xFF;
93
94 // Cambio de display
95 PORTD=8>>i;
96
97 // Visualización de dígito
98 PORTC=mapa7Seg[num%10];
99
100 // Tiempo de espera para visualización
101 _delay_ms(1);
102
103 // División entre 10 y ruptura de ciclo de
104 // graficación para no mostrar ceros a la izquierda
105 if((num/=10)==0)
106 break;
107 }
108
109 }
110
111 // Rutina de servicio de interrupción por evento de captura
112 // de la unidad Timer1
113 ISR(TIMER1_CAPT_vect)
114 {
115 // Enciende bit 0 del puerto A para verificar
116 // que las interrupciones se ejecuten adecuadamente
117 PORTA=1;
118
119 // Actualización de marcas de tiempo de eventos
120 tiempo1=tiempo2;
121 tiempo2=ICR1;
122
123 if(tiempo2 > tiempo1)
124 // Diferencia entre eventos para hallar período
125 diff=tiempo2-tiempo1;
126 else
127 // Si la diferencia es negativa, significa que el
128 // contador tuvo overflow, por tanto se debe añadir 0x10000
129 diff=0x10000+tiempo2-tiempo1;
130
131 // Apagado del bit 0 del puerto A
132 PORTA=0;
133 }
Diagrama esquemático de la solución
Explicación de la solución
El programa para solucionar este ejemplo hace uso del módulo de captura del Timer1 el cual se conecta a la salida del comparador, detectando de esta forma cada vez que se presente un evento de comparación de voltajes. Para medir la frecuencia de una señal sinusoidal, se compara la señal de entrada con respecto a una referencia de 2.5 V; cada vez que la onda sinusoidal cruza esta referencia, se tendrá medio período de la señal, a partir del cual se puede calcular su frecuencia. En la rutina de servicio de interrupción se agregan un par de líneas que activan y desactivan el pin 0 del puerto A, para verificar que se estén capturando correctamente los eventos.
Link recomendado: http://www.administracionsatelital.com/
que compilador utilizas para tu código en C??
ResponderEliminarHola, para las aplicaciones de AVR uso el gcc para linux. En windows uso el paquete WinAVR, a través de la IDE de Atmel AvrStudio, la cual tiene herramientas muy útiles.
Eliminar