DÍA 015 DE 365

Fase 1: Fundamentos de C y Lógica de Software

📖 Teoría Explícita: Bitwise y el Control por Máscaras

En el Día 3 aprendimos la diferencia crítica entre los operadores lógicos (&&, ||) y los operadores bit a bit (&, |) dentro de la ALU del STM32. Hoy usaremos los operadores bit a bit para modificar el hardware directamente manipulando los Registros.

Un registro en el STM32 (como el ODR - Output Data Register) es un espacio de memoria de 32 bits donde cada bit está conectado a un pin físico. Si queremos encender la Válvula de Líquido (conectada al pin 0) sin apagar accidentalmente la Bomba (conectada al pin 2), no podemos simplemente reescribir todo el registro (ej. REG = 1;). Tenemos que aplicar una Máscara:

OPERACIÓN DE ENMASCARAMIENTO (BITWISE OR: SET) Estado Actual: 0 0 0 0 1 0 0 0 Bomba Encendida (Bit 3) | (OR) Máscara (1<<0): 0 0 0 0 0 0 0 1 Encender Válvula Líquido (Bit 0) Nuevo Estado: 0 0 0 0 1 0 0 1 Ambas operan sin interferirse

⚡ Circuito KiCad: Aislamiento Óptico y Drivers de Potencia

Al programar usando máscaras para encender actuadores de manera simultánea, nuestro STM32 exige corriente a múltiples pines al mismo tiempo. El microcontrolador trabaja a 3.3V y puede suministrar un máximo de 20mA por pin. Jamás conectes un pin del microcontrolador directamente a un relevador mecánico o válvula de 24V. El pico de corriente inductivo y el retorno electromagnético (Flyback) freirán el procesador en milisegundos.

En tu ruteo de KiCad, la señal que activamos con nuestra máscara debe ir a la resistencia limitadora de un optoacoplador (ej. PC817). Este aisla galvánicamente el STM32 (lógica de 3.3V) del circuito de potencia. Del lado del fototransistor del PC817, excitarás la compuerta de un MOSFET de potencia (ej. IRF540N) para disparar la bobina del solenoide, siempre colocando un Diodo Flyback (ej. 1N4007) en antiparalelo sobre los bornes de la válvula.

⚙️ Ejercicios Prácticos: Control por Máscaras (Non-blocking)

Ejercicio 1: Define macros usando el operador Shift Left (<<) para crear máscaras legibles, y programa la activación simultánea del despachador modificando una variable de estado (Shadow Register).


#include <stdint.h>

// --- Definición de Máscaras usando Shift Left (<<) ---
// Desplazamos un '1' a la posición del bit correspondiente.
#define MASK_VALVULA_LIQUIDO   (1 << 0)  // Bit 0: 0000 0001
#define MASK_VALVULA_RETORNO   (1 << 1)  // Bit 1: 0000 0010
#define MASK_BOMBA_MOTOR       (1 << 2)  // Bit 2: 0000 0100
#define MASK_ALARMA_VISUAL     (1 << 3)  // Bit 3: 0000 1000

// Registro Espejo (Shadow Register). 
// Usamos esto para construir el estado final en RAM antes de enviarlo
// al hardware, evitando estados inestables transitorios.
static uint32_t registro_salidas = 0; 

/**
 * @brief  Función de bajo nivel simulada para escribir al hardware
 */
void STM32_Actualizar_GPIO(uint32_t estado) {
    // En código Bare-Metal real, esto sería: GPIOB->ODR = estado;
    // Esto aplica todas las salidas físicas de manera estricta y simultánea.
}

/**
 * @brief Secuencia de inicio de despacho
 */
void despachador_iniciar_bombeo(void) {
    // 1. SET (Activar): Queremos encender el flujo sin alterar 
    // si la alarma visual está o no parpadeando.
    // Usamos el operador bit a bit OR (|).
    
    // Encendemos líquido y bomba a la vez combinando máscaras.
    registro_salidas |= (MASK_VALVULA_LIQUIDO | MASK_BOMBA_MOTOR);
    
    // Impactamos el hardware
    STM32_Actualizar_GPIO(registro_salidas);
}

/**
 * @brief Secuencia de apagado normal (no emergencia)
 */
void despachador_detener_bombeo(void) {
    // 2. CLEAR (Apagar): Usamos AND (&) combinado con NOT (~).
    // Esto limpia (pone a 0) los bits específicos, conservando los demás.
    
    registro_salidas &= ~(MASK_VALVULA_LIQUIDO | MASK_BOMBA_MOTOR);
    
    // Impactamos el hardware
    STM32_Actualizar_GPIO(registro_salidas);
}

/**
 * @brief Tarea no bloqueante (FreeRTOS) para parpadeo de alarma
 */
void vTask_ParpadearAlarma(void *pvParameters) {
    for(;;) {
        // 3. TOGGLE (Invertir): Usamos XOR (^). 
        // Si estaba en 0 pasa a 1. Si estaba en 1 pasa a 0.
        registro_salidas ^= MASK_ALARMA_VISUAL;
        
        STM32_Actualizar_GPIO(registro_salidas);
        
        // Retardo no bloqueante del RTOS (1 HZ)
        vTaskDelay(pdMS_TO_TICKS(500)); 
    }
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Hito Logrado: Erradicación del "Golpe de Ariete" por latencia de software.

Al migrar del uso de múltiples HAL_GPIO_WritePin() a la modificación de un "Shadow Register" por medio de máscaras, hemos logrado que tu ARM Cortex-M dispare la Válvula de Líquido y la Bomba en la misma instrucción ensambladora. Esto previene micro-retrasos que causan presiones diferenciales nocivas en las mangueras de Gas LP, elevando la robustez mecánica de todo tu ensamble electromecánico y consolidando las reglas de failsafe que abordamos anteriormente.

📝 Resumen del Día

❓ Cuestionario de Autoevaluación

1. Un practicante novato en tu equipo intenta encender la Bomba usando registro_salidas = MASK_BOMBA_MOTOR; (usando solo = en vez de |=). ¿Qué catástrofe ocurrirá en el despachador?
Respuesta: El operador de asignación directa (=) sobrescribe todo el registro de 32 bits con ceros, excepto el bit de la bomba. Esto significa que si la Alarma Visual estaba encendida o la Válvula de Retorno estaba activada por seguridad, se apagarán de inmediato e involuntariamente. Destruir el estado previo es inaceptable, por eso siempre debemos usar el enmascaramiento con |=.