En el siguiente tutorial veremos cómo aprovechar el display de un DVD para nuestros proyectos con Arduino. He dividido en tutorial en dos partes para que no quede muy extenso.
En esta primera parte vamos a utilizar la librería SevSeg para controlar el display, y en la segunda parte veremos cómo hacer un reloj-termómetro con el integrado 74HC595.
Seguro que quien más, quien menos, ha desmontado alguna vez un viejo reproductor DVD o TDT. Entre los componentes recuperados hay un mando por infrarrojos con su receptor, pequeñas botoneras y algún display.
El mando y el receptor de infrarrojos se pueden aprovechar para hacer un control remoto como hemos visto aquí en tutoriales anteriores.
El display también puede ser útil para hacer algún proyecto. Tanto los DVD´s como los TDT´s utilizan algún tipo de driver dedicado, para controlar el display. Siempre que se pueda, lo ideal es aprovechar dicho controlador para conectar el display al Arduino y ayudarnos de alguna librería a la hora de programar el sketch.
Por desgracia estos drivers no son muy comunes y cuesta encontrar en internet información clara de cómo funcionan o alguna librería que nos facilite el trabajo. En ese caso toca retirar el display de la placa y buscar alguna manera de conectarlo al Arduino por nuestros propios medios.
En el caso que nos ocupa, se trata de un display ánodo común con cinco dígitos y siete segmentos, que usa el controlador TM1668 de TITANMEC. Al no encontrar información de este driver, decidí desoldar el display y conectarlo por mi cuenta.
Lo primero que hay que hacer es obtener el mapa de conexiones. Con una fuente de 5V y una resistencia de entre 560Ω o 1K ir recorriendo todos los pines y en todas las combinaciones posibles hasta sacar el pinout.
Al margen de su procedencia y del controlador que utilice, el display no deja de ser un display de ánodo común con cinco dígitos y siete segmentos. Controlarlo con Arduino es pan comido.
A continuación utilizaremos la librería SevSeg para hacer un reloj sencillo.
En primer lugar vamos a comprobar que el display funciona correctamente. Para ello conectamos una resistencia en los pines CA de cada dígito. A menor resistencia más se ilumina el display, pero sin llegar a la corriente máxima (típicamente 20mA) o dañaremos los leds. En caso de duda poned un valor tirando por lo alto 440Ω ó 560Ω.
Para verificar el funcionamiento del display vamos cargar en el Arduino el testWholeDisplay que acompaña la librería SevSeg. Se puede encontrar en Archivo→Ejemplos→SevSeg→testWholeDisplay.
Hay que modificar en el void setup los parámetros numDigits, digitPins y segmentPins de acuerdo con las características de cada display. En este caso:
byte numDigits = 5; // Display de 5 dígitos
byte digitPins[] = {2, 3, 4, 5, 13}; // Arduino Pin de cada dígito CA
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12}; //Segmentos: a,b,c,d,e,f,g
A continuación el sketch
/*
testWholeDisplay.ino
-test each segment in the display
-a simple example using Dean Reading's SevSeg library to light up all 4 digits plus the 4 decimal points on a 4 digit display
-the purpose of this example is to ensure you have the wires all hooked up right for every segment and digit, and to troubleshoot the display and wiring
to ensure *every* segment and period lights up
By Gabriel Staples
Website: http://www.ElectricRCAircraftGuy.com
My contact info is available by clicking the "Contact Me" tab at the top of my website.
Written: 1 Oct. 2015
Last Updated: 1 Oct. 2015
*/
/*
LICENSING:
-this *example file* only is modified from Dean Reading's original example, and is in the public domain.
Dean Reading's SevSeg library is as follows:
Copyright 2014 Dean Reading
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <SevSeg.h>
SevSeg sevseg; //Instantiate a seven segment controller object
void setup()
{
byte numDigits = 5;
byte digitPins[] = {2, 3, 4, 5, 13}; //Digits: 1,2,3,4,5 <--put one resistor on each digit pin
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12}; //Segments: a,b,c,d,e,f,g
sevseg.begin(COMMON_ANODE, numDigits, digitPins, segmentPins);
sevseg.setBrightness(10); //Note: 100 brightness simply corresponds to a delay of 2000us after lighting each segment. A brightness of 0
//is a delay of 1us; it doesn't really affect brightness as much as it affects update rate (frequency).
//Therefore, for a 4-digit 7-segment + pd, COMMON_ANODE display, the max update rate for a "brightness" of 100 is 1/(2000us*8) = 62.5Hz.
//I am choosing a "brightness" of 10 because it increases the max update rate to approx. 1/(200us*8) = 625Hz.
//This is preferable, as it decreases aliasing when recording the display with a video camera....I think.
}
void loop()
{
//local vars
static byte decPlace = 0;
sevseg.setNumber(8888,decPlace);
decPlace++;
decPlace %= 4; //rollover back to 0 once variable gets to 4; To anyone wondering: the % is called the "modulo" operator; see here for explanation & example: https://www.arduino.cc/en/Reference/Modulo
sevseg.refreshDisplay(); // Must run repeatedly; don't use blocking code (ex: delay()) in the loop() function or this won't work right
}
Una vez cargado el sketch, veremos cómo se iluminan todos los leds del display.
Conectamos el RTC como en el esquema y quitamos la resistencia del dígito número 5, ya que no lo vamos a utilizar en el reloj.
Partiendo del código del Proyecto 35 quitamos las líneas correspondientes al SAA1064 y añadimos la librería SevSeg.
Modificamos el setup de acuerdo con los pines del display:
byte numDigits = 4; // display de 4 dígitos
byte digitPins[] = {2, 3, 4, 5}; // digitos conectados en 2,3,4 y 5
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12}; // segmentos a,b,c,d,e,f,g
Para mostrar la hora empleamos la función sevseg.setNumber(hhmm, 0);
Esta función muestra una cifra de cuatro dígitos como un número entero, por lo que tenemos que darle a la hora el formato de número entero de cuatro cifras.
La hora está formada por dos variables t.hour y t.min ambas de dos cifras. Para darles el formato adecuado multiplicamos x100 t.hour y al resultado sumamos t.min.
Por ejemplo si son las 23:15 significa que t.hour=23 y t.min=15 luego hacemos (23x100=2300) + 15 = 2315 ya tenemos la hora como un entero de cuatro cifras.
Por último, y para que la hora se muestre correctamente hay que hacer una pequeña modificación en el archivo SevSeg.cpp de la librería.
Esto se debe a que la función sevseg.setNumber(hhmm, 0); no muestra los ceros situados a la izquierda, por ejemplo cuando sean las 00:06 el display muestra solo el 6.
Tan solo hay que desmarcar la parte del código que se encarga de borrar los ceros situados a la izquierda. Para ello basta con abrir el archivo SevSeg.cpp con el WordPad y colocar un /* al principio de la función y un */ al final, de la misma manera que se hace en el IDE del Arduino.
Debe quedar como se muestra a continuación:
/* Find unnnecessary leading zeros and set them to BLANK
for (digitNum = 0 ; digitNum < (numDigits - 1 - decPlaces) ; digitNum++){
if (digits[digitNum] == 0) {
digits[digitNum] = BLANK;
}
// Exit once the first non-zero number is encountered
else if (digits[digitNum] <= 9) {
break;
}
}*/
Al terminar guardar los cambios en el archivo SevSeg.cpp
A continuación el sketch
/*
* Proyecto 36.1 - Tiny Clock with Small DVD Display & DS3231 RTC Module
* Angel M.
* https://sites.google.com/site/angmuz/home
*
*/
#include <Wire.h>
#include "ds3231.h"
#define BUFF_MAX 128
#include "SevSeg.h"
SevSeg sevseg; //Instantiate a seven segment controller object
uint8_t time[8];
char recv[BUFF_MAX];
unsigned int recv_size = 0;
unsigned long prev, interval = 1000;
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("T002323091052015",16);
// setup the display
byte numDigits = 4; // display de 4 dígitos
byte digitPins[] = {2, 3, 4, 5}; // digitos conectados en los pines 2, 3, 4 y 5
byte segmentPins[] = {6, 7, 8, 9, 10, 11, 12}; // segmentos a, b, c, d, e, f, g
sevseg.begin(COMMON_ANODE, numDigits, digitPins, segmentPins);
sevseg.setBrightness(100);
}
void loop()
{
char in;
char tempF[6];
float temperature;
int hhmm;
char buff[BUFF_MAX];
unsigned long now = millis();
struct ts t;
// show time once in a while
if ((now - prev > interval) && (Serial.available() <= 0)) {
DS3231_get(&t); //Get time
parse_cmd("C",1);
temperature = DS3231_get_treg(); //Get temperature
dtostrf(temperature, 5, 1, tempF);
hhmm = t.hour*100;
hhmm = hhmm+t.min;
sevseg.setNumber(hhmm, 0);
prev = now;
}
sevseg.refreshDisplay(); // Must run repeatedly
if (Serial.available() > 0) {
in = Serial.read();
if ((in == 10 || in == 13) && (recv_size > 0)) {
parse_cmd(recv, recv_size);
recv_size = 0;
recv[0] = 0;
} else if (in < 48 || in > 122) {; // ignore ~[0-9A-Za-z]
} else if (recv_size > BUFF_MAX - 2) { // drop lines that are too long
// drop
recv_size = 0;
recv[0] = 0;
} else if (recv_size < BUFF_MAX - 2) {
recv[recv_size] = in;
recv[recv_size + 1] = 0;
recv_size += 1;
}
}
}
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);
}
}
Dean Reading: Arduino library SevSeg for 7-Segment Displays
Petre Rodan: Arduino library for DS3231 RTC
Descarga el proyecto aquí abajo. Incluye el esquema y los sketch.