DÍA 119 DE 365

Fase 2: Fundamentos de Hardware y Electrónica

📖 Teoría a Estudiar

El Bus SPI (Serial Peripheral Interface)

Los procesadores hablan con el exterior a través de protocolos. A diferencia del viejo UART que es Asíncrono (donde ambos chips deben acordar una velocidad a ciegas), el SPI es Síncrono. El STM32 actúa como Maestro (Master) y dicta el ritmo exacto entregando una señal de reloj física. El bus SPI utiliza fuerza bruta (pines Push-Pull) en lugar de resistencias de Pull-up, lo que le permite alcanzar velocidades monstruosas de hasta 42 Megabits por segundo (42 MHz).

La Arquitectura de 4 Hilos

Todo bus SPI estándar necesita cuatro pistas de cobre en tu placa KiCad:

Full-Duplex y Shift Registers

En SPI no se "envía" y luego se "recibe". La comunicación es Full-Duplex. Funciona como un círculo: por cada bit que empujas hacia afuera por el MOSI, la señal de reloj jala un bit hacia adentro por el MISO al mismo tiempo. Es un intercambio obligatorio. Si quieres leer un dato del Esclavo, tienes que enviarle bytes de "basura" (Dummy bytes, ej. 0xFF) solo para mantener el reloj girando.

ARQUITECTURA SPI (FULL-DUPLEX SHIFT REGISTERS) MASTER (STM32) Shift Register (8-bit) MOSI MISO SCK CS Clock Generator SLAVE (EEPROM Flash) Shift Register (8-bit) MOSI MISO SCK CS Datos al Esclavo (RX) Datos al Maestro (TX) Reloj Maestro (CPOL=0, CPHA=0) CS baja a 0V para activar chip

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a configurar el hardware del SPI1 Bare-Metal. En el STM32F4, el SPI1 está atado a los pines PA5 (SCK), PA6 (MISO) y PA7 (MOSI) a través de la Función Alternativa 5 (AF5). El pin PA4 lo usaremos como GPIO normal para controlar manualmente el Chip Select (CS) por software, ya que es más seguro y predecible que dejar que el hardware lo haga solo.


/**
 * @file spi_driver.c
 * @brief Configuración Bare-Metal del bus SPI1
 */

#include "stm32f401xe.h"

// Macro rápida para manipular el pin CS (Chip Select en PA4)
#define SPI_CS_BAJO()   (GPIOA->BSRR = (1 << (4 + 16))) // Seleccionar Esclavo
#define SPI_CS_ALTO()   (GPIOA->BSRR = (1 << 4))        // Deseleccionar Esclavo

void inicializar_spi1(void) {
    
    // 1. HABILITAR RELOJES (GPIOA y SPI1)
    // El SPI1 es el más rápido de todos y vive en el bus APB2 (84 MHz)
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
    
    // 2. CONFIGURAR PINES (PA5=SCK, PA6=MISO, PA7=MOSI) a Alternate Function
    // Limpiamos los modos y los seteamos a '10' (AF)
    GPIOA->MODER &= ~(GPIO_MODER_MODE5 | GPIO_MODER_MODE6 | GPIO_MODER_MODE7);
    GPIOA->MODER |=  (GPIO_MODER_MODE5_1 | GPIO_MODER_MODE6_1 | GPIO_MODER_MODE7_1);
    
    // Velocidad Muy Alta para estos pines (Importante en SPI)
    GPIOA->OSPEEDR |= (3 << (5*2)) | (3 << (6*2)) | (3 << (7*2));
    
    // Asignar la Función Alternativa 5 (AF5) en el registro AFRL
    GPIOA->AFR[0] &= ~(0xFFF << 20); // Limpiar PA5, PA6, PA7
    GPIOA->AFR[0] |=  (0x555 << 20); // Escribir AF5 ('0101') en los tres pines
    
    // Configurar PA4 como Salida Normal (Para el Chip Select por software)
    GPIOA->MODER &= ~GPIO_MODER_MODE4;
    GPIOA->MODER |=  GPIO_MODER_MODE4_0;
    SPI_CS_ALTO(); // Por defecto en Alto (Esclavo apagado)
    
    // 3. CONFIGURAR REGISTROS DEL SPI1 (Control Register 1)
    SPI1->CR1 = 0; // Limpiar registro
    
    // - MSTR = 1: STM32 es el Maestro del bus
    // - BR = 011: Baud Rate Prescaler (F_PCLK / 16) -> 84MHz / 16 = 5.25 Mbps
    // - CPOL = 0, CPHA = 0: Modo SPI 0 (Estándar para memorias y sensores)
    // - SSM = 1, SSI = 1: Software Slave Management (Evita errores de Hardware CS)
    SPI1->CR1 |= SPI_CR1_MSTR | (3 << SPI_CR1_BR_Pos) | SPI_CR1_SSM | SPI_CR1_SSI;
    
    // 4. ENCENDER EL PERIFÉRICO SPI
    SPI1->CR1 |= SPI_CR1_SPE; // SPI Enable
}

/**
 * @brief Transmite un byte y simultáneamente recibe otro (Full-Duplex)
 * @param dato_tx: Byte a enviar al esclavo
 * @return uint8_t: Byte recibido del esclavo
 */
uint8_t spi_transferir_byte(uint8_t dato_tx) {
    
    // 1. Esperar a que el buffer de transmisión esté vacío (TXE = 1)
    while(!(SPI1->SR & SPI_SR_TXE));
    
    // 2. Enviar el dato cargándolo en el Registro de Datos
    SPI1->DR = dato_tx;
    
    // 3. Esperar a que el buffer de recepción tenga el dato de vuelta (RXNE = 1)
    // El ciclo de reloj físico no termina hasta que llega la respuesta.
    while(!(SPI1->SR & SPI_SR_RXNE));
    
    // 4. Leer y devolver el dato recibido
    return (uint8_t)SPI1->DR;
}

int main(void) {
    inicializar_spi1();
    
    while(1) {
        // Ejemplo genérico de uso de memoria SPI
        
        SPI_CS_BAJO();                // Despertar esclavo
        
        spi_transferir_byte(0x9F);    // Enviar comando (Ej. Leer ID del chip)
        uint8_t id = spi_transferir_byte(0xFF); // Enviar basura (0xFF) para recibir la respuesta
        
        SPI_CS_ALTO();                // Dormir esclavo
    }
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Memoria de Auditoría Fiscal (Data Logging Incorruptible).

Por ley, un Despachador de Gas LP debe almacenar cada transacción, los precios de venta, los cortes de turno y las alarmas de seguridad. ¿Qué pasa si el Despachador se queda sin energía eléctrica justo cuando está facturando? Si la variable de litros estaba en la memoria RAM, se borrará instantáneamente (Volatilidad) y perderás la venta y el dinero.

La solución de la industria es incorporar un chip de memoria EEPROM o Flash NOR externa (ej. Serie W25Q64) soldada a la placa. Al conectar esta memoria por el bus SPI a una velocidad de 5 Megabits por segundo, tu STM32 puede guardar la cantidad de litros parciales decenas de veces por segundo sin retrasar el algoritmo metrológico. Si ocurre un apagón, al regresar la energía, el STM32 envía el comando por SPI, lee la memoria externa y recupera exactamente la cifra en la que se había quedado. Esta es la diferencia entre un prototipo que se reinicia y un equipo comercial de misión crítica.

📝 Resumen del Día