Archivos para agosto, 2013

En publicaciones anteriores compartí con ustedes 2 (dos) circuitos amplificadores de audio con el circuito integrado LM386 (ver Amplificador de Audio LM386 o Amplificador de Audio LM386 v2). En esta publicación veremos cómo construir un amplificador de audio (Clase AB) estéreo con el circuito integrado TDA7297, el cual entrega una potencia de salida de 15W + 15W RMS, ideal para usar con la PC, laptop, tablet, MPx, etc. dada su alta impedancia de entrada (30KO).

 
TDA7297 Datasheet
 
El circuito tiene como elemento principal un circuito integrado TDA7297, en el cual se han agrupado todos los componentes necesarios para conformar una etapa de potencia de audio, internamente consta de dos amplificadores, cada uno en configuración “puente”. Como puede observarse la cantidad de componentes externos es mínima, además cuenta con funciones de “Stand-By” y “Silencio”, protección contra corto circuitos y protección contra exceso de temperatura.

El rango de alimentación es amplio, desde 6V hasta 18V. Dentro de ese rango se encuentran los 12V que nos proporciona generosamente la fuente de la PC y dado que el consumo ronda los 2A a plena potencia, podemos alimentar el circuito con una fuente actual sin mayores inconvenientes.

El circuito integrado debe ser dotado de un disipador de calor como se observa en la imagen de abajo. La grasa siliconada debe aplicarse en su justa medida (con una capa delgada es suficiente), ya que en exceso produce el efecto contrario. Además puede incluirse un led indicador de encendido del color y/o brillo que deseemos con una resistencia acorde al mismo.

Ver Amplificador de Audio TDA7297

Lista de Componentes
1 Circuito Integrado TDA7297
1 Potenciómetro estéreo 50KΩ Logarítmico (A) [sin llave, para PCB]
1 Capacitor Electrolítico 10uF x 25V
2 Resistencias 47KΩ 0.25W [metalfilm]
2 Capacitores 220nF x 100V [poliéster]
1 Capacitor Electrolítico 2200uF x 25V
1 Capacitor 100nF x 250V [poliéster]
1 Disipador de Calor

Opcional
1 Resistencia 1.5KΩ 0.25W [metalfilm]
1 LED a elección (indicador de encendido)

A continuación se encuentra el PCB en escala 1:1 (el documento PDF debe ser impreso con la escala al 100%), el cual se encuentra listo para la aplicación por transferencia térmica (método “de planchado” ). Además se encuentra la vista de disposición de los componentes (layout) como información complementaria.

Amplificador de Audio TDA7297 PCB

Amplificador de Audio TDA7297 Layout

Actualmente, cuando realizamos proyectos que requieren cierto nivel de flexibilidad, pensar en utilizar un microcontrolador siempre es una buena idea y, siempre que el proyecto lo requiera, es interesante la aplicación de un RTC, es decir, un reloj en tiempo real. Además debo admitir que desde mi infancia tengo un interés muy particular en los relojes y la medición del tiempo.

Si bien podemos adquirir el módulo armado, en esta publicación veremos cómo construir nuestro propio RTC dada su simplicidad y el reducido número de componentes necesarios.

El circuito integrado que utilizaremos será el DS1307, fabricado actualmente por Maxim y Cypress. Su consumo de energía es bajo, dispone de reloj y calendario (ambos codificación BCD) con ajuste de año bisiesto hasta el año 2100 y ajuste automático de fin de mes (para meses con menos de 31 días). El reloj, por su parte, opera en formato de 12/24 horas (configurable) con indicador de AM/PM en caso de operar en formato de 12 horas. Además, el circuito integrado dispone de 56 bytes de memoria SRAM de escritura ilimitada y un pin de salida con señal lógica programable (pin 7: SQW/OUT).

Presentado el componente principal de nuestro proyecto, observamos en la siguiente imagen el circuito:

 
DS1307 Datasheet

Como puede verse en el circuito, la cantidad de componentes externos es mínima. Las resistencias y el capacitor cerámico pueden adquirirse en cualquier comercio de electrónica y con respecto al cristal de cuarzo de 32768 kHz, recomiendo obtenerlo de un reloj (¿bastante lógico no?) en desuso ya que suelen ser más precisos y estables. En mi caso particular, al cristal lo obtuve de un reloj de pared en desuso como puede observarse en la siguiente imagen:

 
La conexión de los pines de salida del DS1307 a la placa Arduino UNO es la siguiente:

