Herramienta pedagógica manipulativa para averiguar de forma rápida el conocimiento de las tablas de multiplicar y ayudar en su memorización sin intervención del docente.


Descripción

Reconozco que el título y la descripción son un poco raras, pero el artefacto que presento a continuación pretende ser útil en el colegio para preguntar las tablas de multiplicar sin intervenir, minimizando así la fatiga mental del profesor y permitiendo dedicar ese valioso tiempo para otros menesteres, además de realizarlo de una forma más atractiva y lúdica para el alumno. 

En esencia, el aspecto es el de una calculadora. Esta en vez de dar los resultados de las multiplicaciones, lo que hace es preguntarnos. 

En la pantalla aparecerá una operación en forma de producto de forma aleatoria. Ejemplo: «3X4=», y permanecerá a la espera de que el alumno introduzca la respuesta.

Tanto si es correcta como si no, seguirá preguntando continuamente, pero cuando acumule cierta cantidad de aciertos en una de las tablas (digamos que ha acertado 10 veces la tabla del 3), se encenderá un led indicando que el alumno se sabe dicha tabla y seguirá el proceso infinito. 

Al cabo de cierto tiempo, quedarán encendidos los leds de las tablas que mejor domine y apagados aquellas tablas que debería mejorar. Esto da al docente una idea rápida de un simple vistazo incluso desde lejos de las tablas que debe repasar.

Materiales

  • LCD 16×2 con conexión I2C
  • Pila 9 Voltios
  •  Regulador de voltaje DC-DC (no obligatorio)
  • Keypad de Matriz 4×4
  • Chip 74HC595
  • 8 leds
  • 8 resistencias

Esquema Eléctrico

Esquema electrónico
Esquema electrónico

Montaje

Prefiero emplear conectores XH para evitar enredos de cables. El montaje constará de dos placas de prototipo de soldadura, la LCD, el teclado matricial, interruptor, conector USB y conexión con la pila.

Montaje interno con todas sus partes conectadas por XH

Teclado

Usaré un teclado de 16 teclas dispuesto en una matriz de 4×4. Esta técnica se usa para ahorrar pines. Si fuesen pulsadores independientes, necesitaría destinar 16 entradas en el microcontrolador para detectar cada una de las pulsaciones. Usando una matriz de 4 filas y 4 columnas como se muestra en la imagen, sólo se necesitarán 8 pines de entradas para el mismo propósito. 

Teclado matricial 8x8
Teclado matricial 8×8

Tener en cuenta que para detectar las pulsaciones en una matriz, hay que realizar una especie de barrido. Primero se colocan en pulso alto las filas y se procede a la lectura de todas las columnas a través de una resistencia pull up (interna en arduino). 

Se irán cambiando los pulsos a low progresivamente en cada una de las filas y se procederá de nuevo a la lectura de entrada de las columnas.

Remarcar que este método nos resta inmediatez y requiere de operaciones que pueden relentizar la lectura de pulsación, pero como nuestro proyecto no requiere gran precisión en la rapidez de respuesta, no buscaremos alternativas.

Además, la librería que usaremos para realizar este barrido de lecturas de la matriz nos facilitará mucho el trabajo. Se trata de Keypad.h, que se encuentra en el repositorio del IDE de arduino.

Matriz de pulsadores para un teclado

Los pines de conexión al microcontrolador serán 8 en mi caso. Usaremos las entradas digitales D5, D6, D7 y D8 para las filas; y D9, D10, D11, y D12 para las columnas. Aunque esto podremos cambiarlo en la programación.

Pantalla LCD 16×2

Para el código usaremos la librería LiquidCrystal_I2C.h puesto que usaré una pantalla con conexión I2C que nos permite su control usando sólo 2 pines. 

LCD 16×2

Conectaremos SCL de la pantalla al pin A5 de arduino y el SDA al A4 del arduino. Además necesitaremos alimentar la pantalla con Vcc y tierra, siempre a 5 voltios.

