Traducción


by Transposh - Plugin de traducción para WordPress

Categorías

Vusibino Tachometer

Podemos ver los microcontroladores como el cerebelo de un ordenador, comunicando este con aquellos podemos alimentar a la CPU con datos del mundo real a través de los sensores, y ordenarle que ejecute acciones respecto a estos datos o las memorice para actuar autónomamente.

En esta y las siguientes entradas veremos cómo construir unos sencillos circuitos para controlar ventiladores de ordenador con nuestro VUSiBino. Los circuitos se conectarán con cables, no haremos un escudo para que sea más sencillo colocar las placas en donde queramos. Empezaremos con un circuito para controlar dos ventiladores de CPU con cuatro pines, estos ventiladores son los más fáciles de controlar ya que el PWM lo podemos aplicar con el mismo voltaje de nuestro microcontrolador, y llevan tacómetro incorporado para ver la velocidad a la que está girando.

Circuito

Las siguientes imágenes muestran los diseños del circuito. Para hacerlo más flexible y compatible con otros aparatos he optado poner las conexiones al microcontrolador a través de clemas. Para montarlo, usad de referencia la entrada del VUSiBino. Necesitaremos:

 PCB perfboard  18×11
Resistencias 2x10kOhm.

2×68 Ohm.

Conectores 4xClema

2x4Pin strip macho

 

Las resistencias de 10k Ohm son el “Pullup” de los pin que recogen la señal del tacómetro en caso de que no lo  activemos en el micro, las de 68 Ohm sirven de sumidero hacia la fuente. Las clemas se conectan al VUSiBino, como se indica en la última imagen de la secuencia y conectan con los pines de PWM y tacómetro del ventilador, que se conecta con las tiras de pin (o un conector de cuatro pines). La alimentación del ventilador va por la cuarta clema, justo detrás de los conectores del ventilador a una fuente de 12V.

D1 y D4 irán a RPM1 y RPM2, que conectan con el lector de revoluciones del ventilador. Este consiste en un sensor hall, y dos pequeños imanes diametralmente opuestos, por cada revolución darán dos señales.  D3 y D5 dan la señal PWM a cada ventilador conectando las clemas PWM1 y PWM2.

Breve explicación de qué es el PWM

Los pines digitales del ATMEGA sólo pueden tener dos voltajes, 0 y +5V, pero si queremos enviar un voltaje más bajo para ralentizar un motor DC, bajar la luminosidad de un led o variar el comportamiento de cualquier otro aparato, podemos usar un truco que depende de la digamos “inercia” de cada aparato, o el tiempo que tarde en reaccionar al cambio de voltaje.  Si conectamos un motor podemos observar que tarda un poco en arrancar, y cuando lo desconectamos otro poco en pararse, si apagamos y encendemos muy rápido el motor parecerá que funciona contínuamente. Si variamos el tiempo entre encendidos y apagados parecerá que estamos variando el voltaje, si hacemos que el tiempo entre apagados sea muy corto y ampliamos el de encendido el resultado es que el voltaje aparente es el máximo, pero si acortamos el tiempo de encendido y alargamos el de apagado, parecerá que el voltaje ha bajado. Una explicación más detallada del PWM la podremos encontrar en este enlace.

Hay que tener en cuenta que no todos los aparatos eléctricos y electrónicos soportan de igual manera un ciclo rápido de apagados y encendidos, conviene informarse antes de intentarlo con alguno caro o del que no tengamos repuesto a mano.

Firmware

Declaraciones

Empezamos con las declaraciones de librerías, constantes y variables globales.

La librería “<avr/io.h>” nos permite acceder a los pines del AVR mediante nombres de constantes y operar con ellos de forma sencilla, “<avr/interrupt.h>” nos facilita llamar y manipular las interrupciones, “<avr/wdt.h>” es para usar el “watchdog”, “<avr/eeprom.h>” la usaremos para acceder a la EEPROM, “<util/delay.h>” para usar la función de retraso en vez de tener que crearnos nuestro propio temporizador para su uso que puede ser engorroso, “<stdbool.h>” es una función para usar las variables de verdad y falsedad, finalmente “usbdrv.h” es la librería donde están las funciones que usaremos para comunicarnos con el puerto USB.

