Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Industrial Internet of Things >> Integrado

Aprenda a linguagem de programação C incorporada:Compreendendo o objeto Union Data

Saiba mais sobre objetos de dados chamados uniões em linguagem C incorporada.


Aprenda sobre objetos de dados chamados uniões em linguagem C incorporada.

A diferença entre estrutura e união em C incorporado


Em um artigo anterior desta série, discutimos que as estruturas em C embutido nos permitem agrupar variáveis ​​de diferentes tipos de dados e lidar com elas como um único objeto de dados.

Além das estruturas, a linguagem C suporta outra construção de dados, chamada de união, que pode agrupar diferentes tipos de dados como um único objeto de dados. Este artigo fornecerá algumas informações básicas sobre sindicatos. Vamos primeiro dar uma olhada em um exemplo introdutório de declaração de uma união e, em seguida, examinar uma aplicação importante desse objeto de dados.


Exemplo introdutório


Declarar uma união é muito parecido com declarar uma estrutura. Precisamos apenas substituir a palavra-chave “estrutura” por “união”. Considere o seguinte código de exemplo:
  teste de união {uint8_t c; uint32_t i;};  

Isso especifica um modelo que tem dois membros:“c”, que ocupa um byte, e “i”, que ocupa quatro bytes.

Agora, podemos criar uma variável deste modelo de união:
  teste de união u1;  

Usando o operador membro (.), Podemos acessar os membros do sindicato “u1”. Por exemplo, o código a seguir atribui 10 ao segundo membro da união acima e copia o valor de “c” para a variável “m” (que deve ser do tipo uint8_t).
  u1.i =10; m =u1.c;  

Quanto espaço de memória será alocado para armazenar a variável “u1”? Enquanto o tamanho de uma estrutura é pelo menos tão grande quanto a soma dos tamanhos de seus membros, o tamanho de uma união é igual ao tamanho de sua maior variável. O espaço de memória alocado para um sindicato será compartilhado entre todos os membros do sindicato. No exemplo acima, o tamanho de "u1" é igual ao tamanho de uint32_t, ou seja, quatro bytes. Este espaço de memória é compartilhado entre “i” e “c”. Portanto, atribuir um valor a um desses dois membros mudará o valor do outro membro.

Você pode estar se perguntando:"Qual é o ponto de usar o mesmo espaço de memória para armazenar várias variáveis? Existe algum aplicativo para esse recurso?" Exploraremos esse problema na próxima seção.

Precisamos de espaço de memória compartilhada?


Vejamos um exemplo em que uma união pode ser um objeto de dados útil. Suponha que, conforme mostrado na Figura 1 abaixo, haja dois dispositivos em seu sistema que precisam se comunicar um com o outro.



Figura 1



O “Dispositivo A” deve enviar informações de status, velocidade e posição para o “Dispositivo B”. As informações de status consistem em três variáveis ​​que indicam a carga da bateria, o modo de operação e a temperatura ambiente. A posição é representada por duas variáveis ​​que mostram as posições dos eixos xey. Finalmente, a velocidade é representada por uma única variável. Suponha que o tamanho dessas variáveis ​​seja o mostrado na tabela a seguir.


Nome da Variável Tamanho (Byte) Explicação
poder 1 Carga da bateria
op_mode 1 Modo de operação
temp 1 Temperatura
x_pos 2 Posição X
y_pos 2 Posição Y
vel 2 Velocidade



Se o “Dispositivo B” precisa constantemente ter todas essas informações, podemos armazenar todas essas variáveis ​​em uma estrutura e enviar a estrutura para o “Dispositivo B”. O tamanho da estrutura será pelo menos tão grande quanto a soma do tamanho dessas variáveis, ou seja, nove bytes.

Assim, cada vez que o “Dispositivo A” fala com o “Dispositivo B”, ele precisa transferir um quadro de dados de 9 bytes através do link de comunicação entre os dois dispositivos. A Figura 2 mostra a estrutura que o “Dispositivo A” usa para armazenar as variáveis ​​e o quadro de dados que precisa passar pelo link de comunicação.