Como norma general las pantallas llevan la dirección 0x27 para identificarlas en el I2C, pero es posible que esta dirección cambie por lo que deberás usar un scaneador de direcciones I2C, un código fácil de encontrar en la web.

Detalle del controlador I2C en la parte trasera del LCD.

Posee un potenciómetro que sirve para ajustar el contraste de la pantalla (a veces está tan clara que parece que no funciona). Además trae un jumper para conectar o desconectar el led interno que ilumina la pantalla (esto se puede controlar por software, por lo que mantenlo en corto). También cuenta con unas soldaduras en abierto  (A0, A1 y A2) estas son para modificar la dirección del I2C de la que hemos hablado antes en caso de necesitarlo por si usamos otro dispositivo con la misma dirección. 

Leds indicadores de progreso.

En mi caso usaré blancos. Irán controlados por mi gran amigo el chip 74HC595 que me permitirá controlar los 8 diodos luminosos con tan solo 3 salidas digitales del arduino (D2, D3 y D4; para DATA, LATCH y CLOCK respectivamente).

Placa secundaria que contiene los 8 leds controlados por el 74HC595

Para limitar la corriente en los leds he empleado en mi caso resistencias de 1/4 de watio y de 100 ohmios, pero se pueden usar distintos valores dependiendo de la intensidad pretendida y del color del led.

Mejora de control de los leds

Es posible que el 74HC595 no entregue la corriente necesaria pues solo puede entregar 75mA en total, así que algunos leds se verán de menor intensidad cuando todos estén encendidos. No es algo que nos desluzca demasiado el proyecto, pero es algo que me molesta demasiado así que lo mejoraré con otro  «Shift Register» capaz de entregar más corriente. Se trata del chip TPIC6B595N, que en términos de funcionamiento y programación se comporta exactamente igual que el 74HC595n. Está compuesto de mosfets, las conexiones varían pero es capaz de entregar 500mA.

Esta mejora modifica el esquema electrónico que se propone al principio de esta entrada, queda a tu elección. 

Esquema de conexiones del chip de remplazo TPIC6B595n con los leds y el microcontrolador

Alimentación

El invento será alimentado por una pila estandar de 9 voltios pues no requiere demasiada corriente para su funcionamiento y son fáciles de reemplazar. Aunque en un principio pensé en usar baterías recargables, esto complicaría un poco el proyecto y requeriría algo más de espacio, pues el diseño de la carcasa me está dando muchos quebraderos de cabeza para ser un artefacto tan simple.

conexión pila 9V

Para alimentar arduino con 9V necesitamos recular el voltaje a 5V. Arduino posee un regulados incorporado que emplea cuando es alimentado por VIN. Se trata de un 7805 que funciona estupendamente con lo cual podríamos usarlo sin mayores problemas.

En mi caso, he decidido no hacer uso de ese regulador pues se calienta y es muy poco eficiente energéticamente. Así que para mejorar la durabilidad de la batería, emplearé un regulador de voltaje regulable DC -DC.  (este paso es opcional, pues como he dicho antes podemos prescindir de él usando eso sí la entrada de VIN).

Regulador de voltaje DC-DC

El regulador de voltaje incorpora un potenciómetro minúsculo mediante el cual regular el voltaje de salida. Debemos rotarlo hasta conseguir los 5 voltios con los que alimentaremos el microprocesador.

Interruptor

Por último, añadiremos un interruptor entre el positivo de la batería y el regulador de voltaje para evitar el consumo de energía.

Alargador USB

Como cada proyecto que realizo, añado un alargador USB para poder reprogramar o alimentar el artefacto sin tener que acceder a su interior ni abrir la carcasa. En un tutorial anterior Alargador USB explico como hacerlo y detallo su utilidad.

Alargador USB

Diseño de la carcasa

Compuesta de dos partes principales que puedes descargar a continuación pinchando en el enlace.