Siguen las constantes que usaremos para evaluar las acciones a tomar cuando el programa “host” llame al micro, tras ellas definimos las posiciones de la EEPROM donde guardaremos la configuración del programa de forma permanente. Explicaré con más detalle estas declaraciones en la parte del programa donde son usadas.

Las cadenas de caracteres”replyBuf”, “dataReceived” y “adcVal” ya las hemos usado en VUSiBino demo, sirven para enviar y recibir información desde y hacia el “host”.

Usamos “sw_auto” para saber cómo hemos de mover los ventiladores, los dos primeros bits se corresponden con la acción que ehemos de tomar en cada ventilador, si este cambiará su velocidad automáticamente o tendrá una velocidad fija que elegimos en el programa “host”. “tach_01” y “tach_02” recogen la velocidad de cada ventilador, las defino como entero de dos bytes porque el número puede ser mayor de 255 si hacemos la transformación de pulsos a RPM. “s” es una variable intermedia que usaremos para cambier el tipo de dato que enviamos al host a través del USB ya que las librerías USB sólo aceptan determinados tipos.

Usaremos dos variables como contadores, “lapse_01” y “countA” para controlar tiempos y ciclos del tacómetro mientras “portdhistory” guarda los estados de los puertos donde el tacómetro envía sus señales para compararlos con los nuevos y ver si hay cambios.

Las variables”pwm_01″ y “pwm_02” nos servirán para fijar la velocidad de los ventiladores, mientras “bool_up_01” y “bool_up_02” nos servirán para controlar si aumentamos o disminuimos la velocidad cuando los dejamos en automático.

 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <stdbool.h>
#include "usbdrv.h"

#define USB_MOD_FAN 0
#define USB_WRITE_CONF 1
#define USB_READ_CONF  2
#define USB_RESET_CONF  3
#define USB_READ_SENS  4

//Define EEPROM locations for restoring state
#define E_INIT 0
#define E_AUTO 1
#define E_PWM_01 2
#define E_PWM_02 3

static char replyBuf[16] = "Hello, USB!";
static uchar dataReceived = 0, dataLength = 0; // for USB_DATA_IN
static char adcVal[16] = "00000000aaaa0000";

int sw_auto  = 0b00000111;
long tach_01 = 0;
long tach_02 = 0;
static long s = 0;

int lapse_01 = 0;
volatile int countA = 0;

volatile uint8_t portdhistory = 0xFF;

uint8_t pwm_01 = 0x00;
uint8_t pwm_02 = 0x00;

bool up_01 = true;
bool up_02 = true;

Gestión de los mensajes de control

Caso “USB_READ_SENS”. Ponemos en “usbmsgPtr” los valores de los tacómetros y los enviamos para que el “host” los presente. Como veremos más adelante, hemos tratado los datos y se envían las revoluciones por minuto en vez de los pulsos que ha detectado el tacómetro cuando esto se podría hacer perfectamente en el “host” que tiene más memoria y velocidad, pero en este programa lo hago en el microcontrolador para ilustrar y probar un método de conversión de datos que podría ser útil en alguna otra circunstancia.

Caso USB_WRITE_CONF. Escribimos en la EEPROM los valores del pwm y el comportamiento del micro a la hora de mover los ventiladores, de este modo si lo desenchufamos, al volver a ser alimentado volverá a funcionar con los parámetros que hemos guardado. La EEPROM es una memoria no volátil, se puede escribir unas diezmil veces más que el espacio de firmware y requiere de un método de acceso especial, en este caso usamos las macros y funciones que nos ofrece el avr toolchain a través de “avr/eeprom.h” para evitar tener que usar ensamblador, que es siempre engorroso. Usamos la función eeprom_write_byte porque el dato que vamos a escribir es de ese tamaño, recordamos al compilador que la constante que hemos definido antes es un byte sin signo y un puntero con “(uint8_t*)” y le indicamos a la función cual es la variable que queremos guardar, que evidentemente tiene que ser un dato de ocho bits. Al final dejamos en usbMsgPtr la información de los sensores y el PWM para que lo recoja el host, lo hacemos en los otros casos porque nos interesa actualizar constantemente esta información.

