Frontal de descodificador

      No hay comentarios en Frontal de descodificador

Revisando en el trastero todo aquello que me pudiera servir para mis arduinos, me encontré algún que otro descodificador. De uno de ellos obtuve este panel:

Panel frontal descodificador (Frontal)
Panel frontal descodificador (Frontal)

Básicamente tenemos ocho botones, un display de 4 dígitos, dos leds bicolores (rojo-verde) y tres de color (rojo, amarillo, verde) y un receptor IR con su mando a distancia… todo por el módico precio de averiguar cómo demonios controlar esto con un Arduino. Tenía la esperanza de que al tener tantos componentes en la placa y sólo un bus de diez líneas como única comunicación con la placa base sería posible controlar el panel de forma sencilla con Arduino.

Así que lo primero que hice fue revisar la trasera del frontal para ver qué circuitos integrados (aka garrapatas negras) lleva el trasto:

Panel frontal descodificador (Trasera)
Panel frontal descodificador (Trasera)

En la foto no se observa, pero con una lupa y la iluminación adecuada pude comprobar que los dos únicos circuitos integrados son el MM74HC595M:

MM74HC959M
MM74HC959M

Una rápida búsqueda nos da como resultado que el MM74HC595M es un registro de desplazamiento de 8 bits (8bits Shift Register) del que hay sobrada documentación con respecto a cómo controlarlo con Arduino. Así que mi primera intuición empezaba a cobrar fuerza.

Sin embargo necesitaba saber el esquema para poder averiguar cómo controlar el panel. Así que en dos o tres días ya tenía los primeros esquemas a mano de la parte de los cuatro dígitos de siete segmentos y en dos o tres días más el esquema definitivo:

Panel frontal descodificador (esquema)
Panel frontal descodificador (esquema)

Del esquema se puede ver que con sólo 8 cables (6 si descontamos Vcc y GND) podemos controlar los 4 dígitos de siete segmenos, los 5 leds (dos de ellos bicolor), recibir las señales del mando a distancia y saber qué pulsador es el que se ha accionado.

Lo más fácil es utilizar el receptor de infrarrojos, ya que con conectar la alimentación (pin 1 del panel a GND y pin 9 a +5V) y conectar el pin 2 del panel a una entrada digital del Arduino ya podemos jugar con un mando a distancia.

Evidentemente para jugar con el resto de elementos hay que pensar un poco más. Por ejemplo, algo que en los ejemplos y circuitos que he visto con Arduino y el 74595 suelen obviar el pin Enable de estos chips. El panel lo utiliza y le da salida, siendo muy útil pues si en setup lo ponemos en HIGH antes de poner el panel en su configuración inicial (la que decidamos que deba ser) y una vez establecida dicha configuración ponemos el Enable a LOW habremos tenido el panel completamente apagado (los leds y los segmentos de los dígitos) hasta obtener una situación estable y conocida:

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(swPin, INPUT);

  // Desactivamos las salidas de los 74595
  digitalWrite(enablePin, HIGH);

  // Establecemos la condición inicial del Panel
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, 0b00001111);
  shiftOut(dataPin, clockPin, LSBFIRST, 0b00110000);
  digitalWrite(latchPin, HIGH);

  // Activamos las salidas de los 74595
  digitalWrite(enablePin, LOW);
}

La verdad, el resultado es muy chulo, sobre todo por las opciones que me da sin tener que cablear o soldar nada:

Y el código completo:

const byte Digits[] = {
  0b00001110, // Digit 0
  0b00001101, // Digit 1
  0b00001011, // Digit 2
  0b00000111, // Digit 3
};

const byte swMask = 0b00001111;

const byte buttons[] = {
  0b10001110, // 0 SW1 POWER
  0b10001101, // 1 SW2 UP
  0b10001011, // 2 SW3 DOWN
  0b10000111, // 3 SW4 RADIO
  0b01001110, // 4 SW5 MENU
  0b01001101, // 5 SW6 LEFT
  0b01001011, // 6 SW7 YES
  0b01000111, // 7 SW8 RIGHT
};

const byte dec7seg[] = {
  0b00111111, // 0
  0b00000110, // 1
  0b01011011, // 2
  0b01001111, // 3
  0b01100110, // 4
  0b01101101, // 5
  0b01111101, // 6
  0b00000111, // 7
  0b01111111, // 8
  0b01101111, // 9
};