En la parte superior llamada «arriba.stl» encajaremos la pantalla LCD, el teclado, los leds de progreso y el interruptor de alimentación.

Para destacar los números he usado un rotulados indeleble de color blanco. Los tornillos que usaremos para unir las carcasas serán los de siempre, 2.5mm de diámetro y 10mm de largo. 

Para asegurar el teclado y la pantalla he usado tornillos y tuercas M3.

La parte inferior llamada «suelo.stl» albergará la cavidad de la pila de 9V con su tapadera, el puerto USB para alimentación y programación del microcontrolador.

Cuenta con unas patas para elevar el artefacto y así mejorar su estabilidad.

En ella colocaremos la placa principal que contiene el arduino y el convertidor de voltaje a 5Vcon tornillería 2.5x10mm. El puerto de programación por USB con tornillos y tuercas M3 y la tapadera de la pila de 9V.

En esta parte añadiremos la tapadera para el departamento destinado a la pila de 9 voltios.

Por último el archivo para imprimir en traslúcido o blanco las protecciones embellecedoras de los leds que marcarán la progresión.

Acabado real

Programación.

Librerías

Usaremos <LiquidCrystal_I2C.h>para controlar la pantalla LCD que nos permite imprimir datos de manera muy sencilla. Recuerda encontrar el marcador I2C de tu pantalla que generalmente es 0x27.

Para el teclado usaremos la librería <Keypad.h>, recuerda que debes definir las teclas por filas y columnas. Además, nótese que por errores de conexión, definí los pines de las columnas como 8, 7, 5 y 6 intercalando el 5 por el 6, pero no tiene porqué sucederte a tí.

En cuanto a los leds, los controlaremos directamente sin usar ninguna librería, mediante la función shiftOut como en tutoriales anteriores (de igual manera si usamos el 74HC595n como si ponemos el TPIC6B595n).

Código

/************************************************
   Nombre del proyecto: PREGUNTADOR DE TABLAS DE MULTIPLICAR

   Programado por Juan Carlos Pedrajas del Molino

   Https:\\www.geniero.es
   pedrajas@geniero.es

   Fecha de inicio: 01/10/21

   Fecha de finalización: 08/11/21

   ESQUEMA DE CONEXION

                                      +-----+
         +----[PWR]-------------------| USB |--+
         |                            +-----+  |
         |         GND/RST2  [ ][ ]            |
         |       MOSI2/SCK2  [ ][ ]  A5/SCL[ ] |
         |          5V/MISO2 [ ][ ]  A4/SDA[ ] |
         |                             AREF[ ] |
         |                              GND[ ] |
         | [ ]N/C                    SCK/13[ ] |
         | [ ]IOREF                 MISO/12[ ] |  Teclado
         | [ ]RST                   MOSI/11[ ]~|  Teclado
         | [ ]3V3    +---+               10[ ]~|  Teclado
         | [ ]5v    -| A |-               9[ ]~|  Teclado
         | [ ]GND   -| R |-               8[ ] |  Teclado
         | [ ]GND   -| D |-                    |
         | [ ]Vin   -| U |-               7[ ] |  Teclado
         |          -| I |-               6[ ]~|  Teclado
         | [ ]A0    -| N |-               5[ ]~|  Teclado
         | [ ]A1    -| O |-               4[ ] |  74HC595n clockPin
         | [ ]A2     +---+           INT1/3[ ]~|  74HC595n latchPin
         | [ ]A3                     INT0/2[ ] |  74HC595n dataPin
  LCD    | [ ]A4/SDA  RST SCK MISO     TX>1[ ] |
  LCD    | [ ]A5/SCL  [ ] [ ] [ ]      RX<0[ ] |
         |            [ ] [ ] [ ]              |
         |  UNO_R3    GND MOSI 5V  ____________/
          \_______________________/

********************************************************************
*/

#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#if defined(ARDUINO) && ARDUINO >= 100
#define printByte(args)  write(args);
#else
#define printByte(args)  print(args,BYTE);
#endif

