Está en la página 1de 23

Grado en Ingenierı́a de Datos

Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

Solución propuesta
Actividad 1 Conectar un botón a Arduino de forma adecuada. Imprimir por pantalla la lectura del
botón.

Solución:
Un botón es, normalmente, un contacto abierto que se cierra al pulsarse. Cada uno de los dos estados debe ir
asociado a un voltaje lógico distinto: 5V o 0V. En ningún caso debemos dejar uno de los estados (normalmente
el estado suelto) en un nivel lógico no forzado, es decir, desconectado de toda referencia. Además, debemos
evitar un cortocircuito entre 5V y 0V al pulsar el botón, para ello conectaremos la señal del estado ”suelto”
mediante una resistencia de pull-up (a 5V) o pull-down (a 0V).

VCC VCC

Rpull−up Botón

Arduino Arduino

Botón Rpull−down

Configuración pull-up Configuración pull-down

Suele ser más recomendable emplear la configuración con pull-up. En este caso además, el ATMega328P in-
cluye resistencias de pull-up configurables en todas las entradas, por lo que no es necesario emplear resistencias
externas. Con esta configuración, cuando el botón este suelto leeremos un 1 lógico (debido al pull-up a 5V) y
cuando este pulsado leeremos un 0 lógico (el botón fuerza el nivel del pin a 0V).

Para ver el resultado solo es necesario emplear la función digitalRead(...) empleando como argumento el
pin deseado, por ejemplo el 2. La configuración del pin como entrada se puede realizar con pinMode(...,INPUT)
(sin pull-up interno) o pinMode(...,INPUT PULLUP) (con pull-up interno).

Imprimimos los datos por pantalla empleando la funcion Serial.println(...) y convirtiendo el dato a
una cadena de caracteres con String(...). Lógicamente hay que inicializar previamente el puerto serie con
Serial.begin(...) al baudrate deseado.

1 void s e t u p ( ) {
2 pinMode ( 2 , INPUT PULLUP) ;
3 S e r i a l . begin (9600) ;
4 }
5
6 void l o o p ( ) {
7 Serial . println ( digitalRead (2) ) ;
8 }
Codigos/button1.c
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

Actividad 2 Desarrollar un programa que mida cuanto tiempo ha estado pulsado el botón y haga
parpadear un LED con ese mismo periodo. El LED debe seguir parpadeando mientras el botón está
pulsado. Recomendaciones:
• Emplear un tipo enumerado (enum) para almacenar el estado del botón.

• Emplear una estructura condicional switch-case para gestionar la lógica del sistema (No la gestión
del botón).

Solución:
ORGANIZACIÓN Y JERARQUIZACIÓN DE LAS TAREAS

El primer paso consiste en darse cuenta de que aunque un botón tiene dos estados (pulsado y suelto), la
actividad hace referencia a las transiciones. Estas transiciones las podemos tratar como estados en sı́ mismos.
De esta forma el botón cuenta con cuatro estados: estable pulsado, estable suelto, transición hacia pulsado,
transición hacia suelto. Para este dato podemos comenzar empleando un tipo entero, donde cada valor repre-
senta un estado:
0 → Botón Pulsado.
1 → Botón Suelto.
2 → Transición a Pulsado.
3 → Transición a Suelto.

En este punto desarrollamos la lógica de la lectura del botón, debemos tener en cuenta los cuatro estados
posibles. En alto nivel podrı́amos definirla de la siguiente forma:
• Si el botón en el ciclo anterior estaba suelto o soltandose :

– Si el botón ahora está pulsado → Transición a pulsado.


– Si el botón ahora está suelto → Estable suelto.

• Si el botón en el ciclo anterior estaba pulsado o pulsandose:

– Si el botón ahora está pulsado → Estable pulsado.


– Si el botón ahora está suelto → Transición a suelto.

La lectura actual del botón lo podemos leer directamente con digitalRead(...). Además, incluimos varios
mensajes por el puerto serie (Serial.println(...)) para comprobar como esta funcionando el programa y
un delay(...) para reducir la velocidad del mismo. Por lo tanto, el programa queda como:
1 int estadoMiBoton = 1 ;
2
3 void s e t u p ( ) {
4 pinMode ( 2 , INPUT PULLUP) ;
5 S e r i a l . begin (9600) ;
6 }
7
8 void l o o p ( ) {
9 i f ( estadoMiBoton == 1 | | estadoMiBoton == 3 ) {
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

10 i f ( d i g i t a l R e a d ( 2 )==LOW) {
11 estadoMiBoton = 2 ;
12 S e r i a l . p r i n t l n ( ” Transicion a pulsado ” ) ;
13 }
14 i f ( d i g i t a l R e a d ( 2 )==HIGH) {
15 estadoMiBoton = 1 ;
16 S e r i a l . println (” Suelto estable ”) ;
17 }
18 }
19 i f ( estadoMiBoton == 0 | | estadoMiBoton == 2 ) {
20 i f ( d i g i t a l R e a d ( 2 )==LOW) {
21 estadoMiBoton = 0 ;
22 S e r i a l . p r i n t l n ( ” Pulsado e s t a b l e ” ) ;
23 }
24 i f ( d i g i t a l R e a d ( 2 )==HIGH) {
25 estadoMiBoton = 3 ;
26 S e r i a l . println (” Transicion a soltado ”) ;
27 }
28 }
29 d e l a y ( 2 5 0 ) ;
30 S e r i a l . p r i n t l n ( estadoMiBoton ) ;
31 }
Codigos/button2.c
Al probar el código podemos ver que hay un problema. Al cumplir el primer if (if(estadoMiBoton ==1
|| estadoMiBoton == 3)), se puede cambiar estadoMiBoton a 2 e inmediatamente cumplir el segundo if,
pasando rápidamente al estado 0 y no pasando nunca por 2. Para solucionarlo es suficiente con convertir las
condiciones en mutuamente excluyentes con else. En el segundo nivel condicional no deberı́a hacer falta, ya
que el botón no estará nunca suelto y pulsado a la vez, pero es una buena práctica.

Como trabajar con valores numéricos con un estado asociado puede ser confuso, y no hay una dependencia
real a los valores concretos, vamos a sustituir la variable estadoMiBoton de tipo int por una variable similar
de tipo enum. De esta forma podremos trabajar con valores más comprensibles.

Creamos el tipo enumerado con un nombre representativo, por ejemplo EstadoBoton (comenzamos por
mayúscula al tratarse de una declaración de tipo y no de una variable). Hay que tener cuidado con darles
nombres representativos a los estados y que no nos puedan llevar a confusión. Los estados los nombramos con
mayúsculas ya que (técnicamente) son constantes de programa.

enum EstadoBoton{EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO};

Susituimos el tipo de la variable empleada para guardar el estado del botón, y le damos un valor incial, por
ejemplo EST SUELTO.

EstadoBoton estadoMiBoton = EST SUELTO;

La ventaja de emplear un tipo enumerado es que las condiciones lógicas quedan bastante más claras:
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

1 enum EstadoBoton {EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4
5 void s e t u p ( ) {
6 pinMode ( 2 , INPUT PULLUP) ;
7 S e r i a l . begin (9600) ;
8 }
9
10 void l o o p ( ) {
11 i f ( estadoMiBoton == EST SUELTO | | estadoMiBoton == TRANS SOLTANDO) {
12 i f ( d i g i t a l R e a d ( 2 )==LOW) {
13 estadoMiBoton = TRANS PULSANDO;
14 }
15 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
16 estadoMiBoton = EST SUELTO ;
17 }
18 }
19 e l s e i f ( estadoMiBoton == EST PULSADO | | estadoMiBoton == TRANS PULSANDO) {
20 i f ( d i g i t a l R e a d ( 2 )==LOW) {
21 estadoMiBoton = EST PULSADO ;
22 }
23 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
24 estadoMiBoton = TRANS SOLTANDO;
25 }
26 }
27 d e l a y ( 2 5 0 ) ; // S o l o para compr obacion es
28 S e r i a l . p r i n t l n ( estadoMiBoton ) ;
29 }
Codigos/button3.c
En este punto el control del botón ya esta completo, podemos pasar a la gestión de la lógica del programa.
Vamos a implementarla mediante una estructura switch-case ya que son muy útiles para gestionar múltiples
estados mediante una variable de control. Va muy bien emplearlo con variables de tipo enumerado.
switch(variableControl) {
case valor 1:
Código del Caso 1
....
break;
case valor 2:
Código del Caso 2
....
break;
...
case valor n:
Código del Caso n
....
break;
default:
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

Código si no se cumple ningún valor


....
}
En nuestro caso vamos a hacer una primera comprobación de que esta estructura de control funciona cor-
rectamente con nuestra variable de control: estadoMiBoton. Para ello imprimiremos por pantalla un mensaje
distinto en cada caso del switch. Mantendremos un delay(...) para ver correctamente la salida. Por ahora
podemos suprimir el caso default, más adelante lo añadiremos para devolver el programa a un estado conocido
si hay algún error inesperado.

1 enum EstadoBoton {EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4
5 void s e t u p ( ) {
6 pinMode ( 2 , INPUT PULLUP) ;
7 S e r i a l . begin (9600) ;
8 }
9
10 void l o o p ( ) {
11 // −−−−−−−−−−−− C o n t r o l d e l b o t o n −−−−−−−−−−−
12 i f ( estadoMiBoton == EST SUELTO | | estadoMiBoton == TRANS SOLTANDO) {
13 i f ( d i g i t a l R e a d ( 2 )==LOW) {
14 estadoMiBoton = TRANS PULSANDO;
15 }
16 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
17 estadoMiBoton = EST SUELTO ;
18 }
19 }
20 e l s e i f ( estadoMiBoton == EST PULSADO | | estadoMiBoton == TRANS PULSANDO) {
21 i f ( d i g i t a l R e a d ( 2 )==LOW) {
22 estadoMiBoton = EST PULSADO ;
23 }
24 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
25 estadoMiBoton = TRANS SOLTANDO;
26 }
27 }
28 // −−−−−−−−−−−− L o g i c a de programa −−−−−−−−−−−
29 switch ( estadoMiBoton ) {
30 case EST PULSADO :
31 S e r i a l . p r i n t l n ( ” El boton e s t a p u l s a d o ” ) ;
32 break ;
33 case EST SUELTO :
34 S e r i a l . p r i n t l n ( ” El boton e s t a s u e l t o ” ) ;
35 break ;
36 case TRANS PULSANDO:
37 S e r i a l . p r i n t l n ( ” El boton e s t a p u l s a n d o s e ” ) ;
38 break ;
39 case TRANS SOLTANDO:
40 S e r i a l . p r i n t l n ( ” El boton e s t a s o l t a n d o s e ” ) ;
41 break ;
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

42 }
43 d e l a y ( 2 5 0 ) ; // S o l o para compr obacion es
44 }
Codigos/button4.c
Una vez se ha comprobado que tanto la gestión del botón como la estructura de control funcionan adecuada-
mente (aunque pueden mejorarse), vamos a continuar con la lógica solicitada por la actividad. Debemos calcular
cuanto tiempo ha estado pulsado el botón; la forma más sencilla es calcular la diferencia entre el instante en
el que pulsa el botón y el instante en el que se suelta. Es decir, guardaremos el instante de tiempo cuando
el botón se pulse; y cuando el botón se suelte calcularemos cuanto tiempo ha pasado (diferencia entre ambos
tiempos). Por lo tanto, la lógica de acuerdo a los estados del botón es:

• Botón pulsado (estable) → Nada especial.

• Botón suelto (estable) → Nada especial.

• Botón pulsandose (transición) → Captura de tiempo para futuro cálculo.

• Botón soltandose (transición) → Cálculo (y guardado) de tiempo de parpadeo.

Incluimos esta lógica y añadimos una variable para almacenar la captura de tiempo al pulsar el botón, otra
para almacenar el tiempo de soltado y una tercera para almacenar el tiempo que ha estado pulsado el botón.
En el caso del instante de pulsación y soltado es necesario emplear unsigned long int como tipo; en el caso
del tiempo de pulsación es suficiente con unsigned int. Además, debemos tener en cuenta que el cálculo será
el tiempo del instante de soltado menos el tiempo del instante de pulsado, ya que el tiempo va incrementandose
y no queremos un resultado negativo. Añadimos los mensajes por puerto serie necesarios para comprobar el
funcionamiento. Eliminamos el delay(...), ya no hace falta ralentizar el flujo del programa.
1 enum EstadoBoton {EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4 unsigned long int i n s t a n t e S o l t a d o =0, i n s t a n t e P u l s a d o =0;
5 unsigned int tiempoParpadeo = 5 0 0 ;
6 void s e t u p ( ) {
7 pinMode ( 2 , INPUT PULLUP) ;
8 S e r i a l . begin (9600) ;
9 }
10
11 void l o o p ( ) {
12 // −−−−−−−−−−−− C o n t r o l d e l b o t o n −−−−−−−−−−−
13 i f ( estadoMiBoton == EST SUELTO | | estadoMiBoton == TRANS SOLTANDO) {
14 i f ( d i g i t a l R e a d ( 2 )==LOW) {
15 estadoMiBoton = TRANS PULSANDO;
16 }
17 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
18 estadoMiBoton = EST SUELTO ;
19 }
20 }
21 e l s e i f ( estadoMiBoton == EST PULSADO | | estadoMiBoton == TRANS PULSANDO) {
22 i f ( d i g i t a l R e a d ( 2 )==LOW) {
23 estadoMiBoton = EST PULSADO ;
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

24 }
25 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
26 estadoMiBoton = TRANS SOLTANDO;
27 }
28 }
29 // −−−−−−−−−−−− L o g i c a de programa −−−−−−−−−−−
30 switch ( estadoMiBoton ) {
31 case EST PULSADO :
32 break ;
33 case EST SUELTO :
34 break ;
35 case TRANS PULSANDO:
36 instantePulsado = m i l l i s () ;
37 S e r i a l . p r i n t l n ( ” El boton e s t a p u l s a n d o s e , t : ”+ S t r i n g ( i n s t a n t e P u l s a d o ) ) ;
38 break ;
39 case TRANS SOLTANDO:
40 instanteSoltado = m i l l i s () ;
41 tiempoParpadeo = i n s t a n t e S o l t a d o −i n s t a n t e P u l s a d o ;
42 S e r i a l . p r i n t l n ( ” El boton e s t a s o l t a n d o s e , t : ”+ S t r i n g ( i n s t a n t e S o l t a d o ) ) ;
43 S e r i a l . p r i n t l n ( ” El tiempo de p u l s a c i o n ha s i d o ”+ S t r i n g ( tiempoParpadeo )+ ”ms” ) ;
44 break ;
45 }
46 }
Codigos/button5.c
En este punto ya tenemos el tiempo de parpadeo calculado adecuadamente. Incluimos la función de parpadeo
de la primera sesión, configuramos el pin deseado como salida (pinMode(PIN BUILTIN, OUTPUT) y llamamos
a la función parpadeo con el tiempo deseado (parpadeo(LED BUILTIN, tiempoParpadeo, tiempoParpadeo))
fuera del switch, ya que queremos que se ejecute siempre:
1 enum EstadoBoton {EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4 unsigned long int i n s t a n t e S o l t a d o =0, i n s t a n t e P u l s a d o =0;
5 unsigned int tiempoParpadeo = 5 0 0 ;
6
7 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) ;
8
9 void s e t u p ( ) {
10 pinMode ( 2 , INPUT PULLUP) ;
11 pinMode (LED BUILTIN , OUTPUT) ;
12 S e r i a l . begin (9600) ;
13 }
14
15 void l o o p ( ) {
16 // −−−−−−−−−−−− C o n t r o l d e l b o t o n −−−−−−−−−−−
17 i f ( estadoMiBoton == EST SUELTO | | estadoMiBoton == TRANS SOLTANDO) {
18 i f ( d i g i t a l R e a d ( 2 )==LOW) {
19 estadoMiBoton = TRANS PULSANDO;
20 }
21 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

22 estadoMiBoton = EST SUELTO ;


23 }
24 }
25 e l s e i f ( estadoMiBoton == EST PULSADO | | estadoMiBoton == TRANS PULSANDO) {
26 i f ( d i g i t a l R e a d ( 2 )==LOW) {
27 estadoMiBoton = EST PULSADO ;
28 }
29 e l s e i f ( d i g i t a l R e a d ( 2 )==HIGH) {
30 estadoMiBoton = TRANS SOLTANDO;
31 }
32 }
33 // −−−−−−−−−−−− L o g i c a de programa −−−−−−−−−−−
34 switch ( estadoMiBoton ) {
35 case EST PULSADO :
36 break ;
37 case EST SUELTO :
38 break ;
39 case TRANS PULSANDO:
40 instantePulsado = m i l l i s () ;
41 S e r i a l . p r i n t l n ( ” El boton e s t a p u l s a n d o s e , t : ”+ S t r i n g ( i n s t a n t e P u l s a d o ) ) ;
42 break ;
43 case TRANS SOLTANDO:
44 instanteSoltado = m i l l i s () ;
45 tiempoParpadeo = i n s t a n t e S o l t a d o −i n s t a n t e P u l s a d o ;
46 S e r i a l . p r i n t l n ( ” El boton e s t a s o l t a n d o s e , t : ”+ S t r i n g ( i n s t a n t e S o l t a d o ) ) ;
47 S e r i a l . p r i n t l n ( ” El tiempo de p u l s a c i o n ha s i d o ”+ S t r i n g ( tiempoParpadeo )+ ”ms” ) ;
48 break ;
49 }
50 parpadeo (LED BUILTIN , tiempoParpadeo , tiempoParpadeo ) ;
51 }
52
53 // −−−−−−−−−−−−−−− D e c l a r a c i o n de f u n c i o n e s −−−−−−−−−−−−−−−
54
55 /∗ Funcion de parpadeo que a c t u a s o b r e e l p i n d e s e a d o
56 Entradas :
57 pinLed −> p i n en e l que e s t a e l l e d a p a r p a d e a r
58 i n t e n t r e 0 y 19
59 tiempoEncendido −> tiempo que d e b e e s t a r e n c e n d i d o por c i c l o
60 i n t e n t r e 1 0 [ ms ] y 2 0 0 0 0 [ms ]
61 tiempoApagado −> tiempo que d e b e e s t a r apagado por c i c l o
62 i n t e n t r e 1 0 [ ms ] y 2 0 0 0 0 [ms ]
63 Salida :
64 0 −> E j e c u c i o n s i n e r r o r e s
65 1 −> Pin de parpadeo no v a l i d o
66 ∗/
67 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) {
68 s t a t i c unsigned long int instanteUltimoCambio =0; // Almacena e l u l t i m o e n c e n d i d o o
apagado
69 unsigned long int tiempoActual = m i l l i s ( ) ; // G e s t i o n d e l tiempo a c t u a l
70 unsigned long int tiempoDesdeUltimoCambio = tiempoActual − instanteUltimoCambio ; //
C a l c u l a d e l tiempo que ha pasado d e s d e e l u l t i m o cambio de e s t a d o
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

71 b o o l e s t a d o P i n = d i g i t a l R e a d ( pinLed ) ; // G e s t i o n d e l e s t a d o i n i c i a l d e l p i n
72
73 // C o n d i c i o n e s que a s e g u r a n r a n g o s adecuados de l o s par am etr os f o r m a l e s
74 i f ( pinLed >19) return 1 ;
75 i f ( tiempoEncendido <10) tiempoEncendido =10;
76 i f ( tiempoApagado <10) tiempoApagado =10;
77 i f ( tiempoEncendido >20000) tiempoEncendido =20000;
78 i f ( tiempoApagado >20000) tiempoApagado =20000;
79
80 // S i e l p i n l l e v a e n c e n d i d o ( o apagado ) mas tiempo d e l d e s e a d o . . .
81 i f ( ( e s t a d o P i n == HIGH && tiempoDesdeUltimoCambio > tiempoEncendido )
82 | | ( e s t a d o P i n == LOW && tiempoDesdeUltimoCambio > tiempoApagado ) ) {
83 d i g i t a l W r i t e ( pinLed , ! e s t a d o P i n ) ; // Se cambia e l e s t a d o d e l p i n
84 instanteUltimoCambio = tiempoActual ; // Se guarda e l i n s t a n t e d e l cambio
85 }
86 return 0 ; // Retorno s i n e r r o r e s
87 }
Codigos/button6.c

Actividad 3 Implementar un sistema antirrebote en la lectura del botón. Organizar el código en


funciones de forma adecuada. Eliminar todo acceso de las funciones a variables globales.

Solución:
Antes de comenzar con el antirebote, vamos a separar el control del botón a una función. Para ello empezamos
separando el trozo de código y comprobando las entradas y salidas de información del mismo.

• Entradas (parámetros formales):

– Lectura actual del botón → digitalRead(2). Solo puede valer 0 o 1 (bool)


– Estado del botón → estadoMiBoton. Tipo EstadoBoton.

• Salidas (tipo de la función):

– Estado del botón → estadoMiBoton. Tipo EstadoBoton.

Por lo tanto, dando un nombre a la función (gestionBoton) y asignando nuevos nombres a los parámetros
locales para evitar confusiones, el prototipo de la función queda como:
EstadoBoton gestionBoton(bool lecturaActual, EstadoBoton estadoAnterior)

Dentro de la función hacemos las sutituciones de variables necesarias y devolvemos el nuevo estado en cuanto
lo tenemos calculado. En el siguiente código no se muestra la implementación de la función de parpadeo por
motivos de longitud.

1 enum EstadoBoton {EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4 unsigned long int i n s t a n t e S o l t a d o =0, i n s t a n t e P u l s a d o =0;
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

5 unsigned int tiempoParpadeo = 5 0 0 ;


6
7 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) ;
8 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) ;
9
10 void s e t u p ( ) {
11 pinMode ( 2 , INPUT PULLUP) ;
12 pinMode (LED BUILTIN , OUTPUT) ;
13 S e r i a l . begin (9600) ;
14 }
15
16 void l o o p ( ) {
17 estadoMiBoton = g e s t i o n B o t o n ( d i g i t a l R e a d ( 2 ) , estadoMiBoton ) ;
18 switch ( estadoMiBoton ) {
19 case EST PULSADO :
20 break ;
21 case EST SUELTO :
22 break ;
23 case TRANS PULSANDO:
24 instantePulsado = m i l l i s () ;
25 break ;
26 case TRANS SOLTANDO:
27 instanteSoltado = m i l l i s () ;
28 tiempoParpadeo = i n s t a n t e S o l t a d o −i n s t a n t e P u l s a d o ;
29 break ;
30 }
31 parpadeo (LED BUILTIN , tiempoParpadeo , tiempoParpadeo ) ;
32 }
33
34 // −−−−−−−−−−−−−−− D e c l a r a c i o n de f u n c i o n e s −−−−−−−−−−−−−−−
35
36 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) {
37 i f ( e s t a d o A n t e r i o r == EST SUELTO | | e s t a d o A n t e r i o r == TRANS SOLTANDO) {
38 i f ( l e c t u r a A c t u a l==LOW) {
39 return TRANS PULSANDO;
40 }
41 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
42 return EST SUELTO ;
43 }
44 }
45 e l s e i f ( e s t a d o A n t e r i o r == EST PULSADO | | e s t a d o A n t e r i o r == TRANS PULSANDO) {
46 i f ( l e c t u r a A c t u a l==LOW) {
47 return EST PULSADO ;
48 }
49 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
50 return TRANS SOLTANDO;
51 }
52 }
53 }
54
55 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

tiempoApagado ) {}
Codigos/button7Simp.c
A continuación vamos a desarrollar el antirebote. A veces los botones pueden generar datos inadecuados,
especialmente en los cambios, por lo que no es una salida tan ”limpia”. Un antirrebote es un trozo de código que
se encarga de asegurar que no hay lecturas inadecuadas. Hay dos planteamientos: por repetición de entradas o
por tiempo.
Por repetición de entradas implica que solo se dará por valida una lectura si se ha dado de forma consecutiva
un número determinado de veces, por ejemplo 2. Si la lectura actual no es válida, se mantendrá la última lectura
válida. A continuación se muestra un ejemplo:
Lectua del botón 1 1 0 1 0 0 0 0 1 0 1 1 1 1
Antirrebote - ✓ X X X ✓ ✓ ✓ X X X ✓ ✓ ✓
Lectura resultante 1 1 1 1 1 0 0 0 0 0 0 1 1 1
Es necesario almacenar la última lectura válida, la última lectura real y un contador con cuantas veces lleva
sin cambiar la salida. De forma lógica podemos expresar el comportamiento del antirrebote como:

• Si la lectura actual es igual a la anterior:

– Si el contador es 2 o más: la lectura es válida.


– Si el contador es menor a 2: aumentar el contador.

• Si no son iguales: reiniciar el contador.

• Guardar la lectura como lectura anterior para el siguiente ciclo.

Como la lectura del botón solo puede ser HIGH o LOW, se pueden emplear variables de tipo bool para las
variables de última lectura válida y la última lectura real; ultimaLecturaValida y lecturaAnterior por
ejemplo. Para el contador empleamos una variable de tipo entero, int lecturasConsecutivas El código en C
quedarı́a de la siguiente forma:
1 bool ultimaLecturaValida ;
2 bool lecturaAnterior ;
3 unsigned int l e c t u r a s C o n s e c u t i v a s ;
4
5 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
6 i f ( l e c t u r a s C o n s e c u t i v a s >= 2 ) {
7 ultimaLecturaValida = lecturaActual ;
8 }
9 else {
10 l e c t u r a s C o n s e c u t i v a s=l e c t u r a s C o n s e c u t i v a s +1;
11 }
12 }
13 else {
14 lecturasConsecutivas = 0;
15 }
16 lecturaAnterior = lecturaActual ;
Codigos/buttonDebounceA1.c
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

Lo lógico es separar el antirrebote en su propia función. Esta función recibe como parámetro formal la
lectura real del botón y devuelve la lectura válida. Adicionalmente podemos incluir otro parámetro formal que
indique cual es la cantidad de veces que se tiene que repetir una lectura para considerarse válida, en lugar de
emplear siempre 2, le podemos llamar repeticionesNecesarias. Por lo tanto, el prototipo de la función será:
bool antirebote(bool lecturaActual, unsigned int repeticionesNecesarias);
Todas las variables que almacenen información entre llamadas deben ser estáticas; además, siempre se les
debe dar un valor de inicialización. De esta forma la función completa del control del botón queda como:
1 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) {
2 lecturaActual = a n t i r e b o t e ( lecturaActual , 2) ;
3 i f ( e s t a d o A n t e r i o r == EST SUELTO | | e s t a d o A n t e r i o r == TRANS SOLTANDO) {
4 i f ( l e c t u r a A c t u a l==LOW) {
5 return TRANS PULSANDO;
6 }
7 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
8 return EST SUELTO ;
9 }
10 }
11 e l s e i f ( e s t a d o A n t e r i o r == EST PULSADO | | e s t a d o A n t e r i o r == TRANS PULSANDO) {
12 i f ( l e c t u r a A c t u a l==LOW) {
13 return EST PULSADO ;
14 }
15 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
16 return TRANS SOLTANDO;
17 }
18 }
19 }
20
21 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int r e p e t i c i o n e s N e c e s a r i a s ) {
22 s t a t i c b o o l u l t i m a L e c t u r a V a l i d a = HIGH ;
23 s t a t i c b o o l l e c t u r a A n t e r i o r = HIGH ;
24 s t a t i c unsigned int l e c t u r a s C o n s e c u t i v a s =0;
25
26 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
27 i f ( l e c t u r a s C o n s e c u t i v a s >= r e p e t i c i o n e s N e c e s a r i a s ) {
28 ultimaLecturaValida = lecturaActual ;
29 }
30 else {
31 l e c t u r a s C o n s e c u t i v a s=l e c t u r a s C o n s e c u t i v a s +1;
32 }
33 }
34 else {
35 lecturasConsecutivas = 0;
36 }
37 lecturaAnterior = lecturaActual ;
38 return u l t i m a L e c t u r a V a l i d a ;
39 }
Codigos/buttonDebounceA2.c
Existe otra forma de plantear el antirrebote: empleando tiempo transcurrido en lugar de repeticiones. Según
este planteamiento, una lectura solo será válida si la entrada lleva sin cambiar más de un tiempo determinado,
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

