Vous êtes sur la page 1sur 131

http://coffeebrain.org/wikicontrol/index.php?

title=(1620-IELE3330-
TC)_Proyecto:_Pendulo_Invertido_Grupo_D
(1620-IELE3330-TC) Proyecto: Pendulo Invertido Grupo D
From CoffeeBrain-WikiControl
Contents

1Introduccin

2Descripcin del Proyecto

3Variables a controlar

4Controladores

5Modelo del sistema

o 5.1Modelo Matemtico

5.1.1Linealizacin

5.1.2Representacin en espacio de estados

5.1.3Funcin de transferencia

o 5.2Modelo en SimScape

6Diseo de controladores

o 6.1PID

6.1.1Sintonizacin

o 6.2Interaccin con el modelo en SimScape

o 6.3Fuzzy Logic

7Estado final del proyecto

8Implementacin
9Funcionamiento Inicial del montaje

10Protocolo de Pruebas

11Problemas en la implementacin

12Resultados

o 12.1PID

o 12.2Fuzzy Logic

13Videos de funcionamiento

o 13.1PID

o 13.2Fuzzy Logic

14Conclusiones y comparacin de resultados

15Referencias

Introduccin
En el siguiente documento se presenta la implementacin y diseo a
nivel fsico de un pndulo invertido. En este se va a especificar el
estado de las actividades con respecto al cronograma presentado al
inicio del proyecto, el estado actual de la implementacin y la
asignacin de roles para concluir la implementacin en el plazo
establecido.

Descripcin del Proyecto


El pndulo invertido es un servomecanismo formado por un carro, un
pndulo que se monta sobre este y puede girar libremente, y un riel
por el que se desplaza el carro. La meta es poder controlar tanto la
posicin del carro que lleva al pndulo, como el ngulo de este para
mantenerlo en su punto de equilibrio inestable. La posicin del carro
se sensa por medio de un encoder incremental que est acoplado a un
pin; y, el ngulo del pndulo es sensado con otro encoder ptico
incremental [2].

El objetivo de este proyecto es entonces llevar a cabo un algoritmo de


control de un sistema real (como un Segway, un cuadricptero,
vehculos mviles, etc.), de tal manera que se modifique la dinmica
del sistema para que, en la posicin vertical del pndulo, arriba, se
tenga un punto de equilibrio estable; es decir, hallar la fuerza que se
debe aplicar al carro para que el pndulo no se caiga, aun si hay
perturbaciones en el sistema. [3]

Fig 1. Modelo del pndulo invertido [3].


Variables a controlar
En la siguiente figura se puede ver el diagrama de bloques del sistema
y las variables que lo componen,

Fig 2. Diagrama de bloques [3].


: ngulo con respecto al eje.

: Longitud.

: Masa.

: posicin de la masa a lo largo del eje.

: posicin del carro con respecto al origen.

Para el modelo, las variables de estado, es decir, las que van a ser
controladas van a ser la posicin del carro , y el ngulo del pndulo
con respecto al eje , para poder mantener la estabilidad de este.

Controladores
Teniendo en cuenta el objetivo del proyecto, para lograr la precisa
estabilizacin del pndulo invertido, es necesario aplicar un
controlador que sea capaz de actuar con rapidez y llevar al pndulo a
su punto de equilibrio de la manera ms eficiente posible,
independientemente de las perturbaciones que puedan afectar al
sistema. Para esto, se trabajar un controlador PID, un LQR y
finalmente un rechazo de perturbaciones que en conjunto permitirn
un control optimo del sistema.

Inicialmente, se trabajar con un controlador PID debido a su


importancia a nivel general en la industria y por el conocimiento previo
que se tiene del mismo. En general, el diagrama descriptivo de un PID
se caracteriza de la siguiente manera:

Fig 3. Controlador PID [4].


El diseo de este controlador es simple, debido a que se considera un
lazo de control con una entrada y una salida. Sin embargo, la
estructura sencilla del PID puede no cumplir con todas las
consideraciones necesarias para lograr la estabilidad total del sistema.
Dado esto, se implementar a la par un regulador LQR para verificar si
puede generar una respuesta ms ptima sobre este.

Ahora bien, un controlador LQR, se caracteriza por establecer una


retroalimentacin completa de todo el sistema mediante una relacin
entre las variables de estado que lo caracterizan. Para lograr un
algoritmo completo de control, utilizando la estrategia LQR, es
necesario implementar un observador dentro del sistema, puesto que
el LQR no mide completamente el estado del pndulo.

Teniendo en cuenta lo anterior, el modelo final caracterstico del LQR


estara especificado como se muestra en el siguiente diagrama:

Fig 4. Controlador LQR [4].


Es importante mencionar que, para acercarse de manera ms concreta
al objetivo presentado, es necesario implementar en ambas
estrategias de control un rechazo de perturbaciones. El ideal es lograr
que, a pesar de cualquier perturbacin que pueda presentarse, el
sistema no pierda su punto de equilibrio y no se desestabilice. .

Modelo del sistema


Con el fin de encontrar el modelo que describe el sistema, es
fundamental llevar a cabo un anlisis de fuerzas tanto del pndulo,
como del carro; sin embargo, en dicho anlisis se obtienen senos y
cosenos del ngulo del pndulo cuando no es estable, por lo que se va
a obtener un modelo no lineal.

Teniendo esto en consideracin, el modelo matemtico no solo


consiste en obtener las ecuaciones que describen al sistema, sino en
una linealizacin de estas para, posteriormente, obtener una
representacin del sistema en espacio de estados, as como una
funcin de transferencia para la posicin del carro, y el ngulo del
pndulo; pues son estas dos variables las que se van a controlar.

Despus de tener el modelo matemtico, se va a simular el


comportamiento fsico de este sistema dinmico que evoluciona con el
tiempo. Para esto, se va a utilizar la herramienta de MATLAB
SimScape, que a partir de un modelo CAD del sistema, genera un
diagrama de bloques con las uniones y restricciones.

El sistema se va a modelar con motores DC, y se va a observar la


respuesta para un voltaje de entrada sin ningn tipo de control.

A continuacin, se muestra el modelo CAD diseado en Autodesk


Inventor del pndulo invertido.

Fig 5. Modelo en Autodesk Inventor.


Modelo Matemtico
Para el modelo matemtico, se presenta el diagrama de fuerzas del
pndulo y del carro mvil en la Figura 6.

Fig 6. Diagrama de fuerzas del pndulo.


En donde, cada una de las variables representa:
En primer lugar, se analizan las fuerzas en el eje horizontal tanto para
el carro, como para el pndulo:

Igualando las ecuaciones anteriores, se tiene que:

La ecuacin anterior es la primera ecuacin dinmica del sistema. Para


encontrar otra, se van a analizar las fuerzas perpendiculares al
pndulo, as como las fuerzas de reaccin presentes en la unin de los
dos cuerpos.
Adicionalmente, es fundamental considerar el torque debido a las
fuerzas sobre la barra del pndulo:

Dividiendo esta ecuacin entre l (longitud del pndulo), y,


combinndola con la ecuacin anterior a esta, se tiene que:

Multiplicando toda la ecuacin por l, y organizando trminos, se tiene


que:

Con esto, se tienen las dos ecuaciones dinmicas del sistema, y


ninguna de estas es lineal, por lo que es necesario linealizarlas con el
fin de poder aplicar las dos estrategias de control propuestas.

Linealizacin
El control del pndulo invertido se lleva a cabo en el punto de
equilibrio superior, por lo que las ecuaciones se deben linealizar
utilizando el ngulo del pndulo respecto a la lnea vertical superior .
Este ngulo debe ser pequeo para que haya una estabilizacin del
sistema, por lo que:

Derivando se tiene que:


Y al querer que el ngulo sea tan pequeo, se tiene que:

Ahora bien, reemplazando estos valores en las ecuaciones dinmicas


del sistema, y considerando la fuerza F como la seal de control u, se
tienen las siguientes ecuaciones linealizadas del sistema, con un rango
de operacin de 0.

Representacin en espacio de estados


Teniendo entonces las dos ecuaciones en trmino de las variables de
estado y x, se va a modelar el sistema por medio de espacio de
estados, en donde se proponen matrices que renen la informacin de
las funciones determinsticas del sistema.

El sistema est dado por:

Funcin de transferencia
Teniendo este modelo, se proceder a encontrar la funcin de
transferencia, que dar la posibilidad, al igual que la representacin en
espacio de estados, de manipular el sistema y llevar a cabo el control
deseado sobre la posicin y el ngulo del pndulo.

Por un lado, la funcin de transferencia se halla sencillamente con las


ecuaciones encontradas en el modelo matemtico, estas se trabajaran
en el dominio de la frecuencia, por lo que se utilizara Laplace, y
posteriormente, se relacionarn la entrada y la salida para encontrar la
funcin de transferencia caracterstica de cada variable.

Considerando con lo anterior, el paso a seguir es transformar las


ecuaciones del dominio del tiempo al dominio de la frecuencia como
se muestra a continuacin:

Como se puede observar, se aplic la transformada de Laplace a las


dos ecuaciones. Ahora, debido a que son dos variables a controlar,
cada una de ellas estar representada por una funcin de
transferencia.

En trminos generales, el procedimiento fue despejar el valor X(s) de


la ecuacin con la variable a inters, y posteriormente, reemplazar
este valor en la segunda ecuacin para as hallar la relacin entrada-
salida correspondiente. Entonces, para la funcin de transferencia del
ngulo del pndulo se despej X(s), y como paso a proceder, se
reemplaz en la segunda ecuacin:

Finalmente, se obtuvo la funcin de transferencia que relacionaba el


ngulo de salida con la entrada.

Ahora, con la misma metodologa se determin la relacin de entrada


con respecto a la posicin del carro como parmetro de salida. La
ecuacin final encontrada fue:

Modelo en SimScape
Para llevar a cabo el modelo mecnico del sistema en SimScape, se
utiliz como referencia, en primer lugar, el modelo de MathWorks [5].

Teniendo esto en consideracin, se model el motor DC del sistema,


como lo muestra la Figura 7.

Fig 7. Modelo del motor DC en SimScape.

A continuacin, se export el modelo diseado en Autodesk Inventor a


MATLAB, y con este se obtuvo el modelo mecnico, con cada una de
las uniones del sistema, como se muestra en la Figura 8.

Fig 8. Modelo mecnico del pndulo.


La unin Prismtica da cuenta de la unin entre la base por la que se
desliza el sistema, y el carro del pndulo; y esta representa un grado
de libertad traslacional.

Por otro lado, la unin de revolucin da cuenta de la unin entre el


carro y el pndulo como tal, y representa un grado de libertad
rotacional.

El bloque de Simulink-PS Converter convierte la seal de entrada de


Simulink en una seal fsica. Este se utiliza para conectar fuentes de
Simulink a otros bloques de entrada a un sistema fsico.

Finalmente, en las dos uniones se aument el coeficiente de


amortiguamento con el fin de que el carro del pndulo no se
desplazara de manera indefinida.

Ahora bien, el sistema mecnico mostrado anteriormente se form


como un subsistema, al que se le aplic una fuerza simulada por el
motor DC al aplicarle un voltaje, como se puede ver en la Figura 9.

Fig 9. Modelo final del sistema.


El sistema, arroj la posicin del carro y el ngulo del pndulo. A
continuacin se muestran los resultados de la simulacin, y el estado
final del pndulo despus de aplicada la fuerza (considerando que
comenz orientado hacia arriba), y sin ningn tipo de control.
Fig 10. Resultados simulacin sin controlador.
Se puede ver que tanto la posicin, como el ngulo del pndulo son
bastante inestables ante una fuerza aplicada. Por lo que es
fundamental implementar una estrategia de control para estas dos
variables con el fin de lograr estabilidad del pndulo.

Diseo de controladores
PID
Se van a implementar dos PID en Simulink para controlar la posicin
del carro y el ngulo del pndulo, teniendo en consideracin que es el
controlador ms utilizado en la industria y que por su flexibilidad, se
acopla a diferentes tipos de problemas.

En primer lugar, se consider el modelo para un motor DC como lo


muestra la Figura 11, que tuviera como salidas torque y posicin
angular.
Fig 11. Diagrama de bloques motor DC [6].
Seguido a esto, se utilizaron los siguientes parmetros para el modelo
del motor:

Fig 12. Parmetros del motor DC [7].


Donde,

Ra es la resistencia de armadura, La es la inductancia de armadura,


Kma es la constante mecnica, Kb es la constante elctrica, c es el
coeficiente de friccin, y J es el momento de inercia.

El torque del motor DC se defini como una entrada en la unin de la


base del sistema y el carro, como se muestra en la Figura 13.
Fig 13. Modelo mecnico en SimScape.
Ahora bien, el modelo mecnico de SimScape, junto con el motor, se
implement como un subsistema, que tiene como entrada una seal
que representa una perturbacin o fuerza, y los dos controladores se
aplican al motor DC, como se puede ver en la figura 14.

Fig 14. Sistema implementado en MATLAB.


Hay un PID para la posicin del carro y otro para el ngulo del pndulo,
donde, el primero tiene como referencia una posicin de -0.001 m, y el
segundo tiene como referencia un ngulo de 0.

Sintonizacin
Dado que para este sistema se busc controlar el ngulo del pndulo y
la posicin del carro que lo lleva, se obtuvieron las funciones de
transferencia respectivas, con los siguientes parmetros:
Masa del carro: 0.023 kg

Masa del pndulo: 0.146 kg

Coeficiente de friccin del carro (b): 0.1 N/m/s

Momento de inercia de la masa del pndulo (I): 0.006 kg*m^2

Gravedad (g): 9.8 m/s

Longitud del pndulo (l): 0.3 m

Con estos valores, se obtuvieron las funciones de transferencia del


sistema:

Ahora bien, con el fin de poder sintonizar los PID, se verific en primer
lugar, que el sistema fuera controlable. Para esto, se encontraron las
matrices de representacin de estados, como se muestra a
continuacin:

Y los polos del sistema son:


Dado que hay polos en el semiplano derecho, el sistema es inestable,
por lo que es fundamental controlarlo, as que para esto se va a definir
la matriz de controlabilidad.

Donde, si el rango es igual a p, se puede afirmar que el sistema es


controlable.

En este caso, la matriz de controlabilidad es la siguiente:

El rango de la matriz debe ser 4, y con la funcin rank() de MATLAB se


verific esto.

Fig 15. Controlabilidad del sistema.


