Proyecto 36.4 - Reloj- Termómetro con Display DVD
En el tutorial 36.2 vimos cómo conectar un pequeño display de DVD al Arduino para hacer un reloj-termómetro. En dicho proyecto la corriente para iluminar los 7 segmentos de cada dígito es suministrada por los pines del Arduino, lo que supone una limitación.
Para solucionar este problema, en el siguiente tutorial vamos a utilizar transistores para alimentar los dígitos del display, de forma que la corriente máxima viene impuesta por la corriente del led que forma cada segmento.
Esquema
El siguiente esquema representa los dos tipos de conexión del display. En el circuito con transistor, las resistencias de los segmentos pueden ser de pequeño valor 100 ó 120 Ohm para que los ledes se iluminen con mayor intensidad. En el datasheet del display que se vaya a utilizar, hay que ver la corriente máxima de cada segmento y así calcular la resistencia que corresponda.
También hay que tener en cuenta la corriente máxima de colector del transistor que se va a utilizar. Si por ejemplo, la corriente de cada segmento es de 20mA, cuando se iluminen los 7 segmentos, la corriente en el colector del transistor es de 7x20=140mA. Con este dato escogemos el transistor más adecuado. Además debe ser un transistor con tiempos de conmutación muy rápidos, del tipo switching transistor.
Nota importante: Dado que la corriente de cada dígito puede alcanzar 140mA multiplicado por 10 dígitos que tiene el display son 1,4A. Para alimentar el circuito no vale el puerto USB de un ordenador. Tampoco el regulador interno de +5V del Arduino. Hay que utilizar una fuente de alimentación.
Después de este rollo a modo de introducción, vamos con el proyecto.
El display utilizado en este tutorial es un display de un reproductor DVD del fabricante MxOnda, aunque también se puede encontrar en reproductores DVD de otras marcas.
Es un display de 10 dígitos y 7 segmentos de ánodo común. Utiliza el controlador PT6961 de Princeton Technology Corp. De los 10 dígitos hay 7 para mostrar números y 3 para caracteres especiales.
Googleando un poco se pueden encontrar en Internet varias librerías sobre el PT6961, pero no he conseguido que funcione correctamente, en parte supongo porque el display no está conectado de la forma correcta -de acuerdo a la librería- con el controlador.
Así que toca desoldar el display y conectarlo al Arduino por nuestros propios medios.
Antes de continuar necesitamos saber que es cada pin del display. Para ello necesitamos una resistencia de 1K, una fuente de 5V y un poco de paciencia e ir recorriendo todos los pines, en todas las combinaciones posibles, hasta sacar el pinout.
Como decía más arriba, es un display de 10 dígitos, de los cuales 7 son para representar números y los otros 3 para caracteres especiales.
Al disponer de 7 dígitos para los números, con este display podemos mostrar a la vez la temperatura y la hora. Con los otros dígitos se puede programar algún juego de luces para hacer el display más llamativo. Por ejemplo, con el disco se puede hacer un segundero.
Esquema
El transistor utilizado es el BC337 de Philips. Es un transistor NPN de propósito general, con alta corriente de colector (500mA) para aplicaciones de conmutación y amplificación.
También se puede utilizar cualquier otro transistor que cumpla con los requisitos del circuito, por ejemplo el 2N2222 es un modelo típico en los proyectos con Arduino.
Aunque en el esquema están conectados los diez dígitos del display, para simplificar cada uno puede conectar sólo los dígitos que vaya a utilizar.
Una vez conectado el display, lo primero que hay que hacer es verificar que funciona correctamente. En este paso no es necesario conectar el RTC.
Para ello cargamos el siguiente sketch de prueba. Veremos cómo se iluminan los siete segmentos del display uno tras otro y a continuación se muestran los números del 0 al 9 y las letras A, b, C, d, E y F.
Sketch
/*
* Proyecto 36.4
* Testing 10-Digit 7-Segment MxOnda DVD Player Display with 74HC595
*
* Este sketch ilumina de uno en uno todos los segmentos de todos los dígitos a la vez
* Angel M. https://sites.google.com/site/angmuz
* Public domain
*/
int digitPins[10] = {2,3,4,5,6,7,8,9,10,11};
int latchPin = 14; // Arduino Pin 14(A0) to 74HC595 Pin 12 (Latch)
int dataPin = 13; // Arduino Pin 13 to 74HC595 Pin 14 (Data)
int clockPin = 12; // Arduino Pin 12 to 74HC595 Pin 11 (Clock)
// Cada segmento se enciende con LOW por ser de ánodo común
const byte digit[24] = {
0b11111111, // BLANK
0b11111110, // seg a
0b11111101, // seg b
0b11111011, // seg c
0b11110111, // seg d
0b11101111, // seg e
0b01011111, // seg f
0b10111111, // seg g
0b01000000, // 0
0b11111001, // 1
0b10100100, // 2
0b10110000, // 3
0b00011001, // 4
0b00010010, // 5
0b00000010, // 6
0b11111000, // 7
0b00000000, // 8
0b00010000, // 9
0b00001000, // A
0b00000011, // b
0b01000110, // C
0b10100001, // d
0b00000110, // E
0b00001110};// F
void setup() {
//Serial.begin(9600);
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
for(int j=0; j<10; j++) //number of digits
{
digitalWrite(digitPins[j], HIGH);
}
}
void loop(){
for (int i=0; i<24 ;i++)
{
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, digit[i]);
digitalWrite(latchPin, HIGH);
delay(1000);
}
}
Reloj-Termómetro
En el reloj-termómetro que presento a continuación no he utilizado el dígito 1 (CA1) así que podemos prescindir del transistor T10 conectado en el pin 11 del Arduino.
De los tres dígitos de la izquierda, dos son para mostrar la temperatura y el tercero para el símbolo º. Los cuatro dígitos de la derecha son para mostrar la hora. Y el disco lo utilizaré para hacer un segundero, de manera que los segmentos se iluminan uno tras otro, durante un segundo.
Sketch
La función void displayHT es la encargada de descomponer en decenas y unidades las variables hora, minutos y temperatura. Cada uno de los dígitos simples se almacena en digitBuffer[i] y se muestra en el display mediante la función updateDisp.
La función Millis() es para iluminar durante un segundo los segmentos del disco y de ese modo implementar un segundero en el display.
Para que todo se muestre correctamente en el display hay que colocar en orden los pines utilizados en int digitPins[9] = {5,6,7,8,9,10,4,3,2} al principio del sketch.
La siguiente tabla nos ayuda a obtener el orden correcto.
A continuación el sketch.
/*
* Proyecto 36.4
* Clock-Thermometer with 7-Segment 10-Digit Large DVD Player Display
* DS3231 RTC Module & 74HC595 Shift Register
* Reloj-Termometro con funcion segundero en disco OK
* Angel M. https://sites.google.com/site/angmuz
* Public domain
*/
#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 128
uint8_t time[8];
char recv[BUFF_MAX];
unsigned int recv_size = 0;
//digitBuffer[]=0,1,2,3,4, 5,6,7,8
int digitPins[9] = {5,6,7,8,9,10,4,3,2};
int latchPin = 14; // Arduino Pin 14(A0) to 74HC595 Pin 12 (Latch)
int dataPin = 13; // Arduino Pin 13 to 74HC595 Pin 14 (Data)
int clockPin = 12; // Arduino Pin 12 to 74HC595 Pin 11 (Clock)
int i =0;
int digitScan = 0;
int digitBuffer[10] = {0};
unsigned long previousMillis = 0;
const long interval = 1000;
int dB7 = 12;
int dB8 = 19;
int cnt = 0;
// Cada segmento se enciende con LOW por ser de ánodo común
const byte digit[20] = {
0b11000000, // 0
0b11111001, // 1
0b10100100, // 2
0b10110000, // 3
0b10011001, // 4
0b10010010, // 5
0b10000010, // 6
0b11111000, // 7
0b10000000, // 8
0b10010000, // 9
0b10011100, // º
0b11000110, // C
0b11111110, // seg a
0b11111101, // seg b
0b11011111, // seg f
0b10111111, // seg g
0b11110111, // seg d
0b11111011, // seg c
0b11101111, // seg e
0b11111111};// BLANK
void setup() {
//Serial.begin(9600);
Wire.begin();
DS3231_init(DS3231_INTCN);
memset(recv, 0, BUFF_MAX);
//Serial.println("GET time");
// the first day of the week is monday=1
// (seconds,minutes,hour,week day,date,month,year)
// parse_cmd("T003922420102016",16);
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
pinMode(11, OUTPUT);
}
void loop(){
char in;
char tempF[6];
float temperature;
char buff[BUFF_MAX];
unsigned long now = millis();
struct ts t;
DS3231_get(&t); //Get time
parse_cmd("C",1);
temperature = DS3231_get_treg(); //Get temperature
dtostrf(temperature, 5, 1, tempF);
displayHT(t.hour, t.min, temperature);
}
void parse_cmd(char *cmd, int cmdsize)
{
uint8_t i;
uint8_t reg_val;
char buff[BUFF_MAX];
struct ts t;
//snprintf(buff, BUFF_MAX, "cmd was '%s' %d\n", cmd, cmdsize);
//Serial.print(buff);
// TssmmhhWDDMMYYYY aka set time
if (cmd[0] == 84 && cmdsize == 16) {
//T355720619112011
t.sec = inp2toi(cmd, 1);
t.min = inp2toi(cmd, 3);
t.hour = inp2toi(cmd, 5);
t.wday = inp2toi(cmd, 6);
t.mday = inp2toi(cmd, 8);
t.mon = inp2toi(cmd, 10);
t.year = inp2toi(cmd, 12) * 100 + inp2toi(cmd, 14);
DS3231_set(t);
//Serial.println("OK");
} else if (cmd[0] == 49 && cmdsize == 1) { // "1" get alarm 1
DS3231_get_a1(&buff[0], 59);
//Serial.println(buff);
} else if (cmd[0] == 50 && cmdsize == 1) { // "2" get alarm 1
DS3231_get_a2(&buff[0], 59);
//Serial.println(buff);
} else if (cmd[0] == 51 && cmdsize == 1) { // "3" get aging register
//Serial.print("aging reg is ");
//Serial.println(DS3231_get_aging(), DEC);
} else if (cmd[0] == 65 && cmdsize == 9) { // "A" set alarm 1
DS3231_set_creg(DS3231_INTCN | DS3231_A1IE);
//ASSMMHHDD
for (i = 0; i < 4; i++) {
time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // ss, mm, hh, dd
}
byte flags[5] = { 0, 0, 0, 0, 0 };
DS3231_set_a1(time[0], time[1], time[2], time[3], flags);
DS3231_get_a1(&buff[0], 59);
//Serial.println(buff);
} else if (cmd[0] == 66 && cmdsize == 7) { // "B" Set Alarm 2
DS3231_set_creg(DS3231_INTCN | DS3231_A2IE);
//BMMHHDD
for (i = 0; i < 4; i++) {
time[i] = (cmd[2 * i + 1] - 48) * 10 + cmd[2 * i + 2] - 48; // mm, hh, dd
}
byte flags[5] = { 0, 0, 0, 0 };
DS3231_set_a2(time[0], time[1], time[2], flags);
DS3231_get_a2(&buff[0], 59);
//Serial.println(buff);
} else if (cmd[0] == 67 && cmdsize == 1) { // "C" - get temperature register
//Serial.print("temperature reg is ");
//Serial.println(DS3231_get_treg(), DEC);
} else if (cmd[0] == 68 && cmdsize == 1) { // "D" - reset status register alarm flags
reg_val = DS3231_get_sreg();
reg_val &= B11111100;
DS3231_set_sreg(reg_val);
} else if (cmd[0] == 70 && cmdsize == 1) { // "F" - custom fct
reg_val = DS3231_get_addr(0x5);
//Serial.print("orig ");
//Serial.print(reg_val,DEC);
//Serial.print("month is ");
// Serial.println(bcdtodec(reg_val & 0x1F),DEC);
} else if (cmd[0] == 71 && cmdsize == 1) { // "G" - set aging status register
DS3231_set_aging(0);
} else if (cmd[0] == 83 && cmdsize == 1) { // "S" - get status register
//Serial.print("status reg is ");
//Serial.println(DS3231_get_sreg(), DEC);
} else {
//Serial.print("unknown command prefix ");
//Serial.println(cmd[0]);
//Serial.println(cmd[0], DEC);
}
}
// función para mostrar la hora y la temperatura
void displayHT(int hora, int minutos, int temp)
{
int decHora, udsHora, decMins, udsMins, decTemp, udsTemp;
// descomponemos la hora decenas y unidades
decHora = hora/10;
udsHora = hora-(decHora*10);
// Para que no se muestre el pimer 0 de la hora
if (decHora==0){
decHora=19; //BLANK
}
// descomponemos los minutos decenas y unidades
decMins = minutos/10;
udsMins = minutos-(decMins*10);
// descomponemos la temperatura decenas y unidades
decTemp = temp/10;
udsTemp = temp-(decTemp*10);
// hacemos un segundero con los leds del "disco"
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
cnt++;
dB7++;
if (cnt>6)
{
dB7 = 19; //BLANK
dB8 = 15; //seg g digito 10
}
else
{
dB8=19;
}
if (cnt>7)
{
cnt=0;
dB7=12;
dB8=19;
}
}
//digitBuffer[i] es la posición que ocupa
//el dígito en el array digitPins[9]
digitBuffer[8] = dB8; //seg g digito 10
digitBuffer[7] = dB7; //disco
digitBuffer[6] = 10; //simbolo º
digitBuffer[5] = udsMins; //unidades de minuto
digitBuffer[4] = decMins; //decenas de minuto
digitBuffer[3] = udsHora; //unidades de hora
digitBuffer[2] = decHora; //decenas de hora
digitBuffer[1] = udsTemp; //unidades de temp.
digitBuffer[0] = decTemp; //decenas de temp.
updateDisp();
}
void updateDisp(){
for(int j=0; j<9; j++) //number of digits
{
digitalWrite(digitPins[j], LOW);
}
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, B11111111);
digitalWrite(latchPin, HIGH);
delayMicroseconds(100);
digitalWrite(digitPins[digitScan], HIGH);
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, digit[digitBuffer[digitScan]]);
digitalWrite(latchPin, HIGH);
digitScan++;
if(digitScan>8) digitScan=0; //number of digits - 1
}
Links
Petre Rodan: Arduino library for DS3231 RTC
Arduino Tutorials: Serial to Parallel Shifting-Out with a 74HC595
Arduino Tutorials: Blink Without Delay
http://randomnerdtutorials.com/arduino-temperature-displayed-on-4-digit-7-segment/
http://www.electrontools.com/Home/WP/2016/03/09/registro-de-desplazamiento-74hc595/
Descarga el esquema y los sketch en un .rar aquí abajo.