DÍA 177 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Fin de `extern`: La palabra mágica `static`

En el C tradicional, si quieres compartir variables entre archivos, usas extern float precio;. Esto es un antipatrón en sistemas críticos. Permite que el módulo de la pantalla, el de la red y el de botones alteren el precio sin control.

La solución es declarar tus variables valiosas dentro del archivo metrologia.c y anteponerles la palabra static. En C, static a nivel global significa: "Esta variable es invisible para el resto de los archivos del proyecto". El enlazador (Linker) bloqueará cualquier intento de acceso directo desde afuera.

Control de Aduanas: Getters y Setters

Como las variables ahora son invisibles, creamos "Puertas de Aduana" públicas llamadas Getters (Obtenedores) y Setters (Asignadores). Son funciones simples que retornan o modifican la variable privada. ¿Por qué agregar este paso extra?

  1. Validación (Setters): Si el Servidor Modbus ordena `SetPrecio(-5.00)`, el Setter evalúa el número, se da cuenta de que es negativo, rechaza la modificación y retorna `false`.
  2. Atomicidad (Getters): Si el procesador de 32 bits intenta leer una variable uint64_t estructurada, necesitará 2 ciclos de reloj. Si una interrupción ocurre exactamente entre esos dos ciclos, leerá la mitad del número viejo y la mitad del nuevo. Un Getter bloquea temporalmente las interrupciones (__disable_irq()) para garantizar que la variable se entregue intacta.
ENCAPSULAMIENTO DE DATOS: BARRERAS STATIC Y ACCESO ATÓMICO Mundo Público lcd_pantalla.c float L = Get_Litros(); modbus_rx.c Set_Precio( -15.0 ); hacker.c litros_globales = 0; Muralla 'static' Módulo Privado: metrologia.c Memoria Oculta static float _litros; static float _precio; static bool _despachando; GETTER (Aduana) 1. Desactiva Interrupciones 2. Retorna Copia Segura SETTER (Validador) if (valor < 0) RECHAZAR! else __disable_irq(); _precio = valor; LINKER ERROR!

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a crear el módulo metrologia.h/c. Ocultaremos las variables de la transacción actual (Litros, Precio, Estado) usando la palabra clave static. Luego escribiremos un Setter para el Precio que verifique límites legales (no vender por debajo de $5 ni arriba de $30 por litro). Finalmente, crearemos un Getter Atómico para leer los litros, garantizando que el Súper Bucle nunca lea una variable a medio escribir por culpa de una interrupción.

Archivo: metrologia.h (El Contrato Público)


/**
 * @file metrologia.h
 * @brief Interfaces de acceso seguro (Aduana de Datos)
 */

#ifndef METROLOGIA_H
#define METROLOGIA_H

#include <stdint.h>
#include <stdbool.h>

// Prototipos de Getters
float Metro_GetLitrosTotales(void);
float Metro_GetPrecioActual(void);
bool  Metro_GetEstadoVenta(void);

// Prototipos de Setters
bool Metro_SetPrecioActual(float nuevo_precio);
void Metro_IniciarVentaNueva(void);

#endif /* METROLOGIA_H */
        

Archivo: metrologia.c (La Bóveda Segura)


/**
 * @file metrologia.c
 * @brief Implementación de variables estáticas protegidas
 */

#include "metrologia.h"
#include "stm32f401xe.h" // Para __disable_irq() y __enable_irq()

// =========================================================================
// VARIABLES PRIVADAS (El prefijo '_' denota que son internas por convención)
// =========================================================================
static float _litros_despachados = 0.0f;
static float _precio_vigente = 10.55f;
static bool  _venta_en_curso = false;

// =========================================================================
// RUTINA DE HARDWARE (Interrupción)
// =========================================================================
// Supongamos que esta función es llamada por el hardware (EXTI) cada vez que el medidor gira.
// Como está en el mismo archivo, SÍ tiene permiso de tocar las variables directamente.
void EXTI0_IRQHandler(void) {
    // Limpiar bandera de interrupción
    EXTI->PR = (1 << 0);
    
    if (_venta_en_curso) {
        _litros_despachados += 0.01f; // Sumar 10 ml
    }
}