Figura 2



No entanto, vamos considerar um cenário diferente em que precisamos enviar as informações de status apenas ocasionalmente. Além disso, suponha que não seja necessário ter informações de posição e velocidade em um determinado momento. Em outras palavras, às vezes enviamos apenas posição, às vezes enviamos apenas velocidade, às vezes enviamos apenas informações de status. Nessa situação, não parece uma boa ideia armazenar as informações em uma estrutura de nove bytes e transferi-las por meio do link de comunicação.

As informações de status podem ser representadas por apenas três bytes; para posição e velocidade, precisamos de apenas quatro e dois bytes, respectivamente. Portanto, o número máximo de bytes que o “Dispositivo A” precisa enviar em uma transferência é quatro e, conseqüentemente, precisamos de apenas quatro bytes de memória para armazenar essa informação. Esse espaço de memória de quatro bytes será compartilhado entre nossos três tipos de mensagem (consulte a Figura 3).

Além disso, observe que o comprimento do quadro de dados passado pelo link de comunicação é reduzido de nove para quatro bytes.



Figura 3



Para resumir, se nosso programa tem variáveis ​​que são mutuamente exclusivas, podemos armazená-las em uma área compartilhada da memória para preservar um valioso espaço de memória. Isso pode ser importante, especialmente no contexto de sistemas incorporados com restrição de memória. Nesses casos, podemos usar uniões para criar o espaço de memória compartilhada necessário.

O exemplo acima mostra que usar uma união para lidar com variáveis ​​mutuamente exclusivas também pode nos ajudar a conservar a largura de banda de comunicação. Conservar a largura de banda de comunicação às vezes é ainda mais importante do que conservar a memória.

Usando Uniões para Pacotes de Mensagens


Vamos ver como podemos usar uma união para armazenar as variáveis ​​do exemplo acima. Tínhamos três tipos diferentes de mensagens:status, posição e velocidade. Podemos criar uma estrutura para as variáveis ​​das mensagens de status e posição (de forma que as variáveis ​​dessas mensagens sejam agrupadas e manipuladas como um único objeto de dados).

As seguintes estruturas atendem a esse propósito:
  struct {uint8_t power; unit8_t op_mode; uint8_t temp;} status; struct {uint16_t x_pos; unit16_t y_pos;} posição;  

Agora, podemos colocar essas estruturas junto com a variável "vel" em uma união:
  união {struct {uint8_t power; unit8_t op_mode; uint8_t temp;} status; struct {uint16_t x_pos; unit16_t y_pos;} posição; uint16_t vel;} msg_union;  

O código acima especifica um modelo de união e cria uma variável desse modelo (chamada “msg_union”). Dentro desta união, existem duas estruturas (“status” e “posição”) e uma variável de dois bytes (“vel”). O tamanho desta união será igual ao tamanho do seu maior membro, a saber, a estrutura de “posição”, que ocupa quatro bytes de memória. Este espaço de memória é compartilhado entre as variáveis ​​de “status”, “posição” e “vel”.

Como acompanhar o membro sindicalizado


Podemos usar o espaço de memória compartilhada da união acima para armazenar nossas variáveis; no entanto, resta uma pergunta:como o receptor deve determinar que tipo de mensagem foi enviada? O receptor precisa reconhecer o tipo de mensagem para poder interpretar com sucesso as informações recebidas. Por exemplo, se enviarmos uma mensagem de “posição”, todos os quatro bytes dos dados recebidos são importantes, mas para uma mensagem de “velocidade”, apenas dois dos bytes recebidos devem ser usados.

Para resolver este problema, precisamos associar nosso sindicato a outra variável, digamos “msg_type”, que indica o tipo de mensagem (ou o membro do sindicato que foi escrito pela última vez). Um sindicato emparelhado com um valor discreto que indica o membro ativo do sindicato é denominado “sindicato discriminado” ou “sindicato etiquetado”.

