Llegamos a la quinta entrega de esta programación embebida de Arduino con Rust:
Continuamos con la quinta entrega en la que explicaremos un tema muy interesante de como utilizar las interrupciones externas.
Tenemos dos interrupciones externas que se activa en diferentes pines:
- INT0 Se activara mediante el pin digital D2
- INT1 Se activara mediante el pin digital D3
Definiremos una función callback para el tratamiento de la ISR (Interruption Service Rutine) esta función no tendrá parámetros ni retorno solamente ejecutara código, las reglas basicas de las funciones ISR son:
- Cortas y rápidas
- No usar funciones de tiempo
- Dentro de la función ISR los datos serie pueden perderse, por lo que no es buen idea su lectura.
- Para imprimir por consola, por ejemplo para saber cuando entra y que quede registro, se usaria una bandera y luego fuera ver si esta activa imprimelo que queramos.
- Si usamos variables que tienen que ser usada dentro y fuera de la función deberá declararse como std::sync::atomic.
Para su programación lo primero es hacer que el compilador aplique la convención de llamada correcta a los controladores de interrupción mediante el siguiente código.
#![feature(abi_avr_interrupt)]
Para habilitar una interrupción externa (INT0 o INT1) debemos tocar los registros EICRA(Registro de control de interrupciones A) que se ve en la figura y el registro EIMSK que es la mascara de las interrupciones externas
Registro EICRA (Registro de control de interrupciones A)
Registro EIMSK (Registro de mascara de interrupciones externas
Donde se ve que tenemos dos bits para la configuración de la interrupción INT0 que corresponde al registro ISC0 y la interrupción INT1 que usa el registro ISC1 y los valores que pueden tomarse se ven en la siguiente tabla.
// Habilitamos INT0 en flanco de bajada, es decir al pulsar el botón
dp.EXINT.eicra.modify(|_, w| w.isc0().bits(0x02)); // Registro EICRA isc0() Bits 0:1
dp.EXINT.eimsk.modify(|_, w| w.int0().set_bit()); // Registro EIMSK INT0 bit0 a 1.
Con los registros EICRA y EIMSK configurados debemos habilitar las interrupciones indicando que se trata de código inseguro «unsafe«
unsafe { avr_device::interrupt::enable() }; // Habilita las interrupciones
Y ya solo nos queda definir la función de tratamiento de la ISR, recordando que no tienen ni parámetros ni retorno así que la comunicación con el exterior lo deberá realizar con variables globales, para ser más exacto variables atómicas.
Las variables atómicas son de dos tipos (8bits y booleana) y para usarla tenemos tres funciones
// Declaración de variables atomicas
static ESTADO: AtomicI8 = AtomicI8::new(0); // 8 bits
static REBOTE: AtomicBool = AtomicBool::new(false); // Boolean
// Uso de las variables atómicas
ESTADO.load(Ordering::SeqCst); // Recoge el valor de la variable ESTADO
ESTADO.store(12, Ordering::SeqCst); // Cambia a 12 el valor de ESTADO
REBOTE.store(true, Ordering::SeqCst); // Cambia a true REBOTE
Y ya solo nos queda definir la función ISR que tratara la interrupción cuando se produzca.
#[avr_device::interrupt(atmega328p)]
fn INT0() { … } Interrupción 0 que se activa en el pin2.
#[avr_device::interrupt(atmega328p)]
fn INT1() { … } Interrupción 0 que se activa en el pin3.
Tenemos un código de ejemplo en https://github.com/pinguytaz/Arduino-ESP32/blob/master/CuadernoTecnico/RustArduino/src/bin/intexternas.rs