Hola controleros y controleras, en esta entrada aprenderemos a programar las interrupciones con MicroPython y para eso usaremos la poderosa Raspberry Pi Pico o también el NodeMCU8266.
Antes de comenzar, te hago la invitación que aprendas a programar microcontroladores con nuestro Curso Gratuito de MicroPython.
Y que te suscríbas al canal si te interesa la programación de microcontroladores o la teoría del control.
Interrupciones con Micropython
En esta entrada, veremos una característica más flexible presente en la mayoría de microcontroladores como el RP2040 o los ESP.
Veremos como emplear las interrupciones o IRQs (interrupt requests) en MicroPython.
En entradas pasadas aprendimos a programar interrupciones con el Arduino y de la misma forma interrupciones con el microcontrolador PIC.
Una interrupción es un requerimiento de prioridad que se le hace al microcontrolador. En ese punto, el microcontrolador deja de hacer su tarea, va y atiende la interrupción y finalmente retorna donde estaba para continuar con su tarea.
Interrupcciones en Micropython
Tener en cuenta que siempre que se programen interrupciones con micropython estas deben ser lo más simples posible, para que el microcontrolador vuelva rápidamente a la ejecución del programa principal.
Una buena práctica es informarle al código principal que ha ocurrido la interrupción mediante el uso de una variable global.
A continuación vamos a aprender como configurar las interrupciones en la raspberry pi pico y las interrupciones en el ESP8266 pero puedes extenderlo a otro microcontrolador usando MicroPython.
La función de manejo de interrupciones debe aceptar un parámetro de tipo Pin. Este parámetro le indica al sistema cual fue el GPIO que generó la interrupción.
def handle_interrupt(pin):
En nuestro programa principal se procede a configurar el GPIO como entrada el cual se va a configurar con el llamado de interrupción.
boton = Pin(14, Pin.IN)
Con el método de micropython irq() configuramos el Pin de Entrada para que actue ante el llamado de la interrupción. Simplificadamente podemos definir dos argumentos de entrada:
boton.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt)
Pero todos los parámetros del método irq de micropython son:
boton.irq(trigger=Pin.IRQ_RISING, handler=handle_interrupt, priority=1, wake=None, hard=False)
El método irq micropython acepta los siguientes argumentos, si el modo pin es, Pin.IN
entonces la fuente de interrupción es el valor externo en el pin. Si el modo pin es, Pin.OUT
entonces la fuente de disparo es el búfer de salida del pin. De lo contrario, si el modo pin es Pin.OPEN_DRAIN
, la fuente de disparo es el búfer de salida para el estado ‘0’ y el valor del pin externo para el estado ‘1’.:
- Trigger: define el modo de disparo. Hay 3 condiciones diferentes:
- Pin.IRQ_FALLING (flanco de bajada): para activar la interrupción siempre que el pin pase de ALTO a BAJO;
- Pin.IRQ_RISING (flanco de subida): para activar la interrupción siempre que el pin pase de BAJO a ALTO.
- handler: esta es una función que será llamada cuando se detecte una interrupción, en este caso la función handle_interrupt().
priority
establece el nivel de prioridad de la interrupción. Los valores que puede tomar son específicos del puerto, pero los valores más altos siempre representan prioridades más altas.wake
selecciona el modo de energía en el que esta interrupción puede despertar el sistema. Puede sermachine.IDLE
,machine.SLEEP
omachine.DEEPSLEEP
. Estos valores también pueden combinarse con OR para hacer que un pin genere interrupciones en más de un modo de alimentación.hard
si es verdadero, se utiliza una interrupción de hardware. Esto reduce la demora entre el cambio de pin y la llamada al controlador. Es posible que los manejadores de interrupciones estrictas no asignen memoria. No todos los puertos admiten este argumento.
Si necesita escribir un programa que ejecute una interrupción cada vez que cambia un pin, sin importar si está subiendo o bajando, puede combinar los dos argumentos del flanco de subida y bajada usando una or con la barra vertical (|):
boton.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=handle_interrupt)
Conteo de tiempo con utime micropython
Muchas veces cuando usamos interrupciones con nuestro microcontrolador (Raspberry Pi Pico, NodeMCU8266v3, etc) con micropython, se vuelve interesante poder medir tiempos de ejecución de la llamada del servicio de interrupción.
Además de las funciones de retardo, el método utime de Micropython nos ofrece otras funcionalidades para medir el tiempo.
Podemos comenzar, creando una variable que almacene la cantidad de milisegundos que han transcurrido desde que la biblioteca utime comenzó a contar. Para eso usamos la función de micropython utime.ticks_ms().
timer_start = utime.ticks_ms()
Esta variable nos puede servir como valor de referencia para próximas mediciones de tiempo.
Podemos usar utime.ticks_diff () para obtener la diferencia entre el momento en que se activa esta línea de código y el punto de referencia que se encuentra en la variable timer_start.
timer_elapsed = utime.ticks_diff(utime.ticks_ms(), timer_start)
Medir la Velocidad de un Motor DC con MicroPython
Para este ejemplo iremos a utilizar las interrupciones de nuestra placa Raspberry Pi Pico, o de la NodeMCU8266v3 lolin usando Micropython para medir la velocidad de un motor DC.
Para esto, vamos a colocarle un aspa a nuestro motor DC y colocaremos un encoder el cual lo vamos a realizar con leds infrarojos receptor y emisor. En este punto, puedes emplear si lo deseas ya el módulo del encoder que venden comercialmente.
En mi caso, dado que no dispongo de este sensor, voy a usar los componentes electrónicos leds infrarojos receptor y emisor (fotodiodos y fototransistores) y con un schmitt trigger 4093 para garantizar el estado lógico de la señal. Y de esa forma usar el Encoder con MicroPython para la lectura de la velocidad del motor.
Vamos a emplear el mismo circuito para controlar la velocidad de un motor DC usando la Señal de PWM con la Raspberry Pi Pico y también vamos a programar la NodeMCU ESP8266 a través de MicroPython como lo vimos en la entrada anterior.
""" Programa de Ejemplo de PWM Control de Giro y Velocidad de un motor DC con Puente H by: Sergio Andrés Castaño Giraldo controlautomaticoeducacion.com Canal de YouTube: https://www.youtube.com/c/SergioACastañoGiraldo """ import machine import utime def encoder_handler(pin): global paso paso += 1 def main(): global paso paso = 0 #Placa -> Raspberry Pi Pico = True, ESP8266 = False placa= False frequency = 10000 #10Khz sentido = True #Sentido derecha if placa: potenciometro = machine.ADC(26) #Raspberry Pi Pico ADC0 r_pwm = machine.PWM(machine.Pin(16), frequency) #PWM derecha l_pwm = machine.PWM(machine.Pin(17), frequency) #PWM izquierda boton = machine.Pin(15, machine.Pin.IN, machine.Pin.PULL_UP) encoder = machine.Pin(14, machine.Pin.IN) encoder.irq(trigger=machine.Pin.IRQ_FALLING, handler=encoder_handler) else: potenciometro = machine.ADC(0) #NodeMCU8266v3 ADC0 r_pwm = machine.PWM(machine.Pin(4), frequency) #PWM derecha l_pwm = machine.PWM(machine.Pin(5), frequency) #PWM izquierda boton = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP) encoder = machine.Pin(14, machine.Pin.IN) encoder.irq(trigger=machine.Pin.IRQ_FALLING, handler=encoder_handler) timer_start = utime.ticks_ms() while True: #Pregunta por el boton if not boton(): utime.sleep_ms(200) #Anti-Rebote while not boton(): pass utime.sleep_ms(200) #Anti-Rebote sentido = not sentido #Aplica el PWM al motor if placa: velocidad = potenciometro.read_u16(); if sentido: r_pwm.duty_u16(velocidad) l_pwm.duty_u16(0) else: r_pwm.duty_u16(0) l_pwm.duty_u16(velocidad) else: velocidad = potenciometro.read(); if sentido: r_pwm.duty(velocidad) l_pwm.duty(0) else: r_pwm.duty(0) l_pwm.duty(velocidad) """ # Usando únicamnete Retardo utime.sleep_ms(1000) state = machine.disable_irq() rpm = paso * 60 / 2 paso = 0 print(rpm, 'RPM') machine.enable_irq(state) """ timer_elapsed = utime.ticks_diff(utime.ticks_ms(), timer_start) if timer_elapsed >= 1000: #Calculo de las RPM (2 aspas) state = machine.disable_irq() rpm = paso * 60 / 2 paso = 0 machine.enable_irq(state) timer_start = utime.ticks_ms() print(rpm, 'RPM') if __name__ == '__main__': main()
Eso es todo por la entrada del dia de hoy, espero les haya gustado y hayan aprendido algo nuevo. Si te ha servido el contenido de esta entrada, de los videos y los códigos de implementación y deseas apoyar mi trabajo invitandome a un café super barato, puedes hacerlo en el siguiente link:
👉 Invitar a Sergio a un Café ☕️
Que esten muy bien, nos vemos en la siguiente entrada.
Mi nombre es Sergio Andres Castaño Giraldo, y en este sitio web voy a compartir una de las cosas que mas me gusta en la vida y es sobre la Ingeniería de Control y Automatización. El sitio web estará en constante crecimiento, voy a ir publicando material sobre el asunto desde temas básicos hasta temas un poco más complejos. Suscríbete al sitio web, dale me gusta a la página en Facebook y únete al canal de youtube. Espero de corazón que la información que comparto en este sitio, te pueda ser de utilidad. Y nuevamente te doy las gracias y la bienvenida a control automático educación.