Teniendo entonces que el sistema es controlable, se procedi a llevar a
cabo la sintonizacin de los PID por el mtodo de Ziegler-Nichols; sin
embargo, no se lleg a una solucin que permitiera una
implementacin fsica del sistema. Esto debido a que la dinmica del
ngulo del pndulo y la posicin del carro estn relacionadas, por lo
que un cambio en algn parmetro del controlador afecta tanto a la
dinmica del ngulo, como a la dinmica del carro, lo cual hace el
proceso de sintonizacin bastante tedioso [8].

A pesar de esto, existe otro mtodo de ajuste de Ziegler-Nichols, que


se utiliza para sistemas que pueden tener oscilaciones sostenidas.
Consiste en eliminar, en primer lugar, los efectos de la parte integral y
derivativa, y se modifica la ganancia proporcional hasta que el sistema
tenga oscilaciones sostenidas. La ganancia con la que se logre esto
ser la ganancia crtica Kcr que va a corresponder a un periodo crtico
Pcr [9].

Fig 16. Oscilaciones sostenidas [9].

Cuadro 1. Valores de sintonizacin para el mtodo de oscilaciones


sostenidas (Z/N) [8].
En primer lugar, se busc sintonizar el PID del ngulo del pndulo, por
lo que se estableci Kp=1, y Ki, Kd=0. La respuesta de la planta se
muestra en la Figura 17:
Fig 17. Respuesta del sistema con Kp=1, Ki=0, Kd=0.
El Kcr se defini entonces como 1, y Pcr=2.888s-1.951s=0.937s.

De esta manera, los parmetros del PID fueron los siguientes:

Kp=0.6*1=0.6

Ti=0.5*0.937=0.4685

Td=0.125*0.937=0.1171

Donde,

Ahora bien, para el control de la posicin del carro no fue posible hallar
un valor Kp tal que se obtuvieran oscilaciones sostenidas en la salida.
Por lo cual, se sintoniz el PID por prueba y error. Tras bastantes
iteraciones, se obtuvo que era necesario una ganancia proporcional
negativa de -35 por la direccin de los ejes en el modelo en inventor, y
manteniendo Ki y Kd en cero, se obtuvo la siguiente respuesta:
Fig 18. Respuesta del sistema con Kp=-35, Ki=1, Kd=1.
Se puede ver que la respuesta an no es estable, por lo que se
modificaron los valores de las otras dos acciones de la siguiente
manera: Ki=-13 y K_d=0.15, para lo cual se obtuvo que:

Fig 19. Control de la posicin del carro.


Donde, a pesar de que las constantes son negativas, se establece el
sistema para una posicin dada.

Con el PID del ngulo del pndulo ya sintonizado, se procedi a


verificar la respuesta de este ante una serie de perturbaciones, dadas
por la seal que se muestra en la Figura 20.
Fig 20. Seal de perturbacin.
Para la cual, la respuesta fue la siguiente:

Fig 21. Respuesta del ngulo del pndulo ante perturbaciones.


Se puede ver que el ngulo vara para cada perturbacin hasta en 10;
sin embargo, esta no es una variacin muy alta, y el pndulo vuelve a
su posicin de referencia (0) en cada ocasin; por lo que, el mtodo
de sintonizacin fue adecuado para este controlador.

Y para la posicin del carro, la respuesta fue la siguiente:


Fig 22. Respuesta de la posicin del carro del pndulo ante
perturbaciones.
Interaccin con el modelo en SimScape
Para simular el PID con los archivos de SimScape es necesario seguir
estos pasos:

Descargar: File:Pendulo PID.zip Modelo SimScape

Guarde estos archivos en una misma carpeta y siga las


instrucciones del video 1.

Video 1. Interaccin con el modelo en SimScape


Fuzzy Logic
Para implementar el controlador con lgica difusa, se defini una
entrada y una salida, la entrada corresponde al ngulo del pndulo y
tiene 13 reglas, por diferentes regiones del sistema. Dependiendo de
la regin, la salida es un PWM ms alto o ms bajo. Entre ms lejos
est el ngulo de la referencia, mayor es el PWM, y entre ms cerca
est el ngulo de la referencia, el PWM es menor.
Fig 23. Definicin entradas de controlador Fuzzy

Fig 24. Definicin salidas de controlador Fuzzy


Las reglas se muestran a continuacin:

Fig 25. Definicin reglas de controlador Fuzzy


Y la regin de superficie estuvo dada por:
Fig 26. Superficie
Estado final del proyecto
Teniendo en consideracin el cronograma de trabajo planteado al inicio
del proyecto, se llevaron a cabo con xito las siguientes actividades:

o Estudio fsico del sistema

o Planteamiento del modelo matemtico del pndulo


invertido y representacin en espacio de estados.

o Implementacin del modelo en SimScape.

o Modelamiento, pruebas y anlisis del modelo


implementado.

o Diseo del primer controlador (PID) y sintonizacin.

o Simulacin en Simulink del sistema para mantener al


pndulo en su punto de equilibrio inestable.

o Especificacin de materiales y otros recursos para el


montaje.

o Implementacin del montaje

o Implementacin del PID para controlar el ngulo del


pndulo.

o Implementacin de Fuzzy Logic para controlar el ngulo


del pndulo.

Implementacin
Para empezar, se compraron los siguientes materiales para la
implementacin del sistema:

Compra de materiales.

Material Precio Dnde conseguirlo

Bloque de madera de
$15,000 Homecenter
2.5x8cm

$200,00 Novena: Hector Romero


Base del pndulo
0 3203940640

Pndulo (Tubo de PVC) $3,950 Homecenter

Arduino Mega 2560 $35,000 Novena: TNC

Novena: Hector Romero


Encoder (x2) $35,000
3203940640

Novena: Hector Romero


Motor DC 24V $20,000
3203940640

Puente H $15,000 Novena: TNC

$328,95
Total
0

Las siguientes Figuras muestran dichos materiales y el montaje final


de la estructura:
Fig 27. Bloque de madera.

Fig 28. Encoder.


Fig 29. Motor DC de 24V.

Fig 30. Montaje Final.


Fig 31. Montaje Final.

El bloque de madera se utiliz para el diseo del carro;


adicionalmente, este se uni con la correa, la cual por medio de un
engranaje se sujet al motor DC de 24V.

A continuacin, se encontraron las variables fsicas pertinentes tanto


del carro, como del pndulo:.

Variables fsicas del sistema.

Variable Magnitud

Masa del carro 540.2 g

Masa del pndulo 79.7 g

Longitud del pndulo 51.7 cm

Y luego, se encontraron los parmetros del motor [1].

En primer lugar, se encontr la resistencia de armadura Ra. Para esto,


se ajust un valor de voltaje mnimo para la alimentacin del motor,
de tal manera que se midiera la corriente de armadura Ia justo antes
de comenzar el movimiento del eje del motor, y con la ley de Ohm se
obtuvo la resistencia de armadura.

Ra=Va/Ia

Ra=1.5 V/0.33 A = 4.54 Ohm

A continuacin se encontr la inductancia de armadura L_a en mH,


midiendo en los devanados del motor utilizando un instrumento para
medir inductancias, conocido como LCR meter. Este valor fue: 0.0035
mH.

Para la constante electromotriz Ke se tuvo la siguiente ecuacin:

Ke=V-I*Ra V/rad*seg

En este caso, para diferentes valores de voltaje se encontr la


corriente, las revoluciones por minuto y la velocidad en rad/seg, con lo
cual se calcul K_e y se obtuvo un promedio, con el fin de obtener un
valor ms preciso. El siguiente Cuadro muestra este procedimiento.

Obtencin de la constante electromotriz.

Voltaje Corriente Velocidad Velocidad Ke


(V) (A) (RPM) (rad/seg) (V/rad*s)

2 0.13 252.9 26.49 0.053

3 0.14 430.8 45.11 0.052

4 0.14 594.6 62.27 0.054

5 0.15 751.1 78.66 0.0549

6 0.15 929.5 97.34 0.0546

7 0.16 1080 113.097 0.05546

8 0.16 1260 131.95 0.05511

9 0.18 1400 146.607 0.0558

10 0.18 1570 164.41 0.0558

11 0.18 1739 182.107 0.0559

12 0.17 1925 201.59 0.0559

13 0.17 2095 219.39 0.0557

14 0.19 2254 236.04 0.0556

15 0.19 2415 252.898 0.05589

En promedio, se obtuvo que: Ke=0.0550202.

Ahora bien, para encontrar la constante de par par Kt se utiliza una


tcnica conocida como paramtrico dimensional, la cual se basa en
utilizar expresiones que guardan una relacin paramtrica dimensional
directa entre Kt y Ke [1]. La relacin que se utiliz fue:

Kt (Nm/A)=Ke (V/rad*seg)

Por lo que, Kt=0.0550202.

Seguido a esto, se determin la constante de tiempo mecnica Tm,


conectando la sonda del osciloscopio al motor para su valor nominal
(24V). En la grfica dada por el osciloscopio, se midi el tiempo
requerido para que la seal de salida alcanzara el 63.2% de su valor
final, y ese tiempo correspondi a la constante de tiempo mecnica
Tm, el cual fue: Tm=0.0166 s.

Con esta constante, se calcul entonces el momento de inercia Jm, con


la siguiente ecuacin:

Jm=(Tm*Kt*Ke)/ Ra

Jm=(0.0166*0.05502^(2))/ 4.5454"" = 1.1055e-5 Kg-m^2

Finalmente, se encontr el coeficiente de friccin b con la ecuacin:

Tm=Kt*Ia=b*Omega + Tf

Siendo Tf=Kt*Ia=0.05502*4.5454=0.25 s

Por lo que, b=1.72e-5 Nms.

Finalmente, se implement el cdigo en Arduino para llevar a cabo la


lectura del Encoder del pndulo.
Fig 32. Cdigo en arduino para la lectura del encoder.
Funcionamiento Inicial del montaje
A continuacin se muestra un video del funcionamiento inicial del
pndulo: https://youtu.be/UIyzQkVWEFc

Las siguientes Figuras muestran el montaje y el correcto


funcionamiento del encoder:
Fig 33. Pndulo Invertido.

Fig 34. Funcionamiento Encoder.


Protocolo de Pruebas
En primer lugar se llev a cabo el ensamble del pndulo mostrado en
el manual de ensamble que se puede descargar aqu: File:Manual
pendulo.pdf

Segudi a esto, se realizaron las conexiones entre Arduino, el puente H


y el encoder de este, siguiendo el esquemtico de la Figura 35.
Fig 35. Diagrama de conexiones elctricas
Ahora bien, para la implementacin de ambos controladores se hizo
una conexin entre Arduino MEGA y Simulink en tiempo real, siguiendo
el procedimiento a continuacin:

Instalar el soporte para Arduino en el siguiente link: Soporte


Arduino.

En el paquete que se descarga para el Toolbox de Matlab hay un


carpeta llamada "pde", dentro de la cual se encuentran 5
carpetas, cada una con un cdigo de Arduino en formato ".pde".
Es necesario abrir este en un bloc de notas, y copiarlo y pegarlo
en la interfaz de Arduino. Las carpetas que se encuentran son:

o adio.pde : IO anlogo y digital, y comandos seriales


bsicos.

o adioe.pde : adio.pde + soporte para encoders.

o adioes.pde : adioe.pde + soporte para servo.

o motor v1.pde : adioes.pde + afmotor v1 shield.

o motor v2.pde : adioes.pde + afmotor v2 shield.

En este caso, el Arduino se va a programar con el archivo adioes.pde.

Para completar la conexin con Arduino desde Simulink se deben


implementar los bloques en Simulink: "Arduino IO Setup" y "Real-
Time Pacer". En el primero se debe fijar el puerto al que est
conectado Arduino, y en el segundo se debe fijar la relacin de
tiempo de la simulacin con el tiempo real (generalmente es 1).

Es importante mencionar que antes de ejecutar el programa en


Simulink es necesario siempre cargar el cdigo en arduino con MATLAB
cerrado y una vez est cargado, abrir MATLAB y correr el programa.

A continuacin se muestra el diagrama de bloques en Simulink:

Fig 36. Implementacin del PID en Simulink


Para encontrar las constantes del PID se observ la respuesta del
sistema ante una entrada escaln y se hall la funcin de
transferencia de este utilizando IDENT. Con esta, se hallaron unas
constantes aproximadas que se adecuaron correctamente al modelo.

La ganancia a la salida del controlador es para compensar la zona


muerta del motor y se encontr de manera experimental.

El bloque de polaridad da la direccin en la que debe moverse el motor


para mantener al pndulo estable, y la funcin realizada se muestra a
continuacin:
Fig 37. Cdigo para el Puente H en MATLAB.
Adicionalmente hay un switch manual para decidir qu controlador se
va a utilizar, si lgica difusa o PID.

Problemas en la implementacin
A la hora de implementar el sistema mecnico se presentaron
bastantes problemas. En primer lugar, se rompi el eje del motor por
lo que toc comprar uno nuevo.

Adicionalmente, se observ que controlar el pndulo con la longitud


que este tena era muy complicado, por lo cual se consigui uno nuevo
y se implement ms corto.

Cuando se iba a implementar el Swing Up se presentaron bastantes


problemas con la conmutacin entre los controladores y no fue posible
levantarlo en ningn momento. Por esta razn se tom la decisin de
controlar nicamente el ngulo del pndulo mediante PID y Fuzzy
Logic.

En un principio las constantes del PID hacan que el sistema oscilara


bastante, por lo que se decidi obtener la funcin de transferencia
experimental y obtener las constantes a partir de esta.

Resultados
PID
Con las constantes encontradas para el PID despus de obtener la
funcin de transferencia experimental del sistema, se obtuvo el
comportamiento mostrado en la Figura 34 para la respuesta del ngulo
del pndulo ante varias perturbaciones.

Fig 34. Respuesta del sistema ante perturbaciones con PID


Se puede ver que este siempre tiende a la referencia (0) a pesar de
las perturbaciones y tiene un tiempo de respuesta rpido.

Entre mayor sea la perturbacin, el sistema responde con mayor


velocidad y ms rpido para evitar que el pndulo se caiga.

Fuzzy Logic
Para el segundo controlador se implement fuzzy con una entrada y
una salida, se definieron 13 reglas y como mtodo de defuzzificacin
se utiliz Centroide. A contiacin se muestra la respuesta del sistema
ante perturbaciones.
Fig 35. Respuesta del sistema ante perturbaciones con Fuzzy
Videos de funcionamiento
PID
El siguiente video muestra un avance inicial del PID para mantener al
pndulo en su punto de equilibrio inestable:

