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

Aprenda el lenguaje de programación C integrado:comprensión del objeto de datos de unión

Obtenga información sobre los objetos de datos llamados uniones en lenguaje C integrado.

Obtenga más información sobre los objetos de datos denominados uniones en lenguaje C integrado.

La diferencia entre estructura y unión en C integrado

En un artículo anterior de esta serie, discutimos que las estructuras en C incrustado nos permiten agrupar variables de diferentes tipos de datos y tratarlas como un solo objeto de datos.

Además de las estructuras, el lenguaje C admite otra construcción de datos, llamada unión, que puede agrupar diferentes tipos de datos como un solo objeto de datos. Este artículo proporcionará información básica sobre los sindicatos. Primero veremos un ejemplo introductorio de declaración de una unión, luego examinaremos una aplicación importante de este objeto de datos.

Ejemplo introductorio

Declarar una unión es muy parecido a declarar una estructura. Solo necesitamos reemplazar la palabra clave "estructura" por "unión". Considere el siguiente código de ejemplo:

  prueba de unión {uint8_t c; uint32_t i;};  

Esto especifica una plantilla que tiene dos miembros:"c", que ocupa un byte, e "i", que ocupa cuatro bytes.

Ahora, podemos crear una variable de esta plantilla de unión:

  prueba de unión u1;  

Usando el operador de miembro (.), Podemos acceder a los miembros de la unión "u1". Por ejemplo, el siguiente código asigna 10 al segundo miembro de la unión anterior y copia el valor de "c" a la variable "m" (que debe ser de tipo uint8_t).

  u1.i =10; m =u1.c;  

¿Cuánto espacio de memoria se asignará para almacenar la variable "u1"? Mientras que el tamaño de una estructura es al menos tan grande como la suma de los tamaños de sus miembros, el tamaño de una unión es igual al tamaño de su variable más grande. El espacio de memoria asignado a un sindicato se compartirá entre todos los miembros del sindicato. En el ejemplo anterior, el tamaño de "u1" es igual al tamaño de uint32_t, es decir, cuatro bytes. Este espacio de memoria se comparte entre "i" y "c". Por lo tanto, asignar un valor a uno de estos dos miembros cambiará el valor del otro miembro.

Quizás se esté preguntando, "¿Cuál es el punto de usar el mismo espacio de memoria para almacenar múltiples variables? ¿Existe alguna aplicación para esta función?" Exploraremos este problema en la siguiente sección.

¿Necesitamos espacio de memoria compartida?

Veamos un ejemplo en el que una unión puede ser un objeto de datos útil. Suponga que, como se muestra en la Figura 1 a continuación, hay dos dispositivos en su sistema que necesitan comunicarse entre sí.

Figura 1

El "Dispositivo A" debe enviar información de estado, velocidad y posición al "Dispositivo B". La información de estado consta de tres variables que indican la carga de la batería, el modo de funcionamiento y la temperatura ambiente. La posición está representada por dos variables que muestran las posiciones de los ejes X e Y. Finalmente, la velocidad está representada por una sola variable. Suponga que el tamaño de estas variables es el que se muestra en la siguiente tabla.

Nombre de variable Tamaño (Byte) Explicación
poder 1 Carga de la batería
op_mode 1 Modo de funcionamiento
temp 1 Temperatura
x_pos 2 Posición X
y_pos 2 Posición Y
vel 2 Velocidad

Si el "Dispositivo B" necesita constantemente cada pieza de esta información, podemos almacenar todas estas variables en una estructura y enviar la estructura al "Dispositivo B". El tamaño de la estructura será al menos tan grande como la suma del tamaño de estas variables, es decir, nueve bytes.

Por lo tanto, cada vez que el "Dispositivo A" habla con el "Dispositivo B", necesita transferir una trama de datos de 9 bytes a través del enlace de comunicación entre los dos dispositivos. La Figura 2 muestra la estructura que usa el "Dispositivo A" para almacenar las variables y el marco de datos que necesita pasar por el enlace de comunicación.