Com relação ao tipo de dados para a variável “msg_type”, podemos usar o tipo de dados de enumeração da linguagem C para criar constantes simbólicas. No entanto, usaremos um caractere para especificar o tipo de mensagem, apenas para manter as coisas o mais simples possível:
  struct {uint8_t msg_type; união {struct {uint8_t power; unit8_t op_mode; uint8_t temp;} status; struct {uint16_t x_pos; unit16_t y_pos;} posição; uint16_t vel;} msg_union;} mensagem;  

Podemos considerar três valores possíveis para a variável "msg_type":'s' para uma mensagem de "status", 'p' para uma mensagem de "posição" e 'v' para uma mensagem de "velocidade". Agora, podemos enviar a estrutura da “mensagem” para o “Dispositivo B” e usar o valor da variável “msg_type” como um indicador do tipo de mensagem. Por exemplo, se o valor do "msg_type" recebido for ‘p’, o "Dispositivo B" saberá que o espaço de memória compartilhada contém duas variáveis ​​de 2 bytes.

Observe que teremos que adicionar outro byte ao quadro de dados enviado através do link de comunicação porque precisamos transferir a variável “msg_type”. Observe também que, com esta solução, o receptor não precisa saber com antecedência que tipo de mensagem está chegando.

A solução alternativa:alocação dinâmica de memória


Vimos que os sindicatos nos permitem declarar uma área de memória compartilhada para conservar espaço de memória e largura de banda de comunicação. No entanto, há outra maneira de armazenar variáveis ​​mutuamente exclusivas, como as do exemplo acima. Esta segunda solução usa alocação de memória dinâmica para armazenar as variáveis ​​de cada tipo de mensagem.

Novamente, precisaremos ter uma variável “msg_type” para especificar o tipo de mensagem tanto no transmissor quanto no receptor do link de comunicação. Por exemplo, se o "Dispositivo A" precisar enviar uma mensagem de posição, ele definirá "msg_type" como 'p' e alocará quatro bytes de espaço de memória para armazenar as variáveis ​​"x_pos" e "y_pos". O receptor verificará o valor de “msg_type” e, dependendo de seu valor, criará o espaço de memória apropriado para armazenar e interpretar o quadro de dados de entrada.

O uso de memória dinâmica pode ser mais eficiente em termos de uso de memória porque estamos alocando apenas espaço suficiente para cada tipo de mensagem. Não foi o que aconteceu com a solução sindical. Lá, tínhamos quatro bytes de memória compartilhada para armazenar todos os três tipos de mensagem, embora as mensagens de “status” e “velocidade” precisassem de apenas três e dois bytes, respectivamente. No entanto, a alocação de memória dinâmica pode ser mais lenta e o programador precisa incluir um código que libere a memória alocada. É por isso que os programadores geralmente preferem usar a solução baseada em união.


Próximo:Solicitações de sindicatos


Parece que o propósito original dos sindicatos era criar uma área de memória compartilhada para variáveis ​​mutuamente exclusivas. No entanto, os sindicatos também têm sido amplamente usados ​​para extrair partes menores de dados de um objeto de dados maior.

O próximo artigo desta série se concentrará nessa aplicação de sindicatos, que pode ser particularmente importante em aplicativos incorporados.



Para ver uma lista completa dos meus artigos, visite esta página.

Integrado

  1. A melhor linguagem de programação para aplicativos industriais da Internet das coisas
  2. Programação do microprocessador
  3. O que eu faço com os dados ?!
  4. Democratizando a IoT
  5. 9 Novas linguagens de programação para aprender em 2021
  6. C - Sindicatos
  7. O futuro dos data centers
  8. A nuvem na IoT
  9. Comentário:entendendo os métodos de programação de robôs
  10. Falando a mesma linguagem industrial:compreendendo as unidades de medida comuns de um compressor