DÍA 174 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Secreto de la Memoria en C (Herencia)

¿Cómo puede un lenguaje que no tiene objetos heredar propiedades? La respuesta está en cómo el compilador acomoda los datos en la memoria RAM. Si colocas un `struct Base` como el **primer elemento exacto** de un `struct Derivado`, el compilador garantiza que ambas estructuras compartirán la misma dirección de inicio en memoria.

Esto significa que puedes tomar un puntero al objeto Derivado (Caudalímetro), hacerle un "Casteo Fuerte" (Base*), y pasárselo a una función genérica. ¡El compilador se lo tragará perfectamente sin corromper la memoria!

Punteros a Función (Polimorfismo / V-Table)

El Polimorfismo significa "muchas formas". Quieres que todos los sensores tengan un método llamado `Actualizar()`, pero que la forma en que lo hagan sea diferente. En C, agregamos un **Puntero a Función** dentro del `struct Base`. Al crear el objeto específico (ej. Termómetro), conectamos ese puntero a la función real `Leer_ADC()`. Así, el Súper Bucle solo llama a `sensor->Actualizar()`, y el lenguaje enruta la llamada dinámicamente a la función correcta de hardware.

OOP EN C PURO: MAPA DE MEMORIA PARA HERENCIA Y POLIMORFISMO struct Sensor_Base uint32_t id; (4 bytes) float valor; (4 bytes) void (*actualizar)(Sensor*) Puntero "Virtual" (4 bytes) Herencia struct PT100_ADC Sensor_Base super; (Offset 0) id valor (*actualizar) uint8_t canal_adc; float compensacion; Memoria Flash (ROM) void Leer_PT100_HW( Sensor_Base *self ) Binding (Setup) Súper Bucle (Casting Polimórfico) Sensor_Base *ptr = (Sensor_Base *)&mi_pt100; ptr->actualizar(ptr); // Llama a Leer_PT100_HW()!

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a aplicar OOP al Hardware. Crearemos una clase genérica Sensor_t. Luego derivaremos dos sensores específicos: SensorCaudal_t (que usa timers/EXTI) y SensorTemperatura_t (que usa ADC). Construiremos un Arreglo de Punteros en el Súper Bucle para recorrerlos todos ciegamente, invocando sus funciones de hardware específicas mediante el poder de los punteros a función.


/**
 * @file oop_sensors.c
 * @brief Implementación de OOP, Herencia y Polimorfismo en C Bare-Metal
 */

#include "stm32f401xe.h"
#include <stdint.h>

// =========================================================================
// 1. LA CLASE BASE (Sensor Genérico)
// =========================================================================

// Usamos un typedef adelantado para poder referenciar el puntero en la misma estructura
typedef struct Sensor_Base_t Sensor_Base_t;

struct Sensor_Base_t {
    uint32_t id_nodo;
    float valor_actual;
    
    // MÉTODO VIRTUAL (Polimorfismo)
    // Un puntero a una función que recibe su propio objeto como argumento (simulando el 'this' de C++)
    void (*Metodo_Actualizar)(Sensor_Base_t *self);
};

// =========================================================================
// 2. CLASES DERIVADAS (Herencia Estructural)
// =========================================================================

// ---> Derivado 1: Sensor de Temperatura (Vía ADC)
typedef struct {
    // REGLA DE ORO DE LA HERENCIA C: La Base DEBE ser el primer miembro absoluto.
    Sensor_Base_t super; 
    
    // Propiedades exclusivas de este hijo
    uint8_t canal_adc;
    float offset_calibracion;
} Sensor_TempADC_t;


// ---> Derivado 2: Caudalímetro (Vía Interrupciones/Timers)
typedef struct {
    Sensor_Base_t super;
    
    // Propiedades exclusivas de este hijo
    uint32_t *registro_hardware_timer; 
    float factor_k_litros;
} Sensor_Caudal_t;

// =========================================================================
// 3. IMPLEMENTACIÓN DE MÉTODOS ESPECÍFICOS DEL HARDWARE
// =========================================================================

// Función real de Hardware para Temperatura
void Leer_Temperatura_Hardware(Sensor_Base_t *self) {
    // 1. "Downcasting" explícito (Revertimos el polimorfismo para acceder a las propiedades del hijo)
    Sensor_TempADC_t *pt100 = (Sensor_TempADC_t *)self;
    
    // 2. Ejecutamos la lógica específica (Ej. Lectura simulada del registro ADC)
    uint32_t lectura_cruda = ADC1->DR; // (En un código real, pedirías la conversión del canal pt100->canal_adc)
    float grados = (lectura_cruda * 0.08f) + pt100->offset_calibracion;
    
    // 3. Guardamos el resultado en la Base para que el resto del sistema lo vea
    pt100->super.valor_actual = grados;
}