Figura 2

Sin embargo, consideremos un escenario diferente en el que solo ocasionalmente necesitamos enviar la información de estado. Además, suponga que no es necesario tener información sobre la posición y la velocidad en un momento dado. En otras palabras, a veces solo enviamos la posición, a veces solo enviamos la velocidad y, a veces, solo enviamos información de estado. En esta situación, no parece una buena idea almacenar la información en una estructura de nueve bytes y transferirla a través del enlace de comunicación.

La información de estado se puede representar con solo tres bytes; para la posición y la velocidad, solo necesitamos cuatro y dos bytes, respectivamente. Por lo tanto, el número máximo de bytes que el “Dispositivo A” necesita enviar en una transferencia es cuatro y, en consecuencia, solo necesitamos cuatro bytes de memoria para almacenar esta información. Este espacio de memoria de cuatro bytes se compartirá entre nuestros tres tipos de mensajes (consulte la Figura 3).

Además, tenga en cuenta que la longitud de la trama de datos que pasa a través del enlace de comunicación se reduce de nueve bytes a cuatro bytes.

Figura 3

En resumen, si nuestro programa tiene variables que se excluyen mutuamente, podemos almacenarlas en un área compartida de memoria para preservar un valioso espacio de memoria. Esto puede ser importante, especialmente en el contexto de sistemas integrados con restricciones de memoria. En tales casos, podemos usar uniones para crear el espacio de memoria compartida requerido.

El ejemplo anterior muestra que el uso de una unión para manejar variables mutuamente excluyentes también puede ayudarnos a conservar el ancho de banda de comunicación. Conservar el ancho de banda de comunicación es a veces incluso más importante que conservar la memoria.

Uso de uniones para paquetes de mensajes

Veamos cómo podemos usar una unión para almacenar las variables del ejemplo anterior. Teníamos tres tipos de mensajes diferentes:estado, posición y velocidad. Podemos crear una estructura para las variables de los mensajes de estado y posición (de modo que las variables de estos mensajes se agrupen y manipulen como un único objeto de datos).

Las siguientes estructuras sirven para este propósito:

  struct {uint8_t power; unit8_t op_mode; uint8_t temp;} estado; estructura {uint16_t x_pos; unit16_t y_pos;} posición;  

Ahora, podemos poner estas estructuras junto con la variable "vel" en una unión:

  union {struct {uint8_t power; unit8_t op_mode; uint8_t temp;} estado; estructura {uint16_t x_pos; unit16_t y_pos;} posición; uint16_t vel;} msg_union;  

El código anterior especifica una plantilla de unión y crea una variable de esta plantilla (llamada "msg_union"). Dentro de esta unión, hay dos estructuras ("estado" y "posición") y una variable de dos bytes ("vel"). El tamaño de esta unión será igual al tamaño de su miembro más grande, es decir, la estructura de "posición", que ocupa cuatro bytes de memoria. Este espacio de memoria se comparte entre las variables de "estado", "posición" y "vel".

Cómo realizar un seguimiento del miembro activo de la Unión

Podemos usar el espacio de memoria compartida de la unión anterior para almacenar nuestras variables; sin embargo, queda una pregunta:¿Cómo debe determinar el receptor qué tipo de mensaje se ha enviado? El receptor necesita reconocer el tipo de mensaje para poder interpretar con éxito la información recibida. Por ejemplo, si enviamos un mensaje de "posición", los cuatro bytes de los datos recibidos son importantes, pero para un mensaje de "velocidad", solo se deben usar dos de los bytes recibidos.

Para resolver este problema, necesitamos asociar nuestra unión con otra variable, digamos "msg_type", que indica el tipo de mensaje (o el miembro de la unión en el que se escribió por última vez). Un sindicato emparejado con un valor discreto que indica que el miembro activo del sindicato se denomina "sindicato discriminado" o "sindicato etiquetado".