Caso USB_READ_CONF. Leemos la configuración guardada en la EEPROM con una función similar a la anterior, pero en este caso enviamos la información a la cadena adcVal ordenada según veremos luego. De nuevo la enviamos al “host” por usbMsgPtr.

Caso USB_RESET_CONF. Escribimos la EEPROM con variables predeterminadas.

Caso USB_MOD_FAN. Recibimos desde el host datos sobre cómo van a actuar los ventiladores, si van a estar en automático o qué ciclo PWM le vamos a aplicar. Una vez recibidos, los pasamos a usbFunctionWrite para asignar las variables que deberán leer la rutinas que controlan los ventiladores.

 

USB_PUBLIC uchar usbFunctionSetup(uchar data[8]) {
	usbRequest_t *rq = (void *)data; // cast data to correct type
	
	switch(rq->bRequest) { // custom command is in the bRequest field
    case USB_READ_SENS:
	    usbMsgPtr = adcVal;
        return sizeof(adcVal);
		return 0;
	case USB_WRITE_CONF: // Write actual PWM and autoPWM values to eeprom
		eeprom_write_byte((uint8_t*)E_AUTO,sw_auto);//Automatic pwm settings
		eeprom_write_byte((uint8_t*)E_PWM_01,pwm_01);//PWM 01 actual value
		eeprom_write_byte((uint8_t*)E_PWM_02,pwm_02);//PWM 02 actual value
	    usbMsgPtr = adcVal;
        return sizeof(adcVal);		
		return 0;	
	case USB_READ_CONF: // Read and restore PWM and autoPWM values from eeprom
		adcVal[8]=eeprom_read_byte((uint8_t*)E_AUTO);
		adcVal[1]=eeprom_read_byte((uint8_t*)E_PWM_01);		//Low byte		
		adcVal[3]=eeprom_read_byte((uint8_t*)E_PWM_02);		//Low byte		
	    usbMsgPtr = adcVal;
        return sizeof(adcVal);
		return 0;
	case USB_RESET_CONF: // Reset EEPROM values to initial
		eeprom_write_byte((uint8_t*)E_INIT,'T');//marks once that eeprom init is done
		eeprom_write_byte((uint8_t*)E_AUTO,0b00000111);//All autos active
		eeprom_write_byte((uint8_t*)E_PWM_01,0x80);//50%
		eeprom_write_byte((uint8_t*)E_PWM_02,0x80);//50%
		sw_auto=eeprom_read_byte((uint8_t*)E_AUTO);
		pwm_01=eeprom_read_byte((uint8_t*)E_PWM_01);
		pwm_02=eeprom_read_byte((uint8_t*)E_PWM_02);
		return 0;
	case USB_MOD_FAN: // modify automatic fan control and PWM values
		dataLength  = (uchar)rq->wLength.word;
        dataReceived = 0;
		if(dataLength  > sizeof(replyBuf)) // limit to buffer size
			dataLength  = sizeof(replyBuf);
		return USB_NO_MSG; // usbFunctionWrite will be called now
    }

    return 0; // should not get here
}

Gestión de información entrante.

Aquí leeremos la información que el host nos manda sobre cómo operar los ventiladores. Nos envía tres datos, pero vienen en empaquetados palabras de cuatro bytes ya que son producidos por controles de windows que funcionan así. Como sólo necesitamos uno de esos cuatro bytes, lo primero que hacemos es extraerlos, declaramos la variable “fourth” (cuarto byte) obteniendo el resultado de una operación “and” con un byte relleno de unos y el primer caracter que recoge replyBuf, como sólo cabe un byte y hemos comparado los primeros ocho bits de la palabra el resultado será el primer byte.

