Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial Internet of Things >> Incrustado

Uniones en C embebido explicadas: compartición eficiente de memoria y mensajería

Entendiendo las uniones en C embebido

En sistemas embebidos, una unión es una estructura de datos potente que permite almacenar diferentes tipos de datos en la misma ubicación de memoria. Este artículo recorre los fundamentos de las uniones, muestra ejemplos prácticos y explica por qué son una herramienta clave para aplicaciones con memoria limitada y sensibles al ancho de banda.

La diferencia entre estructura y unión en C embebido

Anteriormente cubrimos estructuras: una forma de agrupar variables heterogéneas en una sola entidad. En C, las uniones son una construcción complementaria que también agrupa diferentes tipos, pero con una distinción clave: cada miembro comparte la misma dirección de memoria.

Ejemplo introductorio

Declarar una unión es casi idéntico a declarar una estructura; simplemente reemplaza struct con union:

union test { uint8_t c; uint32_t i; };

La plantilla define dos miembros: c ocupa un byte, y i ocupa cuatro bytes. Para crear una variable de este tipo:

union test u1;

Acceder a los miembros usa el operador punto:

u1.i = 10; m = u1.c; // m debe ser uint8_t

Debido a que el tamaño de almacenamiento de una unión es igual al tamaño de su miembro más grande, u1 ocupa cuatro bytes. Escribir en i sobrescribe la misma memoria que c usaría, por lo que los dos miembros son mutuamente excluyentes.

¿Necesitamos espacio de memoria compartido?

Imagine dos dispositivos embebidos, Dispositivo A y Dispositivo B, que intercambian datos de estado, velocidad y posición (ver Figura 1). Los elementos de datos y sus tamaños son:

Uniones en C embebido explicadas: compartición eficiente de memoria y mensajería

Figura 1
Variable Tamaño (bytes) Significado
power 1 Carga de batería
op_mode 1 Modo de operación
temp 1 Temperatura ambiente
x_pos 2 Coordenada X
y_pos 2 Coordenada Y
vel 2 Velocidad

Si el Dispositivo B requiere cada pieza de información, una estructura simple necesitaría nueve bytes, y cada ronda de comunicación transferiría una trama de 9 bytes (ver Figura 2). Sin embargo, en muchos escenarios reales, los tres tipos de mensaje son mutuamente excluyentes: a veces solo estado, a veces solo posición, a veces solo velocidad. En ese caso, la carga útil más grande es de cuatro bytes (el mensaje de posición). Una unión que comparte un búfer de cuatro bytes puede contener cualquiera de los tres mensajes, reduciendo el tamaño de la trama de nueve a cuatro bytes (ver Figura 3).

Uniones en C embebido explicadas: compartición eficiente de memoria y mensajería

Figura 2

Uniones en C embebido explicadas: compartición eficiente de memoria y mensajería

Figura 3

Usando uniones para paquetes de mensajes

A continuación creamos dos estructuras —una para estado y otra para posición— y las incrustamos en una unión junto con el campo de velocidad:

struct status_s { uint8_t power; uint8_t op_mode; uint8_t temp; }; struct position_s { uint16_t x_pos; uint16_t y_pos; }; union msg_union { struct status_s status; struct position_s position; uint16_t vel; };

El tamaño de la unión es de cuatro bytes (el tamaño de position_s), que se comparte entre los tres tipos de mensaje.

Cómo rastrear el miembro activo de la unión

Cuando un receptor lee una unión, debe saber qué miembro es válido. El patrón común es una unión etiquetada: un campo separado que registra el miembro activo. Podemos agregar un byte msg_type antes de la unión:

struct message_s { uint8_t msg_type; // 's' = status, 'p' = position, 'v' = velocity union { struct status_s status; struct position_s position; uint16_t vel; } msg; };

Al transmitir el byte msg_type junto con la unión, el receptor puede interpretar correctamente la carga útil sin conocimiento previo de su forma.

La solución alternativa: asignación dinámica de memoria

Otro enfoque es asignar memoria sobre la marcha para cada tipo de mensaje. El transmisor establecería msg_type y asignaría solo los bytes necesarios para esa carga útil. El receptor interpretaría los datos entrantes basándose en msg_type y liberaría la memoria al terminar. Aunque esto puede ser ligeramente más eficiente en memoria, la asignación dinámica introduce sobrecarga en tiempo de ejecución y la necesidad de desasignación explícita, por lo que muchos desarrolladores embebidos prefieren la estrategia de unión estática.

Próximo: Aplicaciones de las uniones

Las uniones también se usan frecuentemente para extraer piezas de datos más pequeñas de una palabra mayor —una técnica especialmente valiosa al analizar protocolos de comunicación o registros de hardware. Esté atento al próximo artículo de esta serie, donde profundizamos en esos casos de uso.

Para explorar más artículos sobre C embebido, visite el índice de artículos.

Incrustado

  1. Aprovechando el poder de la nube para IoT: Desbloqueando conectividad fluida e insights de datos
  2. Entendiendo la Programación de Robots: Métodos Explicados
  3. Fundamentos de Programación de Microprocesadores
  4. Descubre los 9 Lenguajes de Programación Imprescindibles de 2021
  5. Unidades de compresor maestro: Guía clara de términos de medición comunes
  6. Programación de Sistemas Embebidos: Lenguajes, Arquitectura y Aplicaciones
  7. Elegir el lenguaje de programación ideal para soluciones IIoT industriales
  8. Aprovechar los datos en el Internet de la Fiabilidad: estrategias para una gestión efectiva
  9. Democratizando el Internet de las Cosas: IoT satelital de nueva generación brinda conectividad universal y asequible
  10. El Futuro de los Centros de Datos: Tendencias, Tecnología y Sostenibilidad
  11. Uniones en C explicadas: Gestión eficiente de memoria para múltiples tipos de datos