CMSIS significa Cortex Microcontroller Software Interface Standard. Es una iniciativa de la empresa ARM para unificar la forma en que programamos sus procesadores. CMSIS-OS v2 es una capa de abstracción para Sistemas Operativos (RTOS). En lugar de escribir xTaskCreate (que es exclusivo de FreeRTOS), la industria exige usar osThreadNew. Por debajo, esta función llama a FreeRTOS, pero si mañana te cambias de RTOS, tu código de alto nivel se mantiene intacto.
En el Súper Bucle, la pantalla LCD es tu peor enemigo. Dibujar un número a color por SPI puede tardar 10 o 15 milisegundos. En el mundo de la metrología de fluidos, 15ms es una eternidad. Si la válvula debía cerrarse en el milisegundo 5, pero el procesador estaba ocupado pintando píxeles verdes, derramarás gas y regalarás dinero.
Al dividir la máquina en Hilos (Threads), le asignamos la prioridad osPriorityHigh a la Tarea de Metrología, y la prioridad osPriorityNormal a la Pantalla. Cuando la pantalla esté a la mitad de dibujar un número, el reloj del sistema (Tick) interrumpirá a la CPU, el Scheduler revisará y dirá: "¡La Metrología es urgente!". Congelará la pantalla, leerá el gas, cerrará la válvula, y luego devolverá el control a la pantalla. Todo en microsegundos.
Ejercicio 1: Abandonamos el código nativo de FreeRTOS e importamos cmsis_os2.h. Declararemos las estructuras de atributos (osThreadAttr_t) que le dicen al sistema operativo cuánta memoria SRAM necesita cada hilo. Luego, construiremos los dos hilos principales: el bucle bloqueante de la pantalla (UI) y el bucle estricto de la Válvula.
/**
* @file main_os2.c
* @brief Orquestación de Tareas con CMSIS-OS v2 en el Despachador de Gas
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h" // Abstracción ARM oficial para RTOS
#include "metrologia.h"
#include "st7789_driver.h"
// =======================================================
// HILO 1: INTERFAZ GRÁFICA (Baja Prioridad)
// =======================================================
void Thread_Pantalla(void *argument) {
// Todo lo que ocurra antes del while(1) se ejecuta solo 1 vez al arrancar
ST7789_TFT_Init();
// Este es el "Súper Bucle" exclusivo de la pantalla
for (;;) {
// Leer datos atómicos protegidos por el Getter
float litros = Metro_GetLitrosTotales();
// Esta operación es lentísima (15 a 20ms) dibujando por SPI.
// En Bare-Metal, bloquearía todo. Aquí, no importa.
UI_Dibujar_Numeros_Gigantes(litros);
// Rendimos la CPU durante 33 milisegundos (~30 fps)
// La tarea pasa al estado BLOCKED.
osDelay(33);
}
}
// =======================================================
// HILO 2: METROLOGÍA DE PRECISIÓN (Alta Prioridad)
// =======================================================
void Thread_Metrologia(void *argument) {
for (;;) {
// Ejecutamos el algoritmo PID y leemos los pulsos
float lts = Leer_Caudalimetro();
// Si alcanzamos el límite exacto, la válvula se cierra AHORA.
// Debido a que su prioridad es ALTA, si este hilo despierta,
// destrozará cualquier dibujo de pantalla a la mitad para ejecutarse.
if (lts >= 20.0f) {
Valvula_SetApertura(0.0f);
}
// Dormimos exactamente 10 milisegundos.
// osDelayUntil garantiza un ritmo absoluto (ejecución periódica estricta),
// ideal para algoritmos matemáticos como el PID.
osDelay(10);
}
}
// =======================================================
// CONFIGURACIÓN E INICIO DEL RTOS
// =======================================================
int main(void) {
// 1. Inicializar Hardware Crítico
SystemClock_Config();
// 2. Inicializar el Kernel CMSIS-OS
osKernelInitialize();
// 3. ATRIBUTOS DEL HILO DE PANTALLA
const osThreadAttr_t pantalla_attr = {
.name = "UI_Task",
.stack_size = 1024 * 4, // Pantallas a color necesitan mucha RAM (4KB)
.priority = (osPriority_t) osPriorityNormal,
};
// 4. ATRIBUTOS DEL HILO DE METROLOGÍA
const osThreadAttr_t metrologia_attr = {
.name = "Metro_Task",
.stack_size = 1024 * 1, // Matemáticas simples, poca RAM (1KB)
.priority = (osPriority_t) osPriorityHigh, // Prioridad de vida o muerte
};
// 5. CREACIÓN DE LOS HILOS
osThreadNew(Thread_Pantalla, NULL, &pantalla_attr);
osThreadNew(Thread_Metrologia, NULL, &metrologia_attr);
// 6. ARRANCAR EL MOTOR DEL TIEMPO
osKernelStart();
// En un RTOS sano, la ejecución jamás llega a este punto.
while (1);
}
Objetivo del día: Aislamiento de Procesos Lentos mediante Multi-Threading.
Cuando programaste tu pantalla a color ST7789 (Día 171), viste que transferir $115,200$ bytes por SPI tomaba un tiempo físico inevitable. En el antiguo paradigma Bare-Metal, el Despachador entero se "congelaba" mientras se pintaba el rojo y el azul en la pantalla. Si en esos milisegundos el usuario soltaba el gatillo de la pistola de gas, la máquina no se enteraba, provocando un grave riesgo de seguridad.
Al implementar `osThreadNew` hoy, le has otorgado a tu sistema **Separación de Intereses (Separation of Concerns) en tiempo de ejecución**. Le diste a la Metrología el poder dictatorial (`osPriorityHigh`). Ahora, la pantalla gráfica puede tardar 20 o 50 milisegundos en dibujarse sin problemas; si en medio de un trazo el sensor requiere atención, el Kernel de CMSIS-OS pausa microscópicamente el dibujo, cierra la válvula con precisión milimétrica, y reanuda el pintado de la pantalla sin que el usuario note el corte. Has logrado la magia de la verdadera multitarea concurrente.
osDelay()** es fundamental. Pone a la tarea en estado `BLOCKED` (durmiendo) y le avisa al Planificador que entregue el 100% de la capacidad de procesamiento de la CPU a otras tareas durante esa ventana de tiempo.