DS1307 –> Arduino UNO
SDA(Pin 5) –> Arduino UNO (Pin A4)
SCL(Pin 6) –> Arduino UNO (Pin A5)

La alimentación para este circuito puede obtenerse directamente de la placa Arduino UNO. Existe en dicha placa un terminal que provee 5V.

En la siguiente imagen se observa el reloj ya armado:

 
Antes de continuar, recomiendo una lectura profunda y analítica de la hoja de datos del circuito integrado DS1307.

Habiendo interconectado exitosamente nuestro reloj con la placa Arduino procederemos a escribir el programa cuya función será controlar nuestro reloj, nos permitirá: establecer fecha y hora, consultar fecha y hora, controlar el pin SQW/OUT y hacer un volcado de toda la memoria. Si bien existe una gran cantidad de librerías para controlar el DS1307, escribiremos nuestro propio programa con fines didácticos y además economizar recursos que a veces son desperdiciados con el uso de grandes librerías de las cuáles sólo necesitamos un par de funciones. En primer lugar, necesitamos comunicarnos con nuestro reloj a través del bus I2C, afortunadamente Arduino proporciona la librería “Wire” que nos permitirá comunicarnos con dispositivos I2C. En segundo lugar debemos conocer los registros y direcciones de memoria del DS1307. Los registros y sus direcciones se observan en la siguiente imagen:

 
Podemos dividir la memoria del DS1307 en 3 (tres) segmentos:
1) 00h-006h: Registros de Fecha y Hora (resaltado amarillo).
2) 07h: Registro de Control (resaltado verde).
3) 08h-03Fh: RAM 56 bytes.

Registros de Fecha y Hora
El contenido de los registros de fecha y hora se almacena en codificación BCD. El registro “Día de la semana” (03h) se incrementa a la medianoche, el rango de valores es de 01-07 y la correspondencia con los días de la semana la define el usuario. El bit 7 del primer registro (00h) detiene o inicia el reloj, cuando el bit se establece en 1 se desactiva el oscilador, cuando el bit se establece en 0 sucede lo contrario. Inicialmente, el oscilador se encuentra desactivado (CH=1) y los registros de fecha y hora se inician con la fecha 01/01/00 01 00:00:00. El bit 6 del registro de hora (02h) permite seleccionar formato de 12/24 horas. En caso de seleccionar el formato de 24 horas (nuestro caso), el bit 5 del registro de hora (02h) nos indicará la veintena (20 a 23 horas). Cada vez que cambiemos el formato de 12/24 horas deberemos volver a introducir la hora. Por último, el ingreso de valores inválidos de fecha y hora darán como resultado un comportamiento inesperado.

Registro de Control
El registro de control (07h) se utiliza para controlar la operación del pin de salida SQW/OUT (pin 7). En la siguiente tabla observamos los posibles valores con sus respectivos resultados.

 
He resaltado la frecuencia de 1Hz. No es menos importante señalar que puede obtenerse una frecuencia de 32768 kHz (la misma del cristal) con la cual podríamos, por ejemplo, poner en funcionamiento otro DS1307 sin necesidad de un segundo cristal.

Memoria RAM
Por último, disponemos de 56 bytes de SRAM para utilizarla según nuestras necesidades particulares. Son demasiadas las aplicaciones como para enumerarlas.

El programa que permite controlar nuestro reloj es el siguiente:

/*
* Comandos:
* T(00-59)(00-59)(00-23)(1-7)(01-31)(01-12)(00-99)
*  (segs )(mins )(hora )(dia)(dMes )(mes  )(anio )
* Ejemplo, establecer fecha y hora: 19-Ago-13 @ 15:32:11 del dia 1 (Lunes) de la semana.
* utilizar el comando: T1132151190813
*
* W(0-5) Establece SQW/OUT 0=0|1=1|2=1Hz|3=4096kHz|4=8192kHz|5=32768kHz
* Ejemplo, establecer la salida SQW/OUT en 1Hz
* utilizar el comando: W2
*
* Q Consultar fecha y hora
*
* D Volcado de Memoria
*
* NOTA: La norma ISO 8601 (convención internacional que indica el orden de los días de la semana) establece que la semana comienza el Lunes.
*/