Video 1. Funcionamiento pndulo invertido con PID


El siguiente vdeo muestra una implementacin del PID que resulta en
un mejor control sobre el ngulo de pndulo

Video 2. Funcionamiento pndulo invertido con PID


El siguiente vdeo muestra el montaje controlado con PID

Video 3. Funcionamiento pndulo invertido con PID


Fuzzy Logic
En el siguiente vdeo se muestra el comportamiento del sistema con la
estrategia de control Fuzzy,

Video 4. Funcionamiento pndulo invertido con Fuzzy


Conclusiones y comparacin de resultados
Al comparar los resultados obtenidos se puede determinar que el
modelo en Simulink no tiene en cuenta factores mecnicos, como la
elasticidad que tiene la correa dentada, que pueden llegar a ser
variables que evitan que la estrategia de control funcione
correctamente. Por tal razn las estrategias de control PID y Fuzzy no
se comportan de la misma forma en la implementacin fsica como en
la simulacin realizada en Simulink.

En la implementacin fsica la estrategia de control PID se comporta


muy similar que la simulacin realizada con dicha estrategia, sin
embargo, se presenta mayor oscilacin al intentar corregir el error en
estado estacionario cuando se presenta una perturbacin. La
implementacin fsica de la estrategia de control Fuzzy se comporto
mucho ms inestable con respecto a la simulacin realizada con la
estrategia en Simulink.

Teniendo en cuenta las grficas de la respuesta del sistema y los


vdeos de funcionamiento se puede ver que el sistema es ms estable
con PID que con Fuzzy, esto debido a que en Fuzzy falt implementar
otra entrada con el error del ngulo para darle una mayor correccin a
este.

El Swing Up no fue posible implementarlo, pero se tiene como trabajo


futuro al igual que mejorar la segunda estrategia de control.

A pesar de esto, se observ que ambos controladores se adecuaron


correctamente al sistema y fue posible entender y visualizar el
comportamiento del pndulo invertido.

Referencias

[1] F. B. Ortega. Modelado y control del pndulo invertido sobre


carro mediante sistemas hbridos. Disponible en: Escuela Tcnica
Superior de Ingenieros

[2] C. A. Sanabria and O. M. Hernndez, Control de un pndulo


invertido simple por mtodos de realimentacin de estados, Nov.
2009.

[3] J. L. Beltrn, Simulacin de un pndulo invertido, Dec. 2010.

[4] A. Hernndez, M. Legaspi, and M. Pelez, Control Inteligente


del Pndulo Invertido., en Universidad Complutense de Madrid,
2011.

[5] CTMS. Inverted Pendulum: Simulink Modeling. Disponible en:


MathWorks

[6] R. Dorf y R. Bishop, Modern Control Systems, 9.aed. Prentice-


Hall, Inc, 2001.

[7] ISA, Modelado de un motor cc, [accessed September 5,


2016]. direccin: Modelado de un motor cc.

[8] L. Bahadur, B. Tyagi y H. Om, Optimal control of nonlinear


inverted pendulum system using pid controllerand lqr:
Performance analysis without and with disturbance input,
International Journal of Automationand Computing, vol. 11, pgs.
661-670, 2015.

[9] V. Mazzone, Controladores pid - control automtico, en


Automatizacin y Control Industrial, Universidad Nacional de
Quilmes, mar. de 2002.

Category:

Pages with broken file links

https://www.luisllamas.es/arduino-encoder-rotativo/
Medir el ngulo y sentido de giro con Arduino y encoder rotativo
Qu es un encoder rotativo?
Un encoder rotativo es un dispositivo genrico que permite determinar
la posicin y velocidad angular de un accionamiento, y registrar la
medicin desde un procesador o autmata como Arduino.

No es la primera vez que hablamos de encoders y su utilidad.


Previamente hemos visto encoders pticos y encoders magnticos, as
como su importancia a la hora de aplicar criterios de seleccin de
motores y accionamientos.

Existen mltiples tipos de encoders rotativos, pero en el mbito de


Arduino y los proyectos de electrnica caseros es muy frecuente
encontrar encoders rotativos electromecanicos.

Externamente estos encoders pueden ser parecidos a ciertos tipos


modelos de potencimetros, lo cual puede ser una ventaja porque
hace que ciertos accesorios sean similares, e incluso sea posible
sustituir uno por otro.

Sin embargo, no hay que confundir un encoder rotativo con un


potencimetro ya que tanto su electrnica como comportamiento
son totalmente diferentes.

Este tipo de encoder rotativo es un dispositivo incremental que


proporciona un pulso digital cada vez que el encoder gira un
determinado ngulo. El nmero de pulsos por vuelta depende del
encoder empleado, siendo habitual 256 pulsos/vuelta.

Frecuentemente, tambin incorporan un pulsador que actual al apretar


la palanca del encoder.

Curiosamente, este tipo de encoders no son demasiado tiles para


actuar como encoders propiamente dichos, es decir, para registrar
el giro de un elemento (por ejemplo, la rueda de un robot) de un
debido a la dificultad de acoplarlo al eje y la baja resolucin del
encoder.

Su uso principal es como mando o dispositivo de control o en


sustitucin de potencimetros. Por ejemplo, pueden emplearse para
regular el brillo de una pantalla LCD, el volumen de un dispositivo, o el
ngulo de un motor paso a paso o servo.

Precio
Los encoders son dispositivos baratos. Podemos encontrarlos en
mdulos preparados para conectar de forma sencilla a Arduino por
0,45 en vendedores internacionales de Ebay y Aliexpress.

Tambin podemos adquirir el encoder suelto como componente, sin


estar integrado en una placa. Sin embargo el precio es prcticamente
el mismo por lo que, en general, es recomendable adquirirlo en un
mdulo.

Cmo funciona un encoder rotativo?


Internamente el encoder est formado por dos escobillas que deslizan
sobre una pista de metlica con divisiones. Al girar el eje, una pequea
bola metlica cierra el contacto, actuando como un pulsador.
Para leer el encoder deberemos entender cmo leer un pulsador, como
vimos en Leer un pulsador con Arduino y Leer un pulsador con
Arduino con interrupciones y debounce.
Normalmente disponen de dos salidas formando un sistema
equivalente a disponer dos pulsadores (Canal A y B). Estos pulsadores
estn desplazados uno respecto al otro, formando lo que se denomina
un encoder en cuadratura.

En un encoder en cuadratura existe un desfase entre ambos


sensores de forma que la seal que producen est desplazada 90
elctricos. Grficamente, la seal de ambos canales respecto al ngulo
girado sera la siguiente.

La ventaja de los encoder en cuadratura es que, adems de detectar


la posicin y la velocidad, permiten determinar el sentido de giro.

Para visualizarlo, consideremos que tomamos como origen de eventos


los flancos de subida o bajada del Canal A. Si giramos en el sentido
CW, se producirn los eventos t0, t1, t2, t3 tn.

Si en estos eventos miramos el Canal B, vemos que la seal A es


siempre inversa al Canal B.
Si invertimos el sentido de giro, e igualmente tomamos como
referencia los flancos de bajada o subida del Canal A, vemos que en
los instantes (t0, t1, t2, t3 tn) la seal del Canal A y B son
siempre idnticas.

En realidad, que el sentido de giro sea CW o CCW depender de la


construccin interna del sensor, de la conexin, y del canal que
tomemos como referencia.

Pero, en cualquier caso, vemos que es posible diferenciar el


sentido de giro simplemente comparando las seales obtenidas
en el encoder en cuadratura, y asignar un significado fsico CW o CCW
es inmediato, simplemente probando el montaje una vez.

Respecto a la precisin, tenemos ms de una opcin.


Precisin simple, registrando un nico flanco (subida o bajada)
en un nico canal.

Precisin doble, registrando ambos flancos en un nico canal.

Precisin cudruple, registrando ambos flancos en ambos


canales.

Esquema de montaje
Para conectar el encoder a Arduino, necesitamos tres entradas
digitales, dos para la deteccin del encoder y una adicional si
queremos registrar la pulsacin de la palanca.

Idealmente, deberamos emplear interrupciones para registrar el


movimiento del encoder. Lamentablemente, la mayora de placas de
Arduino slo tienen dos pines asociados a interrupciones. En el caso de
querer precisin cudruple esto supone emplear los dos pines con
interrupciones.

Ms informacin sobre interrupciones en Arduino en Qu son y cmo


usar interrupciones en Arduino
En este caso, la conexin del encoder sera la siguiente.

Mientras que la conexin vista desde Arduino sera la siguiente.


No obstante, es posible emplear el encoder sin emplear
interrupciones, lo que permite emplear entradas digitales. Sin
embargo, tendremos que preguntar por pool el estado de la entrada, lo
que supone un peor rendimiento.

En Arduino Uno, Nano y Mini Pro los pines de interrupciones son D2 y


D3. Para otros modelos de Arduino consultar el esquema patillaje
correspondiente.
Ejemplos de cdigo
Precisin simple o doble por pool
En este primer ejemplo realizamos la lectura del encoder por pool, sin
emplear interrpciones. Para ello podemos usar dos entradas digitales
cualquiera, en el ejemplo D9 y D10. La precisin puede ser doble o
simple, para lo cul tendris que cambiar la linea comentada en el
condicional (aunque no se me ocurre una razn para preferir precisin
simple a doble)
const int channelPinA = 9;
const int channelPinB = 10;

unsigned char stateChannelA;

1 constintchannelPinA=9;
2 constintchannelPinB=10;
3
4 unsignedcharstateChannelA;
5 unsignedcharstateChannelB;
6 unsignedcharprevStateChannelA=0;
7
8 constintmaxSteps=255;
9 intprevValue;
1 intvalue;
0
1
constinttimeThreshold=5;
1
unsignedlongcurrentTime;
1
2 unsignedlongloopTime;
1
3
boolIsCW=true;
1
4
1 voidsetup(){
5 Serial.begin(9600);
1 pinMode(channelPinA,INPUT);
6
pinMode(channelPinB,INPUT);
1
7 currentTime=millis();
1 loopTime=currentTime;
8 value=0;
1
9 prevValue=0;
2 }
0
2
voidloop(){
1
currentTime=millis();
2
2 if(currentTime>=(loopTime+timeThreshold))
2 {
3
stateChannelA=digitalRead(channelPinA);
2
4 stateChannelB=digitalRead(channelPinB);
2 if(stateChannelA!=prevStateChannelA) // Para precision simple if((!
5 stateChannelA) && (prevStateChannelA))
2 {
6 if(stateChannelB)// B es HIGH, es CW
2 {
7
boolIsCW=true;
2
8 if(value+1<=maxSteps)value++;// Asegurar que no sobrepasamos
maxSteps
2
9 }
3 else // B es LOW, es CWW
0 {
3 boolIsCW=false;
1
if(value-1>=0)value=value--;// Asegurar que no tenemos negativos
3
2 }
3
3 }
3 prevStateChannelA=stateChannelA;// Guardar valores para
4 siguiente
3
5
// Si ha cambiado el valor, mostrarlo
3
6 if(prevValue!=value)
3 {
7
prevValue=value;
3
Serial.print(value);
8
3
9 }
4
0
loopTime=currentTime; // Actualizar tiempo
4
1 }
4 // Otras tareas
2 }
4
3
4
4
4
5
4
6
4
7
4
8
4
9
5
0
5
1
5
2
5
3
5
4
5
5
5
6
5
7
5
8
5
9
6
0
6
1
6
2
Precisin doble con una interrupcin
En este ejemplo cambiamos una de las entradas digitales por una
interrupcin, registrando flancos de subida y bajada, por lo que
tenemos precisin doble.

const int channelPinA = 2;


const int channelPinB = 10;

const int timeThreshold = 5;

1 constintchannelPinA=2;
2 constintchannelPinB=10;
3
4 constinttimeThreshold=5;
5 longtimeCounter=0;
6
7 constintmaxSteps=255;
8 volatileintISRCounter=0;
9 intcounter=0;
10
11 boolIsCW=true;
12
13 voidsetup()
14 {
15 pinMode(channelPinA,INPUT_PULLUP);
16 Serial.begin(9600);
17 attachInterrupt(digitalPinToInterrupt(channelPinA),doEncode,CHANG
E);
18
}
19
20
voidloop()
21
{
22
if(counter!=ISRCounter)
23
{
24
counter=ISRCounter;
25
Serial.println(counter);
26
}
27
delay(100);
28
}
29
30
voiddoEncode()
31
{
32
if(millis()>timeCounter+timeThreshold)
33
{
34
if(digitalRead(channelPinA)==digitalRead(channelPinB))
35
{
36
IsCW=true;
37
if(ISRCounter+1<=maxSteps)ISRCounter++;
38
}
39
else
40
{
41
IsCW=false;
42
if(ISRCounter-1>0)ISRCounter--;
43
}
44
timeCounter=millis();
45
}
46
}
Precisin cudruple con dos interrupciones
En este ltimo ejemplo, empleamos interrupciones para ambos
canales, y en ambos flancos. Obtenemos precisin cudruple, pero a
cambio dejamos sin ms pines con interrupciones a la mayora de
modelos de Arduino.

const int channelPinA = 2;


const int channelPinB = 3;

const int timeThreshold = 5;

1 constintchannelPinA=2;
2 constintchannelPinB=3;
3
4 constinttimeThreshold=5;
5 longtimeCounter=0;
6
7 constintmaxSteps=255;
8 volatileintISRCounter=0;
9 intcounter=0;
10
11 boolIsCW=true;
12
13 voidsetup()
14 {
15 pinMode(channelPinA,INPUT_PULLUP);
16 Serial.begin(9600);
17 attachInterrupt(digitalPinToInterrupt(channelPinA),doEncodeA,CHAN
GE);
18
attachInterrupt(digitalPinToInterrupt(channelPinB),doEncodeB,CHAN
19
GE);
20
}
21
22
voidloop()
23
{
24
if(counter!=ISRCounter)
25
{
26
counter=ISRCounter;
27
Serial.println(counter);
28
}
29
delay(100);
30
}
31
32
voiddoEncodeA()
33
{
34
35 if(millis()>timeCounter+timeThreshold)
36 {
37 if(digitalRead(channelPinA)==digitalRead(channelPinB))
38 {
39 IsCW=true;
40 if(ISRCounter+1<=maxSteps)ISRCounter++;
41 }
42 else
43 {
44 IsCW=false;
45 if(ISRCounter-1>0)ISRCounter--;
46 }
47 timeCounter=millis();
48 }
49 }
50
51 voiddoEncodeB()
52 {
53 if(millis()>timeCounter+timeThreshold)
54 {
55 if(digitalRead(channelPinA)!=digitalRead(channelPinB))
56 {
57 IsCW=true;
58 if(ISRCounter+1<=maxSteps)ISRCounter++;
59 }
60 else
61 {
62 IsCW=false;
if(ISRCounter-1>0)ISRCounter--;
63 }
64 timeCounter=millis();
65 }
}