Asignamos ese byte a “sw_auto” y comprobamos si nos dice que el primer ventilador debe estar en automático viendo si el primer bit de la variable es uno, en ese caso ponemos a cero el contador de vueltas del tacómetro y seguimos la rutina. De no ser así lo que haremos es recoger el valor que deberá tener el ciclo PWM del primer ventilador extrayéndolo del segundo dato que podemos recoger de “replyBuf” y seguimos.

Ahora comprobamos “sw_auto” y si nos indica en su segundo bit que debemos activar el modo automático para el segundo ventilador iniciamos el contador, en otro caso ponemos el valor del PWM para el segundo ventilador y salimos de la función.

// This gets called when data is sent from PC to the device
USB_PUBLIC uchar usbFunctionWrite(uchar *data, uchar len) {
	uchar i;
			
	for(i = 0; dataReceived < dataLength && i < len; i++, dataReceived++)
		replyBuf[dataReceived] = data[i];
		
        uint8_t fourth = replyBuf[0] & 0xff ;
		adcVal[8] = fourth;
        sw_auto = fourth;
	if (sw_auto & 0b00000001)
		lapse_01 = 0;
	else
	{
        fourth = replyBuf[1] & 0xff ;
        pwm_01 = fourth;
	}	

	if (sw_auto & 0b00000010)
		lapse_01 = 0;
	else
	{
        fourth = replyBuf[2] & 0xff ;
        pwm_02 = fourth;
	}	
    return (dataReceived == dataLength); // 1 if we received it all, 0 if not
}

Rutina principal

Inicialización

Lo primero que hará el microcontrolador al encenderlo es leer la EEPROM, y si el byte que definimos en “E_INIT” tiene un valor distinto de “T” (vale cualquier valor que queramos ponerle), grabaremos unos valores por defecto, que serán; Los ventiladores cambiarán de ciclo en automático y empezarán al 50% de ciclo PWM. Luego inicializaremos las variables de trabajo con los valores que leemos de la EEPROM. El método es el mismo que describimos anteriormente.

Tras eso pasamos a inicializar los valores de los puertos y temporizadores. En DDRB ponemos a uno (salida) los pines cero, uno y tres, nos aseguramos de que PB2 es de entrada. Los pines tres, cinco y seis del puerto D serán de salida, PD1(3) y PD4(6) serán de entrada.

Nos aseguramos de activar el “pullup” de puerto D con una asignación “or” “Puerto |= (valor<<Pin)”, es equivalente a un “or” con bits pero usamos las definiciones de pines que nos da “avr/io.h”. Esto ayudará a que la señal del tacómetro sea más estable y no haya rebotes que nos darían falsas señales. Si por alguna razón no queremos usar esta instrucción deberemos conectar los bornes de +5v a la fuente del VUSiBino para usar un pullup que nos estabilice la señal.

Ahora configuraremos los registros de los temporizadores para que “Timer0” y “Timer2” puedan ser usados para controlar el PWM y “Timer1” como un temporizador para medir los pulsos que nos da el tacómetro. En este envío explico las operaciones en Timer1. Si nos fijamos en los nombres de los registros de temporizadores vemos que nos referimos a cada uno según el número y una letra que se corresponde con la palabra superior y la inferior del registro donde cambiamos las funciones del temporizador. Así, en TCCR (Timer Counter Control Register) tenemos dos segmentos, el A y el B con distintos bits que indicarán su funcionamiento, en temporizador 1 ponemos el bit WGM12 del segmento B a uno para decirle que va a ser contador, y más tarde con el bit CS12 indicamos su escala. Todos estos bits tienen su nombre y función que se pueden consultar en el “datasheet” del microcontrolador. Me limitaré a decir que para que los temporizadores hagan funcionar el PWM en los pines que corresponden, debemos seguir las instrucciones que nos da tal documento, como queremos un modo concreto de PWM que va muy bien para nuestro propósito, los activaremos como aparece en el código. TCCR_A llevará activados los bits COM_A1, COM_B1, WGM_1 y WGM_0 para ir en “fase correcta” en ambos temporizadores, sustituyendo “_” por el número de temporizador. A TCCR_B lleva la información del escalado y el comparador, que varían un poco la frecuencia de la señal. Cuando hagamos pruebas con ventiladores de tres y dos pines veremos qué diferencias aparecen, pero en un ventilador de cuatro pines no las notaremos porque su hardware viene perfectamente ajustado para las señales PWM.

