Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial Internet of Things >> Встроенный

Изучение встроенного языка программирования C:понимание объекта данных Union

Узнайте об объектах данных, называемых объединениями во встроенном языке C.

Узнайте об объектах данных, называемых объединениями во встроенном языке C.

Разница между структурой и объединением во встроенном C

В предыдущей статье этой серии мы обсуждали, что структуры во встроенном C позволяют нам группировать переменные разных типов данных и работать с ними как с одним объектом данных.

Помимо структур, язык C поддерживает другую конструкцию данных, называемую объединением, которая может группировать различные типы данных в один объект данных. Эта статья предоставит некоторую основную информацию о профсоюзах. Сначала мы рассмотрим вводный пример объявления объединения, а затем рассмотрим важное применение этого объекта данных.

Вводный пример

Объявление объединения очень похоже на объявление структуры. Нам нужно только заменить ключевое слово «структура» на «объединение». Рассмотрим следующий пример кода:

  проверка объединения {uint8_t c; uint32_t i;};  

Это определяет шаблон, который имеет два члена:«c», который занимает один байт, и «i», который занимает четыре байта.

Теперь мы можем создать переменную этого шаблона объединения:

  проверка объединения u1;  

Используя оператор-член (.), Мы можем получить доступ к членам объединения «u1». Например, следующий код присваивает 10 второму члену указанного выше объединения и копирует значение «c» в переменную «m» (которая должна иметь тип uint8_t).

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

Сколько места в памяти будет выделено для хранения переменной «u1»? В то время как размер структуры по крайней мере равен сумме размеров ее членов, размер объединения равен размеру его наибольшей переменной. Пространство памяти, выделенное для объединения, будет совместно использоваться всеми членами объединения. В приведенном выше примере размер «u1» равен размеру uint32_t, то есть четырем байтам. Это пространство памяти делится между «i» и «c». Следовательно, присвоение значения одному из этих двух элементов изменит значение другого элемента.

Вы можете спросить:«Какой смысл использовать одно и то же пространство памяти для хранения нескольких переменных? Есть ли какое-нибудь применение для этой функции?» Мы рассмотрим этот вопрос в следующем разделе.

Нужно ли нам общее пространство памяти?

Давайте посмотрим на пример, в котором объединение может быть полезным объектом данных. Предположим, что, как показано на рисунке 1 ниже, в вашей системе есть два устройства, которым необходимо взаимодействовать друг с другом.

Рисунок 1

«Устройство A» должно отправлять информацию о состоянии, скорости и положении на «Устройство B». Информация о состоянии состоит из трех переменных, которые указывают заряд аккумулятора, режим работы и температуру окружающей среды. Положение представлено двумя переменными, которые показывают положения по осям x и y. Наконец, скорость представлена ​​единственной переменной. Предположим, что размер этих переменных показан в следующей таблице.

Имя переменной Размер (байт) Объяснение
мощность 1 Заряд аккумулятора
op_mode 1 Режим работы
временный 1 Температура
x_pos 2 Позиция X
y_pos 2 Позиция Y
vel 2 Скорость

Если «устройству B» постоянно требуется вся эта информация, мы можем сохранить все эти переменные в структуре и отправить ее на «устройство B». Размер структуры будет как минимум равным сумме размеров этих переменных, то есть девяти байтам.

Таким образом, каждый раз, когда «Устройство A» обращается к «Устройству B», ему необходимо передать 9-байтовый фрейм данных через канал связи между двумя устройствами. На рисунке 2 изображена структура, которую «Устройство A» использует для хранения переменных и фрейма данных, который должен пройти через канал связи.

Рисунок 2

Однако давайте рассмотрим другой сценарий, когда нам нужно отправлять информацию о статусе только изредка. Также предположим, что нет необходимости иметь информацию о положении и скорости одновременно. Другими словами, иногда мы отправляем только позицию, иногда мы отправляем только скорость, а иногда мы отправляем только информацию о статусе. В этой ситуации не кажется хорошей идеей хранить информацию в девятибайтовой структуре и передавать ее по каналу связи.

Информация о состоянии может быть представлена ​​только тремя байтами; для позиции и скорости нам нужно всего четыре и два байта соответственно. Следовательно, максимальное количество байтов, которое «Устройство A» должно отправить за одну передачу, равно четырем, и, следовательно, нам нужно только четыре байта памяти для хранения этой информации. Это четырехбайтовое пространство памяти будет совместно использоваться тремя типами сообщений (см. Рисунок 3).

Кроме того, обратите внимание, что длина кадра данных, передаваемого по каналу связи, уменьшена с девяти до четырех байтов.

Рисунок 3

Подводя итог, можно сказать, что если в нашей программе есть взаимоисключающие переменные, мы можем сохранить их в общей области памяти, чтобы сохранить ценное пространство памяти. Это может быть важно, особенно в контексте встроенных систем с ограничением памяти. В таких случаях мы можем использовать объединения для создания необходимого пространства разделяемой памяти.

Приведенный выше пример показывает, что использование объединения для обработки взаимоисключающих переменных также может помочь нам сохранить полосу пропускания связи. Сохранение пропускной способности канала связи иногда даже важнее, чем экономия памяти.

Использование объединений для пакетов сообщений

