DÍA 172 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

El Problema Matemático de la Memoria

Un Framebuffer es un arreglo bidimensional en RAM que representa la pantalla entera. Si nuestra pantalla es de 240x240 píxeles, y cada píxel usa 2 bytes (RGB565), necesitamos 240 * 240 * 2 = 115,200 Bytes (115.2 KB). El problema es que nuestro microcontrolador STM32F401RE tiene un máximo absoluto de 96 KB de SRAM. ¡La pantalla no cabe en el cerebro del microcontrolador!

La Solución: Framebuffers Parciales y Enventanado (Windowing)

No necesitamos redibujar toda la pantalla para actualizar el precio del gas. Solo necesitamos redibujar el rectángulo donde están los números. El controlador ST7789 tiene una función mágica llamada Windowing (Enventanado) a través de los comandos CASET (Column Address Set) y RASET (Row Address Set).

El flujo de trabajo es el siguiente:

  1. Reservamos un pequeño arreglo en RAM, por ejemplo, de 50x50 píxeles (Apenas 5 KB). Esto es el Framebuffer Parcial.
  2. Dibujamos los números y los colores fondo dentro de ese arreglo en la RAM. Al no usar el bus SPI, esto ocurre a la velocidad de la CPU (84 MHz), en microsegundos.
  3. Le decimos a la pantalla: "Abre una ventana en las coordenadas X=100, Y=100 con ancho 50 y alto 50".
  4. Enviamos los 5 KB del arreglo de un solo golpe. El ST7789 rellenará automáticamente esa "ventana" de izquierda a derecha y de arriba a abajo. Al ser masivo y offline, el usuario verá cómo el número cambia instantáneamente sin un solo parpadeo.
ARQUITECTURA DE DIBUJADO: OFFLINE RENDERING & WINDOWING STM32 SRAM (96 KB) Demasiado Pequeño Para 115KB Full Buffer Partial Framebuffer uint16_t buf[50][80] " $10.55 " (8 KB) CPU dibuja Offline sin tocar el SPI Burst Transfer (RAMWR) Comando 0x2C seguido de 8,000 bytes continuos ST7789 TFT GRAM X: 0, Y: 0 LITROS DESPACHADOS 45.02 L IMPORTE TOTAL: $10.55 CASET (0x2A) Col: 80 a 160 RASET (0x2B) Row: 180 a 230

⚙️ Ejercicios Prácticos

Ejercicio 1: Vamos a agregar a nuestro st7789_driver.c la función crítica que configura las ventanas (CASET y RASET). Luego crearemos una función que tome un arreglo en RAM y lo inyecte masivamente a la pantalla, lidiando con la trampa mortal del Endianness: El procesador ARM guarda los datos en memoria en formato Little-Endian (Byte Menos Significativo primero), pero la pantalla ST7789 exige recibir los colores en formato Big-Endian (Byte Más Significativo primero).


/**
 * @file st7789_driver.c
 * @brief Implementación de Ventanas y Framebuffers para ST7789
 */

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

// Dependencias previas (Día 171)
extern void ST7789_SendCmd(uint8_t cmd);
extern void ST7789_SendData(uint8_t data);
extern void SPI_WriteByte(uint8_t dato);

// =========================================================================
// DEFINICIÓN DE VENTANA Y TRANSFERENCIA MASIVA
// =========================================================================

/**
 * @brief Define el área activa de dibujo en la GRAM de la pantalla.
 *        Cualquier dato enviado posteriormente se dibujará confinado aquí.
 */
void ST7789_SetWindow(uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end) {
    
    // 1. Comando CASET: Configurar rango de Columnas (X)
    ST7789_SendCmd(0x2A); 
    ST7789_SendData(x_start >> 8);   // MSB Inicio
    ST7789_SendData(x_start & 0xFF); // LSB Inicio
    ST7789_SendData(x_end >> 8);     // MSB Fin
    ST7789_SendData(x_end & 0xFF);   // LSB Fin
    
    // 2. Comando RASET: Configurar rango de Filas (Y)
    ST7789_SendCmd(0x2B); 
    ST7789_SendData(y_start >> 8);
    ST7789_SendData(y_start & 0xFF);
    ST7789_SendData(y_end >> 8);
    ST7789_SendData(y_end & 0xFF);
    
    // 3. Comando RAMWR: Memory Write. Prepara a la pantalla para recibir píxeles.
    ST7789_SendCmd(0x2C); 
}