Los registros “OCR_A” se corresponden con pines con capacidad PWM y además marcan el límite superior de la señal, esto es, el ciclo de trabajo máximo será el que indique este registro en el modo de operación que hemos elegido, esto hace que los pines que hemos elegido “OCR_B” en el circuito dependerán de esta señal. Si usáramos los cuatro pines PWM tal y como hemos configurado los temporizadores, los pines “OCR_B” nunca irán más rápido que “OCR_A”. Otra cosa a tener en cuenta es que los temporizadores 0 y 2 tnenen registros de ocho bits, su valor máximo será de 255, el temporizador 1 es de 16 bit pudiendo escalar hasta 65535 pasos, o lo que es lo mismo un rango de frecuencias mayor. Dependiendo del dispositivo a controlar deberíamos tener esto en cuenta.

Hay “ATMEGAS” con más temporizadores y PWM como el 32PA, el 644 y el 1284, un poco más caros, con más pines y más voluminosos, luego están los de montaje en superficie con muchas más opciones pero un poco más complejos de montar.

Tras “inicializar” el USB activamos el vector de interrupciones de entrada para los pines donde recibiremos la señal PWM, en “PCICR” (Pin Change Interrupt Control Register” activamos el bit “PCIE2”, que dice que la máscara de detección de cambios dos “PCMSK2” (Pin Change Mask 2) estará activa, y le decimos que vigile los pines PD4 y PD1 activando los bits PCINT20 y PCINT17.

Activamos las interrupciones y ajustamos el temporizador uno para que cuente cada 62 ticks con un preescalado de 62500 Hz.

int main()
{
	if (eeprom_read_byte((uint8_t*)E_INIT)!='T')
	{
		eeprom_write_byte((uint8_t*)E_INIT,'T');//marks once that eeprom init is done
		eeprom_write_byte((uint8_t*)E_AUTO,0b00000111);//All autos active
		eeprom_write_byte((uint8_t*)E_PWM_01,0x80);//50%
		eeprom_write_byte((uint8_t*)E_PWM_02,0x80);//50%
		//once this procedure is held, no more initialization is performed
	}
	sw_auto=eeprom_read_byte((uint8_t*)E_AUTO);
	pwm_01=eeprom_read_byte((uint8_t*)E_PWM_01);
	pwm_02=eeprom_read_byte((uint8_t*)E_PWM_02);
	
	uchar i;

	DDRB = DDRB | 0b00001011; // PB0 PB1 PB3 as output Rest as input
	DDRD = DDRD | 0b01101000; // PD3 PD5 PD6 as output Rest as input

	DDRB   = DDRB & 0b11111011; // PB2 as input
	DDRD   = DDRD & 0b11101101; // PD1 PD4 as input
	
	PORTD |= (1 << PORTD1) | (1 << PORTD4); // turn On the Pull-up

	TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode
	TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt

	TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM01) | _BV(WGM00); //Phase correct Timer0
	TCCR0B = _BV(WGM22) | _BV(CS20);	

	TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //Phase correct  Timer2
	TCCR2B = _BV(CS21);

	OCR2A = 255; //Upper limit
	OCR0A = 255;
	
	OCR2B = pwm_01;
	OCR0B = pwm_02;
		

    wdt_enable(WDTO_1S); // enable 1s watchdog timer

    usbInit();
	
    usbDeviceDisconnect(); // enforce re-enumeration
    for(i = 0; i<250; i++)
		{ // wait 500 ms
        wdt_reset(); // keep the watchdog happy
        _delay_ms(2);
    	}
    usbDeviceConnect();

	PCICR |= (1<<PCIE2);		// set PCIE2 to enable PCMSK2 scan
	PCMSK2 |= (1<<PCINT20);		// set PCINT20 to trigger an interrupt on state change
	PCMSK2 |= (1<<PCINT17);		// set PCINT17 to trigger an interrupt on state change

    sei(); // Enable interrupts after re-enumeration
	OCR1A    = 62;  // 1 Tick = 0.000992 Secs
	TCCR1B |= (1 << CS12);					// Start timer at Fcpu/256 16MHz/256=62500 ticks per second.

Bucle principal

Activados el “guadián” y la rutina de refresco de USB, lo primero que hacemos es ver si han pasado 100 ticks del temporizador 1, aproximadamente una décima de segundo, este lapso es arbitrario, podemos hacer este ciclo lo largo o corto que queramos. De ser así comprobamos si debemos cambiar la velocidad del ventilador correspondiente según sw_auto. Si en efecto es así, comprobamos si el ciclo ha de subir o bajar, le sumamos uno si “up_0x” es cierto, o le restamos uno si “up_0x” es falso. Cuando “pwm_0x” llega al límite superior (255) cambiamos “up_0x” a falso para bajar la cuenta, y al contrario para cuando “pwm_0x” llega a 0. Tras esto pasamos el valos de la variable “pwm_0x” a su correspondiente registro para cambiar el ciclo del PWM. Así “pwm_01” pasará su valos a “OCR0B” (Output Compare Register), en el pin PD5 (11) y pasamos “pwm_02” a OCR2B en el pin PD3 (5).

Cuando “lapse_01” llega a cien, aproximadamente una décima de segundo, comenzamos a pasar los valores del PWM (OCRxB) a la cadena adcVal, el primer byte irá a cero, al segundo le asignamos el valor de “OCR_xB” en ese momento. Pasamos a calcular las revoluciones por minuto (se puede hacer en el programa “host”), si en 0.992 segundos han pasado “tach_0x”/2 vueltas (el tacómetro da dos señales por vuelta), en 60, que es un minuto, pasarían “s”, resolviendo nos sale que “s” es el producto de “tach_0x” por 302. El número que nos da es bastante elevado, según las especificaciones, los ventiladores de CPU alcanzan unas velocidades de varios miles de revoluciones por minuto, así que lo dividimos en bytes para tratarlo luego en el “host”. Empezamos con el byte 4 de adcVal, y le decimos que coja ocho bits contando desde ocho de “s”, a adc[5] le ponemos los de abajo. Repetimos con los siguientes bytes el el valor de “tach_02”. Ahora, cuando el “host” nos pida información acerca del estado de los ventiladores, ya la tendremos actualizada.

Al haber calculado el valor de los tacómetros, reiniciamos el contador y sus valores.

 while(1)
	{
        wdt_reset(); // keep the watchdog happy
        usbPoll();

		if (countA >= 100) //Have 100 clicks passed?
		{
			if (sw_auto & 0b00000001) //Is the auto PWM checked for fan 1 in the host?
			{
				pwm_01 += up_01 ? 1 : -1;
				if (pwm_01 == 255)
					up_01 = false;
				else if (pwm_01 == 0)
					up_01 = true;			
			}
	  		OCR0B = pwm_01;
			if (sw_auto & 0b00000010)  //Is the auto PWM checked for fan 2 in the host?
			{
				pwm_02 += up_02 ? 1 : -1;
				if (pwm_02 == 255)
					up_02 = false;
				else if (pwm_02 == 0)
					up_02 = true;		
			}
			OCR2B = pwm_02;
			countA = 0;
		}

		if(lapse_01 >= 100)
		{
				adcVal[0] =  0;		//High byte
				adcVal[1] = OCR0B;		//Low byte
	
				adcVal[2] =  0;		//High byte
				adcVal[3] = OCR2B;		//Low byte

				s = tach_01 * 302;			//Crude aproximation to RPM, 100 lapses are 0.992 secs.
				adcVal[4] =  s >> 8;		//High byte
				adcVal[5] = s & 0x00ff;		//Low byte
			
				s = tach_02 * 302;			//A DV07020_12U can run up to 6800 RPM according to it's datasheet.
				adcVal[6] =  s >> 8;		//High byte
				adcVal[7] = s & 0x00ff;		//Low byte

				adcVal[12] =  0;		//High byte
				adcVal[13] = lapse_01;		//Low byte
				lapse_01 = 0;
				tach_01 = 0;
				tach_02 = 0;
		}
	}
    return 0;
}