#include <Wire.h>

#define DS1307_I2C_ADDRESS 0x68 //Direccion DS1307
#define DS1307_TIMEKEEPER_REGISTERS 7 //Cantidad de registros de Fecha y Hora
#define DS1307_BASE_ADDRESS 0x00 //Registro 00h
#define DS1307_CTRLREG_ADDRESS 0x07 //Registro de control
#define DS1307_MEMORY 64 //Total de memoria

// Convertir valores decimales a valores BCD
byte decToBcd(byte val)
{
	return ((val / 10 * 16) + (val % 10));
}

//Convertir valores BCD a valores decimales
byte bcdToDec(byte val)
{
	return ((val / 16 * 10) + (val % 16));
}

//Convertir valores ASCII a valores decimales
byte asciiToVal(int tens, int units)
{
	return ((byte)((tens - 48) * 10 + (units - 48)));
}

//Establecer fecha DS1307
void setDateDs1307(int* buffer)
{
	byte rtcDate[DS1307_TIMEKEEPER_REGISTERS];
	
	rtcDate[0] = asciiToVal(buffer[0], buffer[1]); //segundos
	rtcDate[1] = asciiToVal(buffer[2], buffer[3]); //minutos
	rtcDate[2] = asciiToVal(buffer[4], buffer[5]); //horas
	rtcDate[3] = asciiToVal(48, buffer[6]); //dia de la semana
	rtcDate[4] = asciiToVal(buffer[7], buffer[8]); //dia del mes
	rtcDate[5] = asciiToVal(buffer[9], buffer[10]); //mes
	rtcDate[6] = asciiToVal(buffer[11], buffer[12]); //anio
	
	Wire.beginTransmission(DS1307_I2C_ADDRESS);
	Wire.write(DS1307_BASE_ADDRESS);
	Wire.write(decToBcd(rtcDate[0]));
	Wire.write(decToBcd(rtcDate[1]));
	Wire.write(decToBcd(rtcDate[2]));
	Wire.write(decToBcd(rtcDate[3]));
	Wire.write(decToBcd(rtcDate[4]));
	Wire.write(decToBcd(rtcDate[5]));
	Wire.write(decToBcd(rtcDate[6]));
	Wire.endTransmission();
}

//Obtener fecha DS1307
byte* getDateDs1307()
{
	byte* rtcDate = (byte*) malloc(DS1307_TIMEKEEPER_REGISTERS * sizeof(byte));
	
	Wire.beginTransmission(DS1307_I2C_ADDRESS);
	Wire.write(DS1307_BASE_ADDRESS);
	Wire.endTransmission();
	Wire.requestFrom(DS1307_I2C_ADDRESS, DS1307_TIMEKEEPER_REGISTERS);
	
	rtcDate[0] = bcdToDec(Wire.read() & 0x7f);
	rtcDate[1] = bcdToDec(Wire.read());
	rtcDate[2] = bcdToDec(Wire.read() & 0x3f);
	rtcDate[3] = bcdToDec(Wire.read());
	rtcDate[4] = bcdToDec(Wire.read());
	rtcDate[5] = bcdToDec(Wire.read());
	rtcDate[6] = bcdToDec(Wire.read());
	
	return rtcDate;
}

//Establecer SQW/OUT
void setSqwOutput(int out)
{
	byte control;
	
	switch(out)
	{
		case '0':
		{
			control = 0x00; //SQWOUT = 0
			
			break;
		}
		case '1':
		{
			control = 0x80; //SQWOUT = 1
			
			break;
		}
		case '2':
		{
			control = 0x10; //SQWOUT = 1Hz
			
			break;
		}
		case '3':
		{
			control = 0x11; //SQWOUT = 4096kHz
			
			break;
		}
		case '4':
		{
			control = 0x12; //SQWOUT = 8192kHz
			
			break;
		}
		case '5':
		{
			control = 0x13; //SQWOUT = 32768kHz
			
			break;
		}
		default:
		{
			control = 0x00; //SQWOUT = 0
			
			break;
		}
	}
	
	Wire.beginTransmission(DS1307_I2C_ADDRESS);
	Wire.write(DS1307_CTRLREG_ADDRESS);
	Wire.write(control);
	Wire.endTransmission();
}

