DÍA 037 DE 365

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

📖 Teoría a Estudiar: El Protocolo I2C y Open-Drain

1. Arquitectura de Hardware: SDA y SCL

I2C utiliza solo dos cables de comunicación sincrónica: SCL (Serial Clock), generado exclusivamente por el Maestro para marcar el ritmo; y SDA (Serial Data), una línea bidireccional donde viajan los bits. Debido a que múltiples dispositivos pueden intentar hablar al mismo tiempo, I2C utiliza una configuración física llamada Drenador Abierto (Open-Drain).

2. El Poder del Pull-up y Open-Drain

Ningún chip en el bus I2C inyecta voltaje (3.3V) a los cables. Los cables están forzados a 3.3V externamente por dos resistencias físicas (Pull-ups, típicamente de 4.7kΩ). Cuando un microcontrolador quiere enviar un 0 lógico, conecta el cable a tierra (GND). Cuando quiere enviar un 1, simplemente "suelta" el cable y la resistencia externa lo eleva a 3.3V. Si dos chips hablan a la vez, el que envíe un 0 ganará sin provocar un cortocircuito.

3. La Trama I2C: El Protocolo de Conversación

A nivel de software (Bare-Metal), no enviamos bytes a ciegas. Una transmisión I2C sigue una estricta coreografía (Trama):

  1. START (S): El Maestro baja el SDA mientras SCL está en alto. ¡Atención todos!
  2. ADDRESS (7-bit) + R/W: El Maestro transmite la dirección única del Esclavo (ej. 0x50 para la EEPROM) y un bit extra: 0 para Escribir, 1 para Leer.
  3. ACK (Acknowledge): El Maestro suelta el SDA. El Esclavo aludido debe bajar el SDA al suelo (0V) en el siguiente ciclo de reloj para decir "¡Aquí estoy!". Si la línea se queda en alto, es un NACK (Dispositivo no encontrado).
  4. DATA (8-bit): Se envía el registro o el valor. Cada byte requiere un ACK del receptor.
  5. STOP (P): El Maestro sube SDA mientras SCL está en alto. Libera el bus.
TOPOLOGÍA HARDWARE Y TRAMA I2C 3.3V (VCC) R_PU (4.7k) R_PU (4.7k) SCL (Reloj) SDA (Datos) MCU (Master) EEPROM 0x50 RTC 0x68 SCL SDA START ADDR (7) + W(0) ACK (Esclavo) DATA (8-bit) STOP

⚙️ Ejercicios Prácticos: Implementación Bare-Metal C

Ejercicio 1: Programa la abstracción genérica para enviar una trama I2C modificando directamente los registros de memoria de la CPU. Emplearemos macros que representan la estructura típica de un registro de control de periférico en arquitecturas ARM Cortex-M o núcleos similares.


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

// --- 1. DEFINICIONES DE HARDWARE (Mapeo de Registros) ---
// Simulamos las direcciones base de un periférico I2C
#define I2C_BASE_ADDR      0x40066000
#define I2C_CR_REG         (*((volatile uint8_t *)(I2C_BASE_ADDR + 0x00))) // Control Register
#define I2C_SR_REG         (*((volatile uint8_t *)(I2C_BASE_ADDR + 0x01))) // Status Register
#define I2C_DR_REG         (*((volatile uint8_t *)(I2C_BASE_ADDR + 0x02))) // Data Register

// Máscaras de Bits (Bitwise)
#define I2C_MASTER_MASK    (1 << 5) // Set = Maestro
#define I2C_TX_MASK        (1 << 4) // Set = Transmitir
#define I2C_IF_FLAG        (1 << 1) // Interrupción (Transferencia Completa)
#define I2C_RXAK_FLAG      (1 << 0) // Receive ACK (0 = ACK, 1 = NACK)


// --- 2. CONTROLADORES DE TRAMA ---

/**
 * @brief Genera la condición START en el bus I2C.
 */
void I2C_Generar_START(void) {
    // Al configurar la máquina de hardware como Maestro y Transmisor simultáneamente,
    // el silicio genera el START físico (Bajar SDA mientras SCL es Alto).
    I2C_CR_REG |= (I2C_MASTER_MASK | I2C_TX_MASK);
}

/**
 * @brief Genera la condición STOP en el bus I2C liberando la línea.
 */
void I2C_Generar_STOP(void) {
    // Retirar el modo Maestro le dice al hardware que libere el bus.
    I2C_CR_REG &= ~(I2C_MASTER_MASK | I2C_TX_MASK);
}

/**
 * @brief  Envía un byte crudo a la línea y espera el ACK del esclavo.
 * @param  data El byte a transmitir.
 * @return True si se recibió ACK (Éxito), False si hubo NACK o Timeout.
 */
bool I2C_Escribir_Byte(uint8_t data) {
    // Un simple timeout en ciclos para evitar que el Súper Bucle se congele
    // si el esclavo se desconectó físicamente o se cortocircuitó el cable.
    uint16_t timeout = 10000; 

    // Al escribir en el Data Register, el hardware inicia el reloj SCL automáticamente.
    I2C_DR_REG = data;
    
    // Polling: Esperamos a que la bandera IF (Interrupt Flag) se levante
    while (!(I2C_SR_REG & I2C_IF_FLAG)) {
        if (--timeout == 0) return false; // Hardware colgado
    }
    
    // Limpiamos la bandera (en muchos MCUs se limpia escribiendo un 1 lógico)
    I2C_SR_REG |= I2C_IF_FLAG;

    // Verificamos si el esclavo bajó la línea (ACK = 0)
    if (I2C_SR_REG & I2C_RXAK_FLAG) {
        return false; // NACK recibido
    }

    return true; // Transmisión perfecta
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Infraestructura para Memoria No Volátil (EEPROM) y Reloj (RTC).

El núcleo de metrología del Despachador LP necesita resiliencia. Si el microcontrolador se reinicia, la memoria RAM (SRAM) donde almacenamos la estructura de Base de Datos del Turno (Día 23) se borrará. Utilizando esta interfaz I2C cruda, forjaremos funciones para respaldar sistemáticamente cada despacho hacia una EEPROM externa (como la 24C32). Además, nos permitirá interrogar a un módulo RTC (Real Time Clock, como el DS3231) para asignar marcas de tiempo exactas (Timestamp) a cada ticket impreso, garantizando trazabilidad fiscal.

📝 Resumen del Día

❓ Cuestionario de Autoevaluación

1. Si quitas las dos resistencias Pull-Up del circuito físico de la PCB, ¿qué leerá el Maestro en la línea SDA?
Respuesta: Leerá ceros permanentemente o ruido aleatorio. Al no haber un voltaje base que empuje el cable hacia arriba (3.3V) cuando los chips sueltan la línea, el voltaje "flotará", y la transmisión I2C fallará de inmediato produciendo NACKs infinitos.