Funciones de interrupción

Cuando se dan las condiciones de interrupción se llamará a estas funciones. La primera se acel temporizador uno ha contado 62 “ticks”, que vienen a ser 0.00992 segundos con un preescalado de 256 sobre 16Mhz. Sumamos uno a las variables “countA” y “lapse_01” tratadas en el apartado anterior. Ya está, nada complicado controlar el tiempo.

La segunda se activa cuando uno de los pines del puerto D, que hemos activado como pines de entrada (PD1 y PD4). Notemos que la función lleva un parámetro añadido “ISR_NOBLOCK”, que recomienda usar v-usb para no bloquear otras funciones de USB que puedan estar activas en ese momento y fastidiar la comunicación. Es un parámetro que no se debería usar demasiado, especialmente si cabe la posibilidad de que las variables que usan la función pueden ser cambiadas durante la ejecución del programa, como sólo damos un dato de información “tach_0x” que no influye en el flujo del programa no pasará nada serio, como mucho que el tacómetro indique de vez en cuando un dato falso. Habrá que tener en cuenta esto si el dato sí influye, y activar un interruptor que evite cambios no deseados para evitar problemas.

Hacemos XOR sobre PIND (el registro donde se guardan los estados del puerto D) con la variable “portdhistory” en donde guardamos el estado anterior de PIND, y almacenamos el resultado en “changedbits”. En “changedbits” quedarán como uno, o activado sólo los bits que hayan cambiado según la tabla de verdad del XOR. Después guardamos para la siguiente llamada a la interrupción el estado de actual de PIND en “portdhistory”. Y comparamos individualmente los pines D1 y D4 con el bit correspondiente en “changedbits”. Si el resultado es positivo cambiamos un led que conectemos a PB1 o PB0 y sumamos uno al contador de vueltas del tacómetro.

ISR(TIMER1_COMPA_vect) //Timer 1 Compare A triggered.
{
countA++;
lapse_01++;
}
//Manage digital inputs. Interrupt uses "ISR_NOBLOCK" as recommended in v-usb code.
ISR(PCINT2_vect, ISR_NOBLOCK)
{
    uint8_t changedbits;

    changedbits = PIND ^ portdhistory; //We compare port D pins with their later states.
    portdhistory = PIND; //Store the actual state of Port D for the next interrupt.
	
	if(changedbits & (1 << PIND1))
	{
		PORTB = PORTB ^ 0b00000010; // Toggle a LED
		tach_01++;
	}
			
	if(changedbits & (1 << PIND4))
	{
		PORTB = PORTB ^ 0b00000001; // Toggle a LED
		tach_02++;
		
	}
}

Enlaces de interés

Tutorial de PWM de MaxEmbedded

Tutorial de PWM en avrfreaks

Tutorial de entrada/salida para AVR

Datashhet de ATMEGA328p en la pñagina de Michocrip

Página de AVRs con un buen listado de proyectos y datasheets.

 

Enlace de descarga de los planos, fuentes y ejecutables del proyecto.

 

 

Pines del VUSibino y sus funciones.

VUSiBino Pinout

Deja un comentario