const byte LedsRight = 0b00001111;
const byte LedsRYG_G = 0b00100001;
const byte LedsRYG_Y = 0b00100010;
const byte LedsRYG_R = 0b00101000;
const byte LedsRG_G1 = 0b00010001;
const byte LedsRG_R1 = 0b00010010;
const byte LedsRG_G4 = 0b00010100;
const byte LedsRG_R4 = 0b00011000;


const int dataPin = 11;  // 74HC595 Pin 14 (GRIS)
const int latchPin = 10; // 74HC595 Pin 12 (VERDE)
const int clockPin = 9;  // 74HC595 Pin 11 (MORADO)
const int enablePin = 8; // 74HC595 Pin 13 (MARRON)
const int swPin = 7;     // AZUL

byte LedsRG = 0b00000000;
byte LedsRYG = 0b00000000;

int wait = 0;

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(enablePin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(swPin, INPUT);
  digitalWrite(enablePin, HIGH);
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, 0b00001111);
  shiftOut(dataPin, clockPin, LSBFIRST, 0b00110000);
  digitalWrite(latchPin, HIGH);
  digitalWrite(enablePin, LOW);
}

void Display (int Num, int wait) {
  for (int d = 0; d < sizeof(Digits); d++) {
    int N = int(Num / pow(10, d)) % 10;
    digitalWrite(latchPin, LOW);
    byte dataR = (dec7seg[N] << 4) | Digits[d];
    byte dataL = (dec7seg[N] >> 4) | 0b00110000;
    shiftOut(dataPin, clockPin, LSBFIRST, dataR);
    shiftOut(dataPin, clockPin, LSBFIRST, dataL);
    digitalWrite(latchPin, HIGH);
    delay(wait);
  }
}

int readsw () {
  for (int sw = 0; sw < sizeof(buttons); sw++) {
    digitalWrite(latchPin, LOW);
    byte dataR = swMask & buttons[sw];
    byte dataL = (~swMask) & buttons[sw];
    shiftOut(dataPin, clockPin, LSBFIRST, dataR);
    shiftOut(dataPin, clockPin, LSBFIRST, dataL);
    digitalWrite(latchPin, HIGH);
    if (digitalRead(swPin) == LOW) return sw;
  }
  return -1;
}

const byte swLeds[] {
  0b00000000,
  LedsRYG_G,
  LedsRYG_R,
  LedsRYG_R | LedsRYG_G,
  LedsRYG_Y,
  LedsRYG_Y | LedsRYG_G,
  LedsRYG_Y | LedsRYG_R,
  LedsRYG_Y | LedsRYG_R | LedsRYG_G,
};

void loop() {
  int Num = millis() / 1000;


  LedsRG = 0b00000000;
  LedsRYG = 0b00000000;
  int sw = readsw();
  if (sw >= 0) {
    LedsRG = LedsRG_G4;
    LedsRYG = swLeds[sw];
  }
  if (Num % 2) LedsRG = LedsRG | LedsRG_R1;
  switch (sw) {
    case 1:
  if (sw == 1) wait += 10;
      for (int i = 0; i < 10; i++) {
        Display(wait, 0);        
        delay(20);
      }
      break;
    case 2:
  if (sw == 2 && wait > 0) wait -= 10;
      for (int i = 0; i < 10; i++) {
        Display(wait, 0);        
        delay(20);
      }
      break;
    case 4:
      Display(wait, 0);
      break;
    default:
      Display(Num, wait);
  }

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, LedsRight);
  shiftOut(dataPin, clockPin, LSBFIRST, LedsRYG);
  digitalWrite(latchPin, HIGH);

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, LSBFIRST, LedsRight);
  shiftOut(dataPin, clockPin, LSBFIRST, LedsRG);
  digitalWrite(latchPin, HIGH);
}

Lo único que no estaría en un código de producción sería la variable wait de la función Display, que quedaría así:

void Display (int Num) {
  for (int d = 0; d < sizeof(Digits); d++) {
    int N = int(Num / pow(10, d)) % 10;
    digitalWrite(latchPin, LOW);
    byte dataR = (dec7seg[N] << 4) | Digits[d];
    byte dataL = (dec7seg[N] >> 4) | 0b00110000;
    shiftOut(dataPin, clockPin, LSBFIRST, dataR);
    shiftOut(dataPin, clockPin, LSBFIRST, dataL);
    digitalWrite(latchPin, HIGH);
  }
}

¿Por qué introducir una pausa variable entre el refresco de cada dídito?, pues simplemente como demostración de que sólo uno de los cuatro dígitos está encendido al mismo tiempo. Vamos, por aumentar el elemento didáctico, que a fin de cuentas esa es la excusa con la que me he comprado los juguetes…

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *