Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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
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 :
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.
Susituimos el tipo de la variable empleada para guardar el estado del botón, y le damos un valor incial, por
ejemplo 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
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:
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
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
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.
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
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:
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:
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
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.
• 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.
• La variable instanteSoltado es innecesaria, ya que solo se usa una vez y se puede sustituir directamente
por millis().
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
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.