// =========================================================================
// GETTERS (Lecturas Atómicas Inquebrantables)
// =========================================================================

float Metro_GetLitrosTotales(void) {
    // ATOMICIDAD: Si no deshabilitamos interrupciones aquí, un evento EXTI 
    // podría interrumpir al procesador a la mitad de la copia de los 4 bytes 
    // que conforman este float, devolviendo un número totalmente corrupto.
    
    __disable_irq(); // Cerramos la bóveda
    float copia_segura = _litros_despachados;
    __enable_irq();  // Reabrimos la bóveda
    
    return copia_segura;
}

float Metro_GetPrecioActual(void) {
    // Como el precio rara vez cambia, la atomicidad no es crítica, 
    // pero es una excelente práctica industrial incluirla siempre.
    __disable_irq();
    float p = _precio_vigente;
    __enable_irq();
    return p;
}

// =========================================================================
// SETTERS (Validación y Filtrado Antifraude)
// =========================================================================

bool Metro_SetPrecioActual(float nuevo_precio) {
    
    // 1. REGLAS DE NEGOCIO Y LÍMITES
    // Un hacker Modbus inyecta un precio de 0.00
    if (nuevo_precio < 5.0f || nuevo_precio > 30.0f) {
        return false; // Rechazo inmediato. Operación abortada.
    }
    
    // 2. REGLAS DE CONCURRENCIA
    // La ley metrológica prohíbe cambiar el precio si una venta ya inició
    if (_venta_en_curso) {
        return false; 
    }
    
    // 3. APLICACIÓN ATÓMICA
    __disable_irq();
    _precio_vigente = nuevo_precio;
    __enable_irq();
    
    return true; // Éxito
}

void Metro_IniciarVentaNueva(void) {
    __disable_irq();
    _litros_despachados = 0.0f;
    _venta_en_curso = true;
    __enable_irq();
}
        

Archivo: main.c (El Consumidor Seguro)


#include "metrologia.h"

int main(void) {
    
    // Supongamos que recibimos un comando RS485 del servidor central
    float precio_recibido_red = -10.0f; 
    
    // Intentamos asignar el precio. El Setter nos protegerá del valor malicioso.
    if (Metro_SetPrecioActual(precio_recibido_red)) {
        // Exito
    } else {
        // Alerta: El servidor envió un precio ilegal o intentó 
        // cambiarlo durante una venta activa.
        // Enviar bandera de error Modbus.
    }
    
    Metro_IniciarVentaNueva();
    
    while(1) {
        // Actualizamos la pantalla con total confianza de que la lectura es matemáticamente perfecta
        float l = Metro_GetLitrosTotales();
        float p = Metro_GetPrecioActual();
        
        // lcd_print(l * p);
    }
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Integridad de Memoria y Resistencia Operacional Continua.

Imagina que tu pantalla gráfica a color (Día 172) lee la variable `litros` directamente, y justo en ese milisegundo de lectura, el sensor de flujo gira y dispara la interrupción. El procesador ARM Cortex se pausa a la mitad de leer los 4 bytes del float, suma los litros, y reanuda. La pantalla unirá la mitad del viejo número binario con la mitad del nuevo número binario, graficando "8,504 Litros" por una fracción de segundo, antes de volver a "15.01 Litros". El usuario verá un salto numérico monstruoso y cancelará el pago.

Al diseñar tu Arquitectura en C basada en **Aduanas (Getters/Setters)**, resolviste tres problemas de raíz: Protegiste tu sistema de precios contra inyecciones maliciosas del Modbus, aislaste el módulo de Metrología para que nadie en todo el software pueda resetear la venta por accidente, y al incluir `__disable_irq()` dentro de las llamadas, eliminaste el problema del **Data Tearing**. Tu máquina ahora puede leer y despachar a altas velocidades, manteniendo la pureza de sus matemáticas intacta.

📝 Resumen del Día