DÍA 173 DE 365

Fase 3: Comunicaciones y Redes Industriales

📖 Teoría a Estudiar

Glifos y Arreglos Constantes (Bitmaps)

Una fuente (Font) en sistemas embebidos no es más que una colección de imágenes monocromáticas llamadas Glifos (Glyphs). Imagina un papel cuadriculado: si quieres dibujar el número '3', coloreas algunos cuadros y dejas otros en blanco. En el código, un cuadro coloreado es un bit 1, y un cuadro en blanco es un bit 0.

Un número gigante de 32 píxeles de ancho por 50 píxeles de alto tiene $32 \times 50 = 1600$ cuadros. Si usáramos un byte completo (8 bits) para cada cuadro, gastaríamos 1600 bytes por número. Pero como solo necesitamos saber si está "encendido" o "apagado", agrupamos la información a nivel de bit. Un número de 32x50 requiere apenas 200 Bytes.

El Modificador `const` y la Memoria Flash

Los caracteres nunca cambian. El número '3' siempre tendrá la misma forma. Por lo tanto, jamás debes guardar las fuentes en la memoria RAM. Al utilizar la palabra reservada const en C, el compilador aloja este inmenso arreglo de datos matemáticos directamente en la memoria Flash (ROM) del STM32, la cual es gigante (512 KB) y no interfiere con tu preciada y escasa SRAM (96 KB).

ANATOMÍA DEL RENDERIZADO BITMAP: DEL BYTE AL PÍXEL Memoria Flash (ROM) const uint8_t glifo_3[8] = { 0x3C, 0011 1100 0x06, 0000 0110 0x06, 0000 0110 0000 0110 0x1C, 0001 1100 0x06, 0000 0110 0x06, 0000 0110 0x3C, 0011 1100 0x00 0000 0000 }; Solo 8 Bytes de memoria requeridos & (1 << bit) Desempaquetado Bit por Bit Framebuffer (SRAM) uint16_t buffer[8 * 8]; // RGB565 Color Asignado: 0xF800 (Rojo)

⚙️ Ejercicios Prácticos

Ejercicio 1: Integraremos una fuente gigante. Para mantener el ejemplo manejable, usaremos una fuente de `16x24` píxeles para dibujar los dígitos del Despachador de Gas. Como la fuente tiene 16 píxeles de ancho, cada "fila" ocupará exactamente 2 Bytes (16 bits). El bucle de renderizado tomará el código ASCII del carácter, buscará el arreglo de bits, verificará cada bit individual usando una Máscara AND (&) y dibujará el color resultante en el Framebuffer parcial que creamos ayer.


/**
 * @file bitmap_renderer.c
 * @brief Renderizador Bare-Metal de Tipografía para Framebuffers
 */

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

// =========================================================================
// 1. LA FUENTE (BITMAP CONSTANTE EN FLASH)
// =========================================================================

// Definición: 16 píxeles de ancho = 2 bytes por fila. Altura = 24 filas.
// Total: 48 bytes por cada número.
#define FONT_WIDTH  16
#define FONT_HEIGHT 24
#define BYTES_PER_ROW 2

// El modificador 'const' fuerza al compilador GCC a colocar esto en la ROM
const uint8_t Font_Digitos_16x24[10][48] = {
    // [0] (Patrón simulado para el cero)
    { 0x0F,0xF0, 0x1F,0xF8, 0x38,0x1C, 0x70,0x0E, /* ... 24 filas en total ... */ },
    
    // [1] (Patrón simulado para el uno)
    { 0x01,0x80, 0x03,0x80, 0x0F,0x80, 0x01,0x80, /* ... */ },
    
    // [2] 
    // { ... },
    
    // Y así sucesivamente del 0 al 9...
};

// =========================================================================
// 2. EL MOTOR DE DIBUJADO (RENDERER)
// =========================================================================

/**
 * @brief Dibuja un solo carácter numérico dentro del Framebuffer de RAM.
 * 
 * @param digito El carácter a dibujar ('0' a '9')
 * @param f_buffer El arreglo en RAM que será enviado al ST7789
 * @param buffer_w Ancho total del Framebuffer receptor
 * @param offset_x Posición X dentro del buffer donde empezará la letra
 * @param offset_y Posición Y dentro del buffer donde empezará la letra
 * @param fg_color Color del número (Ej. Verde brillante 0x07E0)
 * @param bg_color Color de fondo (Ej. Negro absoluto 0x0000)
 */
