2021-04-04
múltiples botones con un solo pin analógico.
Uno de los mayores problemas que tenemos cuando queremos controlar cosas con múltiples botones, es que por cada botón usamos un pin, limitando el numero de pines que podemos usar para otras cosas.
En esta entrada veremos como solucionar este problema a costa de un solo pin analógico.
El circuito.
Hay varias formas de conseguir el efecto y todas se basan en el mismo efecto: cada vez que pulsemos un botón debemos hacer que cambie la tensión que llega al pin analógico.
El método más fácil es usar un divisor resistivo, como muestra el esquema de la siguiente imagen:
Como podemos observar el circuito consiste en un juego de resistencias en serie en cuyas uniones colocamos los pulsadores. Cuando apretamos un pulsador hacemos que la tensión formada por el divisor correspondiente llegue al pin analógico.
En la imagen hemos usado seis pulsadores, lo cual implica usar un divisor resistivo de siete resistencias. En general, para N botones necesitaremos N+1 resistencias.
Si hacemos que todas las resistencias sean iguales podemos simplificar el código bastante si sabemos aplicar la ley de Ohm correctamente.
La corriente que va a circular por todo el circuito de resistencias será I=VCC/(N+1)R, siendo N el número de botones, VCC la tensión de alimentación (por ejemplo los 5 voltios de Arduino) y R el valor de una de las resistencias.
Si pulsamos el botón B1, veremos que la tensión que hay en él será I*R. Cuando pulsamos B2 la tensión será I*2R, para el botón B3 será I*3R, y así sucesivamente.
Por lo tanto para un botón N la tensión será I*NR. Como previamente hemos calculado I, podemos calcular la tensión para un botón N:
El resultado es totalmente independiente del valor de la resistencia y solo dependa de la tensión de alimentación y el número de botones. Esta fórmula simplifica los cálculos que ha de hacer el microcontrolador.
No conviene poner unos valores altos de resistencia, ni tampoco bajos, un valor entre 1k y 3k debería ser suficiente.
La resistencia RZ será de un valor alto, del orden de cientos de miles de ohmios. Esta fijará un valor de tensión de 0 voltios en el pin analógico cuando no haya ningún botón pulsado.
La mayor limitación de este esquema es que solo sirve para un solo botón pulsado a la vez.
El software
Para leer el estado de un botón hemos de leer el voltaje con la función analogRead(). Si leemos que el voltaje es cero sabremos que no hay botón apretado. Si el voltaje es distinto de cero podemos saber que botón está pulsado despejando N de la ecuación.
Como siempre hay que mejorar un poco la rutina y lo mejor es encapsularlo todo en una clase para poder utilizarla siempre que queramos.
/* EJEMPLO DE USO: BOTONES MúLTIPLE EN UN SOLO PIN Autor: Víctor JAM. Circuito recomendado: VCC + R R R R | ___ ___ ___ ___ + --|___|--+--|___|--+--|___|--+--|___|--+ | | | | | | | | o | o | o | | |=|B3 |=|B2 |=|B1 | o | o | o | | | | | | | | | ___ | +---------+---------+--|___|--+ | 330K | | === o . GND PIN Valores de R recomendados de 1k a 2k. Condensador de 100nF desde PIN a GND opcional. NOTA: El primer pulsador siempre será el que mas cerca de GND esté. */ #define DEBOUNCE_TIME 50 #define NotPressed 0 #define Press 1 #define Pressed 2 #define Release 3 class SimpleAnalogButton { private: uint8_t nButtons; uint8_t pin; uint8_t *state; uint32_t tdebounce; uint8_t lastButton; uint8_t currentButton; bool range(int a, int b, int c); public: SimpleAnalogButton(uint8_t p, uint8_t nb); void update(); int read(int b); }; bool SimpleAnalogButton::range(int a, int b, int c) { return a<=(b+c) && a>=(b-c); } SimpleAnalogButton::SimpleAnalogButton(uint8_t p, uint8_t nb) { pin = p; nButtons = nb; state = new uint8_t [nButtons]; // Iniciamos el botón actual y el último al número de botones para que // no haya ningún problema. currentButton = nButtons; lastButton = nButtons; for (int i=0; i<nButtons; i++) state[i]=NotPressed; } int SimpleAnalogButton::read(int b) { return state[b]; } void SimpleAnalogButton::update() { int value, i, j; value = analogRead(pin); // Obtenemos el botón que está actualmente pulsado ( 0 a nButtons-1 ). for (i=0; i<nButtons; i++) { if ( range(value, (1023*(i+1))/(nButtons+1), 50) ) break; } // En i tenemos el botón que está trabajando ahora. if ( lastButton!=i ) { tdebounce=0; lastButton=i; // Guardamos su valor para hacer el debounce. } else { if ( tdebounce==0 ) tdebounce=millis(); else if ( millis()-tdebounce > DEBOUNCE_TIME ) { currentButton = lastButton; } } // Comprobamos para cada boton su estado y si es el botón actual, cambiando // su estado si es necesario. for (i=0; i<nButtons; i++) { // Estaba pulsado y no es el botón actual, entonces se estaba soltando. if ( state[i]==Pressed && currentButton!=i ) state[i]=Release; else // Estaba soltandose y no es el botón actual, entonces esta suelto. if ( state[i]==Release && currentButton!=i ) state[i]=NotPressed; else // Si estaba suelto y ahora es el actual lo estamos pulsando. if ( state[i]==NotPressed && currentButton==i ) state[i]=Press; else // Si se esta apretando y sigue siendo el acutal es que está apretado. if ( state[i]==Press && currentButton==i ) state[i]=Pressed; } } #define NBOTONES 5 // Creamos el botón. SimpleAnalogButton buttons(A1, NBOTONES); // Configuración de programa. void setup() { // Usaremos el puerto serie para ver el resultado. Serial.begin(9600); } // Bucle principal. void loop() { buttons.update(); for (int i=0; i<NBOTONES; i++) { // Comprobamos que hemos pulsado un botón y enviamos un mensaje diciendo que // lo hemos apretado. if ( buttons.read(i)==Press ) { Serial.print("boton apretado: "); Serial.println(i); } // Comprobamos que hemos soltado un botón y enviamos un mensaje diciendo que // hemos soltado. if ( buttons.read(i)==Release ) { Serial.print("boton soltado: "); Serial.println(i); } } }
Referencias
Historial
13/10/2024
Se ha editado el artículo puesto que se encontró un par de errores graves:
- No se podía cambiar el pin y solo funcionaba con el pin A0.
- Funcionaba casí de casualidad y se ha tenido que reformar la función update para que funcione.
Se ha cambiado el enlace a la librería y esta se ha dejado sola, ya que se está trabajando en una nueva librería para pines digitales.