/**
 * @brief Dibuja un buffer de memoria completo en un área de la pantalla.
 * 
 * @param x Coordenada X inicial
 * @param y Coordenada Y inicial
 * @param w Ancho de la imagen (Width)
 * @param h Alto de la imagen (Height)
 * @param buffer Puntero al arreglo en la SRAM del STM32 (FrameBuffer parcial)
 */
void ST7789_DrawBuffer(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *buffer) {
    
    // Configurar la caja receptora
    ST7789_SetWindow(x, y, x + w - 1, y + h - 1);
    
    // Habilitar modo datos y bajar Chip Select
    GPIOA->BSRR = (1 << 3);  // DC = 1 (Data)
    GPIOA->BSRR = (1 << 20); // CS = 0 (Activo)
    
    uint32_t total_pixels = w * h;
    
    // Transferencia Masiva Píxel por Píxel (Endianness Fix)
    for (uint32_t i = 0; i < total_pixels; i++) {
        
        // El formato es RGB565 (16 bits)
        uint16_t color = buffer[i];
        
        // TRAMPA DE ENDIANNESS: 
        // ARM guarda 0xF800 (Rojo) en RAM como [0x00][0xF8].
        // Si enviamos la memoria cruda, la ST7789 recibe 0x00F8 (Azul oscuro).
        // Solución Bare-Metal: Extraer y enviar manualmente MSB y luego LSB.
        
        while (!(SPI1->SR & SPI_SR_TXE)); 
        SPI1->DR = (color >> 8);   // Enviar Byte Alto (MSB)
        
        while (!(SPI1->SR & SPI_SR_TXE)); 
        SPI1->DR = (color & 0xFF); // Enviar Byte Bajo (LSB)
    }
    
    // Esperar a que el último byte salga físicamente de la línea
    while (SPI1->SR & SPI_SR_BSY); 
    
    // Soltar pantalla
    GPIOA->BSRR = (1 << 4);  // CS = 1 (Inactivo)
}

// =========================================================================
// IMPLEMENTACIÓN EN LA APLICACIÓN (Dibujado Offline)
// =========================================================================

// Reservamos 5 KB de RAM para una "ventana" de números (50 de ancho x 50 de alto)
uint16_t ventana_precio[50 * 50]; 

#define COLOR_ROJO  0xF800
#define COLOR_NEGRO 0x0000

int main(void) {
    
    // inicializar_st7789();
    
    while(1) {
        
        // 1. DIBUJADO OFFLINE (En la RAM del procesador)
        // Esto es instantáneo. La pantalla física no se entera de lo que hacemos.
        for (int i = 0; i < 2500; i++) {
            // Lógica ficticia: Limpiar el fondo
            ventana_precio[i] = COLOR_NEGRO;
            // Escribir fuente tipográfica usando arreglos (se verá más adelante)
            // if (fuente[i]) ventana_precio[i] = COLOR_ROJO; 
        }
        
        // 2. DISPARO BALÍSTICO (Burst Update)
        // Una vez que la imagen está perfecta en la RAM, la disparamos a la 
        // pantalla en las coordenadas X=100, Y=100.
        // La actualización es tan rápida que el ojo humano no percibe parpadeo.
        ST7789_DrawBuffer(100, 100, 50, 50, ventana_precio);
        
        // ... (resto del Súper Bucle)
    }
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Experiencia de Usuario (UX) Ininterrumpida y Fluida.

Cuando un taxista carga gas en tu Despachador, sus ojos están clavados en el indicador de "Importe Total" girando a toda velocidad. Si utilizas las librerías genéricas de Arduino (como Adafruit_GFX) para dibujar este número, la pantalla primero borrará el número viejo dibujando un cuadrado negro, y luego escribirá el número nuevo en verde. Al repetirse 10 veces por segundo, el número parpadeará violentamente causándole dolor de cabeza al usuario.

Al implementar **Framebuffers Parciales y Enventanado**, has separado el cálculo del renderizado físico. Tu STM32 procesa la venta, "pinta" el número nuevo sobre el fondo negro dentro de la memoria RAM interna en unos pocos microsegundos, y luego dispara un bloque contiguo hacia el bus SPI. El viejo número es aplastado por el nuevo número en una sola transacción sólida. El resultado visual es indistinguible de la pantalla OLED de un smartphone moderno: liso, limpio e instantáneo.

📝 Resumen del Día