void Render_Digito_En_Buffer(char digito, uint16_t *f_buffer, uint16_t buffer_w, 
                             uint16_t offset_x, uint16_t offset_y, 
                             uint16_t fg_color, uint16_t bg_color) 
{
    // Validar que es un dígito
    if (digito < '0' || digito > '9') return;
    
    // Obtener el índice numérico restando el valor ASCII del '0' (0x30)
    uint8_t index = digito - '0';
    
    // Iterar fila por fila (De arriba hacia abajo)
    for (uint16_t fila = 0; fila < FONT_HEIGHT; fila++) {
        
        // Iterar los 2 bytes que componen la fila (16 bits)
        for (uint16_t byte_idx = 0; byte_idx < BYTES_PER_ROW; byte_idx++) {
            
            // Extraer el byte crudo desde la memoria Flash
            uint8_t fila_byte = Font_Digitos_16x24[index][(fila * BYTES_PER_ROW) + byte_idx];
            
            // Iterar los 8 bits individuales de este byte (De Izquierda a Derecha)
            for (uint8_t bit = 0; bit < 8; bit++) {
                
                // La máscara usa (7 - bit) para leer desde el Bit más significativo (MSB) 
                // hacia el Bit menos significativo (LSB), coincidiendo con la dirección de escritura visual.
                bool pixel_encendido = (fila_byte & (1 << (7 - bit)));
                
                // Calcular coordenadas absolutas dentro del Framebuffer
                uint16_t x_real = offset_x + (byte_idx * 8) + bit;
                uint16_t y_real = offset_y + fila;
                
                // Aplicar el color correspondiente a la RAM
                if (pixel_encendido) {
                    f_buffer[(y_real * buffer_w) + x_real] = fg_color;
                } else {
                    f_buffer[(y_real * buffer_w) + x_real] = bg_color;
                }
            }
        }
    }
}

// =========================================================================
// IMPLEMENTACIÓN EN EL SÚPER BUCLE
// =========================================================================

// Framebuffer parcial para mostrar "$10.55"
// Ancho: 16px * 6 caracteres = 96px. Alto: 24px.
// RAM requerida: 96 * 24 * 2 = 4,608 Bytes (Perfecto para nuestros 96 KB libres)
uint16_t display_buffer[96 * 24]; 

// Dependencia de la transmisión masiva (Día 172)
extern void ST7789_DrawBuffer(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *buffer);

int main(void) {
    
    // ST7789_Init();
    
    char string_precio[] = "10.55";
    
    while(1) {
        
        // 1. Dibujar cada letra en la RAM (Totalmente Offline e invisible para el usuario)
        for(int i = 0; i < 5; i++) {
            // Cada carácter avanza 16 píxeles en el eje X
            Render_Digito_En_Buffer(string_precio[i], display_buffer, 96, 
                                    (i * 16), 0, 0x07E0 /* Verde */, 0x0000 /* Negro */);
        }
        
        // 2. Transmisión Ráfaga (Burst). ¡Boom! El texto gigante aparece instantáneamente.
        ST7789_DrawBuffer(20, 100, 96, 24, display_buffer);
        
        // ... (Esperar interrupciones Modbus o actualización del caudalímetro)
    }
    
    return 0;
}
        

🚀 Avance del Proyecto Tesis: Despachador de Gas LP

Objetivo del día: Legibilidad de Alto Contraste para la Interfaz Hombre-Máquina (HMI).

En el diseño de gasolineras, existe una regulación ergonómica estricta: los números del odómetro fiscal deben ser legibles a la luz del día y desde una distancia prudente. Si hubieras usado la pequeña fuente estándar de $8\times8$ píxeles que viene en las librerías de hobby, el cliente tendría que pegar la cara a la pantalla para leer su cobro, arriesgando una queja contra la estación.

Al implementar el **Motor de Fuentes Bitmap Bare-Metal**, has resuelto el problema de escalabilidad visual. Has inyectado una tipografía gigante de alta definición directamente en el corazón inquebrantable de la memoria ROM de tu STM32. Cuando el sensor de caudal cuenta un nuevo litro de gas, tu procesador aísla el dígito, aplica máscaras de bits a la velocidad de la luz (84 MHz), construye la imagen a color en la RAM y la transfiere por la interfaz SPI, logrando un odómetro gigante y verde que gira con una fluidez asombrosa. Esta es la diferencia entre un prototipo escolar y un equipo industrial.

📝 Resumen del Día