Habiendo fracasado rotúndamente en usar inputattach en la "Parte 1: inputattach" que ni siquiera he podido publicar, he aceptado rebajarme a usar USBMouse.
Esta placa es de 3.3 v
"tolerante a 5v" en los pines marcados como FT en la hoja de datos, o
sea, prácticamente todos. Siendo que el encoder usa 5v, me facilita la vida, la parte analógica siempre me asusta.
Como en otras tantas ocasiones, lo que voy a contar no es la crónica exacta de lo que transité ni necesariamente respetaré el orden de lo que hice, pues te aseguro que te resultaría completamente incomprensible. En su lugar voy a hacer una especie de collage significativo.
El la introducción hay más contexto.
Conectar un botón y que se comporte como botón izquierdo
Quizás un poco contrario al Diseño Emergente, como ya tenía una idea intuitiva de hacia donde pensaba ir, no hice la implementación más sencilla que funcione, si no que medio que me anticipé.
Lo que hice fue usar una InterruptIn y consideré el rebote mediante un Timeout, tal que si llega una transición, espere un ratito y compruebe un momento despues en qué estado quedo y en caso de corresponder recién ahí actúe.
Como actuar es pedirle al Mouse press() o release(), eso no lo puedo invocar en el Callback de InterruptIn o Timeout pues esos métodos probablemente usan un mutex, cosa que no se puede hacer en un Interrupt Handler y además no es buena práctica ejecutar mucho código durante una interrupción, se lo doy a una EventQueue y en algún momento lo hace y aparentemente funcionó muy bien.
click |
Estos diagramas están en el repo en la carpeta doc, necesitás astah para verlos.
Dicho de otra manera, tenemos una clase genérica Debouncer<> que recibe un objeto y dos de sus métodos para rise() y fall(). Cuando Debouncer recibe por ejempo un rise(), le pide a Timeout que llame a un método propio checkRise() que verifica que esté risen y encola una llamada a otro método propio notifyRise() que va eventualmente a llamar al método asociado al objeto provisto.
Conectar el encoder rotativo
Un encoder rotativo tiene dos pines, que van diciendo 00->01->11->10 si lo girás para un lado y lo inverso si es para el otro, usando un Gray Coding. Si estuviste prestando atención, no podés dejar de notar que es como si estuvieses haciendo clicks en dos botones, asi que si asocio dos InterruptIn a un mismo objeto pero con cuatro métodos, en teoría debería funcionar.
Fijate, esta es la instanciación de un click izquierdo asociado al botón azul de la placa:
Debouncer<MouseClick> leftButtonDebouncer(
BUTTON1,
&queue,
&leftButton,
&MouseClick::press,
&MouseClick::release
);
y esto para el Encoder. objservá que es el mismo objeto encoder:
Debouncer<Encoder> clkEncoderDebouncer(
PC_9,
&queue,
&encoder,
&Encoder::clkUp,
&Encoder::clkDown
);
Debouncer<Encoder> dtEncoderDebouncer(
PC_8,
&queue,
&encoder,
&Encoder::dtUp,
&Encoder::dtDown
);
De hecho, funciona bastante bien usando una leve adaptación de la lógica del ejemplo de uso de USBMouse (en ./mbed-os/drivers/USBMouse.h), pero...
Muy excéntrico e irreversible |
En negro es el giro para un lado, ponele que es aceptable, pero cuando invertís, se destroza. Esto tiene que ver con pequeños errores que debería tratar con un conocimiento intermedio de trigonometría y análisis matemático que me falta, así que lo voy a resolver mediante ingenio. Además, no me quiero anticipar pues prometí que mi camino iba a ser instructivo a costa de la verácidad, hay un error con el tratamiento de los tiempos y los estados de las interrupciones, luego veremos.
Va mi primera POC, qué pasa si en lugar de calcular posiciones según los ángulos, dado que el encoder tiene 30 posiciones precalculo los desplazamientos. Comenzaré con sólo cuatro y girando siempre para el mismo lado.
cuatro puntos |
Ahora para una lado y para el otro, para pescarle la lógica.
ambos sentidos de giro |
En amarillo en el código la modificación para invertir el sentido, en el dibujo un leve movimiento del cursor del mouse para evidenciar que es correcto.
Ahora resta precalcular para 30 puntos.
30 pasos ambos sentidos |
Ese leve desplazamiento a la derecha es para diferenciar un sentido del otro.
30 pasos ambos sentidos |
Ya sé, ya se que le falta un montón de optimizaciones como calcular un solo cuadrante y usarlo de modo espejado, pero la verdad es que a esta placa le sobran bytes y ciclos de reloj, va a quedar así.
Aparte, el STEPS en realidad es 60, pues se generan dos interrupciones por paso.
Links útiles
https://os.mbed.com/blog/entry/Simplify-your-code-with-mbed-events/
El código está versionado y corresponde al commit "math fixed, still excentric"