https://www.luisllamas.es/que-son-y-como-usar-interrupciones-en-
arduino/
Qu son y cmo usar interrupciones en Arduino

Las interrupciones son un mecanismo muy potente y valioso en


procesadores y autmatas. Arduino, por supuesto, no es una
excepcin. En esta entrada veremos qu son las interrupciones, y
como usarlas en nuestro cdigo.

Para entender la utilidad y necesidad de las interrupciones,


supongamos que tenemos Arduino conectado a un sensor, por ejemplo
encoder ptico que cuenta las revoluciones de un motor, un detector
que emite una alarma de nivel de agua en un depsito, o un simple
pulsador de parada.

Si queremos detectar un cambio de estado en esta entrada, el mtodo


que hemos usado hasta ahora es emplear las entradas digitales para
consultar repetidamente el valor de la entrada, con un intervalo
de tiempo (delay) entre consultas.

Este mecanismo se denomina poll, y tiene 3 claras desventajas.

Suponer un continuo consumo de procesador y de energa, al


tener que preguntar continuamente por el estado de la entrada.

Si la accin necesita ser atendida inmediatamente, por ejemplo


en una alerta de colisin, esperar hasta el punto de programa
donde se realiza la consulta puede ser inaceptable.

Si el pulso es muy corto, o si el procesador est ocupado


haciendo otra tarea mientras se produce, es posible que nos
saltemos el disparo y nunca lleguemos a verlo.

Para resolver este tipo de problemas, los microprocesadores


incorporan el concepto de interrupcin, que es un mecanismo que
permite asociar una funcin a la ocurrencia de un determinado
evento. Esta funcin de callback asociada se denomina ISR
(Interruption Service Rutine).

Cuando ocurre el evento el procesador sale inmediatamente


del flujo normal del programa y ejecuta la funcin ISR asociada
ignorando por completo cualquier otra tarea (por esto se llama
interrupcin). Al finalizar la funcin ISR asociada, el procesador vuelve
al flujo principal, en el mismo punto donde haba sido interrumpido.

Como vemos, las interrupciones son un mecanismo muy potente y


cmodo que mejora nuestros programas y nos permite realizar
acciones que no seran posibles sin el uso de interrupciones.