Давайте посмотрим, как мы можем использовать объединение для хранения переменных из приведенного выше примера. У нас было три разных типа сообщений:статус, положение и скорость. Мы можем создать структуру для переменных сообщений о состоянии и положении (так, чтобы переменные этих сообщений были сгруппированы и управлялись как единый объект данных).

Этой цели служат следующие структуры:

  struct {uint8_t power; unit8_t op_mode; uint8_t temp;} статус; struct {uint16_t x_pos; unit16_t y_pos;} позиция;  

Теперь мы можем объединить эти структуры вместе с переменной vel:

  union {struct {uint8_t power; unit8_t op_mode; uint8_t temp;} статус; struct {uint16_t x_pos; unit16_t y_pos;} позиция; uint16_t vel;} msg_union;  

Приведенный выше код определяет шаблон объединения и создает переменную этого шаблона (с именем «msg_union»). Внутри этого объединения есть две структуры («статус» и «позиция») и двухбайтовая переменная («vel»). Размер этого объединения будет равен размеру его самого большого члена, а именно структуры «позиция», занимающей четыре байта памяти. Это пространство памяти делится между переменными «status», «position» и «vel».

Как отслеживать активных членов союза

Мы можем использовать пространство разделяемой памяти вышеупомянутого объединения для хранения наших переменных; однако остается один вопрос:как получатель должен определить, какой тип сообщения было отправлено? Получатель должен распознавать тип сообщения, чтобы иметь возможность успешно интерпретировать полученную информацию. Например, если мы отправляем сообщение «положение», все четыре байта полученных данных важны, но для сообщения «скорость» следует использовать только два из полученных байтов.

Чтобы решить эту проблему, нам нужно связать наше объединение с другой переменной, скажем «msg_type», которая указывает тип сообщения (или член объединения, в который была произведена последняя запись). Объединение в паре с дискретным значением, которое указывает на активный член объединения, называется «размеченным объединением» или «объединением с тегами».

Что касается типа данных для переменной «msg_type», мы можем использовать тип данных перечисления языка C для создания символьных констант. Однако мы будем использовать символ для указания типа сообщения, чтобы все было как можно проще:

  struct {uint8_t msg_type; union {struct {uint8_t power; unit8_t op_mode; uint8_t temp;} статус; struct {uint16_t x_pos; unit16_t y_pos;} позиция; uint16_t vel;} msg_union;} сообщение;  

Мы можем рассмотреть три возможных значения переменной «msg_type»:«s» для сообщения «статус», «p» для сообщения «положение» и «v» для сообщения «скорость». Теперь мы можем отправить структуру «сообщения» на «Устройство B» и использовать значение переменной «msg_type» в качестве индикатора типа сообщения. Например, если значение полученного «msg_type» равно «p», «Устройство B» будет знать, что пространство разделяемой памяти содержит две 2-байтовые переменные.

Обратите внимание, что нам нужно добавить еще один байт к фрейму данных, отправляемому по каналу связи, потому что нам нужно передать переменную msg_type. Также обратите внимание, что с этим решением получателю не нужно заранее знать, какое сообщение приходит.

Альтернативное решение:динамическое распределение памяти

Мы увидели, что объединения позволяют нам объявить общую область памяти, чтобы сохранить как пространство памяти, так и пропускную способность связи. Однако существует другой способ хранения взаимоисключающих переменных, например, в приведенном выше примере. Это второе решение использует динамическое выделение памяти для хранения переменных каждого типа сообщения.

Опять же, нам понадобится переменная msg_type, чтобы указать тип сообщения как на передатчике, так и на приемнике канала связи. Например, если «устройству A» необходимо отправить сообщение о местоположении, оно установит «msg_type» в «p» и выделит четыре байта памяти для хранения переменных «x_pos» и «y_pos». Получатель проверит значение «msg_type» и, в зависимости от его значения, создаст соответствующее пространство памяти для хранения и интерпретации входящего кадра данных.

Использование динамической памяти может быть более эффективным с точки зрения использования памяти, поскольку мы выделяем достаточно места для каждого типа сообщения. В случае решения, основанного на объединении, этого не произошло. Там у нас было четыре байта общей памяти для хранения всех трех типов сообщений, хотя для сообщений «статус» и «скорость» требовалось только три и два байта соответственно. Однако распределение динамической памяти может быть медленнее, и программисту необходимо включить код, освобождающий выделенную память. Вот почему программисты обычно предпочитают использовать решение на основе объединения.

Далее:приложения союзов

Похоже, что первоначальная цель объединений заключалась в создании общей области памяти для взаимоисключающих переменных. Однако объединения также широко используются для извлечения меньших частей данных из более крупного объекта данных.

Следующая статья этой серии будет посвящена этому применению объединений, которое может быть особенно важным во встроенных приложениях.

Чтобы увидеть полный список моих статей, посетите эту страницу.


Встроенный

  1. Лучший язык программирования для приложений промышленного Интернета вещей
  2. Программирование микропроцессора
  3. Что мне делать с данными ?!
  4. Демократизация Интернета вещей
  5. 9 новых языков программирования, которые нужно выучить в 2021 году
  6. С - Союзы
  7. Будущее центров обработки данных
  8. Облако в Интернете вещей
  9. Комментарий:понимание методов программирования роботов
  10. Говорить на одном промышленном языке:понимание общих единиц измерения компрессора