// Función real de Hardware para Caudal
void Leer_Caudal_Hardware(Sensor_Base_t *self) {
    Sensor_Caudal_t *caudal = (Sensor_Caudal_t *)self;
    
    // Leer el registro de cuenta física del Timer directamente
    uint32_t pulsos_totales = *(caudal->registro_hardware_timer);
    
    // Matematica específica del caudalímetro
    caudal->super.valor_actual = (float)pulsos_totales / caudal->factor_k_litros;
}

// =========================================================================
// 4. EL SÚPER BUCLE POLIMÓRFICO
// =========================================================================

int main(void) {
    
    // INSTANCIACIÓN DE OBJETOS
    
    // Creamos el Sensor Termómetro y lo configuramos (El Constructor)
    Sensor_TempADC_t mi_termometro;
    mi_termometro.super.id_nodo = 101;
    mi_termometro.super.valor_actual = 0.0f;
    mi_termometro.canal_adc = 5; // Pin PA5
    mi_termometro.offset_calibracion = -1.2f;
    // BINDING POLIMÓRFICO: Le conectamos su función real al puntero base
    mi_termometro.super.Metodo_Actualizar = Leer_Temperatura_Hardware;
    
    // Creamos el Sensor de Litros (Caudalímetro)
    Sensor_Caudal_t mi_medidor_flujo;
    mi_medidor_flujo.super.id_nodo = 201;
    mi_medidor_flujo.super.valor_actual = 0.0f;
    mi_medidor_flujo.registro_hardware_timer = (uint32_t *)&(TIM2->CNT);
    mi_medidor_flujo.factor_k_litros = 100.0f; // 100 pulsos = 1 litro
    mi_medidor_flujo.super.Metodo_Actualizar = Leer_Caudal_Hardware;
    
    // EL PODER DEL POLIMORFISMO: El Arreglo Genérico
    // Guardamos las direcciones de nuestros hijos distintos en una lista de punteros de clase Base.
    // Esto es seguro gracias a que 'super' es el primer elemento de la memoria.
    Sensor_Base_t *lista_sensores[2];
    lista_sensores[0] = (Sensor_Base_t *)&mi_termometro;
    lista_sensores[1] = (Sensor_Base_t *)&mi_medidor_flujo;
    
    while(1) {
        
        // ¡Magia! El Súper Bucle ya no sabe ni le importa cuántos sensores hay, 
        // ni si son I2C, UART, ADC o Timers. Solo los recorre e invoca la actualización.
        
        for (int i = 0; i < 2; i++) {
            
            // Esta llamada invoca 'Leer_Temperatura_Hardware' en la vuelta 0
            // y 'Leer_Caudal_Hardware' en la vuelta 1. 
            // El propio objeto provee su función oculta.
            lista_sensores[i]->Metodo_Actualizar( lista_sensores[i] );
            
            // Y luego podemos usar los datos genéricamente:
            // if (lista_sensores[i]->valor_actual > LIMITE) { ALARMA! }
        }
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Escalabilidad Industrial (Open/Closed Principle).

¿Qué pasa si mañana el cliente de tu gasolinera te exige que tu controlador maneje 4 mangueras al mismo tiempo, en lugar de 1? Y no solo eso, te exige que una manguera sea de Flujo Másico (por RS485) y las otras tres sean de Pulsos (por Timers). En una programación procedimental "espagueti", tu archivo main.c colapsaría intentando manejar miles de condicionales if (tipo_manguera == MASICO)... else if (tipo == PULSOS)... para cada lectura de datos y cada cálculo en la pantalla LCD.

Al introducir **OOP en C Bare-Metal**, has "encapsulado" el problema. Creaste la clase base `Dispensador`, y la heredaste para crear el `Dispensador_Masico` y el `Dispensador_Pulsos`. El Súper Bucle de tu máquina se quedó inmaculado. Solo tienes un arreglo maestro de `Dispensadores`, e invocas `dispensadores[i]->Manejar_Venta()`. Si quieres agregar una 5ta manguera de otro fabricante, no modificas el núcleo de tu Tesis; simplemente creas un nuevo archivo `.c` con un nuevo "objeto hijo" y lo conectas a la lista maestra. Esto es arquitectura de clase mundial.

📝 Resumen del Día