Para usar interrupciones en dispositivos fsicos (como pulsadores,


sensores pticos, ) debemos antes eliminar el efecto rebote,
como podis ver en la entrada Aplicar debounce al usar interrupciones
en Arduino
INTERRUPCIONES EN ARDUINO
Arduino dispone de dos tipos de eventos en los que definir
interrupciones. Por un lado tenemos las interrupciones de timers (que
veremos en su momento al hablar de temporizadores. Por otro lado,
tenemos las interrupciones de hardware, que responden a
eventos ocurridos en ciertos pines fsicos.

Dentro de las interrupciones de hardware, que son las que nos ocupan
en esta entrada, Arduino es capaz de detectar los siguientes
eventos.
RISING, ocurre en el flanco de bajada de LOW a HIGH.

FALLING, ocurre en el flanco de subida de HIGH a LOW.

CHANGING, ocurre cuando el pin cambia de estado (rising +


falling).

LOW, se ejecuta continuamente mientras est en estado LOW.

Los pines susceptibles de generar interrupciones varan en funcin del


modelo de Arduino.

En Arduino y Nano se dispone de dos interrupciones, 0 y 1, asociados a


los pines digitales 2 y 3.El Arduino Mega dispone de 6 interrupciones,
en los pines 2, 3, 21, 20, 19 y 18 respectivamente. Arduino Due
dispone de interrupciones en todos sus pines.

Modelo INT0 INT1 INT2 INT3 INT4 INT5


UNO 2 3
Nano 2 3
Mini Pro 2 3
Mega 2 3 21 20 19 18
Leonardo 3 2 0 1 7
Due En todos los pines
LA FUNCIN ISR
La funcin asociada a una interrupcin se denomina ISR (Interruption
Service Routines) y, por definicin, tiene que ser una funcin que
no recibe nada y no devuelva nada.

Dos ISR no pueden ejecutarse de forma simultnea. En caso de


dispararse otra interrupcin mientras se ejecuta una ISR, la funcin ISR
se ejecuta una a continuacin de otra.

LA ISR, CUANTO MS CORTA MEJOR


Al disear una ISR debemos mantener como objetivo que tenga el
menor tiempo de ejecucin posible, dado que mientras se est
ejecutando el bucle principal y todo el resto de funciones se
encuentran detenidas.
Imaginemos, por ejemplo, que el programa principal ha sido
interrumpido mientras un motor acercaba un brazo para coger un
objeto. Una interrupcin larga podra hacer que el brazo no para a
tiempo, tirando o daando el objeto.

Frecuentemente la funcin de la ISR se limitar a activar un


flag, incrementar un contador, o modificar una variable. Esta
modificacin ser atendida posteriormente en el hilo principal, cuando
sea oportuno.

No empleis en una ISR un proceso que consuma tiempo. Esto


incluye clculos complejos, comunicacin (serial, I2C y SPI) y, en la
medida de lo posible, cambio de entradas o salidas tanto digitales
como analgicas.
LAS VARIABLES DE LA ISR COMO VOLATILES
Para poder modificar una variable externa a la ISR dentro de la misma
debemos declararla como volatile. El indicador volatile indica
al compilador que la variable tiene que ser consultada siempre antes
de ser usada, dado que puede haber sido modificada de forma ajena al
flujo normal del programa (lo que, precisamente, hace una
interrupcin).

Al indicar una variable como Volatile el compilador desactiva ciertas


optimizaciones, lo que supone una prdida de eficiencia. Por tanto,
slo debemos marcar como volatile las variables que realmente lo
requieran, es decir, las que se usan tanto en el bucle principal
como dentro de la ISR.

EFECTOS DE LA INTERRUPCIN Y LA MEDICIN DEL TIEMPO


Las interrupciones tienen efectos en la medicin del tiempo de
Arduino, tanto fuera como dentro de la ISR, porque Arduino emplea
interrupciones de tipo Timer para actualizar la medicin del tiempo.

Efectos fuera de la ISR


Durante la ejecucin de una interrupcin Arduino no actualiza el
valor de la funcin millis y micros. Es decir, el tiempo de ejecucin
de la ISR no se contabiliza y Arduino tiene un desfase en la medicin
del tiempo.

Si un programa tiene muchas interrupciones y estas suponen un alto


tiempo de ejecucin, la medida del tiempo de Arduino puede
quedar muy distorsionada respecto a la realidad (nuevamente, un
motivo para hacer las ISR cortas).

Efectos dentro de la ISR


Dentro de la ISR el resto de interrupciones estn desactivadas. Esto
supone:

La funcin millis no actualiza su valor, por lo que no podemos


utilizarla para medir el tiempo dentro de la ISR. (s podemos
usarla para medir el tiempo entre dos ISR distintas)

Como consecuencia la funcin delay() no funciona, ya que basa


su funcionamiento en la funcin millis()

La funcin micros() actualiza su valor dentro de una ISR, pero


empieza a dar mediciones de tiempo inexactas pasado el rango
de 500us.

En consecuencia, la funcin delayMicroseconds funciona en ese


rango de tiempo, aunque debemos evitar su uso porque no
deberamos introducir esperas dentro de una ISR.

CREAR INTERRUPCIONES EN ARDUINO


Para definir una interrupcin en Arduino usamos la funcin:

attachInterrupt(interrupt, ISR, mo

1 attachInterrupt(interrupt,ISR,mode);
Donde interrupt es el nmero de la interrupcin que estamos
definiendo, ISR la funcin de callback asociada, y mode una de las
opciones disponibles (Falling, Rising, Change y Low)

No obstante, es ms limpio emplear la funcin


digitalPinToInterrupt(), que convierte un Pin a la interrupcin
equivalente. De esta forma se favorece el cambio de modelo de placa
sin tener que modificar el cdigo.
attachInterrupt(digitalPinToInterr

1 attachInterrupt(digitalPinToInterrupt(pin),ISR,mode);
Otras funcionas interesantes para la gestin de interrupciones son:

DetachInterrupt(interrupt ), anula la interrupcin.

NoInterrupts(), desactiva la ejecucin de interrupciones hasta


nueva orden. Equivale a sli()

Interrupts(), reactiva las interrupciones. Equivale a cli()

Probando las interrupciones en Arduino


Para probar las interrupciones en Arduino, vamos a emplear una salida
digital de Arduino para emular una seal digital. En el mundo real,
sera otro dispositivo (un sensor, otro procesador) el que generara
esta seal, y nosotros la captaramos con Arduino.

Conectamos mediante un cable el pin digital 10 al pin digital 2,


asociado a la interrupcin 0.
Haciendo parpadear un LED a travs de interrupciones
En el siguiente cdigo definimos el pin digital 10 como salida, para
emular una onda cuadrada de periodo 300ms (150ms ON y 150ms
OFF).

Para visualizar el funcionamiento de la interrupcin, en cada flanco


activo del pulso simulado, encendemos/apagamos el LED integrado en
la placa, por lo que el LED parpadea a intervalos de 600ms (300ms ON
y 300ms OFF)

Puede que a estas alturas ver parpadear un LED no parezca muy


espectacular, pero no es tan simple como parece. No estamos
encendiendo el LED con una salida digital, si no que es la
interrupcin que salta la que enciende y apaga el LED (el pin
digital solo emula una seal externa).
const int emuPin = 10;

const int LEDPin = 13;


const int intPin = 2;

1 constintemuPin=10;
2
3 constintLEDPin=13;
4 constintintPin=2;
5 volatileintstate=LOW;
6
7 voidsetup(){
8 pinMode(emuPin,OUTPUT);
9 pinMode(LEDPin,OUTPUT);
10 pinMode(intPin,INPUT_PULLUP);
11 attachInterrupt(digitalPinToInterrupt(intPin),blink,CHANGE);
12 }
13
14 voidloop(){
15 //esta parte es para emular la salida
16 digitalWrite(emuPin,HIGH);
17 delay(150);
18 digitalWrite(emuPin,LOW);
19 delay(150);
20 }
21
22 voidblink(){
23 state=!state;
24 digitalWrite(LEDPin,state);
25 }
Contando disparos de una interrupcin
El siguiente cdigo empleamos el mismo pin digital para emular una
onda cuadrada, esta vez de intervalo 2s (1s ON y 1s OFF).

En cada interrupcin actualizamos el valor de un contador.


Posteriormente, en el bucle principal, comprobamos el valor del
contador, y si ha sido modificado mostramos el nuevo valor.

Al ejecutar el cdigo, veremos que en el monitor serie se imprimen


nmeros consecutivos a intervalos de dos segundos.

const int emuPin = 10;

const int intPin = 2;


volatile int ISRCounter = 0;

1 constintemuPin=10;
2
3 constintintPin=2;
4 volatileintISRCounter=0;
5 intcounter=0;
6
7
8 voidsetup()
9 {
10 pinMode(emuPin,OUTPUT);
11 pinMode(intPin,INPUT_PULLUP);
12 Serial.begin(9600);
13 attachInterrupt(digitalPinToInterrupt(intPin),interruptCount,LOW);
14 }
15
16 voidloop()
17
{
18
//esta parte es para emular la salida
19
digitalWrite(emuPin,HIGH);
20
delay(1000);
21
digitalWrite(emuPin,LOW);
22
delay(1000);
23
24
25
if(counter!=ISRCounter)
26
{
27
counter=ISRCounter;
28
Serial.println(counter);
29
}
30
}
31
32
voidinterruptCount()
33
{
34
ISRCounter++;
35
timeCounter=millis();
36
}
37
Lgicamente, nuestro objetivo es emplear interrupciones de hardware
no solo con otros dispositivos digitales, si no tambin con
dispositivos fsicos como pulsadores, sensores pticos

Sin embargo, como hemos adelantado, estos dispositivos generan


mucho ruido en los cambios de estado, lo que provoca que las
interrupciones se disparen mltiples veces. Este fenmeno se
denomina rebote y aprenderemos a eliminarlo en la siguiente
entrada.
https://www.luisllamas.es/debounce-interrupciones-arduino/
Leer un pulsador con Arduino con interrupciones y debounce

En la entrada anterior vimos qu son las interrupciones y cmo usarlas


para responder a eventos de hardware en pins.

Tambin dejamos claro que los dispositivos fsicos, como pulsadores,


detectores pticos, etc, presentan un efecto rebote que interfiere
con el uso de interrupciones, y que necesitamos eliminarlo o no
podremos usar interrupciones con estos dispositivos.

El proceso de eliminacin de este rebote se llama debounce.


En esta entrada aprenderemos qu es el rebote y aprenderemos a
eliminarlo con debounce por hardware y por software.

Qu es el debounce?
Los dispositivos electrnicos al cambiar de estado generan una seal
que, sin ser perfectamente cuadrada, en general es ms o menos
limpia. Veamos, por ejemplo, la seal que genera Arduino al
cambiar el estado de una salida digital de HIGH a LOW.
Sin embargo el mundo real no es tan bonito. Muchos dispositivos
fsicos habitualmente generan ruido en los flancos de seal. Como
ejemplo, veamos la variacin de tensin que ocurre cuando el cambio
de estado se genera por un pulsador.
Observar la cantidad de ruido ocurrido tras el flanco. En esencia, en el
rango de unos microsegundos la seal es puro ruido. Todos esos picos
pueden provocar disparos mltiples de una interrupcin.

PROBANDO EL REBOTE
Para probar el rebote, simplemente vamos a emplear un cable para
conectar el Pin 2 y Ground (tambin podis usar un pulsador o un
interruptor).

Activamos la resistencia interna de Pull UP en el Pin 2 y definimos una


interrupcin al evento de bajada en el PIN, y en la funcin ISR asociada
simplemente incrementamos un contador.

En el bucle principal, comprobamos si el contador, y si este ha sido


modificado mostramos su valor por el puerto de serie.
const int intPin = 2;
volatile int ISRCounter = 0;
int counter = 0;

1 constintintPin=2;
2 volatileintISRCounter=0;
3 intcounter=0;
4
5
6 voidsetup()
7 {
8 pinMode(intPin,INPUT_PULLUP);
9 Serial.begin(9600);
10 attachInterrupt(digitalPinToInterrupt(intPin),debounceCount,LOW);
11 }
12
13 voidloop()
14 {
15 if(counter!=ISRCounter)
16 {
17 counter=ISRCounter;
18 Serial.println(counter);
19 }
20 }
21
22 voiddebounceCount()
23 {
24 ISRCounter++;
25 }
Al probar nuestro montaje y poner en contacto el PIN 2 a GROUND,
esperaramos que la variable se incrementara de uno en uno. Pero
veremos que en realidad salta varios nmeros cada vez (incluso
varias decenas).

Este es el efecto del rebote. El ruido de la seal est generando


mltiples interrupciones cada vez que conectamos el cable.

Eliminando el rebote
Disponemos de dos formas de aplicar el debounce. Aadiendo
dispositivos electrnicos que filtren la seal (debounce por hardware) o
modificando nuestro cdigo para eliminar el rebote (debounce por
hardware).

Debounce por hardware


Aplicar un debounce por hardware tiene la ventaja de no incrementar
el tiempo de ejecucin de nuestro cdigo. Adems, en general, es una
solucin ms robusta. Por contra, tiene la desventaja de aumentar la
complejidad de nuestro montaje.

La forma ms sencilla de aplicar un debounce por hardware es


colocar un condensador en paralelo con el dispositivo (pulsador,
interruptor, sensor). Un condensador del orden de 1uF debera ser
suficiente para filtrar la mayora del ruido.

El esquema de conexin es el siguiente.


En general siempre es conveniente aadir un filtro por
hardware cuando empleamos entradas fsicas con interrupciones.
Debounce por software
El debounce por software tiene la ventaja de no requerir componentes
adicionales. Resolvemos el rebote nicamente modificando el cdigo
de nuestro programa.

Como desventaja, incrementa levemente el tiempo de ejecucin y la


complejidad del cdigo. Adems, si no aplicamos el cdigo
correctamente podemos ignorar interrupciones verdaderas.

La forma ms sencilla de aplicar un debounce por software es


comprobar el tiempo entre disparos de la interrupcin. Si el
tiempo es inferior a un determinado umbral de tiempo (threshold)
simplemente ignoramos la interrupcin. En definitiva, hemos definido
una zona muerta en la que ignoramos las interrupciones generadas.

Para aplicar el debounce por software, modificamos la funcin ISR de


la siguiente forma.

const int timeThreshold = 150;


const int intPin = 2;
volatile int ISRCounter = 0;
int counter = 0;

1 constinttimeThreshold=150;
2 constintintPin=2;
3 volatileintISRCounter=0;
4 intcounter=0;
5 longtimeCounter=0;
6
7
8 voidsetup()
9 {
10 pinMode(intPin,INPUT_PULLUP);
11 Serial.begin(9600);
12 attachInterrupt(digitalPinToInterrupt(intPin),debounceCount,FALLIN
G);
13
}
14
15
voidloop()
16
{
17
if(counter!=ISRCounter)
18
{
19
counter=ISRCounter;
20
Serial.println(counter);
21
}
22
}
23
24
voiddebounceCount()
25
{
26
if(millis()>timeCounter+timeThreshold)
27
{
28
ISRCounter++;
29
timeCounter=millis();
30
}
31
}
Un tiempo de 100-200ms es correcto para un pulsador pero en otros
casos deberemos ajustar el tiempo de forma que eliminemos el rebote,
pero no ignoremos dos posibles eventos cercanos verdaderos.

En un montaje real, lo mejor es emplear una combinacin de


ambos sistemas, a la vez que ajustamos correctamente el valor del
condensador y los tiempos del filtro por software para adaptarlos a
nuestro sistema.

https://www.luisllamas.es/referencia-lenguaje-arduino/
Referencia para el programador del lenguaje Arduino

Os presentamos una referencia para el programador del lenguaje


Arduino, con las distintas instrucciones y sentencias disponibles en el
lenguaje, organizados por categoras y con un ejemplo de cada caso.

Esta referencia no es un manual de programacin. Es un


chuletario orientado a gente con conocimientos de programacin,
que sirve para aprendizaje rpido, consulta, y compendio de la sintaxis
de programacin del lenguaje Arduino. Si no es vuestro caso, os
recomendamos que empecis aprendiendo a programar en C++ en
alguno de los muchos cursos disponibles gratuitamente en Internet.

Operadores y comparadores
Comparadores
//x igual a y
x == y

//x distinto de y

1 //x igual a y
2 x==y
3
4 //x distinto de y
5 x!=y
6
7 //x menor que y
8 x<y
9
10 //x mayor que y
11 x>y
12
13 //x menor o igual que y
14 x<=y
15
16 //x mayor o igual que y
17 x>=y

Operadores aritmticos
//operador de asignacin
a=b

//adicin

1 //operador de asignacin
2 a=b
3
4 //adicin
5 a+b
6
7 //substraccin
8 a-b
9
10 //multiplicacin
11 a*b
12
13 //divisin
14 a/b
15
16 //modulo
17 a%b

Operadores de bits
//and binario
a&b

//or binario

1 //and binario
2 a&b
3
4 //or binario
5 a|b
6
7 //xor binario
8 a^b
9
10 //not binario
11 a~b
12
13 //desplazamiento a izquierda
14 a<<b
15
16 //desplazamiento a derecha
17 a>>b

Operadores compuestos
//incremento
a++

//decremento

1 //incremento
2 a++
3
4 //decremento
5 a--
6
7 //adicin compuesta
8 a+=b
9
10 //substraccin compuesta
11 a-=b
12
13 //multiplicacin compuesta
14 a*=b
15
16 //divisin compuesta
17 a/=b
18
19 //and compuesto
20 a&=b
21
22 //or compuesto
23 a|=b

Operadores booleanos
//not
!a

//and

1 //not
2 !a
3
4 //and
5 a&&b
6
7 //or
8 a||b

Operadores de acceso
//operacion indireccin
*variable

//operacion direccin

1 //operacion indireccin
2 *variable
3
4 //operacion direccin
5 &variable
Declaracin y conversin de tipos de variables
//tipo vacio (solo para funciones
void

1 //tipo vacio (solo para funciones)


2 void

Booleanos
//booleano, false o true
boolean = false;

1 //booleano, false o true


2 boolean=false;

Enteros
//entero, 16 bits, de -32,768 a 3
int var = 100;

//entero, 16 bits, de 0 a 65535 (

1 //entero, 16 bits, de -32,768 a 32,767


2 intvar=100;
3
4 //entero, 16 bits, de 0 a 65535 (excepto en Due, donde son 32 bits)
5 unsignedintvar=100;
6
7 //entero, 16 bits, de 0 a 65535
8 shortvar=100;
9
10 //entero, 32 bits, de -2,147,483,648 a 2,147,483,647
11 longvar=100000L;
12
13 //entero, 32bits, de 0 a 4,294,967,295
14 unsignedlongvar=100000L;

Coma flotante
//coma floante, 32 bits, de -3.40
float var = 1.117;

//idntico a float, excepto en Ar

//coma floante, 32 bits, de -3.4028235E+38 a 3.4028235E+38.


1 Precision 6 digitos
2 floatvar=1.117;
3
4 //idntico a float, excepto en Arduino Due donde es flotante de 64
bits
5
doublevar=1.117;

Bytes
//8 bits, de 0 a 255
byte var = B10010;

//16bits, sin signo, de 0 a 65535

1 //8 bits, de 0 a 255


2 bytevar=B10010;
3
4 //16bits, sin signo, de 0 a 65535
5 wordvar=10000;

Caracteres
//8 bits, de -128 a 127
char var = 'A';

//8 bits, de 0 a 255

1 //8 bits, de -128 a 127


2 charvar='A';
3
4 //8 bits, de 0 a 255
5 unsignedcharvar=240;

Conversin entre variables


//convierte a char
char(variable);

//convierte a byte

1 //convierte a char
2 char(variable);
3
4 //convierte a byte
5 byte(variable);
6
7 //convierte a int
8 int(variable);
9
10 //convierte a word
11 word(variable);
12
13 //convierte a long
14 long(variable);
15
16 //convierte a float
17 float(variable);

Cualificadores de variables
//STATIC
//Variables visibles nicamente
//y cuyo valor se mantiene entre
static int variable;

1 //STATIC
2 //Variables visibles nicamente en el interior de una funcin,
3 //y cuyo valor se mantiene entre llamadas a la misma.
4 staticint variable;
5
6 //CONST
7 //Variables cuyo valor no puede ser redefinido tras la inicializacin
8 constfloatpi=3.14;
9
10 //VOLATILE
11 //Variables en las que se indica al compilador que no guarde en los
registros
12
//del microprocesador, sino que fuerza la actualizacin en memoria.
13
Esto se
14
//hace cuando existe la posibilidad de que el valor de la variable
15 sea
16 //modificado por otro proceso que se ejecuta concurrentemente con
el actual
//(por ejemplo cuando se emplean hilos o interrupciones)
volatileintvariable=LOW;
Vectores
Creacin de vectores
//declarar vector
int miArray[5];

//iniciar vector

1 //declarar vector
2 intmiArray[5];
3
4 //iniciar vector
5 intmiArray[]={2,4,8,3,6};
6
7 //declarar e iniciar vector
8 intmiArray[5]={2,4,-8,3,2};

Manipulacin vectores
//asignar valor a elemento del ve
miArray[0] = 10;

//obtener valor de elemento del

1 //asignar valor a elemento del vector


2 miArray[0]=10;
3
4 //obtener valor de elemento del vector
5 x=miArray[4];
Cadenas de texto
Textos como vector de caracteres
char cadena1[15];
char cadena2[8] = {'a', 'r', 'd', 'u
char cadena3[8] = {'a', 'r', 'd', 'u
char cadena4[ ] = "texto";
1 charcadena1[15];
2 charcadena2[8] ={'a','r','d','u','i','n','o'};
3 charcadena3[8] ={'a','r','d','u','i','n','o','\0'};
4 charcadena4[] ="texto";
5 charcadena5[8] ="texto";
6 charcadena6[15]="texto";
7
8 //vector de cadenas
9 char*cadenaArray[]={"Cadena 1","Cadena 2","Cadena 3",
10 "Cadena 4","Cadena 5","Cadena 6"};

Textos como objeto String


// literal de cadena de texto
String txtMsg = "Hola";

// convirtiendo un char a String

1 // literal de cadena de texto


2 StringtxtMsg="Hola";
3
4 // convirtiendo un char a String
5 StringtxtMsg= String('a');
6
7 // convirtiendo un literal a String
8 StringtxtMsg= String("Texto");
9
10 // concatenando dos literales a String
11 StringtxtMsg= String("texto1"+"texto2");
Condicionales
Condicional abreviado
condition ? true : false;

1 condition?true:false;

Condicional IF
if (variable < 10)
{
// accion A
}

1 if(variable<10)
2 {
3 // accion A
4 }
5
6
7 if(variable<10)
8 {
9 // accion A
10 }
11 else
12 {
13 // accion B
14 }
15
16
17 if(variable<10)
18 {
19 // accion A
20 }
21 elseif(variable>=100)
22 {
23 // accion B
24 }
25 else
26 {
27 // accion C
28 }

Condicional SWITCH / CASE OF


sw itch (variable) {
case 1:
// accion A
break;

1 switch(variable){
2 case1:
3 // accion A
4 break;
5 case2:
6 // accion B
7 break;
8 default:
9 // caso por defecto (opcional)
10 }
Bucles
Bucle FOR
for (int i=0; i <= 100; i++){
// accion
}
1 for(inti=0;i<=100;i++){
2 // accion
3 }

Bucle WHILE
variable = 0;

w hile(variable < 100){


// accion

1 variable=0;
2
3 while(variable<100){
4 // accion
5 variable++;
6 }

Bucle DO WHILE
do
{
//accion
variable++;

1 do
2 {
3 //accion
4 variable++;
5 }while(variable<100);
Funciones matemticas
Funciones de rango
//devuelve mnimo entra a y b
min(a,b);

//devuelve mximo entra a y b

1 //devuelve mnimo entra a y b


2 min(a,b);
3
4 //devuelve mximo entra a y b
5 max(a,b);
6
7 //devuelve valor absoluto de a
8 abs(a);
9
10 //devuelve x restringido a (a,b)
11 constrain(x,a,b);
12
13 //interpola linealmente y entre x1,y1 x2,y2
14 map(x,x1,x2,y1,y2);

Potenciacin
//devuelve a^b (ambos tipo float
pow (a,b);

//devuelve la raiz cuadrada de a

1 //devuelve a^b (ambos tipo float)


2 pow(a,b);
3
4 //devuelve la raiz cuadrada de a
5 sqrt(a);

Nmeros aleatorios
//inicializa la semilla del generad
randomSeed(semilla);

//devuelve un numero aleatorio

1 //inicializa la semilla del generador de numeros pseudo aleatorios


2 randomSeed(semilla);
3
4 //devuelve un numero aleatorio entre a y b (ambos tipo long)
5 random(a,b);

Trigonometria
//devuelve el seno de a (a tipo f
sin(a);

//devuelve el coseno de a (a tip

1 //devuelve el seno de a (a tipo float y en radianes)


2 sin(a);
3
4 //devuelve el coseno de a (a tipo float y en radianes)
5 cos(a);
6
7 //devuelve la tangente de a (a tipo float y en radianes)
8 tan(a);

Funciones de Bits y Bytes


//devuelve el byte menos signitic
low Byte(variable);

//devuelve el byte ms significa

1 //devuelve el byte menos signiticativo de una palabra o variable.


2 lowByte(variable);
3
4 //devuelve el byte ms significativo de una palabra
5 //(o el segundo byte menos significativo en variables mayores)
6 highByte(variable);
7
8 //devuelve el bit n de una variable x
9 //(siendo el bit 0 el menos significativo)
10 bitRead(x,n);
11
12 //escribe el bit n de la variable x con el valor b
13 //(siendo el bit 0 el menos significativo)
14 bitWrite(x,n,b);
15
16 //pone a 1 el bit n de la variable x
17 bitSet(x,n);
18
19 //pone a 0 el bit n de la variable x
20 bitClear(x,n);
21
22 //obtiene el valor del bit n (idntico a 2^n)
23 bit(n);
Funciones de textos
//delvuelve el caracter en la pos
txtMsg.charAt(3);

//sustituye el caracter en la pos

1 //delvuelve el caracter en la posicin 3 (idntico a txtMsg[3];)


2 txtMsg.charAt(3);
3
4 //sustituye el caracter en la posicin 3 por "A" (idntico a
txtMsg[3]="A";)
5
txtMsg.setCharAt("A",3);
6
7
//concatena texto 1 y texto 2 (idntico a texto1=texto1+texto2;)
8 texto1.concat("texto2");
9
10 //devuelve la longitud de la cadena
11 txtMsg.length();
12
13 //devuelve la cadena convertida en minsculas
14 txtMsg.toLowerCase();
15
16 //devuelve la cadena convertida en maysculas
17 txtMsg.toUpperCase();
18
19 //elimina espacios y carcteres incorrectos
20 txtMsg.trim();
21
22 //devuelve la cadena de texto como entero
23 txtMsg.toInt();

Comparacin
//compara dos cadenas. Devue
//0 si son iguales, y -1 en caso
texto1.compareTo(texto2);

1 //compara dos cadenas. Devuelve 1 si texto1 es mayor que texto2,


2 //0 si son iguales, y -1 en caso contrario
3 texto1.compareTo(texto2);
4
5 //compara si dos cadenas son iguales (idntico a texto1==texto2)
6 texto1.equals(texto2);
7
8 //compara si dos cadenas son iguales, ignorando maysculas y
minsculas
9
texto1.equalsIgnoreCase(texto2);

Subcadenas
//devuelve una subcadena de la
txtMsg.substring(3, 10);

//comprueba si la cadena empie

1 //devuelve una subcadena de la posicion 3 a la 10


2 txtMsg.substring(3,10);
3
4 //comprueba si la cadena empieza por "texto", con offset 3
5 txtMsg.startsWith("texto",3);
6
7 //comprueba si la cadena empieza por "texto", con offset 3
8 txtMsg.endsWith("texto");

Bsqueda y sustitucin
//devuelve el ndice de la primer
//a partir de la posicin offset
txtMsg.indexOf('A', offset);

1 //devuelve el ndice de la primera ocurrencia de 'A',


2 //a partir de la posicin offset
3 txtMsg.indexOf('A',offset);
4
5 //devuelve el ndice de la ltima ocurrencia de 'A'
6 //previa a la posicin offset
7 txtMsg.lastIndexOf('A',offset);
8
9 //sustituye las ocurrencias de "texto1" por "texto2"
10 txtMsg.replace("texto1","texto2");
Funciones de usuario
Variables globales
int option=1;

int cambiar(){
option=4;

1 intoption=1;
2
3 intcambiar(){
4 option=4;
5 }
6
7 voidsetup(){
8 Serial.begin(9600);
9 }
10
11 voidloop(){
12 cambiar();
13 Serial.print(option); //muestra 4
14 delay(10000);
15 }

Paso de parmetros por valor


int cambiar(var){
var=4;
}

1 intcambiar(var){
2 var=4;
3 }
4
5 voidsetup(){
6 Serial.begin(9600);
7 }
8
9 voidloop(){
10 intoption=1;
11 cambiar(option);
12 Serial.print(option); //muestra 1
13 delay(10000);
14 }

Paso de parmetros por referencia


int cambiar(int &var){
var=4;
}

1 intcambiar(int&var){
2 var=4;
3 }
4
5 voidsetup(){
6 Serial.begin(9600);
7 }
8
9 voidloop(){
10 intoption=1;
11 cambiar(option);
12 Serial.print(option); //muestra 4
13 delay(10000);
14 }

Paso de parmetros por puntero


int cambiar(int* var){
*var=4;
}

1 intcambiar(int*var){
2 *var=4;
3 }
4
5 voidsetup(){
6 Serial.begin(9600);
7 }
8
9 voidloop(){
10 intoption=1;
11 cambiar(&option);
12 Serial.print(option); //muestra 4
13 delay(10000);
14 }

Devolucin de valores
int cambiar(){
int var=4;
return var;
}

1 intcambiar(){
2 intvar=4;
3 returnvar;
4 }
5
6 voidsetup(){
7 Serial.begin(9600);
8 }
9
10 voidloop(){
11 intoption=1;
12 option=cambiar();
13 Serial.print(option); //muestra 4
14 delay(10000);
15 }
Tipos datos avanzados (Enum / Struct / Typedef)
Enumeraciones
//declaracion
enum miEnumeracion {
opcion1,
opcion2,

1 //declaracion
2 enummiEnumeracion{
3 opcion1,
4 opcion2,
5 opcion3
6 };
7
8 //ejemplo de uso
9 miEnumeracion variable=opcion2;
10
11 if(variable==opcion2){
12 //accion
13 }

Estructuras
//declaracion
struct miEstructura
{
int campo1;

1 //declaracion
2 structmiEstructura
3 {
4 int campo1;
5 int campo2;
6 charcampo3;
7 };
8
9 //ejemplo de uso
10 structmiEstructura variable;
11
12 variable.campo1=10;

Definicion de tipos de datos de usuario


//declaraciones
typedef int nuevotipo;
typedef enum miEnumeracion nu
typedef struct miEstructura nue

1 //declaraciones
2 typedefintnuevotipo;
3 typedefenummiEnumeracion nuevotipo;
4 typedefstructmiEstructura nuevotipo;
5
6 //ejemplo de uso
7 nuevotipo variable;
Clases
Ejemplo de uso de clase
class MiRobot;

//definicion de clase ejemplo


class MiRobot

1 classMiRobot;
2
3 //definicion de clase ejemplo
4 classMiRobot
5 {
6 public:
7 voidsaludar();//muestra "Hola"
8 voidincCont();//incrementa contador
9 int getCont();//devuelve contador
10 voidsayCont();//muestra valor contador
11 voidsetCont(int);//inicializa contador a un valor
12 private:
13 intcont=0;//variable contador privada
14 };
15
16 //muestra "Hola"
17 voidMiRobot::saludar(){
18 Serial.println("Hola");
19 }
20
21 voidMiRobot::incCont(){
22 this->cont++;
23 }
24
25 //devuelve contador
26 voidMiRobot::getCont(){
27 returnthis->cont;
28 }
29
30 //muestra valor contador
31 voidMiRobot::sayCont(){
32 Serial.println(this->cont);
33 }
34
35 //inicializa contador a un valor
36 voidMiRobot::setCont(int_cont){
37 this->cont=_cont;
38 }
39
40 voidsetup(){
41 Serial.println("Iniciando");
42 Serial.begin(9600);
43 MiRobot robot;
44 robot.saludar();//se muestra hola
45 }
46
47 voidloop(){
48 robot.incCont();//se incrementa el contador
49 robot.sayCont();//muestra el valor
50 delay(1000);
51 }

https://javierloureiro.wordpress.com/2014/04/24/como-usar-un-
encoder-con-un-arduino/
Cmo usar un Encoder con un Arduino
Un encoder es muy parecido a un potencimetro por fuera, pero en
realidad son muy distintos. El potencimetro nos da un valor analgico
que tenemos que leer, pero el encoder nos enva informacin digital
que vamos leyendo segn la ruedecita gira. Un modelo comn es el
PEC11-4215F-S0024, que nos enva 24 pulsos en una vuelta
completa.

El encoder tiene 3 patillas para los pulsos, y 2 patillas para el boton


principal (si pulsas el eje hacia dentro actua como un pulsador). Este
sera el esquema del encoder. Los puntos rojos es donde conectamos
los pines del arduino:
BTN es el pin del Botn. Cuando pulsamos, se conectan los 5V con
tierra, y BTN vale 0. El resto del tiempo, se mantiene alto.

Para detectar el giro, la idea es conectar una patilla comn a tierra, y


las otras dos a 5V (con una resistencia de pull up de por ejemplo, 10k).
Segn vamos girando, el encoder conecta la patilla A y la patilla B a
tierra, con un ligero desfase, efectuando as un pulso, que podremos
detectar en nuestro arduino.

Si la patilla A baja antes que la patilla B, vamos en el sentido del reloj,


y si la patilla B baja antes, vamos hacia atrs.

Esto es una foto del osciloscopio, mostrando las patillas A y B


superpuestas, cuando giramos el encoder:

En reposo, el encoder tiene las 2 patillas en 1. Al girar, se conecta la


patilla A con GND, y el voltaje se pone a 0, seguimos girando y la
patilla B, ms o menos a la mitad del pulso toca GND, y ambas se
ponen a 0. Segun seguimos girando, la patilla A deja de estar
conectada a GND, asi que vuelve a 1, y luego la patilla B, volviendo
ambas a 1. La longitud del pulso depende de la velocidad a la que
nosotros giramos la ruedecita.

Aqui tenemos 1 pulso del encoder :

Podemos pasar esto a binario, 1 cuando el voltaje est alto, 0, cuando


est bajo, tomando los valores en distintos momentos del tiempo:

Esto lo podemos traducir a binario:


O sea, la secuencia seria 3 2 0 1 3. As que, cmo leer esto, y tener
control del encoder?

Yo voy a leer todo en la interrucin del timer, a intervalos regulares, a


una velocidad constante de 125khz. Lo hago as porque luego voy a
comunicarme con otros dispositivos, y asi puedo aprovechar la
interrupcin ms tarde. Asi que 125 veces por milisegundo, voy a leer
el estado de las 2 patillas para ver si estamos en un pulso o no.

Pero aqui surge un problema. Si mirais la foto del osciloscopio, vemos


que hay unos picos raros:

Esto es debido a que los contactos metlicos producen vibraciones,


como minichispazos, cuando se separan, y es posible que durante un
tiempo la carga se mantenga saltando (bouncing en ingls). Asi que no
nos podemos fiar de una lectura simple. Lo que hacemos
generalmente es leer varias veces el valor de las patillas, y ver que no
han cambiado en el tiempo. Si tras varias lecturas, por ejemplo 125, o
lo que es lo mismo, 1 milisegundo, el valor es el mismo, pues
aceptamos esa lectura. Este proceso se denomina debouncing.

static volatile uint8_t enc_value;


static volatile uint8_t enc_tests;

# define PINS_READ_ENC PINB


# define PINS_WRITE_ENC PORTB
# define PINS_DDR_ENC DDRB
# define PINS_OFFSET_ENC_A 0
# define PINS_OFFSET_ENC_B 1

void encoder_read (void )


{
// leo las 2 patillas al mismo tiempo. Debeis de conectar las 2 patillas
del encoder en el mismo puerto!
const uint8_t input = PINS_READ_ENC;
const uint8_t read = ( BitIsSet ( input , PINS_OFFSET_ENC_B ) << 1 ) |
( BitIsSet ( input , PINS_OFFSET_ENC_A ) << 0 ) ;

if ( read != enc_value )
{
enc_tests = 125; // 1 ms a 125khz
enc_value = read;
return;
}

if (enc_tests > 0)
{
-- enc_tests ;
return;
}

if ( enc_tests == 0 )
{
// comprobamos si estamos en un pulso
}

}
El cdigo es C puro, pero facilmente lo podris pasar al arduino.
Realmente q la interrupcin sea de 125Khz no es relevante. 1 ms es
suficiente para la mayoria de usos.

Cmo saber si estamos o no estamos girando, y la direccion? Aqui


muchos tutoriales se lian un poco con la secuencia. Si miramos el
osciloscopio, vemos que un pulso lo tenemos cuando ambos pines
estn a 0. Y el valor previo nos dice si estamos girando hacia un lado o
hacia otro. Asi que cuando el valor de los 2 pines sea 0,
incrementamos o decrementamos en funcin de la posicin.

Tcnicamente necesitaramos salir del pulso, pero en general estos


encoders se usan para mover un cursor en el men de la pantalla y no
tiene sentido conocer los pasos intermedios del giro. Si tuviesemos un
encoder para medir la posicin de un servo entonces usaramos otras
formas de leer los datos, y seguramente, otro encoder ms caro :)

Asi que si recordamos la imagen del osciloscopio, vemos que cuando


ambos pines estn a 0, o bien el A o bien el B tienen que estar abajo
antes. Esto es 01 10 en binario: 1 2 en decimal.

Para incrementar el valor de la posicin actual (index), sumamos 1 al


contador, pero luego comprobamos que no nos pasamos de los pulsos
por vuelta (24 segn este modelo de encoder). Asi que sumamos 1 y
aplicamos el mdulo, para que al llegar a 23+1, volvamos a 0. De este
modo index se mover de 0 a 23.

Para decrementar, en vez de restar 1, damos una vuelta entera,


sumando 23 y aplciando el mdulo. De esta forma, no tenemos que
lidiar con valores negativos, y todo es ms sencillo luego.

El bit ms alto, el signo, del ndice, lo reservo para indicar la direccin


de giro. 0 significa al sentido del reloj, 1 al revs.

Al guardar el indice de esta forma, digamos que tenemos una


representacion angular del encoder, que representa de 0 a 360 grados
(con el valor de 0 a 23), con el signo para decirnos qu direccin
tomamos para girar.

static volatile uint8_t enc_value;


static volatile uint8_t enc_tests;
static volatile uint8_t enc_index;
static volatile uint8_t enc_previ;

# define PINS_READ_ENC PINB


# define PINS_WRITE_ENC PORTB
# define PINS_DDR_ENC DDRB
# define PINS_OFFSET_ENC_A 0
# define PINS_OFFSET_ENC_B 1

# define ENC_STEPS 24

# define ENC_INDEX_INC (enc_index = 0< 0)


{
-- enc_tests ;
return;
}

if ( enc_tests == 0 )
{
if ( ENC_READ_AB == 0)
{
if ( enc_previ == 1 ) ENC_INDEX_INC ;
else if ( enc_previ == 2 ) ENC_INDEX_DEC ;
}
enc_previ = ENC_READ_AB ;

}
}

Tambien podemos incluir aqui el valor del pulsador. Al incluir la rutina


de debounce, nos ahorramos tambien el tema del ruido, pero eso
hace que el pulso sea de al menos 1 ms, que depende de lo rapido que
seamos, puede ser o no suficiente. Usamos el bit 6 del index para
representar el estado actual del pulsador. El cdigo final quedara asi:

static volatile uint8_t enc_value;


static volatile uint16_t enc_tests;
static volatile uint8_t enc_index;
static volatile uint8_t enc_previ;

# define PINS_READ_ENC PINB


# define PINS_WRITE_ENC PORTB
# define PINS_DDR_ENC DDRB
# define PINS_OFFSET_ENC_A 0
# define PINS_OFFSET_ENC_B 1
# define PINS_OFFSET_ENC_BTN 2

# define ENC_STEPS 20

# define ENC_INDEX_INC (enc_index = 0<<7 | ( (enc_index & 0x3f) +


1 ) % ENC_STEPS)
# define ENC_INDEX_DEC (enc_index = 1<<7 | ( (enc_index & 0x3f) +
(ENC_STEPS-1) ) % ENC_STEPS)

# define ENC_READ_AB (read & 0x03)


# define ENC_READ_BTN (read & 1<<2)

void encoder_read (void )


{
// sure both reads same time
const uint8_t input = PINS_READ_ENC;
const uint8_t read = ( BitIsSet ( input , PINS_OFFSET_ENC_BTN ) <<
2 ) | ( BitIsSet ( input , PINS_OFFSET_ENC_B ) << 1 ) | ( BitIsSet ( input ,
PINS_OFFSET_ENC_A ) << 0 ) ;

if ( read != enc_value )
{
enc_tests = 125; // 1 milliseconds on 125khz timer (PEC11 datasheet
max debounce rate)
enc_value = read;
return;
}

if (enc_tests > 0)
{
-- enc_tests ;
return;
}

if ( enc_tests == 0 )
{
if ( ENC_READ_AB == 0)
{
if ( enc_previ == 1 ) ENC_INDEX_INC ;
else if ( enc_previ == 2 ) ENC_INDEX_DEC ;
}
enc_previ = ENC_READ_AB ;

if ( ENC_READ_BTN )
{
ClearBit (enc_index,6);
}
else
{
SetBit (enc_index,6);
}
}
}
Como ejercicio para el lector, queda poder medir la velocidad de giro,
contando los ticks que han pasado desde que dejan de estar ambos
pines a 1, hasta que vuelven a estar ambos pines a 1. Y si tienes
ganas, pasarlo a arduino para que la gente pueda usarlo rpidamente.

Por ahora funciona bien, si giras muy muy rpido puede perder algun
paso, sobre todo por el debouncer, pero no es relevante, ya que
personalmente voy a usar el encoder para controlar el men de una
pantalla, y es ms que suficiente.

http://www.leandroruiz.com/blog/arduino-encoder-rotativo/
Arduino + Encoder rotativo
En la entrada de hoy introduzco la utilizacin de un Encoder rotativo.
Estos encoders son ampliamente utilizados por ejemplo para el control
de volumen en radios de automviles. Mostraremos el resultado de
accionar este encoder en un display LCD de Nokia 5110. Nos
centraremos en la utilizacin del encoder rotativo, el manejo del
display podis verlo en este

El funcionamiento de este encoder rotativo es muy sencillo, al girarlo


se envan dos seales cuadradas desfasadas 90 grados entre s. La
clave es detectar el el flanco de bajada de la seal A. En este
momento, el sentido de la rotacin vienen indicado por el valor de la
seal B, tal y como se aprecia en la imagen.

En caso de realizar un giro horario, el pin B tendr un valor alto,


mientras que con un giro antihorario el pin B tendr valor bajo. El
encoder se conectar siguiendo el siguiente esquema:
Por otro lado, el cdigo usado en Arduino es:

1 #include "NokiaLCD.h"
2
3 // Setup del LCD
4 NokiaLCD NokiaLCD(3,4,5,6,7);// (SCK, MOSI, DC, RST, CS)
5 // Pines de lectura del encoder
6 int a =9;
7 int b =8;
8 // Valores de medida
9 int anterior =1;
10 int contador;
11 char texto[10];
12
13 void setup()
14 {
15 // Inicializamos el LCD
16 NokiaLCD.init();
17 NokiaLCD.clear();
18
19 // Inicializamos la lectura del encoder
20 pinMode(a, INPUT);
21 pinMode(b, INPUT);
22
23 // Inicializamos las variables
24 anterior =1;
25 contador =0;
26 escribir();
27 }
28
29 void loop()
30 {
31 // Lectura de A, si es 0, volvemos a medir para asegurar el valor
32 int actual = digitalRead(a);
33 if(actual ==0){
34 delay(10);
35 actual = digitalRead(a);
36 }
37
38 // Actuamos nicamente en el flanco descendente
39 if(anterior ==1&amp;&amp; actual ==0)
40 {
41 int valor_b = digitalRead(b);
42 if(valor_b ==1) contador++;// Si B = 1, aumentamos el contador
43 else contador--;// Si B = 0, reducimos el contador
44 escribir();// Escribimos el valor en pantalla
45 }
46 anterior = actual;
47 }
48
49 // Esta funcin escribe en el LCD el valor de
50 // la variable "contador"
51 void escribir()
52 {
53 String s = String(contador);// Convertimos el nmero en texto
54 s.toCharArray(texto, 10);// Convertimos el texto en un formato
55 compatible
56 NokiaLCD.clear();
57 NokiaLCD.setCursor(3,2);
58 NokiaLCD.print("Volumen:");
59 NokiaLCD.setCursor(64,2);
60 NokiaLCD.print(texto);
}
Finalmente, os dejo en breve vdeo del montaje realizado en
funcionamiento.

This entry was posted by leandro on 04/06/2013 at 6:27 pm, and is


filed under Arduino, Electrnica. Follow any responses to this post
through RSS 2.0. You can leave a response or trackback from your own
site.
http://www.mactronica.com.co/sensor-encoder-rotatorio-ky040-
75426600xJM
Descripcin:
PINES KY-040

Las salidas de pines para este codificador rotatorio se identifican en la


siguiente ilustracin.

El mdulo est diseado para que la mnima es de salida cuando se


cierran los interruptores y un alto cuando los interruptores estn
abiertos.

La baja se genera mediante la colocacin de un suelo en el pin C y


pasarlo a los pasadores CLK y DT cuando los interruptores estn
cerrados.

El alto se genera con una entrada de alimentacin de 5V y las


resistencias pull-up, de manera que CLK y DT son altos cuando los
interruptores estn abiertos.

No se ha mencionado anteriormente es la existencia de del interruptor


de botn que es parte integral del codificador. Si se presiona sobre el
eje, un interruptor normalmente abierto se cerrar. La funcin es til si
desea cambiar la funcin de interruptor. Por ejemplo, puede que desee
tener la capacidad entre los ajustes gruesos y finos.

Especificaciones tcnicas:

Modelo: KY-040

Tipo: Encoder incremental

Ciclos por revolucin (CPR): 20


voltaje de funcionamiento: 0 - 5V

Material: PCB + Brass

Peso: 10g

Tamao: 32 x 19 x 30 mm

Un codificador giratorio tiene un nmero fijo de posiciones por


revolucin. Estas posiciones se transmiten fcilmente a lo ms
pequeo "clicks" que gira el codificador.

En un lado del interruptor hay tres pasadores. Normalmente se


denominan A, B y C. En el caso de la KY-040, que estn orientadas
como se muestra.

Dentro del codificador hay dos interruptores. Una vez que el


interruptor se conecta al pin Un perno C y el otro conmutador conecta
pasador de B a C.

En cada posicin del codificador, o bien ambos interruptores estn


abiertos o cerrados. Por cada clic hace que estos interruptores para
cambiar los estados de la siguiente manera:

Si ambos interruptores estn cerrados , girando el


codificador hacia la derecha o hacia la izquierda una posicin har que
ambos interruptores para abrir
Si ambos interruptores estn abiertos , girando el
codificador hacia la derecha o hacia la izquierda una posicin har que
ambos interruptores se cierren.
La siguiente ilustracin es representativa de cmo se construye el
interruptor.

Como se puede ver, la posicin angular de la terminal A y el terminal B


es tal que:

Si gira el mando hacia la derecha har que el interruptor de


conexin de A y C para cambiar de estado en primer lugar.
Si gira el mando hacia la izquierda har que el interruptor de
conexin B y C para cambiar de estado en primer lugar.
Si nos vamos a representar a la apertura de un cierre de los
interruptores como formas de onda, se vera algo como esto.

Esencialmente, la determinacin de qu cambiar estados modificados


primero es cmo se determina el sentido de giro.
Si un cambiado de estado en primer lugar, el interruptor est girando
en sentido horario.

Si B cambi estados primero, el interruptor est girando en sentido


contrario a las agujas del reloj.

http://saber.patagoniatec.com/enconder-ky-040-arduino-argentina-
ptec/
Enconder KY 040

Un encoder rotativo es un elemento que indica mediante posiciones


codificadas su posicin. El utilizado en este proyecto es un encoder
rotativo con 12 posiciones (cada 30) e infinito, es decir, que podemos
dar vueltas hacia ambos lados sin lmite. Cuando pasamos por cada
paso se nota un pequeo resalte que indica que se ha llegado a la
nueva posicin. Estos codificadores constan de dos pines para el
pulsador (funciona como un pulsador normal) y tres pines para el
codificador. Los tres pines del codificador van conectados uno a masa
y los otros dos a las respectivas entradas que designemos en la placa
Arduino. Estas dos seales que salen del encoder nos dan un total de 4
combinaciones. 00, 01, 10, y 11. Esto se conoce como 2 bits de cdigo
Grey.
Si tratamos a los pines en cdigo binario, las leemos como 00, 01, 10,
o 11. Lasecuencia de las salidas del codificador mientras giramos en
sentido de las agujas del reloj es 00, 01, 11, 10. As que si se tiene una
lectura de 01, la siguiente lectura puede ser 00 o 11 dependiendo de
la direccin en que se gira. As que, mediante la adicin del valor
codificado anteriormente se botiene se obtiene 1 de 8 posibles
nmeros (0001, 0010, 0100, 0111, 1000, 1011, 1110 y 1101). Estas
cuatro combinaciones 1101, 0100, 0010 y 1011 indican un movimiento
horario y 1110, 0111, 0001 y 1000 un movimiento antihorario.

As que ahora podemos decir lo siguiente: (suma es igual a la ltima


lectura + lectura actual)

if (suma == 0b1101 | | == suma 0b0100 | | == suma 0b0010 | | ==


suma 0b1011) ValorEncoder + +;

/ / movimiento las agujas del reloj

if (suma == 0b1110 | | == suma 0b0111 | | == suma 0b0001 | | ==


suma 0b1000) ValorEncoder ;

/ / movimiento hacia la izquierda

Si quisiramos tratarlo con nmeros decimales tendramos:

if (suma == 13 | | == suma 4 | | suma == 2 | | == suma 11)


ValorEncoder + +;

if (suma == 14 | | == suma 7 | | suma == 1 | | == suma 8)


ValorEncoder ;
Las variables globales que se utilizan dentro de estas funciones tienen
un nombre especial. Se llaman variables voltiles (volatile), y se
utilizan porque sus valores pueden cambiar en cualquier momento. As
que si se utilizan dos variables voltiles, ya que puede no ser el mismo
valor de estas varialbes si se produce el cambio en ellas durante una
funcin de interrupcin.

Necesitamos una libreria que realize lo explicado anteriormente,


aunque se puede hacer sin una, pero fue de la manera en que
obtuvimos mejores resultados. Libreria: Encoder.zip.

El programa de ejemplo es el siguiente, esta incluido en la libreria:

#include <Encoder.h>

// Change these two numbers to the pins connected to your encoder.


// Best Performance: both pins have interrupt capability
// Good Performance: only the first pin has interrupt capability
// Low Performance: neither pin has interrupt capability
Encoder myEnc(5, 6);
// avoid using pins with LEDs attached

void setup() {
Serial.begin(9600);
Serial.println(Basic Encoder Test:);
}

long oldPosition = -999;

void loop() {
long newPosition = myEnc.read();
if (newPosition != oldPosition) {
oldPosition = newPosition;
Serial.println(newPosition);
}
}

Conctamos los pines CLK al pin 6 y el pin DT al 5 del arduino. GND va a


GND del arduino y el pin (+) del enconder a 5V del micro.

Cuando giramos hacia un lado empezara a subir los numeros y hacia el


lado contrario disminuira la cuenta.
Datasheet.

Materiales:

Arduino Uno.

Rotary encoder

http://bildr.org/2012/08/rotary-encoder-arduino/
Rotary Encoder + Arduino

One of the first things anyone does when they start working with the
Arduino is to connect it to a potentiometer and control the brightness
of and LED or move a servo. Well, a rotary encoder may look like a
potentiometer, but other than also having a knob, it is basically the
complete opposite.

A rotary encoder is a device that you can rotate infinitely. Simple ones
like this one I got from sparkfun have no real state like a pot does, so
when you start up, you wont be able to simply read from the encoder
where it is turned to. But because you can keep turning it it has no
beginning, middle or end anyways. However, if you keep track of that
rotation in code, you can use it as a knob input you can turn up or
down as much as you would like.

On most rotary encoders, when you rotate them, you will feel a bump.
These are known as steps, and most rotary encoders like this guy have
about 12 of these per rotation. But some have 200 or more. Basically
this step is the minimum amount you can rotate the encoder to
register any change.

Most simple encoders like this only make use of 3 pins, and one of
those is ground. Those other two pins change state and are always
either high or low, so they can only have a total of 4 combinations. 00,
01, 10, and 11. This is known as 2 bit gray code. So when you turn it,
the arduino can say Well you were at 01, and now you are at 00 so
you move this way. Or you were at 01, but now you are at 10 so you
must have moved the other way. You can see that this encoder has 5
pins, the other 2 are just a simple switch that is engaged when you
press down. (see the second illustration on the right)

It sounds super simple, and it kinda is, but what we can do is every
time a value changes we can check what direction it moved. Then if
we increment a value every time it turned one way, and deincrement it
when we move one step the other, we can keep track of how much it
has moved since we started. So if you want a knob that can turn up to
11, this is your guy. (there is a double pun in there I promise)

So, the really funky thing about a rotary encoder is for it to work, we
need to know every time those values change. This can be hard
because if the arduino is in the middle of doing something, like
delay(1000) or what have you, we will miss the change. So we need a
way to say to the arduino I dont care what you are doing, or when
you are doing it, if you see any of these two pins change state, you
drop everything and attend to them. To do this we need something
called interrupts.

Hookup for just the encoder without the integrated pushbutton


Hookup for encoder and the integrated pushbutton

Interrupts Are Magic


Interrupt pins are special pins that can stop your arduino and force it
to do something else before it moves on. Because they are special pins
you only get a few of them on your arduino, but these pins can watch
for any CHANGE (high to low / low to high), FALLING (high to low) or
RISING (low to high). You can attach interrupt functions to these pins,
so if a change happens, it will drop everything and run that function. It
gets funky as it breaks the basic linear nature of the arduino loop, but
can become the most powerful thing when you get the hang of it.

Any global variables that are used inside these functions have a
special name. They are called volatile variables, and for good reason.
Their values can change at any time. So if you use a volatile twice in
your loop, it may not be the same value the second time if it was
change during an interrupt function.

Code
To keep track of the rotary encoder we are going to do something that
will look really weird, so bear with me. The encoder has 2 digital pins
that are either HIGH (1) or LOW (0) right? If we treat the pins as binary,
we read them as 00, 01, 10, or 11. The sequence the encoder outputs
while spinning clockwise is 00, 01, 11, 10 repeat. So if you have a
reading of 01, the next reading can either be 00 or 11 depending on
the direction the knob is turned. So by adding the previous encoded
value to the beginning of the current encoded value we get 1 of 8
possible numbers (0001, 0010, 0100, 0111, 1000, 1011, 1110 & 1101)
1101, 0100, 0010 & 1011 all mean cockwise movement. 1110, 0111,
0001 & 1000 are all counter-clockwise.

So now we can say this: (sum is last reading + current reading)

if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum ==


0b1011) encoderValue ++; //clockwise movement

if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum ==


0b1000) encoderValue --; //counter-clockwise movement

If we wanted to treat the binary as decimal numbers we could even


shorten that to this:

if(sum == 13 || sum == 4 || sum == 2 || sum == 11) encoderValue +


+;

if(sum == 14 || sum == 7 || sum == 1 || sum == 8 ) encoderValue --;

Without pushbutton code


Copy Code
//From bildr article: http://bildr.org/2012/08/rotary-encoder-arduino/

//these pins can not be changed 2/3 are special pins


int encoderPin1 =2;
int encoderPin2 =3;

volatileint lastEncoded =0;


volatilelong encoderValue =0;

long lastencoderValue =0;

int lastMSB =0;


int lastLSB =0;

void setup(){
Serial.begin(9600);

pinMode(encoderPin1,INPUT);
pinMode(encoderPin2,INPUT);

digitalWrite(encoderPin1,HIGH);//turn pullup resistor on


digitalWrite(encoderPin2,HIGH);//turn pullup resistor on
//call updateEncoder() when any high/low changed seen
//on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);

void loop(){
//Do stuff here

Serial.println(encoderValue);
delay(1000);//just here to slow down the output, and show it will work
even during a delay
}

void updateEncoder(){
int MSB =digitalRead(encoderPin1);//MSB = most significant bit
int LSB =digitalRead(encoderPin2);//LSB = least significant bit

int encoded =(MSB << 1)|LSB;//converting the 2 pin value to single


number
int sum =(lastEncoded << 2)| encoded;//adding it to the previous
encoded value

if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum ==


0b1011) encoderValue ++;
if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum ==
0b1000) encoderValue --;

lastEncoded = encoded;//store this value for next time


}
Unless otherwise stated, this code is released under the MIT License
Please use, change and share it.
With pushbutton code
Copy Code
//From bildr article: http://bildr.org/2012/08/rotary-encoder-arduino/

