Cuando creas una tarea (Hilo) y le asignas 1,000 bytes de Stack, esos bytes inician llenos de "basura" remanente en la RAM. FreeRTOS, al crear la tarea, aplica un truco brillante: Pinta cada uno de esos 1,000 bytes con un patrón conocido, usualmente 0xA5 (10100101 en binario).
A medida que tu tarea se ejecuta, llama a funciones, crea variables locales (como int x = 5;) y guarda los registros de la CPU durante los Cambios de Contexto. Todo esto consume bytes, borrando el patrón 0xA5 desde abajo hacia arriba (o de arriba hacia abajo, dependiendo del procesador).
La "Marca de Agua Máxima" es un término que viene de las inundaciones en las ciudades; es la mancha de humedad que queda en la pared y que indica lo más alto que llegó el agua, incluso si la inundación ya bajó. En FreeRTOS, la función uxTaskGetStackHighWaterMark() empieza a leer el Stack desde el final hacia el principio, contando cuántos bytes conservan el patrón 0xA5 sin tocar. Ese número representa el mínimo espacio libre histórico de tu tarea.
En **CMSIS-OS v2**, esta funcionalidad se abstrae elegantemente mediante la función osThreadGetStackSpace(), que te devuelve directamente los bytes de salvavidas que le quedan a tu tarea.
Ejercicio 1: Vamos a programar una "Tarea Auditora". Su único trabajo será despertar cada 10 segundos, medir la memoria sobrante de las demás tareas (usando el identificador osThreadId_t que guardamos al crearlas) e imprimir esa información por el puerto serial (UART). Si una tarea tiene un remanente menor a 64 bytes, lanzaremos una advertencia de peligro de desbordamiento.
FreeRTOSConfig.hPara que CMSIS-OS v2 pueda leer esta información, debemos asegurarnos de encender el módulo de Watermark en la configuración base del Kernel:
#define INCLUDE_uxTaskGetStackHighWaterMark 1
main.c
/**
* @file monitor_memoria_rtos.c
* @brief Auditoría en tiempo real de uso de Stack mediante CMSIS-OS v2
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <stdio.h>
// IDs globales para poder ser inspeccionados
osThreadId_t hilo_metro_id;
osThreadId_t hilo_wifi_id;
osThreadId_t hilo_pantalla_id;
// =======================================================
// LA TAREA AUDITORA (Prioridad Baja)
// =======================================================
void Thread_Auditor_Memoria(void *argument) {
char buffer_uart[128];
uint32_t espacio_libre;
// Esperamos 5 segundos para dejar que el sistema "caliente" y
// las tareas lleguen a su punto máximo de estrés matemático.
osDelay(5000);
for (;;) {
// 1. Revisar Metrología
espacio_libre = osThreadGetStackSpace(hilo_metro_id);
snprintf(buffer_uart, sizeof(buffer_uart), "[MEM] Metrologia: %lu bytes libres\r\n", espacio_libre);
UART_Enviar_String(buffer_uart);
// 2. Revisar WiFi
espacio_libre = osThreadGetStackSpace(hilo_wifi_id);
snprintf(buffer_uart, sizeof(buffer_uart), "[MEM] WiFi IoT : %lu bytes libres\r\n", espacio_libre);
UART_Enviar_String(buffer_uart);
// 3. Revisar Pantalla UI
espacio_libre = osThreadGetStackSpace(hilo_pantalla_id);
snprintf(buffer_uart, sizeof(buffer_uart), "[MEM] Pantalla : %lu bytes libres\r\n", espacio_libre);
UART_Enviar_String(buffer_uart);
// Lógica de Alarma: Si algún hilo tiene menos de 64 bytes de "salvavidas"
if (espacio_libre < 64) {
UART_Enviar_String("--- ALARMA: RIESGO DE STACK OVERFLOW ---\r\n");
// Aquí en la fase de desarrollo, ajustaríamos los arreglos estáticos del Día 196
}
UART_Enviar_String("----------------------------------\r\n");
// Revisar cada 10 segundos
osDelay(10000);
}
}
// Tareas de Aplicación Simples
void Thread_Dummy(void *argument) {
// Si metes un arreglo inmenso aquí, destrozarás el Stack
// char buffer_gigante[2000]; -> ¡ESTO CAUSARÁ HARDFAULT INMEDIATO!
for(;;) { osDelay(100); }
}
// =======================================================
// INICIALIZACIÓN
// =======================================================
int main(void) {
SystemClock_Config();
osKernelInitialize();
// Creamos las tareas y guardamos estrictamente sus IDs
hilo_metro_id = osThreadNew(Thread_Dummy, NULL, &attr_metro);
hilo_wifi_id = osThreadNew(Thread_Dummy, NULL, &attr_wifi);
hilo_pantalla_id = osThreadNew(Thread_Dummy, NULL, &attr_pantalla);
// Creamos el Auditor con prioridad Baja (No debe robar CPU a la operación)
osThreadNew(Thread_Auditor_Memoria, NULL, NULL);
osKernelStart();
while (1);
}
Objetivo del día: Sintonía Fina de Memoria RAM para Certificación.
Ayer reservamos arreglos estáticos de memoria para tu máquina. A la Pantalla a Color le dimos 4096 bytes, a la Metrología 1024 bytes y al WiFi 2048 bytes. Pero el STM32F401 tiene un límite rígido de 96 KB totales. Para tu tesis, debes justificar matemáticamente por qué asignaste esos tamaños.
Al implementar el **Hilo Auditor de Memoria (Watermark)** durante la fase de validación de tu proyecto, hiciste un descubrimiento empírico: Al imprimir los resultados por el puerto UART, notaste que la Tarea de Metrología jamás bajaba de 800 bytes libres (solo usaba ~224 bytes para sus matemáticas de control PID). En cambio, la Tarea WiFi mostraba una alerta roja: solo le quedaban 16 bytes libres cuando el chip ESP32 procesaba un JSON muy largo. Gracias a esta auditoría en vivo, pudiste regresar al archivo de configuración estática (del Día 196) y transferir 512 bytes de la Metrología hacia el WiFi. Has balanceado el peso de tu arquitectura a ciegas, logrando un sistema que aprovecha el 100% de la RAM disponible sin riesgo de colapsos (HardFaults).
0xA5) al momento de instanciar la tarea.0xA5 quedan intactos al final del área reservada.