Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Control eficiente de 10 botones con una sola interrupción en Arduino UNO

Componentes y suministros

Arduino UNO (o cualquier otro microcontrolador compatible) × 1
Protoboard (genérica) × 1
Cables de conexión (genéricos) × 21
Pulsadores táctiles (cualquier tipo, grande o pequeño) × 10

Aplicaciones y servicios en línea

Sobre este proyecto

Introducción

Las interrupciones son muy útiles. Además de simplificar el código en ocasiones, permiten temporización precisa o despertar al Arduino desde el modo de suspensión (sleep).

Imagina una interfaz de usuario, por ejemplo un mando a distancia alimentado por baterías. Quizás quieras poner el Arduino (o un ATmega independiente) en modo power-down para ahorrar energía. Cuando el Arduino entra en este modo, solo puede despertarse mediante una interrupción externa. El chip ATmega328P usado en el Arduino Uno tiene solo dos interrupciones externas de pin (INT0 y INT1 en los pines 2 y 3). Como una interfaz de usuario suele tener más de dos botones, eso es un problema.

La solución estándar sería conectar todos los botones normalmente, pero también unirlos a un pin de interrupción mediante diodos. Esto, sin embargo, complica significativamente el circuito.

Control eficiente de 10 botones con una sola interrupción en Arduino UNO

Además de las interrupciones externas estándar, el ATmega328P también tiene interrupciones por cambio de pin (pin change interrupts). Existen librerías para gestionarlas y son una buena solución a este problema.

Sin embargo, durante una competición de programación, descubrí cómo hacer esto usando interrupciones externas estándar sin componentes eléctricos adicionales.

El circuito

Control eficiente de 10 botones con una sola interrupción en Arduino UNO
Control eficiente de 10 botones con una sola interrupción en Arduino UNO
Control eficiente de 10 botones con una sola interrupción en Arduino UNO

Usamos varios botones (en este caso 10, que encajan bien en mi protoboard). Puedes tener un botón por pin, lo que permite hasta 20 en un Uno y hasta 70 en un Mega. (Si realmente necesitas 70 botones, recomiendo usar multiplexado; no necesitas un Mega entero para eso).

Cada botón tiene un lado conectado a un pin arbitrario (pines 4-13 en mi caso). Los otros lados de todos los botones se conectan juntos a un único pin capaz de interrupción (pin 2 en mi caso).

Control eficiente de 10 botones con una sola interrupción en Arduino UNO

El código

El código se adjunta a continuación. Para que este ejemplo funcione, cárgalo en tu placa. Abre el monitor serie. Al pulsar un botón, aparecerá su número. Como verás, la función loop() no se usa en absoluto.

Control eficiente de 10 botones con una sola interrupción en Arduino UNO

¿Cómo funciona?

Obviamente hay una interrupción. En mi caso, está conectada al pin 2 y configurada como FALLING (flanco descendente).

Para evitar el uso de diodos, el Arduino reconfigura el circuito sobre la marcha. Hay dos configuraciones posibles: Modo común y Modo distinto.

Modo común

La mayor parte del tiempo, el circuito estará en modo común. El pin de interrupción se configura como INPUT_PULLUP y el resto como OUTPUT y LOW.

Control eficiente de 10 botones con una sola interrupción en Arduino UNO
void configurarComun() {
  pinMode(pinComun, INPUT_PULLUP);
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    pinMode(pinesBotones[i], OUTPUT);
    digitalWrite(pinesBotones[i], LOW);
  }
}

En modo común, pulsar cualquier botón bajará el pin de interrupción y disparará nuestra interrupción. Una vez que esto ocurre, la rutina de servicio de interrupción (ISR) reconfigurará los pines para el modo distinto.

Modo distinto

Una vez que se dispara nuestra interrupción, cambiamos rápidamente al modo distinto.

El modo distinto es lo opuesto al modo común. El pin de interrupción será OUTPUT y LOW, y el resto serán INPUT_PULLUP.

Control eficiente de 10 botones con una sola interrupción en Arduino UNO
void configurarDistinto() {
  pinMode(pinComun, OUTPUT);
  digitalWrite(pinComun, LOW);
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    pinMode(pinesBotones[i], INPUT_PULLUP);
  }
}

En modo distinto, solo los pines correspondientes a los botones realmente pulsados se bajarán. Podemos recorrer fácilmente todos los pines para averiguar cuál disparó la interrupción.

Después, el Arduino puede volver al modo común y esperar otra interrupción. O, dependiendo de tu aplicación, puede permanecer en modo distinto y procesar la entrada del usuario normalmente, volviendo al modo común antes de que el Arduino entre en suspensión.

Un ejemplo más complejo

Probemos algo más complejo. Conectaremos un servo y asignaremos cada botón a un ángulo diferente (1=0°, 2=20°... 10=120°). También alimentaremos el Arduino con unas pilas.

Control eficiente de 10 botones con una sola interrupción en Arduino UNO
Control eficiente de 10 botones con una sola interrupción en Arduino UNO
Control eficiente de 10 botones con una sola interrupción en Arduino UNO

