Cuando utilizas la creación dinámica (el comportamiento por defecto), le dices al RTOS: "Crea una tarea, por favor búscame 1KB de RAM libre donde sea que haya espacio". El RTOS revisa su piscina de memoria (Heap), encuentra un pedazo, lo recorta y te lo da.
A lo largo de los meses, si el sistema crea y destruye colas, tareas temporales o conexiones TCP (WiFi), el Heap se llena de huecos. Llega un momento en que tienes 5KB libres en total, pero están divididos en fragmentos de 100 bytes separados. Si intentas crear una Cola que necesita 512 bytes continuos, el RTOS fallará y tu programa crasheará (Out Of Memory).
La Asignación Estática cambia la narrativa. Tú, como Arquitecto, defines globalmente los arreglos exactos que el RTOS va a usar. Escribes algo como uint8_t stack_metrologia[1024];. El compilador de GCC, en el momento de generar el binario (antes de que la máquina se encienda), asigna esa memoria permanentemente. Nunca se crea, nunca se destruye, y su dirección en la RAM jamás cambia.
Esto garantiza Determinismo de Memoria (100%). Si tu programa compila y el enlazador (Linker) no marca error de RAM excedida, entonces es matemáticamente imposible que la máquina se quede sin memoria mientras esté operando en campo.
Ejercicio 1: Para usar memoria estática en FreeRTOS, primero debemos ir a nuestro panel de control `FreeRTOSConfig.h` y asegurar que la macro configSUPPORT_STATIC_ALLOCATION esté en 1. Luego, utilizando CMSIS-OS v2, llenaremos las estructuras de atributos apuntando físicamente a nuestros arreglos previamente creados, prohibiendo al RTOS que use su Heap dinámico interno.
FreeRTOSConfig.h
// Desactiva la asignación dinámica por completo para cumplir norma aeronáutica/SIL
#define configSUPPORT_DYNAMIC_ALLOCATION 0
// Activa la asignación estática obligatoria
#define configSUPPORT_STATIC_ALLOCATION 1
main.c
/**
* @file alloc_estatica_rtos.c
* @brief Implementación de Hilos sin Memoria Dinámica (Certificable SIL-3)
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
// =======================================================
// 1. DECLARACIÓN ESTÁTICA GLOBAL DE MEMORIA (Sección .bss)
// El compilador reserva estos bytes exactamente aquí, para siempre.
// =======================================================
// A) Memoria para la Tarea de Metrología
#define STACK_SIZE_METRO 1024 // Bytes
uint8_t memory_stack_metro[STACK_SIZE_METRO]; // El Pila de la tarea
osThreadCb_t control_block_metro; // El "TCB" (Cerebro de la tarea)
// B) Memoria para la Cola de Mensajes (Ej. 10 mensajes de 8 bytes)
#define MSG_COUNT 10
#define MSG_SIZE 8
uint8_t memory_buffer_cola[MSG_COUNT * MSG_SIZE]; // El tubo de los mensajes
osMessageQueueCb_t control_block_cola; // El Control Block de la Cola
// =======================================================
// 2. CONFIGURACIÓN DE ATRIBUTOS PARA CMSIS-OS V2
// =======================================================
const osThreadAttr_t attr_tarea_metro = {
.name = "Metro_Task",
.cb_mem = &control_block_metro, // Le decimos: "Usa mi TCB estático"
.cb_size = sizeof(control_block_metro),
.stack_mem = &memory_stack_metro[0], // Le decimos: "Usa mi RAM estática"
.stack_size = sizeof(memory_stack_metro),
.priority = osPriorityHigh
};
const osMessageQueueAttr_t attr_cola_comandos = {
.name = "Cmd_Queue",
.cb_mem = &control_block_cola,
.cb_size = sizeof(control_block_cola),
.mq_mem = &memory_buffer_cola[0],
.mq_size = sizeof(memory_buffer_cola)
};
// =======================================================
// LA TAREA (Idéntica, el código no se entera de cómo fue creada)
// =======================================================
void Thread_Metrologia(void *argument) {
for (;;) {
// Código de despacho de Gas...
osDelay(10);
}
}
// =======================================================
// INICIALIZACIÓN
// =======================================================
int main(void) {
SystemClock_Config();
osKernelInitialize();
// Al usar 'New' con atributos estáticos bien definidos,
// CMSIS-OS / FreeRTOS sabe que NO debe usar malloc() internamente.
// Simplemente "amarra" sus punteros a nuestras variables globales.
osMessageQueueId_t mi_cola = osMessageQueueNew(MSG_COUNT, MSG_SIZE, &attr_cola_comandos);
osThreadId_t mi_hilo = osThreadNew(Thread_Metrologia, NULL, &attr_tarea_metro);
osKernelStart();
while (1);
}
Objetivo del día: Cumplimiento de Normativa MISRA-C y Tolerancia a Fallos a Largo Plazo.
Tu Despachador de Gas LP no es un teléfono celular que la gente reinicia cuando se pone "lento". Es infraestructura industrial crítica que se atornilla al suelo de una estación de servicio y opera ininterrumpidamente bajo el sol y la lluvia durante años. Si el software se fragmenta y sufre un *Out of Memory* mientras una válvula de presión está abierta, las consecuencias son devastadoras.
Al migrar la arquitectura completa del RTOS a **Asignación Estática (Static Allocation)**, has superado uno de los requisitos más severos de las certificaciones de software seguro (como IEC 61508 SIL-3 o la normativa automotriz ISO 26262). Le demostraste al compilador exactamente cada byte de memoria que el sistema utilizará. Si te excedes de los 96 KB del STM32, el error saltará *ahora mismo* en tu computadora durante la compilación, y no dentro de 6 meses a las 3 de la mañana en una gasolinera. Has convertido el firmware de un programa probabilístico a una entidad matemáticamente determinista.
malloc o `configSUPPORT_DYNAMIC_ALLOCATION`)..bss o .data en el momento exacto en que se graba el chip (Flasheo). Es inmutable y no sufre de fragmentación.osThreadAttr_t), llenando explícitamente los punteros .cb_mem (Para el Bloque de Control de Tarea) y .stack_mem (Para el área de trabajo de la tarea o pila) apuntando a nuestros arreglos globales protegidos.