//Mostrar fecha con formato
String showDate(byte* rtcDate)
{
	String fullDate;
	
	if (rtcDate[2] < 10)
	{
		fullDate.concat("0");
	}
	fullDate.concat(rtcDate[2]);
	fullDate.concat(":");
	
	if (rtcDate[1] < 10)
	{
		fullDate.concat("0");
	}
	fullDate.concat(rtcDate[1]);
	fullDate.concat(":");
	
	if (rtcDate[0] < 10)
	{
		fullDate.concat("0");
	}
	fullDate.concat(rtcDate[0]);
	fullDate.concat(" ");
	
	if (rtcDate[4] < 10)
	{
		fullDate.concat("0");
	}
	fullDate.concat(rtcDate[4]);
	fullDate.concat("/");
	
	switch (rtcDate[5])
	{
		case 1:
		{
				fullDate.concat("Ene");
				
				break;
		}
		case 2:
		{
				fullDate.concat("Feb");
				
				break;
		}
		case 3:
		{
				fullDate.concat("Mar");
				
				break;
		}
		case 4:
		{
				fullDate.concat("Abr");
				
				break;
		}
		case 5:
		{
				fullDate.concat("May");
				
				break;
		}
		case 6:
		{
				fullDate.concat("Jun");
				
				break;
		}
		case 7:
		{
				fullDate.concat("Jul");
				
				break;
		}
		case 8:
		{
				fullDate.concat("Ago");
				
				break;
		}
		case 9:
		{
				fullDate.concat("Sep");
				
				break;
		}
		case 10:
		{
				fullDate.concat("Oct");
				
				break;
		}
		case 11:
		{
				fullDate.concat("Nov");
				
				break;
		}
		case 12:
		{
				fullDate.concat("Dic");
				
				break;
		}
		default:
		{
				fullDate.concat("Err_mes");
				
				break;
		}
	}
	
	fullDate.concat("/");
	fullDate.concat("20");
	if (rtcDate[6] < 10)
	{
		fullDate.concat("0");
	}
	fullDate.concat(rtcDate[6]);
	
	fullDate.concat(" Hoy es:");
	switch (rtcDate[3])
	{
		case 1:
		{
				fullDate.concat(" Lunes");
				
				break;
		}
		case 2:
		{
				fullDate.concat(" Martes");
				
				break;
		}
		case 3:
		{
				fullDate.concat(" Miercoles");
				
				break;
		}
		case 4:
		{
				fullDate.concat(" Jueves");
				
				break;
		}
		case 5:
		{
				fullDate.concat(" Viernes");
				
				break;
		}
		case 6:
		{
				fullDate.concat(" Sabado");
				
				break;
		}
		case 7:
		{
				fullDate.concat(" Domingo");
				
				break;
		}
		default:
		{
				fullDate.concat("Err_dia");
				
				break;
		}
	}
	
	return fullDate;
}

//Obtener volcado de memoria
byte* getMemoryDump()
{
	byte* dump = (byte*) malloc(DS1307_MEMORY * sizeof(byte));
	
	Wire.beginTransmission(DS1307_I2C_ADDRESS);
	Wire.write(DS1307_BASE_ADDRESS);
	Wire.endTransmission();
	Wire.requestFrom(DS1307_I2C_ADDRESS, DS1307_MEMORY);
	
	for (int i = 0; i < 64; i++)
	{
		dump[i] = Wire.read();
	}
	
	return dump;
}

void setup()
{
	Wire.begin();
	Serial.begin(57600);
}

void loop()
{
	if (Serial.available())
	{
		byte* rtc;
		int command = Serial.read();
		
		switch(command)
		{
			case 'T':
			{
					delay(50);
					int i = 0;
					int buffer[13];
					while(Serial.available() && i < 14)
					{
						buffer[i] = Serial.read();
						i++;
					}
					setDateDs1307(buffer);
					rtc = getDateDs1307();
					Serial.println(showDate(rtc));
					free(rtc);
					
					break;
			}
			case 'Q':
			{
					rtc = getDateDs1307();
					Serial.println(showDate(rtc));
					free(rtc);
					
					break;
			}
			case 'D':
			{
					rtc = getMemoryDump();
					for (int i = 0; i < 64; i++)
					{
						Serial.print(i);
						Serial.print(":");
						Serial.println(rtc[i], HEX);
					}
					free(rtc);
					
					break;
			}
			case 'W':
			{
				delay(50);
				if(Serial.available())
				{
					command = Serial.read();
					setSqwOutput(command);
				}
				break;
			}
			default:
			{
					Serial.println("Comando no valido.");
					
					break;
			}
		}
	}
}