En este ejemplo, ponemos el Arduino en modo power-down tras cinco segundos de inactividad para ahorrar energía. Puedes encontrar tutoriales sobre el modo sleep en línea. Además, alimentamos el servo a través de un transistor para apagarlo cuando no se usa.

El código para este ejemplo también está adjunto abajo.

Código

Registrador serie (Serial logger)

const int pinComun = 2;
const int pinesBotones[] = {4,5,6,7,8,9,10,11,12,13};
unsigned long ultimaActivacion = 0;

void setup() {
  configurarComun(); // Configura pines para interrupción
  attachInterrupt(digitalPinToInterrupt(pinComun), interrupcionPulsacion, FALLING);
  Serial.begin(9600);
}

void loop() {
  // ¡Vacío!
}

void interrupcionPulsacion() {
  // ISR
  if (millis() - ultimaActivacion < 200) { // Antirrebote
    return;
  }
  ultimaActivacion = millis();
  configurarDistinto(); // Configura pines para probar botones individuales
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    // Prueba cada botón
    if (!digitalRead(pinesBotones[i])) {
      pulsacion(i);
    }
  }
  configurarComun(); // Vuelve al estado original
}

void configurarComun() {
  pinMode(pinComun, INPUT_PULLUP);
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    pinMode(pinesBotones[i], OUTPUT);
    digitalWrite(pinesBotones[i], LOW);
  }
}

void configurarDistinto() {
  pinMode(pinComun, OUTPUT);
  digitalWrite(pinComun, LOW);
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    pinMode(pinesBotones[i], INPUT_PULLUP);
  }
}

void pulsacion(int boton) {
  // Nuestro manejador
  Serial.println(boton + 1);
}

Servo con modo sleep

#include <Servo.h>
#include <avr/sleep.h>
#include <avr/power.h>

const int pinComun = 2;
const int pinesBotones[] = {4,5,6,7,8,9,10,11,12,13};
const int pinHabilitaServo = A1;
const int pinServo = A0;
Servo servo;
unsigned long ultimaActivacion = 0;
int estado = 0;

void setup() {
  pinMode(pinComun, INPUT_PULLUP);
  configurarComun();
  attachInterrupt(digitalPinToInterrupt(pinComun), interrupcionPulsacion, FALLING);
  servo.attach(pinServo);
  pinMode(pinHabilitaServo, OUTPUT);
}

void loop() {
  if (millis() - ultimaActivacion > 5000) {
    digitalWrite(pinHabilitaServo, LOW);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    sleep_mode();
  }
  delay(10);
}

void interrupcionPulsacion() {
  sleep_disable();
  power_all_enable();
  if (millis() - ultimaActivacion < 200) {
    return;
  }
  ultimaActivacion = millis();
  configurarDistinto();
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    if (!digitalRead(pinesBotones[i])) {
      pulsacion(i);
    }
  }
  configurarComun();
}

void configurarComun() {
  pinMode(pinComun, INPUT_PULLUP);
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    pinMode(pinesBotones[i], OUTPUT);
    digitalWrite(pinesBotones[i], LOW);
  }
}

void configurarDistinto() {
  pinMode(pinComun, OUTPUT);
  digitalWrite(pinComun, LOW);
  for (int i = 0; i < sizeof(pinesBotones) / sizeof(int); i++) {
    pinMode(pinesBotones[i], INPUT_PULLUP);
  }
}

void pulsacion(int boton) {
  digitalWrite(pinHabilitaServo, HIGH);
  servo.write(map(boton, 0, 9, 0, 180));
}

Esquemáticos

Control eficiente de 10 botones con una sola interrupción en Arduino UNO
Control eficiente de 10 botones con una sola interrupción en Arduino UNO

Archivos Fritzing: multiinterrupt_hoF76Oc4T5.fzz, servo_with_sleep_u9ZqxF0jhY.fzz

Proceso de manufactura

  1. Construye un Decodificador DTMF Solo con Arduino – Sin CI Externos
  2. Crea un Impresionante Sistema Ambilight para Monitor con Arduino
  3. Construye un Dispositivo de Levitación Ultrasónica con Arduino – Guía Paso a Paso
  4. Construye un Voltímetro Inteligente con Arduino y Smartphone – Proyecto DIY Fácil
  5. Monitor de Frecuencia Cardíaca IoT en Tiempo Real con Arduino y MAX30100
  6. Servidor Web Arduino Uno WiFi: Controla un LED desde el Navegador
  7. Contador Inteligente de Plazas de Aparcamiento: Seguimiento en Tiempo Real con Arduino, Processing y PHP
  8. Automatiza el Juego del Dinosaurio de Chrome con Arduino y Python
  9. Construye una Radio FM Compacta con Arduino Nano y RDA5807M
  10. Shield de Programación Inalámbrica para Arduino con HC-05 Bluetooth – Sin Cable USB
  11. Construye un Reloj Digital en Arduino usando una Máquina de Estados Finitos