Arquitectura de Temporizadores y Despacho por Cola
En un entorno RTOS, la coordinación de eventos cronométricos se delega típicamente a un hilo dedicado. Este patrón desacopla la lógica de caducidad del flujo principal, permitiendo gestionar múltiples vencimientos sin saturar el núcleo de ejecución.
Mecanismo de Espera con Límite Temporal
Cuando la tarea asignada al cronómetro inicia su ciclo, entra en estado de suspensión condicionada a señales externas o umbrales de tiempo. Para evitar bloqueos infinitos, el kernel emplea una rutina de espera acotada que recalcular dinámicamente el intervalo restante:
// Patrón adaptado: espera sincronizada con umbral finito
// El cálculo se realiza considerando la referencia de tick actual
BaseType_t xRetardoRestante = lProximaCaducidad - xTickCountActual;
vTaskSuspendHastaColaRestringida( xColaGestion, xRetardoRestante );
Este enfoque garantiza que la CPU libere ciclos mientras permanece inactiva, pero permite el despertar inmediato si otro módulo requiere reprogramar el cronómetro.
Alternativas de Implementación: Eficiencia vs. Aislamiento
Distintos kernels aplican estrategias opuestas para mantener las listas de activación:
- Actualización directa (Lista ordenada): Entornos como RT-Thread o implementaciones bare-metal insertan los ticks manualmente en una estructura binaria durante la interrupción de reloj base. Exige que las rutinas de servicio sean completamente no suspensivas y de complejidad O(1).
- Despacho mediado por cola (Estilo FreeRTOS): La aplicación empaqueta comandos en una cola protegida. Un hilo secundario consume estos mensajes y actualiza la topología de temporizadores. Este encapsulamiento blindiza al hardware contra latencias variables, aunque introduce un coste adicional de serialización.
La selección arquitectónica depende del criterio de diseño: plataformas automotrices o industriales críticas suelen preferir el acceso directo, mientras que sistemas de consumo priorizan la resiliencia ante picos de excepciones.
Restricciones Críticas en Manejadores de Interrupción (ISR)
Las rutinas ejecutadas en contexto de excepción operan bajo reglas estrictas. Intentar modificar estructuras internas que provoquen cambios de contexto representa un riesgo elevado de estabilidad.
// ⛔ Anti-patrón en ISR: Riesgo de suspensión cruzada
void PIN_EXTRINT_Handler(void) {
// Si la cola interna está saturada y se usa timeout > 0,
// pxCurrentTCB quedará suspendido silenciosamente.
vActualizarEstadoTemporal( pxHandle, pdTRUE );
// ... desborde de flag hardware
}
Ante saturación de la cola, el kernel congela el descriptor de tarea actualmente en ejecución. Durante una ISR, el puntero apunta al hilo preinterrumpido, no a la excepción misma. Aplicar un retardo aquí interrumpe arbitrariamente aplicaciones no relacionadas con el periférico generador.
La práctica recomendada implica utilizar versiones optimizadas para contexto real que garanticen atomicidad:
// ✅ Esquema estándar
void GPIO_EXTI_Callback(void) {
BaseType_t xPrioridadAsignada = pdFALSE;
// Transmisión atómica con retorno inmediato
xPostearEventoTemporalISR( pxCola, eReinicioPeriodico, &xPrioridadAsignada );
// Limpieza de estado hardware
LL_GPIO_EXTI_ClearFallingFlag(GPIOPIN);
// Flag opcional para desalojo posterior
portNOTIFICAR_DESPLAZAMIENTO_ISR( xPrioridadAsignada );
}
Programación Asíncrona del Scheduler y Vector PendSV
Las interfaces etiquetadas con sufijo ISR obedecen tres garantías fundamentales:
- Inmunidad a suspensión: Finalizan en un ciclo fijo, retornando estados de error si los recursos están ocupados.
- Activación de espera: Reubican tareas desde colas de bloqueo hacia listas de disponibilidad.
- Programación diferida: No fuerzan un salto de contexto durante la ejecución del manejador. Si se detecta migración a mayor prioridad, se configura un flag pendiente.
Esta estrategia aprovecha el vector de menor precedencia (PENDSV) en microcontroladores ARM Cortex-M. Tras marcar la bandera, el scheduler posterga la mutación de stack y el registro de contexto hasta que la instrucción en curso concluya. Se evitan así anidamientos destructivos y se mantiene la previsibilidad del uso de memoria.
Matriz de Comparación: Operaciones de Cola
| Parámetro | Rutina Convencional | Rutina FromISR |
|---|---|---|
| Gestión de Timeout | Admite configuración de retardo | Suprimido (completar o fallar inmediato) |
| Impacto en Stack Pointer | Puede redirigir al heap del scheduler | Mantiene rigurosamente el contexto activo |
| Disparo de Cambio de Tarea | Ejecución inmediata si prioridad aumenta | Solo marca bandera para procesamiento diferido |
| Comportamiento ante Saturación | Bloqueo controlable | Fallo no suspensivo |
Escenarios Técnicos Frecuentes
Recálculo de caducidad sin payload pendiente
Cuando expira el temporizador programado pero la cola no contiene nuevas órdenes, el hilo gestor ejecuta la callback asociada, regenera el intervalo hasta el siguiente tick crítico y reinicia su fase de espera acotada. Este ciclo cerrados preserva la periodicidad sin consumir ancho de banda extra.
Coexistencia entre interrupción base y handlers externos
Si una línea EXTI posee mayor precedencia que la interrupción de reloj sistémico, ambas pueden activarse concurrentemente a nivel eléctrico. No obstante, el planificador solo intervendrá una vez retornen todos los manejadores superiores. Los hilos relevantes se colocan en estado Ready, aguardando a que el nivel de anulación de máscaras permita su asignación de CPU.
Naturaleza del estado Pending en arquitectura NVIC
Todos los vectores de excepción integran un latch interno de captura. La presencia de pending no es exclusiva del descriptor PendSV; denota simplemente que un evento hardware fue registrado pero aún no ha sido despachado al Program Counter. El árbol de prioridad del MCU orquesta esta transición automáticamente antes de invocar cualquierrutina de servicio.