Con respecto al tipo de datos para la variable "msg_type", podemos usar el tipo de datos de enumeración del lenguaje C para crear constantes simbólicas. Sin embargo, usaremos un carácter para especificar el tipo de mensaje, solo para mantener las cosas lo más simples posible:

  struct {uint8_t msg_type; union {struct {uint8_t power; unit8_t op_mode; uint8_t temp;} estado; estructura {uint16_t x_pos; unit16_t y_pos;} posición; uint16_t vel;} msg_union;} mensaje;  

Podemos considerar tres valores posibles para la variable "msg_type":"s" para un mensaje de "estado", "p" para un mensaje de "posición" y "v" para un mensaje de "velocidad". Ahora, podemos enviar la estructura del "mensaje" al "Dispositivo B" y usar el valor de la variable "msg_type" como indicador del tipo de mensaje. Por ejemplo, si el valor del "msg_type" recibido es "p", el "Dispositivo B" sabrá que el espacio de memoria compartida contiene dos variables de 2 bytes.

Tenga en cuenta que tendremos que agregar otro byte al marco de datos enviado a través del enlace de comunicación porque necesitamos transferir la variable "msg_type". También tenga en cuenta que, con esta solución, el receptor no necesita saber de antemano qué tipo de mensaje está entrando.

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

Vimos que las uniones nos permiten declarar un área de memoria compartida para conservar tanto el espacio de memoria como el ancho de banda de comunicación. Sin embargo, existe otra forma de almacenar variables mutuamente excluyentes como las del ejemplo anterior. Esta segunda solución utiliza la asignación de memoria dinámica para almacenar las variables de cada tipo de mensaje.

Nuevamente, necesitaremos tener una variable "msg_type" para especificar el tipo de mensaje tanto en el transmisor como en el receptor del enlace de comunicación. Por ejemplo, si el "Dispositivo A" necesita enviar un mensaje de posición, establecerá "msg_type" en "p" y asignará cuatro bytes de espacio de memoria para almacenar las variables "x_pos" e "y_pos". El receptor verificará el valor de "msg_type" y, dependiendo de su valor, creará el espacio de memoria apropiado para almacenar e interpretar la trama de datos entrante.

El uso de la memoria dinámica puede ser más eficiente en términos de uso de la memoria porque estamos asignando el espacio suficiente para cada tipo de mensaje. Este no fue el caso de la solución sindical. Allí, teníamos cuatro bytes de memoria compartida para almacenar los tres tipos de mensajes, aunque los mensajes de "estado" y "velocidad" solo requerían tres y dos bytes, respectivamente. Sin embargo, la asignación de memoria dinámica puede ser más lenta y el programador debe incluir un código que libere la memoria asignada. Es por eso que los programadores generalmente prefieren usar la solución basada en sindicatos.

Siguiente:Aplicaciones de los sindicatos

Parece que el propósito original de las uniones era crear un área de memoria compartida para variables mutuamente excluyentes. Sin embargo, las uniones también se han utilizado ampliamente para extraer partes más pequeñas de datos de un objeto de datos más grande.

El próximo artículo de esta serie se centrará en esta aplicación de uniones, que puede ser particularmente importante en aplicaciones integradas.

Para ver una lista completa de mis artículos, visite esta página.


Incrustado

  1. El mejor lenguaje de programación para aplicaciones industriales de Internet de las cosas
  2. Programación del microprocesador
  3. Qué es la programación del sistema integrado y sus lenguajes
  4. ¡¿Qué hago con los datos ?!
  5. Democratizando el IoT
  6. 9 nuevos lenguajes de programación para aprender en 2021
  7. C - Uniones
  8. El futuro de los centros de datos
  9. La nube en IoT
  10. Comentario:comprensión de los métodos de programación de robots
  11. Hablando el mismo lenguaje industrial:comprender las unidades de medida comunes de un compresor