En el diseño de nuestro Ring Buffer, el desafío no es meter y sacar datos, sino saber cuándo detenerse. En programación Bare-Metal, no tenemos un sistema operativo que nos advierta de un error de memoria (Segmentation Fault); el hardware simplemente escribirá sobre variables contiguas.
Head utilizando la aritmética modular $Next = (Head + 1) \pmod{L}$. Si ese futuro paso coincide con la posición actual del Tail, significa que la memoria se ha quedado sin "cajones" libres. La escritura debe abortarse de inmediato.Ejercicio 1: Construiremos la API del Ring Buffer para gestionar las comunicaciones del UART. Usaremos punteros booleanos de estado para notificar al consumidor si la operación fue exitosa sin bloquear la arquitectura.
#include <stdint.h>
#include <stdbool.h>
// Capacidad ajustada (potencia de 2 optimiza la matemática interna en ARM)
#define UART_BUFFER_SIZE 64
// Estructura maestra del buffer encapsulada
typedef struct {
volatile uint8_t memoria[UART_BUFFER_SIZE];
volatile uint16_t head;
volatile uint16_t tail;
} RingBuffer_t;
// Instancia global protegida
RingBuffer_t rx_buffer = {0};
/**
* @brief Escribe un byte en el buffer. Llamado exclusivamente por la ISR.
* @param nuevo_dato El byte crudo recibido por hardware.
* @return true si se escribió, false si hubo Buffer Overflow.
*/
bool Buffer_Escribir(uint8_t nuevo_dato) {
// Calculamos el índice futuro de forma local (aislada)
uint16_t indice_siguiente = (rx_buffer.head + 1) % UART_BUFFER_SIZE;
// Validar si el futuro paso aplasta la cola de lectura
if (indice_siguiente == rx_buffer.tail) {
return false; // ERROR: Buffer lleno. Dato descartado para proteger la memoria.
}
// Escribir físicamente en RAM y avanzar cabeza
rx_buffer.memoria[rx_buffer.head] = nuevo_dato;
rx_buffer.head = indice_siguiente;
return true;
}
/**
* @brief Lee un byte del buffer. Llamado por el Main (Súper Bucle).
* @param destino Puntero de referencia donde aterrizará el byte leído.
* @return true si había datos, false si estaba vacío.
*/
bool Buffer_Leer(uint8_t *destino) {
// Validar seguridad de punteros
if (destino == 0x00) return false;
// Si la cabeza no se ha movido, no hay datos nuevos
if (rx_buffer.head == rx_buffer.tail) {
return false; // Buffer vacío
}
// Extraer el dato y avanzar cola
*destino = rx_buffer.memoria[rx_buffer.tail];
rx_buffer.tail = (rx_buffer.tail + 1) % UART_BUFFER_SIZE;
return true;
}
Objetivo del día: Sincronización Segura Productor-Consumidor.
El código anterior es la base de las comunicaciones industriales. Mientras tu despachador de gas LP procesa los pulsos del flujómetro (prioridad máxima), el módulo ESP32 puede estar inyectando comandos (ej. cambio de precios, solicitud de ticket) al STM32. La interrupción del UART usará `Buffer_Escribir()` a la velocidad del rayo, y el STM32 retomará el cálculo metrológico al instante. Cuando la venta termine y la CPU esté ociosa, usará `Buffer_Leer()` para atender silenciosamente las peticiones del Wi-Fi. Nunca más perderás un pulso de gas por culpa de una trama de red.