LiquidCrystal_I2C lcd(0x27, 16, 2);
const byte rowsCount = 4;
const byte columsCount = 4;
char keys[rowsCount][columsCount] = {                   // Teclas según el modelo de teclado.
  { '1', '4', '7', '*' },
  { '2', '5', '8', '0' },
  { '3', '6', '9', '#' },
  { 'A', 'B', 'C', 'D' }
};
const byte rowPins[rowsCount] = { 12, 11, 10, 9 };
const byte columnPins[columsCount] = { 8, 7, 5, 6 };     // Puede variar según colocacion de pines como es mi caso
Keypad keypad = Keypad(makeKeymap(keys), rowPins, columnPins, rowsCount, columsCount);

int objetivo = 2;
byte tablas[8] = { 2, 3, 4, 5, 6, 7, 8, 9 };
int ntablas = sizeof(tablas);
int numeroa;
int numerob;
int prime;
int segun;
int prime_dece;
int total;
boolean valorD = 0;
boolean valorU = 0;
const byte luz[10] = {   //Depende las soldaduras de los leds a los drains de salida del TPIC6B595n o 74HC595n (en mi caso).
  0b00000000,
  0b00000000,
  0b00100000,
  0b00010000,
  0b01000000,
  0b10000000,
  0b00001000,
  0b00000100,
  0b00000010,
  0b00000001
};
byte num;                //Almacenaremos en binario el número correspondiente a la luz a iluminar (suma binaria).
const int dataPin  = 2;  // Pin conectado al Pin 14 del 74HC595 (Data)
const int latchPin = 3;  // Pin conectado al Pin 12 del 74HC595 (Latch)
const int clockPin = 4; // Pin conectado al Pin 11 del 74HC595 (Clock)
uint8_t cosa[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};        //acumulamos los aciertos de cada tabla
boolean p2, p3, p4, p5, p6, p7, p8, p9;                   //objetivo cumplido de cada tabla.

void setup() {

  randomSeed(analogRead(0));                         //real aleatorio (ruido electrico)
  pinMode (dataPin, OUTPUT);
  pinMode (latchPin, OUTPUT);
  pinMode (clockPin, OUTPUT);
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST , 0);         //Apaga leds
  digitalWrite(latchPin, HIGH);
  Serial.begin(9600);
  Serial.println ("Saludos");
  lcd.init();
  lcd.backlight();
  lcd.setCursor(2, 0);
  lcd.print("GENIERO 2021");
  lcd.setCursor(3, 1);
  lcd.print("Las tablas");
  // Recorrido leds test
  for (int veces = 0; veces <= 2; veces++) {
    for (int i = 2; i <= 9; i++) {
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST , luz[i]);
      digitalWrite(latchPin, HIGH);
      delay (60);
    }
  }
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST , 0);        //Apaga leds
  digitalWrite(latchPin, HIGH);
  delay (200);
  lcd.init();
  menu();
}

