El lenguaje C fue diseñado para escribir sistemas operativos rápidos, asumiendo que el programador "sabe lo que hace". C te permite leer memoria que no te pertenece, dividir por cero y sobreescribir punteros de ejecución. En un videojuego, esto causa que la aplicación se cierre (Crash). En una bomba de infusión médica o en nuestro Despachador de Gas LP, esto causa la muerte.
MISRA (Motor Industry Software Reliability Association) es un conjunto de reglas creadas originalmente por Ford y Rover para el software de frenos ABS. Hoy es el estándar global para todo sistema crítico embebido (SIL-3 / SIL-4). Si tu código viola una regla MISRA, no puede ser implementado en un hardware crítico. Así de simple.
Aplica esto como un dogma en tu STM32 y serás mejor que el 90% de los programadores comerciales:
malloc): Prohibido usar malloc() o free(). Con 96KB de RAM, la memoria se fragmentará en 3 meses, tu RTOS se quedará sin bloque continuo (Heap Exhaustion) y la máquina colapsará. Usa static o arrays de tamaño fijo en tiempo de compilación.**ptr). Deben inicializarse siempre a NULL o a una dirección válida.stdint.h): Olvida el tipo int o long. Su tamaño cambia entre compiladores de 16, 32 y 64 bits. Usa EXCLUSIVAMENTE uint8_t, int32_t, uint32_t, etc. Sabes exactamente qué tamaño tiene en memoria.default: Todo switch-case DEBE tener un bloque default, incluso si crees que todas las opciones están cubiertas. En ese bloque debes atrapar errores catastróficos.buffer[i], se debe validar que i < MAX_SIZE.uint8_t estado;). Deben tener un valor conocido desde el ciclo 1 (uint8_t estado = 0;).goto: El uso de saltos incondicionales rompe el flujo estructurado del programa y hace el análisis estático casi imposible.Laboratorio de Código C: No basta con saber qué reglas usar, hay que saber cómo se ven. A continuación, compararemos un fragmento de código escrito por un estudiante con un fragmento certificado bajo la directiva MISRA-C para controlar la válvula electromecánica.
/**
* @file valvula_control.c
* @brief Comparativa: C Estándar (Peligroso) vs C-MISRA (Industrial)
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
// =========================================================================
// ❌ EL MODO AMATEUR (VIOLA LAS REGLAS MISRA-C)
// =========================================================================
// - Usa 'int' (Violación Regla 4.6: Tipos básicos sin tamaño fijo).
// - No inicializa 'status' (Violación Regla 9.1: Valor antes de uso).
// - No hay 'default' en el switch (Violación Regla 16.4).
// - Asume que 'pin_array' no se desbordará.
int pines_valvulas[3] = {10, 11, 12};
void Control_Valvula_Malo(int numero_valvula, int comando) {
int status;
// Si numero_valvula es 5, leerá memoria RAM fuera del arreglo (Buffer Overread)
int pin = pines_valvulas[numero_valvula];
switch(comando) {
case 0:
// Apagar PIN
status = 0;
break;
case 1:
// Encender PIN
status = 1;
break;
}
// Si comando era 2, status contendrá basura (memoria residual)
// y aplicará un estado indeterminado al hardware físico.
Aplicar_Hardware(pin, status);
}
// =========================================================================
// ✅ EL MODO ARQUITECTO INDUSTRIAL (CUMPLE MISRA-C)
// =========================================================================
// - Tipos estrictos (uint8_t).
// - Constantes ('static const' en lugar de variables globales libres).
// - Comprobación explícita de límites de matriz.
// - Switch con 'default' seguro.
// - Enum en lugar de "números mágicos" 0 y 1.
#define MAX_VALVULAS 3U // La 'U' obliga al compilador a tratarlo como Unsigned
typedef enum {
VALVULA_CERRAR = 0U,
VALVULA_ABRIR = 1U
} EstadoValvula_t;
static const uint8_t pines_valvulas_safe[MAX_VALVULAS] = {10U, 11U, 12U};
/**
* @brief Aplica estado seguro a válvula física
* @param num_valvula Índice de la válvula (0 a 2)
* @param comando Estado requerido de la enumeración
*/
void Control_Valvula_Seguro(uint8_t num_valvula, EstadoValvula_t comando) {
// 1. Regla MISRA: Inicialización explícita a un estado SEGURO.
uint8_t status_hardware = 0U;
// 2. Regla MISRA: Validación estricta de límites (Evita Buffer Overflow)
if (num_valvula >= MAX_VALVULAS) {
// Log Error Crítico OOB (Out-of-Bounds)
// Entrar a Failsafe.
return;
}
uint8_t pin_objetivo = pines_valvulas_safe[num_valvula];
// 3. Regla MISRA: Estructuras de decisión completas
switch(comando) {
case VALVULA_CERRAR:
status_hardware = 0U;
break;
case VALVULA_ABRIR:
status_hardware = 1U;
break;
default:
// 4. Regla MISRA: El caso default DEBE existir.
// Si la RAM se corrompió y 'comando' es 45, entramos aquí.
// Acción Segura Definitiva: Cerramos Válvula de Gas.
status_hardware = 0U;
// Disparar Alarma_Sistema_Corrupto();
break;
}
Aplicar_Hardware_Seguro(pin_objetivo, status_hardware);
}
Objetivo del día: Certificación SIL (Safety Integrity Level) y Mitigación de Riesgos Catastróficos.
Imagina que estás escribiendo el código de tu STM32 y usas un arreglo de 4 posiciones para guardar la telemetría de 4 sensores de flujo. Por un error humano de cálculo, un bucle for intenta escribir en la posición número 5. En lenguajes de alto nivel como Python, el programa simplemente arrojaría una excepción (`IndexError`). Pero el C Bare-Metal de tu STM32 no hace preguntas; obedece ciegamente. Escribirá el dato del sensor en el bloque de memoria RAM que estaba junto al arreglo. ¿Qué pasaría si ese bloque de memoria casualmente controlaba la variable "Estado de la Válvula"? El gas comenzará a salir de forma incontrolable a la atmósfera de la estación, sin que ningún log registre el fallo.
Al auditar todo el Firmware de tu Despachador de Gas LP con las **Normas MISRA-C:2012**, garantizas que tu sistema sea determinista, seguro y auditable. Utilizar tipos estrictos (`uint8_t`), erradicar el `malloc` dinámico (evitando que la RAM se fragmente tras 2 años encendida), obligar a escribir bloques `default` que cierren las válvulas si un dato se corrompe, y usar validación de límites (Bounds Checking) convertirá tu PCB casera en un equipo certificable internacionalmente para Zonas de Riesgo Explosivo (Normas IEC 61508 / ATEX). Has cruzado la línea de Programador a Arquitecto de Seguridad Industrial.
malloc/free) está estrictamente prohibido. Esto evita la fragmentación del Heap y los Memory Leaks (Fugas de memoria) a largo plazo. Toda la RAM debe reservarse estáticamente en la compilación.int, char, long) viola la norma de la portabilidad, ya que sus tamaños varían entre microcontroladores de 8, 16 o 32 bits. Se exige la librería estándar <stdint.h> para forzar anchos lógicos puros (uint8_t, int32_t).switch deben poseer la cláusula default, donde se debe programar un estado Fail-safe (apagar motores, cerrar válvulas).