Lógicamente es perfectible y cada uno puede adaptarlo a sus necesidades particulares.

Hace aproximadamente dos meses compartí con ustedes la primera versión de este amplificador de audio con el circuito integrado LM386. En esta nueva versión se han realizado algunas modificaciones, como el incremento en la ganancia del circuito integrado y, la modificación más notable, el agregado de una etapa push-pull independiente. Estas modificaciones en conjunto proporcionan un notable incremeto en la potencia de salida con respecto a la versión anterior.

 
LM386 Datasheet

El circuito tiene como elemento principal un circuito integrado LM386, en el cual se han agrupado todos los componentes necesarios para conformar una etapa de potencia de audio. La señal de entrada pasa a través del potenciómetro de 10KOhm, el cual hace las veces de control de volumen ya que permite el paso de mayor o menor voltaje hacia la entrada del amplificador integrado (pin 3). La salida amplificada sale por el pin 5 para luego atravesar la etapa push-pull conformada por los dos transistores Q1 (TIP 32) y Q2 (TIP 31). Dado que estos transistores no se encuentran sometidos a altas exigencias de trabajo alcanzan una temperatura menor y no es necesario el uso de disipadores de calor, o bien, con disipadores de pequeñas dimensiones es más que suficiente.

La red formada por la resistencia de 390 ohm y el capacitor de 10uF establece la ganancia o factor de amplificación del circuito. En este caso, la etapa tiene una ganancia de 150. Dado que el circuito integrado se encuentra ajustado cerca de su máxima ganancia (200) es necesario agregar un capacitor cerámico de 100nF entre el pin 7 y GND.

Todo el circuito debe ser alimentado con una fuente de 12VDC que pueden obtenerse de la fuente de la PC.

Ver Amplificador de Audio LM386

En “ElectroSmash” se encuentra un análisis muy completo e interesante sobre este circuito integrado:
LM386 Audio Amplifier Analysis (inglés)

Al momento de programar una placa Arduino que no incluya un chip FTDI (Arduino Pro Mini, Flora, Arduino Mini Light 05, etc.) debemos utilizar un Adaptador USB – UART TTL para programar la placa. Si bien existen muchos adaptadores en el mercado, existe un modelo en particular que se destaca por ser mucho mas económico que los otros y utiliza el chip CP2102 fabricado por Silicon Labs.

La placa del Adaptador USB – UART TTL se observa en la siguiente imagen:

 
Lamentablemente, este adaptador no funciona correctamente en forma directa, sino que debemos realizar una pequeña modificación en la placa para poder utilizarlo.

El problema es el siguiente, la característica de reinicio automático, que reinicia la placa Arduino luego de cargado el programa, no funciona. Esto se debe a que el pin de “Reset” (RST) del adaptador no corresponde verdaderamente a la señal de “Reset“, sino que es un pin de entrada, es decir, se utiliza para reiniciar el chip CP2102, pero no es la señal de “Reset” que necesitamos para reiniciar nuestra placa. La señal de reinicio que necesita nuestra placa Arduino corresponde a un pad en la placa del adaptador rotulado “DTR” como se observa en la siguiente imagen:

 
CP2102 Datasheet

Por lo tanto, deberemos soldar un pin para así tener disponible la señal que necesitamos. En este caso he utilizado un cabezal macho de 1 X 4 pines (2.54mm) como se observa en la siguiente imagen:

 
La conexión del adaptador a la placa Arduino Pro Mini es la siguiente:

CP2102 USB UART TTL –> Arduino Pro Mini
GND –> GND
RXD –> RXI
TXD –> TX0
DTR –> DTR
3.3V o 5V –> VCC

 

 
CP2102 Driver

Finalmente, en el IDE de Arduino seleccionamos la placa y el puerto serie correspondiente, luego cargamos un sketch de prueba, como “Blink“, para verificar el correcto funcionamiento.