50ms por ejemplo. La lógica es parecida a la del antirebote anterior ya que las condiciones son:

• Si la lectura actual es igual a la anterior:

– Si ha transcurrido más de 50ms desde el último cambio: la lectura es válida.

• Si no son iguales: guardar la referencia de tiempo.

• Guardar la lectura como lectura anterior para el siguiente ciclo.

En código queda de la siguiente forma:


1 bool ultimaLecturaValida ;
2 bool lecturaAnterior ;
3 unsigned long int instanteUltimoCambio ;
4 unsigned long int tiempoActual=m i l l i s ( ) ;
5
6 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
7 i f ( tiempoActual − instanteUltimoCambio >= 5 0 ) {
8 ultimaLecturaValida = lecturaActual ;
9 }
10 }
11 else {
12 instanteUltimoCambio = tiempoActual ;
13 }
14 lecturaAnterior = lecturaActual ;
Codigos/buttonDebounceB1.c
De forma similar al caso anterior, lo podemos pasar a una función haciendo estáticas las variables adecuadas
y añadiendo un parámetro formal para la selección del tiempo de antirebote deseado.
1 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) {
2 l e c t u r a A c t u a l = a n t i r e b o t e ( lecturaActual , 50) ;
3 i f ( e s t a d o A n t e r i o r == EST SUELTO | | e s t a d o A n t e r i o r == TRANS SOLTANDO) {
4 i f ( l e c t u r a A c t u a l==LOW) {
5 return TRANS PULSANDO;
6 }
7 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
8 return EST SUELTO ;
9 }
10 }
11 e l s e i f ( e s t a d o A n t e r i o r == EST PULSADO | | e s t a d o A n t e r i o r == TRANS PULSANDO) {
12 i f ( l e c t u r a A c t u a l==LOW) {
13 return EST PULSADO ;
14 }
15 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
16 return TRANS SOLTANDO;
17 }
18 }
19 }
20
21 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int t i e m p o N e c e s a r i o ) {
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

22 s t a t i c b o o l u l t i m a L e c t u r a V a l i d a = HIGH ;
23 s t a t i c b o o l l e c t u r a A n t e r i o r = HIGH ;
24 s t a t i c unsigned long int instanteUltimoCambio ;
25 unsigned long int tiempoActual=m i l l i s ( ) ;
26
27 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
28 i f ( tiempoActual − instanteUltimoCambio >= t i e m p o N e c e s a r i o ) {
29 ultimaLecturaValida = lecturaActual ;
30 }
31 }
32 else {
33 instanteUltimoCambio = tiempoActual ;
34 }
35 lecturaAnterior = lecturaActual ;
36 return u l t i m a L e c t u r a V a l i d a ;
37 }
Codigos/buttonDebounceB2.c
En este punto se podrı́a escoger entre una función antirrebote o la otra. Sin embargo tambien podrı́amos
combinar ambas, y escoger mediante una variable booleana entre el antirebote por tiempo o por lecturas.
Vamos a llamar a esta variable gestionPorTiempo y a combinar ambas funciones dada su similitud, las
variablesrepeticionesNecesarias y tiempoNecesario se combinan en valorParaCambio. También conviene
poner una protección para que solo se incremente lecturasConsecutivas en el modo de gestión por repeti-
ciones. El resultado es el siguiente:
1 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l
gestionPorTiempo ) {
2 s t a t i c b o o l u l t i m a L e c t u r a V a l i d a = HIGH ;
3 s t a t i c b o o l l e c t u r a A n t e r i o r = HIGH ;
4 s t a t i c unsigned int l e c t u r a s C o n s e c u t i v a s =0;
5 s t a t i c unsigned long int instanteUltimoCambio ;
6 unsigned long int tiempoActual=m i l l i s ( ) ;
7
8 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
9 i f ( ( gestionPorTiempo && ( l e c t u r a s C o n s e c u t i v a s >= valorParaCambio ) ) ||
10 ( ! gestionPorTiempo && ( tiempoActual − instanteUltimoCambio >=
valorParaCambio ) ) ) {
11 ultimaLecturaValida = lecturaActual ;
12 }
13 e l s e i f ( ! gestionPorTiempo ) {
14 l e c t u r a s C o n s e c u t i v a s=l e c t u r a s C o n s e c u t i v a s +1;
15 }
16 }
17 else {
18 lecturasConsecutivas = 0;
19 instanteUltimoCambio = tiempoActual ;
20 }
21 lecturaAnterior = lecturaActual ;
22 return u l t i m a L e c t u r a V a l i d a ;
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

23 }
Codigos/buttonDebounceC.c
El código completo quedarı́a como:
1 enum EstadoBoton {EST PULSADO, EST SUELTO, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4 unsigned long int i n s t a n t e S o l t a d o =0, i n s t a n t e P u l s a d o =0;
5 unsigned int tiempoParpadeo = 5 0 0 ;
6
7 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) ;
8 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) ;
9 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l
gestionPorTiempo ) ;
10
11 void s e t u p ( ) {
12 pinMode ( 2 , INPUT PULLUP) ;
13 pinMode (LED BUILTIN , OUTPUT) ;
14 S e r i a l . begin (9600) ;
15 }
16
17 void l o o p ( ) {
18 estadoMiBoton = g e s t i o n B o t o n ( d i g i t a l R e a d ( 2 ) , estadoMiBoton ) ;
19 switch ( estadoMiBoton ) {
20 case EST PULSADO :
21 break ;
22 case EST SUELTO :
23 break ;
24 case TRANS PULSANDO:
25 instantePulsado = m i l l i s () ;
26 break ;
27 case TRANS SOLTANDO:
28 instanteSoltado = m i l l i s () ;
29 tiempoParpadeo = i n s t a n t e S o l t a d o −i n s t a n t e P u l s a d o ;
30 break ;
31 }
32 parpadeo (LED BUILTIN , tiempoParpadeo , tiempoParpadeo ) ;
33 }
34
35 // −−−−−−−−−−−−−−− D e c l a r a c i o n de f u n c i o n e s −−−−−−−−−−−−−−−
36
37 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) {
38 lecturaActual = antirebote ( lecturaActual , 5 , f a l s e ) ;
39 i f ( e s t a d o A n t e r i o r == EST SUELTO | | e s t a d o A n t e r i o r == TRANS SOLTANDO) {
40 i f ( l e c t u r a A c t u a l==LOW) {
41 return TRANS PULSANDO;
42 }
43 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
44 return EST SUELTO ;
45 }
46 }
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

47 e l s e i f ( e s t a d o A n t e r i o r == EST PULSADO | | e s t a d o A n t e r i o r == TRANS PULSANDO) {


48 i f ( l e c t u r a A c t u a l==LOW) {
49 return EST PULSADO ;
50 }
51 e l s e i f ( l e c t u r a A c t u a l==HIGH) {
52 return TRANS SOLTANDO;
53 }
54 }
55 }
56
57
58 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l
gestionPorTiempo ) {
59 s t a t i c b o o l u l t i m a L e c t u r a V a l i d a = HIGH ;
60 s t a t i c b o o l l e c t u r a A n t e r i o r = HIGH ;
61 s t a t i c unsigned int l e c t u r a s C o n s e c u t i v a s =0;
62 s t a t i c unsigned long int instanteUltimoCambio ;
63 unsigned long int tiempoActual=m i l l i s ( ) ;
64
65 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
66 i f ( ( gestionPorTiempo && ( l e c t u r a s C o n s e c u t i v a s >= valorParaCambio ) ) ||
67 ( ! gestionPorTiempo && ( tiempoActual − instanteUltimoCambio >=
valorParaCambio ) ) ) {
68 ultimaLecturaValida = lecturaActual ;
69 }
70 else {
71 l e c t u r a s C o n s e c u t i v a s=l e c t u r a s C o n s e c u t i v a s +1;
72 }
73 }
74 else {
75 lecturasConsecutivas = 0;
76 instanteUltimoCambio = tiempoActual ;
77 }
78 lecturaAnterior = lecturaActual ;
79 return u l t i m a L e c t u r a V a l i d a ;
80 }
Codigos/button8Simp.c

Actividad 4 Opcional
Proteger los parámetros formales de las funciones para que solo puedan tomar valores adecuados.
Optimizar el código en la medida de lo posible (la prioridad sigue siendo siempre la claridad en el código).
Comentar de forma adecuada.

Solución:
En primer lugar protegemos los parámetros formales de las funciones.
• Los parámetros de tipo bool no hace falta protegerlos porque solo pueden tomar los dos valores válidos.
• El parámetro valorParaCambio de tipo int se acota a unos valores razonable según si se actua en modo
de tiempo o de repeticiones.
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

• El parámetro estadoAnterior de tipo EstadoBoton debe ser uno de los cuatro casos esperados, sino se
convierte a un estado conocido, EST SUELTO por ejemplo. En este caso podrı́a ser razonable elevar un
error.

Las mejoras de código más evidentes que podemos realizar son:

• Podemos asignar valores a EST SUELTO y EST PULSADO y emplearlos también para las lecturas del botón.
De esta forma si el botón cambia de lógica, solo serı́a necesario cambiarlo en la declaración del tipo
enumerado.

• Algunas comprobaciones en los else de la función gestionBoton son innecesarias, ya que la lectura del
botón no cambia dentro de la función y solo puede ser cierta o falsa. Por lo tanto se suprimen dos if que
habia en los else.

• Empleamos una constante de programa para almacenar el pin del botón: PIN BOTON.

• Eliminamos los casos del switch-case no empleados.

• La variable instanteSoltado es innecesaria, ya que solo se usa una vez y se puede sustituir directamente
por millis().

• La sentencia lecturasConsecutivas = lecturasConsecutivas + 1; se simplifica a lecturasConsecutivas++;

1 enum EstadoBoton {EST PULSADO = LOW, EST SUELTO = HIGH, TRANS PULSANDO, TRANS SOLTANDO} ;
2
3 EstadoBoton estadoMiBoton = EST SUELTO ;
4 unsigned long int i n s t a n t e P u l s a d o =0;
5 unsigned int tiempoParpadeo = 5 0 0 ;
6 const int PIN BOTON = 2 ;
7
8 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) ;
9 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) ;
10 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l
gestionPorTiempo ) ;
11
12 void s e t u p ( ) {
13 pinMode (PIN BOTON, INPUT PULLUP) ;
14 pinMode (LED BUILTIN , OUTPUT) ;
15 S e r i a l . begin (9600) ;
16 }
17
18 void l o o p ( ) {
19 estadoMiBoton = g e s t i o n B o t o n ( d i g i t a l R e a d (PIN BOTON) , estadoMiBoton ) ;
20 switch ( estadoMiBoton ) {
21 case TRANS PULSANDO:
22 instantePulsado = m i l l i s () ;
23 break ;
24 case TRANS SOLTANDO:
25 tiempoParpadeo = m i l l i s ( )−i n s t a n t e P u l s a d o ;
26 break ;
27 }
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

28 parpadeo (LED BUILTIN , tiempoParpadeo , tiempoParpadeo ) ;


29 }
30
31 // −−−−−−−−−−−−−−− D e c l a r a c i o n de f u n c i o n e s −−−−−−−−−−−−−−−
32
33 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) {
34 i f ( e s t a d o A n t e r i o r != EST SUELTO && e s t a d o A n t e r i o r != TRANS SOLTANDO &&
35 e s t a d o A n t e r i o r != EST PULSADO && e s t a d o A n t e r i o r != TRANS PULSANDO) {
36 e s t a d o A n t e r i o r = EST SUELTO ;
37 }
38 lecturaActual = antirebote ( lecturaActual , 3 , f a l s e ) ;
39 i f ( e s t a d o A n t e r i o r == EST SUELTO | | e s t a d o A n t e r i o r == TRANS SOLTANDO) {
40 i f ( l e c t u r a A c t u a l==EST PULSADO) {
41 return TRANS PULSANDO;
42 }
43 else {
44 return EST SUELTO ;
45 }
46 }
47 e l s e i f ( e s t a d o A n t e r i o r == EST PULSADO | | e s t a d o A n t e r i o r == TRANS PULSANDO) {
48 i f ( l e c t u r a A c t u a l==EST PULSADO) {
49 return EST PULSADO ;
50 }
51 else {
52 return TRANS SOLTANDO;
53 }
54 }
55 }
56
57
58 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l
gestionPorTiempo ) {
59 s t a t i c b o o l u l t i m a L e c t u r a V a l i d a = HIGH ;
60 s t a t i c b o o l l e c t u r a A n t e r i o r = HIGH ;
61 s t a t i c unsigned int l e c t u r a s C o n s e c u t i v a s =0;
62 s t a t i c unsigned long int instanteUltimoCambio ;
63 unsigned long int tiempoActual=m i l l i s ( ) ;
64
65 i f ( gestionPorTiempo && valorParaCambio > 5 0 0 ) valorParaCambio = 5 0 0 ;
66 i f ( ! gestionPorTiempo && valorParaCambio > 5 0 ) valorParaCambio = 5 0 ;
67
68 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) {
69 i f ( ( gestionPorTiempo && ( l e c t u r a s C o n s e c u t i v a s >= valorParaCambio ) ) ||
70 ( ! gestionPorTiempo && ( tiempoActual − instanteUltimoCambio >=
valorParaCambio ) ) ) {
71 ultimaLecturaValida = lecturaActual ;
72 }
73 e l s e i f ( ! gestionPorTiempo ) {
74 l e c t u r a s C o n s e c u t i v a s ++;
75 }
76 }
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

77 else {
78 lecturasConsecutivas = 0;
79 instanteUltimoCambio = tiempoActual ;
80 }
81 lecturaAnterior = lecturaActual ;
82 return u l t i m a L e c t u r a V a l i d a ;
83 }
Codigos/button9Simp.c
Las siguientes modificaciones son más complejas y se basan en conceptos o estructuras que
aún no hemos visto en clase.
En la función de gestión del botón, las estructuras if-else se emplean para retornas un valor u otro. En estos
casos, o cuando lo que se hace es dar un valor a una variable, y es la única sentencia dentro de los bloques, se
puede sustituir por el operador ?. Este funciona de las siguientes formas:
variable = condición ? valorSiCierto : valorSiFalso;
return condición ? valorSiCierto : valorSiFalso;

Hay que tener cuidado con este operador porque puede hacer menos claro el código, pero a la vez es capaz
de compactarlo bastante. En este caso se reduce toda la lógica de la función gestionBoton a:
if(estadoAnterior == EST SUELTO || estadoAnterior == TRANS SOLTANDO)
return lecturaActual==EST PULSADO ? TRANS PULSANDO : EST SUELTO;
else if(estadoAnterior == EST PULSADO || estadoAnterior == TRANS PULSANDO)
return lecturaActual==EST PULSADO ? EST PULSADO : TRANS SOLTANDO;

Empleando este mismo operador podemos calcular un único valor para la función de antirrebote que se
emplea para comparar tanto en el caso por tiempos como en el caso por repeticiones. Esto simplifica mucho la
condición posterior. El valor se calcula como:
valorAntirebote = gestionPorTiempo ? tiempoActual - instanteUltimoCambio : lecturasConsecutivas;
Y la condición queda como:
if (valorAntirebote >= valorParaCambio)
Tambien podemos dar un valor por defecto al modo de funcionamiento del antirebote, esto se logra asig-
nandole un valor en el prototipo de la función. De esta forma se puede omitir este parámetro en la llamada a
la función. Es decir, al emplear el siguiente prototipo:
bool antirebote(bool lecturaActual, unsigned int valorParaCambio, bool gestionPorTiempo = true);
Las llamadas a la función: antirebote(lecturaActual, 10) y antirebote(lecturaActual, 10, true) son
equivalentes.
Además, todas las funciones en las que empleemos una misma variable como entrada y salida, se deberı́an
optimizar mediante el uso de punteros, empleando paso por referencia. Sin embargo esto lo dejamos por ahora
sin optimizar.

Finalmente comentamos el código:


Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

1 // −−−−− Tipos de v a r i a b l e s c r e a d o s −−−−−


2 enum EstadoBoton {EST PULSADO = LOW, EST SUELTO = HIGH, TRANS PULSANDO, TRANS SOLTANDO} ;
3
4 // −−−−− V a r i a b l e s g l o b a l e s −−−−−
5 // V a r i a b l e s r e l a c i o n a d a s con e l b o t o n
6 EstadoBoton estadoMiBoton = EST SUELTO ;
7 unsigned long int i n s t a n t e P u l s a d o =0;
8 const int PIN BOTON = 2 ;
9 // V a r i a b l e s r e l a c i o n a d a s con e l LED
10 unsigned int tiempoParpadeo = 5 0 0 ;
11
12 // −−−−− P r o t o t i p o s de f u n c i o n e s −−−−−
13 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) ;
14 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l gestionPorTiempo
= true ) ;
15 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) ;
16
17 // −−−−−−−−−−−−−−−−−−−−−−−−−−−−−− main −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
18 // −−−−− C o n f i g u r a c i o n e s −−−−−
19 void s e t u p ( ) {
20 pinMode (PIN BOTON, INPUT PULLUP) ;
21 pinMode (LED BUILTIN , OUTPUT) ;
22 S e r i a l . begin (9600) ;
23 }
24
25 void l o o p ( ) {
26 // −−−−− Entradas d e l programa −−−−−
27 estadoMiBoton = g e s t i o n B o t o n ( d i g i t a l R e a d (PIN BOTON) , estadoMiBoton ) ; // Estado d e l
boton
28 // −−−−− G e s t i o n de l a l o g i c a −−−−−
29 switch ( estadoMiBoton ) { // L o g i c a segun e l b o t o n
30 case TRANS PULSANDO: // Al p u l s a r c a p t u r a e l tiempo
31 instantePulsado = m i l l i s () ;
32 break ;
33 case TRANS SOLTANDO: // Al s o l t a r c a l c u l a e l tiempo de parpadeo
34 tiempoParpadeo = m i l l i s ( )−i n s t a n t e P u l s a d o ;
35 break ;
36 }
37 // −−−−− Actuacion / S a l i d a s −−−−−
38 parpadeo (LED BUILTIN , tiempoParpadeo , tiempoParpadeo ) ; // Parpadeo s i m e t r i c o
39 }
40
41 // −−−−−−−−−−−−−−−−−−−−−−−−−−−−−− D e c l a r a c i o n de f u n c i o n e s
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
42 /∗ −−−−−−−− Funcion que d e t e c t a l o s cambios de e s t a d o de un b o t o n −−−−−−−−
43 Entradas :
44 lecturaActual : bool
45 Valor a c t u a l de l a l e c t u r a d i g i t a l d e l b o t o n
46 e s t a d o A n t e r i o r : EstadoBoton
47 Estado a n t e r i o r d e l b o t o n
48 S a l i d a : t i p o EstadoBoton
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

49 EST PULSADO −> El boton lleva p u l s a d o mas de un c i c l o


50 EST SUELTO −> El boton lleva s u e l t o mas de un c i c l o
51 TRANS PULSANDO −> El boton s e ha pulsado este c i c l o
52 TRANS SOLTANDO −> El boton s e ha soltado este ciclo
53 ∗/
54 EstadoBoton g e s t i o n B o t o n ( b o o l l e c t u r a A c t u a l , EstadoBoton e s t a d o A n t e r i o r ) {
55 // C o n d i c i o n e s que a s e g u r a n r a n g o s adecuados de l o s par am etr os f o r m a l e s
56 i f ( e s t a d o A n t e r i o r != EST SUELTO && e s t a d o A n t e r i o r != TRANS SOLTANDO &&
57 e s t a d o A n t e r i o r != EST PULSADO && e s t a d o A n t e r i o r != TRANS PULSANDO)
58 e s t a d o A n t e r i o r = EST SUELTO ;
59 // F i l t r a m o s l o s d a t o s de e n t r a d a f r e n t e a r e b o t e s : 10ms
60 l e c t u r a A c t u a l = a n t i r e b o t e ( lecturaActual , 10) ;
61
62 // −−−L o g i c a de e s t a d o s d e l boton−−−
63 // S i e s t a b a s u e l t o o s o l t a n d o s e
64 i f ( e s t a d o A n t e r i o r == EST SUELTO | | e s t a d o A n t e r i o r == TRANS SOLTANDO)
65 // S i ahora e s t a p u l s a d o e s t r a n s i c i o n −pul sand o , s i n o e s s u e l t o −e s t a b l e
66 return l e c t u r a A c t u a l==EST PULSADO ? TRANS PULSANDO : EST SUELTO ;
67 // S i e s t a b a p u l s a d o o p u l s a n d o s e :
68 e l s e i f ( e s t a d o A n t e r i o r == EST PULSADO | | e s t a d o A n t e r i o r == TRANS PULSANDO)
69 // S i ahora e s t a p u l s a d o e s p u l s a d o −e s t a b l e , s i n o e s t r a n s i c i o n −s o l t a n d o
70 return l e c t u r a A c t u a l==EST PULSADO ? EST PULSADO : TRANS SOLTANDO;
71 }
72
73 /∗ −−−−−−−− Funcion que g e s t i o n a e l a n t i r e b o t e de una v a r i a b l e b o o l e a n a −−−−−−−−
74 Entradas :
75 lecturaActual : bool
76 Valor a c t u a l de l a v a r i a b l e
77 valorParaCambio : u n s i g n e d i n t
78 Valor n e c e s a r i o para s u p e r a r e l f i l t r o v a l o r A n t i r e b o t e
79 S i gestionPorTiempo e s c i e r t o , e s tiempo en ms
80 S i gestionPorTiempo e s f a l s o , e s numero de r e p e t i c i o n e s
81 gestionPorTiempo : b o o l , por d e f e c t o e s t r u e
82 Salida : bool
83 Valor s i n r e b o t e de l a v a r i a b l e
84 ∗/
85 b o o l a n t i r e b o t e ( b o o l l e c t u r a A c t u a l , unsigned int valorParaCambio , b o o l
gestionPorTiempo ) {
86 s t a t i c b o o l u l t i m a L e c t u r a V a l i d a = HIGH ; // Almacena e l u l t i m o v a l o r v a l i d o
87 s t a t i c b o o l l e c t u r a A n t e r i o r = HIGH ; // Almacena e l v a l o r a n t e r i o r para comparar
88 s t a t i c unsigned int l e c t u r a s C o n s e c u t i v a s =0; // Almacena e l numero de d a t o s i g u a l e s
consecutivos
89 s t a t i c unsigned long int instanteUltimoCambio ; // Almacena e l i n s t a n t e d e l u l t i m o
cambio
90 unsigned long int tiempoActual=m i l l i s ( ) ;
91 // O b t i e n e e l v a l o r de comparacion adecuado segun e l modo ( tiempo o r e p e t i c i o n e s )
92 unsigned long int v a l o r A n t i r e b o t e = gestionPorTiempo ? tiempoActual −
instanteUltimoCambio : l e c t u r a s C o n s e c u t i v a s ;
93
94 // C o n d i c i o n e s que a s e g u r a n r a n g o s adecuados de l o s par am etr os f o r m a l e s
95 i f ( gestionPorTiempo && valorParaCambio > 5 0 0 ) valorParaCambio = 5 0 0 ;
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

96 i f ( ! gestionPorTiempo && valorParaCambio > 5 0 ) valorParaCambio = 5 0 ;


97
98 i f ( l e c t u r a A c t u a l == l e c t u r a A n t e r i o r ) { // S i no hay cambio
99 // S i s e ha s u p e r a d o e l umbral d e l f i l t r o , s e c o n s i d e r a e l v a l o r como adecuado
100 i f ( v a l o r A n t i r e b o t e >= valorParaCambio ) u l t i m a L e c t u r a V a l i d a = l e c t u r a A c t u a l ;
101 // Sino , s e aumenta e l c o n t a d o r de l e c t u r a s c o n s e c u t i v a s i g u a l e s ( s o l o modo por
repeticiones )
102 e l s e i f ( ! gestionPorTiempo ) l e c t u r a s C o n s e c u t i v a s ++;
103 }
104 e l s e { // S i l a s u l t i m a s l e c t u r a s son d i s t i n t a s , r e i n i c i a e l c o n t a d o r y e l tiempo
105 lecturasConsecutivas = 0;
106 instanteUltimoCambio = tiempoActual ;
107 }
108 l e c t u r a A n t e r i o r = l e c t u r a A c t u a l ; // Almacena l a l e c t u r a a n t e r i o r
109 return u l t i m a L e c t u r a V a l i d a ; // D e v u e l v e e l v a l o r adecuado
110 }
111
112 /∗ −−−−−−−− Funcion de parpadeo que a c t u a s o b r e e l p i n d e s e a d o −−−−−−−−
113 Entradas :
114 pinLed : i n t
115 p i n en e l que e s t a e l l e d a p a r p a d e a r
116 l i m i t a d o e n t r e 0 y 19
117 tiempoEncendido : i n t
118 tiempo que d e b e e s t a r e n c e n d i d o por c i c l o en m i l i s e g u n d o s
119 l i m i t a d o e n t r e 1 0 [ ms ] y 2 0 0 0 0 [ms ]
120 tiempoApagado : i n t
121 tiempo que d e b e e s t a r apagado por c i c l o en m i l i s e g u n d o s
122 l i m i t a d o e n t r e 1 0 [ ms ] y 2 0 0 0 0 [ms ]
123 Salida : bool
124 0 −> E j e c u c i o n s i n e r r o r e s
125 1 −> Pin de parpadeo no v a l i d o
126 ∗/
127 b o o l parpadeo ( u i n t 8 t pinLed , unsigned int tiempoEncendido , unsigned int tiempoApagado ) {
128 s t a t i c unsigned long int instanteUltimoCambio =0; // Almacena e l u l t i m o e n c e n d i d o o
apagado
129 unsigned long int tiempoActual = m i l l i s ( ) ; // G e s t i o n d e l tiempo a c t u a l
130 unsigned long int tiempoDesdeUltimoCambio = tiempoActual − instanteUltimoCambio ; //
C a l c u l a d e l tiempo que ha pasado d e s d e e l u l t i m o cambio de e s t a d o
131 b o o l e s t a d o P i n = d i g i t a l R e a d ( pinLed ) ; // G e s t i o n d e l e s t a d o i n i c i a l d e l p i n
132
133 // C o n d i c i o n e s que a s e g u r a n r a n g o s adecuados de l o s par am etr os f o r m a l e s
134 i f ( pinLed >19) return 1 ;
135 i f ( tiempoEncendido <10) tiempoEncendido =10;
136 i f ( tiempoApagado <10) tiempoApagado =10;
137 i f ( tiempoEncendido >20000) tiempoEncendido =20000;
138 i f ( tiempoApagado >20000) tiempoApagado =20000;
139
140 // S i e l p i n l l e v a e n c e n d i d o ( o apagado ) mas tiempo d e l d e s e a d o . . .
141 i f ( ( e s t a d o P i n == HIGH && tiempoDesdeUltimoCambio > tiempoEncendido )
142 | | ( e s t a d o P i n == LOW && tiempoDesdeUltimoCambio > tiempoApagado ) ) {
143 d i g i t a l W r i t e ( pinLed , ! e s t a d o P i n ) ; // Se cambia e l e s t a d o d e l p i n
Grado en Ingenierı́a de Datos
Sistemas Electrónicos Programables (29511) 1 de Febrero de 2024

144 instanteUltimoCambio = tiempoActual ; // Se guarda e l i n s t a n t e d e l cambio


145 }
146 return 0 ; // Retorno s i n e r r o r e s
147 }
Codigos/button10Com.c

También podría gustarte