Introduction to RTOS Part 9 - Hardware Interrupts | Digi-Key Electronics
¿Cómo integrar interrupciones de hardware con FreeRTOS?
Introducción a las interrupciones en sistemas embebidos
- En episodios anteriores, se discutió el uso de ciertas llamadas a funciones de FreeRTOS dentro de rutinas de servicio de interrupción (ISR), destacando la importancia de las interrupciones en sistemas embebidos.
- Muchos microcontroladores tienen periféricos integrados que generan interrupciones por hardware, como temporizadores o botones pulsados que cambian el voltaje en un pin.
- Algunos microcontroladores cuentan con buses de comunicación que permiten enviar y recibir datos sin intervención del CPU, notificando al procesador sobre eventos asíncronos mediante señales eléctricas.
Prioridad y manejo de interrupciones
- Las interrupciones por hardware tienen siempre una prioridad más alta que cualquier tarea en ejecución en FreeRTOS, independientemente de la prioridad asignada a esa tarea.
- Algunos microcontroladores permiten solo una interrupción por hardware a la vez, mientras que otros permiten interrumpir otras interrupciones (interrupciones anidadas).
- Al ocurrir una interrupción, se pausa la ejecución actual para ejecutar la ISR asociada; después se regresa a la tarea anterior o puede cambiarse a otra.
Creación y configuración de un temporizador por hardware
- Se comenzará creando un simple temporizador por hardware en el ESP32, que tiene cuatro temporizadores con un prescaler de 16 bits y un contador de 64 bits.
- El reloj base del temporizador es 80 MHz; al establecer un divisor adecuado, se puede hacer que el temporizador cuente a diferentes frecuencias. Por ejemplo, configurándolo para contar hasta un millón para obtener una frecuencia de 1 Hz.
Implementación del LED parpadeante
- Se utilizará el LED integrado del ESP32 y se creará un manejador para este demo utilizando la biblioteca HAL del ESP32 incluida en Arduino.
- La ISR simplemente alternará el estado del LED. Es importante asegurarse que esta ISR resida en RAM interna para acceso rápido.
Sincronización entre ISRs y tareas
- En la configuración inicial, se define el pin del LED como salida y se inicia el temporizador con los parámetros especificados.
- Se configura la ISR como función callback cuando el temporizador alcanza su valor máximo; además, se habilita para recargarse automáticamente tras cada activación.
Modificaciones para sincronización efectiva
- Se implementa una limitación CPU única y se ajusta el divisor para alcanzar el conteo máximo más rápidamente (en 100 ms).
- Se introduce una variable global contadora marcada como
volatilepara indicar al compilador que su valor puede cambiar fuera del contexto actual debido a su uso dentro de ISRs.
Uso seguro de mutexes y sección crítica
- Para proteger las secciones críticas en FreeRTOS usando ESP-IDF, es necesario emplear "spin locks" que evitan que otras tareas accedan simultáneamente a recursos críticos.
¿Cómo manejar secciones críticas en FreeRTOS?
Secciones Críticas y Tareas
- Las secciones críticas en ISR deben ser breves; la documentación de FreeRTOS advierte no llamar a otras funciones API dentro de estas secciones. Solo se incrementará una variable global antes de liberar el bloqueo y reactivar las interrupciones.
- Se creará una tarea llamada "print values" que decrementará un contador global en un bucle, imprimiendo su valor cada vez. Es crucial proteger esta función como sección crítica para evitar problemas con interrupciones.
- La versión estándar de FreeRTOS simplifica las secciones críticas al no incluir bloqueos por giro (spin locks). Si ocurre una interrupción durante la ejecución, esta no será descartada; el ISR se activará al salir de la sección crítica.
- La tarea esperará dos segundos para permitir que el ISR incremente la variable global varias veces. En la configuración inicial, se inicia el terminal serial y la tarea correspondiente.
- Al abrir el terminal serial, observamos que la variable global cuenta hacia abajo desde 19 cada dos segundos, mostrando repeticiones ocasionales debido a las interrupciones esperadas entre las impresiones seriales.
Sincronización entre Tareas e ISRs
- Existen funciones adicionales en FreeRTOS para sincronizar variables y pasar datos entre ISRs y tareas. Es importante usar solo funciones que terminen con "from isr" cuando se llamen desde un ISR.
- Se presentará cómo utilizar un semáforo binario dentro de un ISR. Dos tareas operan a diferentes prioridades: una tarea puede ser interrumpida por otra que espera un semáforo, lo cual provoca que el planificador ejecute la única tarea disponible.
- Cuando el ISR termina su trabajo sobre recursos compartidos (como modificar una variable global), libera el semáforo, permitiendo que la tarea previamente bloqueada continúe su ejecución.
- El procesamiento del dato debe diferirse a una tarea porque los ISRs deben ser cortos; esto permite que otras tareas tengan oportunidad de ejecutarse mientras los cálculos pesados son manejados posteriormente.
- En este ejemplo práctico, ajustamos temporizadores para activar el ISR cada segundo y leemos valores del convertidor analógico-digital (ADC), almacenando resultados en una variable global.
Uso Eficiente de Semáforos
- Utilizamos un semáforo binario para notificar a nuestra tarea lectora cuando es momento de leer desde la variable global. Esto requiere usar funciones especiales "from isr".
- Estas funciones nunca bloquearán ya que los ISRs no pueden esperar; si intentas tomar un mutex o semáforo sin disponibilidad, deberás manejarlo de manera diferente.
- Muchas funciones "from isr" tienen parámetros especiales para determinar si desbloquearon alguna nueva tarea debido a acciones previas como liberar un mutex o semáforo.
- Dentro de nuestra tarea principal, esperamos indefinidamente hasta que esté disponible el semáforo binario liberado por el ISR. Una vez disponible, imprimimos los valores del ADC obtenidos anteriormente.
Ejecución Final y Desafíos
- Al cargar este código en nuestro ESP32, deberíamos ver valores del ADC impresos en terminal cada segundo. Este método es efectivo para hacer esperar a una tarea hasta recibir datos disponibles desde un ISR.
Sincronización de Tareas e Interrupciones en RTOS
Diseño de un Sistema con Buffer Doble
- Se recomienda utilizar un buffer doble o circular para manejar la recolección de datos, permitiendo que una parte sea leída mientras la otra se llena. Esto es crucial para el cálculo del promedio de 10 muestras.
- La tarea B debe gestionar el terminal serial, haciendo eco de cualquier carácter recibido y mostrando el valor del promedio cuando se ingresa el comando "avg".
- Es importante considerar cómo manejar los desbordamientos del buffer, donde la interrupción intenta escribir en un elemento que aún no ha sido leído por la tarea A.
Aplicaciones Prácticas y Ejemplos
- Este diseño es común en sistemas operativos en tiempo real (RTOS), donde una tarea realiza cálculos complejos sobre datos entrantes mientras otra maneja la entrada/salida del usuario.
- Idealmente, se podría usar un controlador de acceso a memoria directa (DMA) para ayudar con la muestreo de datos, aunque la sincronización entre tareas e interrupciones seguiría siendo similar.
Desafíos y Consideraciones Futuras
- Al ingresar "avg", se muestra el promedio más reciente calculado. Este ejercicio integra muchos conceptos previos aprendidos.