//these pins can not be changed 2/3 are special pins


int encoderPin1 =2;
int encoderPin2 =3;
int encoderSwitchPin =4;//push button switch
volatileint lastEncoded =0;
volatilelong encoderValue =0;

long lastencoderValue =0;

int lastMSB =0;


int lastLSB =0;

void setup(){
Serial.begin(9600);

pinMode(encoderPin1,INPUT);
pinMode(encoderPin2,INPUT);

pinMode(encoderSwitchPin,INPUT);

digitalWrite(encoderPin1,HIGH);//turn pullup resistor on


digitalWrite(encoderPin2,HIGH);//turn pullup resistor on

digitalWrite(encoderSwitchPin,HIGH);//turn pullup resistor on

//call updateEncoder() when any high/low changed seen


//on interrupt 0 (pin 2), or interrupt 1 (pin 3)
attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);

void loop(){
//Do stuff here
if(digitalRead(encoderSwitchPin2)){
//button is not being pushed
}else{
//button is being pushed
}

Serial.println(encoderValue);
delay(1000);//just here to slow down the output, and show it will work
even during a delay
}

void updateEncoder(){
int MSB =digitalRead(encoderPin1);//MSB = most significant bit
int LSB =digitalRead(encoderPin2);//LSB = least significant bit

int encoded =(MSB << 1)|LSB;//converting the 2 pin value to single


number
int sum =(lastEncoded << 2)| encoded;//adding it to the previous
encoded value

if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum ==


0b1011) encoderValue ++;
if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum ==
0b1000) encoderValue --;