void loop() {
  char key = keypad.getKey();
  if (key != NO_KEY && valorD == 0 && (key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9' || key == '0'))
  {
    prime = key - 48;
    lcd.setCursor(4, 0);
    lcd.print(prime);
    valorD = 1;
    key = 0;
  }
  if (key != NO_KEY && valorD == 1 && valorU == 0 && (key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9' || key == '0'))
  {
    segun = key - 48;
    prime = prime * 10;
    valorU = 1;
    lcd.setCursor(5, 0);
    lcd.print(segun);
    key = 0;
    lcd.noBlink ();
  }
  if (key != NO_KEY && (valorD == 1 || valorU == 1) &&  key == 'D')
  {
    total = prime + segun;
    if (total == (numeroa * numerob)) {
      cosa[numeroa] = cosa[numeroa] + 1;
    }
    if (cosa[2] >= objetivo && p2 == false) {
      num = num + luz[2];
      p2 = true;
      tablas[0] = 0;
    }
    if (cosa[3] >= objetivo && p3 == false) {
      num = num + luz[3];
      p3 = true;
      tablas[1] = 0;
    }
    if (cosa[4] >= objetivo && p4 == false) {
      num = num + luz[4];
      p4 = true;
      tablas[2] = 0;
    }
    if (cosa[5] >= objetivo && p5 == false) {
      num = num + luz[5];
      p5 = true;
      tablas[3] = 0;
    }
    if (cosa[6] >= objetivo && p6 == false) {
      num = num + luz[6];
      p6 = true;
      tablas[4] = 0;
    }
    if (cosa[7] >= objetivo && p7 == false) {
      num = num + luz[7];
      p7 = true;
      tablas[5] = 0;
    }
    if (cosa[8] >= objetivo && p8 == false) {
      num = num + luz[8];
      p8 = true;
      tablas[6] = 0;
    }
    if (cosa[9] >= objetivo && p9 == false) {
      num = num + luz[9];
      p9 = true;
      tablas[7] = 0;
    }
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST , num);  //Enciende la luz de las tablas acertadas
    digitalWrite(latchPin, HIGH);
    valorU = 0;
    valorD = 0;
    prime = 0;
    segun = 0;
    key = 0;
    while ( p2 && p3 && p4 && p5 && p6 && p7 && p8 && p9) {
      lcd.clear();
      lcd.setCursor(2, 0);
      lcd.print("COMPLETADO");
      lcd.setCursor(5, 1);
      lcd.print("Reto=");
      lcd.print(objetivo);
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST , 0);
      digitalWrite(latchPin, HIGH);
      delay (100);
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST , 0b11111111);
      digitalWrite(latchPin, HIGH);
    }
    cuenta();
  }
}

void cuenta () {
  numeroa = tablas[random (ntablas)];
  while (numeroa == 0) {
    numeroa = tablas[random (ntablas)];
  }
  numerob = random (2, 10);
  lcd.noBlink ();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(numeroa);
  lcd.setCursor(1, 0);
  lcd.print("x");
  lcd.setCursor(2, 0);
  lcd.print(numerob);
  lcd.setCursor(3, 0);
  lcd.print( "=" );
  lcd.setCursor(10, 0);
  lcd.print( "Reto=" );
  lcd.print( objetivo );
  //delay (150);
  lcd.setCursor(4, 0);
  lcd.blink ();
}

void menu() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("JUGAR:  PULSA A");
  lcd.setCursor(0, 1);
  lcd.print("CONFIG: PULSA B");
  char key = keypad.getKey();
  while (key == NO_KEY || key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9' || key == 'C' || key == 'D' || key == '0' || key == '*' || key == '#') {
    key = keypad.getKey();
  }
  if (key == 'A') {
    lcd.clear();
    lcd.setCursor(3, 0);
    lcd.print("Soluciona");
    lcd.setCursor(3, 1);
    lcd.print("y pulsa D");
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST , luz [objetivo]);
    digitalWrite(latchPin, HIGH);
    delay (2000);
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST , 0);
    digitalWrite(latchPin, HIGH);
    cuenta();
  }
  if (key == 'B') {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Cantidad de ");
    lcd.setCursor(0, 1);
    lcd.print("aciertos:");
    key = keypad.getKey();
    while (key == NO_KEY || key == 'A' || key == 'B' || key == 'C' || key == 'D' || key == '0' || key == '*' || key == '#') {
      key = keypad.getKey();
      if (key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9') {
        objetivo = key - 48;
        lcd.setCursor(11, 1);
        lcd.print(objetivo);
        digitalWrite(latchPin, LOW);
        shiftOut(dataPin, clockPin, MSBFIRST , luz [objetivo]);
        digitalWrite(latchPin, HIGH);
        delay (1000);
        digitalWrite(latchPin, LOW);
        shiftOut(dataPin, clockPin, MSBFIRST , 0);
        digitalWrite(latchPin, HIGH);
        menu();
      }
    }
  }
}