lastEncoded = encoded;//store this value for next time


}

http://www.instructables.com/id/Arduino-Rotary-Encoder-Simple-
Example-KY-040/
Step 1: Schematic Code & Parts List.

Parts List.

1. Arduino uno I used the RobotDyn Uno, a chinese copy with 7


analog pins.

2. Rotary encoder KY-040.


3. RGB Led KY-016 (don't need this can use any leds).

4. Set of Jumper wires MM/FF/MF.

Good luck!

KY-040_ROTARY_ENCODER.ino

http://www.arduinoecia.com.br/2015/08/como-usar-encoder-rotativo-
ky-040-arduino.html
Como utilizar um encoder rotativo com Arduino
Conhece o encoder rotativo ? Por fora, ele um componente muito
parecido com um potencimetro, mas internamente funciona de uma
forma bem diferente, sendo um componente interessante para voc
utilizar com seus projetos para Arduino. Eu utilizei um mdulo encoder
KY-040 para criar um pequeno sistema de seleo com display LCD
20x4.

O encoder um componente utilizado para converso de movimentos


rotativos (ou lineares) em impulsos eltricos de onda quadrada. Esse
impulsos podem ser lidos por um microcontrolador (no nosso caso,
vamos utilizar o Arduino), e geram uma quantidade exata de impulsos
por volta.

O encoder que utilizamos tem rotao contnua, e tambm um boto


(basta empurrar o eixo) que vamos usar para selecionar uma das
opes do "menu" no display.

Ligando o encoder no Arduino


O primeiro circuito de testes utiliza somente o encoder. Vamos utilizar
3 pinos do Arduino para ligar o componente, sendo que os pinos
analgicos A2 e A3 sero ligados nos pinos de medio, e o pino
digital 7 no pino do boto, junto com um resistor pull-up.

Caso voc esteja utilizando somente o encoder (sem o mdulo, siga o


esquema ligao abaixo:
Teste esse circuito carregando o programa abaixo, que utiliza a
biblioteca RotaryEncoder (download). Baixe a biblioteca e coloque-a
na pasta LIBRARIES da IDE do Arduino.
1 //Programa : Teste basico encoder Arduino
2 //Autor : Arduino e Cia
3
4 //Carrega a biblioteca do encoder
5 #include <RotaryEncoder.h>
6
7 //Pinos de ligacao do encoder
8 RotaryEncoder encoder(A2, A3);
9
10 //Variavel para o botao do encoder
11 int valor = 0;
12 int newPos = 0;
13
14 void setup()
15 {
16 pinMode(7, INPUT);
17 Serial.begin(9600);
18 Serial.println("Gire o encoder....");
19 }
20
void loop()
21 {
22 //Verifica se o botao do encoder foi pressionado
23 valor = digitalRead(7);
24 if (valor != 1)
25 {
26 Serial.println("Botao pressionado");
27 while (digitalRead(7) == 0)
28 delay(10);
29 }
30
31 //Le as informacoes do encoder
32 staticint pos = 0;
33 encoder.tick();
34 int newPos = encoder.getPosition();
35 //Se a posicao foi alterada, mostra o valor
36 //no Serial Monitor
37 if (pos != newPos) {
38 Serial.print(newPos);
39 Serial.println();
40 pos = newPos;
41 }
42 }
43

O programa aguarda que o usurio gire o encoder para a direita ou


para a esquerda, atualizando o valor de um contador. Girando para a
direita, o contador aumenta, e para a esqueda, o contador diminui.
Caso o boto seja pressionado, uma mensagem tambm aparece no
serial monitor:
Esse um circuito bem simples que voc pode usar para testar se o
seu encoder est funcionando corretamente.
Sistema de menu com display LCD 20x4 e encoder
Neste segundo circuito, vamos manter as ligaes do encoder que
utilizamos anteriormente, e adicionar um display LCD 20x4 ao
circuito. O potencimetro de 10K e serve para ajustar o contraste do
display. O resistor utilizado como pull-up do boto do encoder de 330
ohms.

No programa deste circuito, vamos primeiramente criar caracteres


customizados para os nmeros de 1 a 6 que forem selecionados (so
os nmeros "invertidos"). Conforme giramos o encoder, os nmeros no
display vo sendo substitudos por esses nmero invertidos.
No loop, testamos se o boto foi pressionado, e tambm limitamos as
opes de seleo entre 1 e 6, atravs do comando
encoder.setPosition(). O programa checa se o valor do encoder foi
alterado e, em caso positivo, chama a
rotina destaque_selecionado(), que atualiza os dados da tela.

1 //Programa : Menu com encoder e LCD 20x4


2 //Autor : Arduino e Cia
3
4 //Carrega biblioteca LCD e encoder
5 #include <LiquidCrystal.h>
6 #include <RotaryEncoder.h>
7
8 //Inicializa o LCD
9 LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
10
11 //Pinos de ligacao do encoder
12 RotaryEncoder encoder(A2, A3);
13
14 int contador = 1;
15 int contador_anterior = 1;
16 int valor = 0;
17
18 byte Bloco_Cheio[8] = {0b11111, 0b11111, 0b11111, 0b11111,
19 0b11111, 0b11111, 0b11111, 0b11111
20 };
21 byte Um_invertido[8] = {0b11011, 0b10011, 0b11011, 0b11011,
22 0b11011, 0b11011, 0b10001, 0b11111
23 };
24 byte Dois_invertido[8] = {0b10001, 0b01110, 0b11110,
25 0b11101,
26 0b11011, 0b10111, 0b00000, 0b11111
27 };
28 byte Tres_invertido[8] = {0b00000, 0b11101, 0b11011,
29 0b11101,
30 0b11110, 0b01110, 0b10001, 0b11111
31 };
32 byte Quatro_invertido[8] = {0b11101, 0b11001, 0b10101,
33 0b01101,
34 0b00000, 0b11101, 0b11101, 0b11111
35 };
36 byte Cinco_invertido[8] = {0b00000, 0b01111, 0b00001,
37 0b11110,
38 0b11110, 0b01110, 0b10001, 0b11111
39 };
40 byte Seis_invertido[8] = {0b11001, 0b10111, 0b01111,
41 0b00001,
42 0b01110, 0b01110, 0b10001, 0b11111
43 };
44
45 staticint pos = 1;
46 int newPos = 0;
47 int selecionado = 0;
48
49 void setup()
50 {
51 Serial.begin(9600);
52 //Inicializa o botao do encoder no pino 7
53 pinMode(7, INPUT);
54 //Define o LCD com 20 colunas e 4 linhas
55 lcd.begin(20, 4);
56 //Cria os caracteres customizados
57 lcd.createChar(0, Bloco_Cheio);
58 lcd.createChar(1, Um_invertido);
59 lcd.createChar(2, Dois_invertido);
60 lcd.createChar(3, Tres_invertido);
61 lcd.createChar(4, Quatro_invertido);
62 lcd.createChar(5, Cinco_invertido);
63 lcd.createChar(6, Seis_invertido);
64
65 //Informacoes iniciais
66 lcd.setCursor(0, 0);
67 lcd.print(" 1 2 3 4 5 6");
68 lcd.setCursor(1, 2);
69 lcd.print("Valor atual: 0");
70 lcd.setCursor(1, 3);
71 lcd.print("Selecionado: -");
72 }
73
74 void loop()
75 {
76 //Verifica se o botao do encoder foi pressionado
77 valor = digitalRead(7);
78 Serial.println(valor);
79 if (valor != 1)
80 {
81 Serial.println("Botao pressionado");
82 lcd.setCursor(14, 3);
83 selecionado = newPos;
84 lcd.print(selecionado);
85 //while (digitalRead(7) == 1)
86 // delay(10);
87 }
88
89 //Le as informacoes do encoder
90 encoder.tick();
91 newPos = encoder.getPosition();
92 if (pos != newPos)
93 {
94 //Limite maximo menu
95 if (newPos >6)
96 {
97 encoder.setPosition(6);
98 newPos = 6;
99 }
100 //Limite minimo menu
101 if (newPos <1)
102 {
103 encoder.setPosition(1);
104 newPos = 1;
105 }
106 Serial.print("Posicao: ");
107 Serial.println(newPos);
108 //Atualiza o menu no display
109 destaque_selecionado(newPos);
110 pos = newPos;
111 }
112 }
113
114 void destaque_selecionado(int conta)
115 {
116 //Define posicao inicial
117 int posicao = (conta * 3) - 1;
118 //Apaga selecao anterior
119 if (conta > pos)
120 {
121 lcd.setCursor(posicao - 4, 0);
122 lcd.print(" ");
123 lcd.print(conta - 1);
124 lcd.print(" ");
125 }
126 //Apaga selecao posterior
127 if (conta < pos)
128 {
129 lcd.setCursor(posicao + 2, 0);
130 lcd.print(" ");
131 lcd.print(conta + 1);
132 lcd.print(" ");
133 }
134
135 //Imprime blocos cheios
136 lcd.setCursor(posicao - 1, 0);
137 lcd.write((uint8_t)0);
138 lcd.write((uint8_t)0);
139 lcd.write((uint8_t)0);
140
141 //imprime valor
142 lcd.setCursor(posicao, 0);
143 lcd.write((uint8_t)(conta));
//Imprime Opcao atual
lcd.setCursor(14, 2);
144 lcd.print(conta);
}

Achou a explicao muito complicada ? D uma olhada no vdeo


abaixo, que mostra esse circuito em funcionamento:

O programa atualiza os valores somente na tela, mas voc pode


modificar facilmente este programa para que ele acione portas do
Arduino ou envie e receba dados de algum sensor.

Vous aimerez peut-être aussi