1. Введение

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

Написание компонента HAL может оказаться утомительным процессом, большая часть которого связана с вызовами функций rtapi_ и hal_ и связанной с ними проверкой ошибок. halcompile автоматически напишет за вас весь этот код. Компилировать компонент HAL также намного проще при использовании halcompile, независимо от того, является ли компонент частью исходного дерева LinuxCNC или вне его.

Например, при кодировании на C простой компонент, такой как "ddt", занимает около 80 строк кода. Эквивалентный компонент очень короткий, если написан с использованием препроцессора halcompile:

Пример простого компонента
component ddt "Compute the derivative of the input function";
pin in float in;
pin out float out;
variable double old;
function _;
license "GPL"; // indicates GPL v2 or later
;;
float tmp = in;
out = (tmp - old) / fperiod;
old = tmp;

2. Installing

Чтобы скомпилировать компонент, если используется упакованная версия LinuxCNC, пакеты разработки необходимо установить либо с помощью Synaptic из главного меню System -> Administration -> Synaptic package manager, либо выполнив одну из следующих команд в окне терминала. :

Установка пакетов разработки для LinuxCNC
sudo apt install linuxcnc-dev
# or
sudo apt install linuxcnc-uspace-dev

Другой метод — использовать менеджер пакетов Synaptic из меню «Приложения» для установки пакетов linuxcnc-dev или linuxcnc-uspace-dev.

3. Использование компонента

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

Пример сценария HAL для установки компонента (ddt) и его выполнения каждую миллисекунду.
loadrt threads name1=servo-thread period1=1000000
loadrt ddt
addf ddt.0 servo-thread

Дополнительную информацию о loadrt и addf можно найти в HAL Основы.

Чтобы протестировать свой компонент, вы можете следовать примерам в HAL Учебник.

4. Определения

  • component — компонент представляет собой один модуль реального времени, который загружается с помощью halcmd loadrt. Один файл .comp определяет один компонент. Имя компонента и имя файла должны совпадать.

  • instance — компонент может иметь ноль или более экземпляров. Каждый экземпляр компонента создается равным (все они имеют одинаковые выводы, параметры, функции и данные), но ведут себя независимо, когда их контакты, параметры и данные имеют разные значения.

  • singleton - компонент может быть "singleton", и в этом случае создается ровно один экземпляр. Редко имеет смысл писать singleton компонент, если только в системе не может быть буквально только один объект такого типа (например, компонент, цель которого — предоставить контакт с текущим временем UNIX или драйвер оборудования для внутреннего динамика ПК).

5. Создание экземпляра

Для singleton один экземпляр создается при загрузке компонента.

Для не-singleton параметр модуля count определяет, сколько пронумерованных экземпляров будет создано. Если count не указано, параметр модуля names определяет, сколько именованных экземпляров будет создано. Если не указано ни количество, ни имена, создается один пронумерованный экземпляр.

6. Неявные параметры

Функциям неявно передается параметр period, который представляет собой время в наносекундах последнего периода выполнения компонента. Функции, использующие числа с плавающей запятой, также могут ссылаться на f period, который представляет собой время с плавающей запятой в секундах, или (период*1e-9). Это может быть полезно в компонентах, которым требуется информация о времени.

7. Syntax

Файл .comp состоит из нескольких объявлений, за которыми следует «;;» в отдельной строке, за которой следует код C, реализующий функции модуля.

Декларации включают в себя:

  • component HALNAME (DOC);

  • pin PINDIRECTION TYPE HALNAME ([SIZE]|[MAXSIZE: CONDSIZE]) (if CONDITION) (= STARTVALUE) (DOC) ;

  • param PARAMDIRECTION TYPE HALNAME ([SIZE]|[MAXSIZE: CONDSIZE]) (if CONDITION) (= STARTVALUE) (DOC) ;

  • function HALNAME (fp | nofp) (DOC);

  • option OPT (VALUE);

  • variable CTYPE STARREDNAME ([SIZE]);

  • description DOC;

  • examples DOC;

  • notes DOC;

  • see_also DOC;

  • license LICENSE;

  • author AUTHOR;

  • include HEADERFILE;

Круглые скобки указывают на необязательные элементы. Вертикальная черта обозначает альтернативы. Слова, написанные ЗАГЛАВНЫМИ буквами, обозначают изменяемый текст следующим образом:

  • NAME - Стандартный идентификатор C

  • STARREDNAME - Идентификатор C с нулем или более * перед ним. Этот синтаксис можно использовать для объявления переменных экземпляра, которые являются указателями. Обратите внимание, что из-за грамматики между * и именем переменной может отсутствовать пробел.

  • HALNAME - Расширенный идентификатор. При использовании для создания идентификатора HAL все подчеркивания заменяются дефисами, а любые конечные дефисы или точки удаляются, так что "this_name_" будет преобразовано в "this-name", а если имя "_", то завершающая точка также удаляется, так что "function _" дает имя функции HAL, например "component.<num>" вместо "component.<num>."

    Если он присутствует, префикс hal_ удаляется из начала имени компонента при создании контактов, параметров и функций.

В идентификаторе HAL для контакта или параметра # обозначает элемент массива и должен использоваться вместе с объявлением [SIZE]. Хэш-метки заменяются числом, дополненным 0, той же длины, что и количество символов #.

При использовании для создания идентификатора C к HALNAME применяются следующие изменения:

  1. Любые символы «#», а также любые символы ".", "_" или "-" непосредственно перед ними удаляются.

  2. Все оставшиеся "." и символы "-" заменяются на "_".

  3. Повторяющиеся символы "_" заменяются одним символом "\_".

Завершающий символ "_" сохраняется, поэтому можно использовать идентификаторы HAL, которые в противном случае могли бы конфликтовать с зарезервированными именами или ключевыми словами (например, min).

HALNAME C идентификатор HAL Идентификатор

x_y_z

x_y_z

x-y-z

x-y.z

x_y_z

x-y.z

x_y_z_

x_y_z_

x-y-z

x.##.y

x_y(MM)

x.MM.z

x.##

x(MM)

x.MM

  • if CONDITION — выражение, включающее переменную personality, которая не равна нулю, когда необходимо создать вывод или параметр.

  • SIZE - Число, указывающее размер массива. Элементы массива пронумерованы от 0 до SIZE-1.

  • MAXSIZE : CONDSIZE - Число, которое дает максимальный размер массива, за которым следует выражение, включающее переменную personality, значение которого всегда меньше MAXSIZE. Когда массив будет создан, его размер будет CONDSIZE.

  • DOC - Строка, документирующая элемент. Строка может быть строкой в стиле C с "двойными кавычками", например:

    "Выбирает желаемые параметры импульса: TRUE означает срез, FALSE означает фронт"

    или строку в "тройных кавычках" в стиле Python, которая может включать встроенные символы новой строки и кавычки, такие как:

    """The effect of this parameter, also known as "the orb of zot",
    will require at least two paragraphs to explain.
    
    Hopefully these paragraphs have allowed you to understand "zot"
    better."""

    Или строке может предшествовать буквальный символ r, и в этом случае строка интерпретируется как необработанная строка Python.

    Строка документации имеет формат "groff -man". Дополнительную информацию об этом формате разметки см. в groff_man(7). Помните, что halcompile интерпретирует символы обратной косой черты в строках, поэтому, например, чтобы установить курсив для слова example, напишите:

    "\\fIexample\\fB"

    В этом случае особенно полезны r-строки, поскольку обратные косые черты в r-строке не нужно удваивать:

    r"\fIexample\fB"
  • TYPE - Один из типов HAL: bit, s32, u32, s64, u64, или float. Имена signed и unsigned также могут использоваться для s32 and u32, но s32 и u32 являются предпочтительными.

  • PINDIRECTION - Одно из следующих: in, out, или io. Компонент устанавливает значение для контакта out, он считывает значение с контакта in и может считывать или устанавливать значение контакта io.

  • PARAMDIRECTION - Одно из следующих: r или rw. Компонент устанавливает значение для параметра r и может читать или устанавливать значение параметра rw.

  • STARTVALUE - Указывает начальное значение контакта или параметра. Если он не указан, то по умолчанию используется значение 0 или FALSE, в зависимости от типа элемента..

  • HEADERFILE - Имя заголовочного файла в двойных кавычках (include "myfile.h";) или в угловых скобках (include <systemfile.h>;). Файл заголовка будет включен (с использованием #include языка C) в начало файла, перед объявлениями контактов и параметров.

7.1. HAL функции

  • fp — указывает, что функция выполняет вычисления с плавающей запятой.

  • «nofp» — указывает, что он выполняет только целочисленные вычисления. Если ни один из них не указан, предполагается fp. Ни halcompile, ни gcc не могут обнаружить использование вычислений с плавающей запятой в функциях, помеченных тегом nofp, но использование таких операций приводит к неопределенному поведению.

7.2. Варианты

В настоящее время определены следующие параметры:

  • option singleton yes - (по умолчанию: нет) Не создавайте параметр модуля count и всегда создавайте один экземпляр. При использовании singleton элементы называются component-name.item-name, а без singleton элементы для пронумерованных экземпляров называются component-name.<num>.item-name.

  • option default_count number - (по умолчанию: 1) Обычно параметр модуля count по умолчанию равен 1. Если он указан, тогда count по умолчанию принимает указанное значение.

  • option count_function yes - (по умолчанию: no) Обычно количество создаваемых экземпляров указывается в параметре модуля count; если указана count_function, вместо нее используется значение, возвращаемое функцией int get_count(void), а параметр модуля count не определен.

  • option rtapi_app no - (по умолчанию: yes) Обычно функции rtapi_app_main() и rtapi_app_exit() определяются автоматически. При option rtapi_app no они отсутствуют и должны быть указаны в коде C. Используйте следующие прототипы:

    `int rtapi_app_main(void);`
    
    `void rtapi_app_exit(void);`

    При реализации собственного rtapi_app_main() вызовите функцию int export(char *prefix, long extra_arg), чтобы зарегистрировать контакты, параметры и функции для prefix.

  • option data TYPE - (по умолчанию: none) устарело
    Если указано, каждый экземпляр компонента будет иметь связанный блок данных типа TYPE (который может быть простым типом, например float, или именем типа, созданным с помощью typedef). В новых компонентах вместо этого следует использовать variable.

  • option extra_setup yes - (по умолчанию: no)
    Если указано, вызовите функцию, определенную EXTRA_SETUP, для каждого экземпляра. При использовании автоматически определенного rtapi_app_main extra_arg — это номер этого экземпляра.

  • option extra_cleanup yes - (по умолчанию: no)
    Если указано, вызовите функцию, определенную EXTRA_CLEANUP, из автоматически определенного rtapi_app_exit или, в случае обнаружения ошибки, из автоматически определенного rtapi_app_main.

  • option userspace yes - (по умолчанию: no)
    Если указано, этот файл описывает компонент не реального времени (ранее известный как "userspace"), а не обычный (т. е. realtime). Компонент, не работающий в режиме реального времени, может не иметь функций, определенных директивой function. Вместо этого, после создания всех экземпляров, вызывается функция C void user_mainloop(void);. Когда эта функция возвращает значение, компонент завершает работу. Обычно user_mainloop() использует FOR_ALL_INSTS() для выполнения действия обновления для каждого экземпляра, а затем на короткое время переходит в режим сна. Другим распространенным действием в user_mainloop() может быть вызов цикла обработчика событий набора инструментов ГИП.

  • option userinit yes - (по умолчанию: no)
    Эта опция игнорируется, если для опции пространство пользователя (см. выше) установлено значение нет. Если указан userinit, функция userinit(argc,argv) вызывается перед rtapi_app_main() (и, следовательно, перед вызовом hal_init()). Эта функция может обрабатывать аргументы командной строки или выполнять другие действия. Тип возвращаемого значения — void; он может вызвать exit(), если хочет завершить работу, а не создать компонент HAL (например, из-за недопустимых аргументов командной строки).

  • option extra_link_args "…" - (по умолчанию: "") Эта опция игнорируется, если для опции userspace (см. выше) установлено no. При линковании компонента, не работающего в реальном времени, указанные аргументы вставляются в строку ссылки. Обратите внимание: поскольку компиляция происходит во временном каталоге "-L". относится к временному каталогу, а не к каталогу, в котором находится исходный файл .comp. Эту опцию можно установить в командной строке halcompile с помощью -extra-link-args="-L…..". Эта альтернатива позволяет установить дополнительные флаги в тех случаях, когда входной файл представляет собой файл .c, а не файл .comp.

  • option extra_compile_args "…" - (по умолчанию: "") Эта опция игнорируется, если для опции userspace (см. выше) установлено значение no. При компиляции компонента, не работающего в реальном времени, указанные аргументы вставляются в командную строку компилятора. Если входной файл представляет собой файл .c, этот параметр можно установить в командной строке halcompile с помощью --extra-compile-args="-I…..". Эта альтернатива позволяет установить дополнительные флаги в тех случаях, когда входной файл представляет собой файл .c, а не файл .comp.

  • option homemod yes - (по умолчанию: no)
    Модуль — это пользовательский модуль Homing, загружаемый с помощью [EMCMOT]HOMEMOD=modulename .

  • option tpmod yes - (по умолчанию: no)
    Модуль — это пользовательский модуль Trajectory Planning (tp), загружаемый с помощью [TRAJ]TPMOD=modulename .

Если VALUE опции не указано, это эквивалентно указанию option … yes.
Результат присвоения опции неподходящего значения не определен.
Результат использования любой другой опции не определен.

7.3. Лицензия и авторство

  • LICENSE — укажите лицензию модуля для документации и для объявления модуля MODULE_LICENSE(). Например, чтобы указать, что лицензия модуля — GPL v2 или новее:

    `license "GPL"; // indicates GPL v2 or later`

    Дополнительную информацию о значении MODULE_LICENSE() и дополнительных идентификаторах лицензий см. в разделе «<linux/module.h>» или на странице руководства «rtapi_module_param(3)».

    Это заявление обязательно.

  • AUTHOR - Укажите автора модуля для документации.

7.4. Поэкземплярное хранилище данных

  • variable CTYPE STARREDNAME; + variable CTYPE STARREDNAME[SIZE]; + variable CTYPE STARREDNAME = DEFAULT; + variable CTYPE STARREDNAME[SIZE] = DEFAULT;

    Объявите переменную STARREDNAME для каждого экземпляра типа CTYPE, при необходимости как массив элементов SIZE и при необходимости со значением по умолчанию DEFAULT. Элементы без DEFAULT инициализируются нулевыми битами. CTYPE — это простой тип C, состоящий из одного слова, такой как float, u32, s32, int и т. д. Для доступа к переменным массива используются квадратные скобки.

Если переменная должна иметь тип указателя, между знаком "*" и именем переменной не может быть пробела. Поэтому допустимо следующее:

variable int *example;

А это не допустимо:

variable int* badexample;
variable int * badexample;

7.5. Comments

В разделе объявлений поддерживаются однострочные комментарии в стиле C++ (//...) и многострочные комментарии в стиле C (/* ... */).

8. Ограничения

Хотя HAL позволяет контакту, параметру и функции иметь одно и то же имя, «halcompile» этого не делает.

Имена переменных и функций, которые нельзя использовать или которые могут вызвать проблемы, включают:

  • Все, что начинается с _comp.

  • comp_id

  • fperiod

  • rtapi_app_main

  • rtapi_app_exit

  • extra_setup

  • extra_cleanup

9. Удобные макросы

На основе элементов раздела объявлений halcompile создает структуру C с именем struct __comp_state. Однако вместо обращения к членам этой структуры (например, *(inst->name)), к ним обычно будут обращаться с помощью приведенных ниже макросов. Детали struct __comp_state и этих макросов могут меняться от одной версии 'halcompile’к другой.

  • FUNCTION(`__name__)` — используйте этот макрос, чтобы начать определение функции реального времени, которая ранее была объявлена с помощью function NAME. Функция включает параметр period, который представляет собой целое число наносекунд между вызовами функции.

  • EXTRA_SETUP() — используйте этот макрос, чтобы начать определение функции, вызываемой для выполнения дополнительной настройки этого экземпляра. Возвращает отрицательное значение Unix errno, чтобы указать на ошибку (например, return -EBUSY, если не удалось зарезервировать порт ввода-вывода), или 0, чтобы указать на успех.

  • EXTRA_CLEANUP() — используйте этот макрос, чтобы начать определение функции, вызываемой для дополнительной очистки компонента. Обратите внимание, что эта функция должна очищать все экземпляры компонента, а не только один. Макросы «pin_name», «parameter_name» и «data» здесь использовать нельзя.

  • pin_name или parameter_name — для каждого контакта имя_контакта или параметра имя_параметра существует макрос, который позволяет использовать имя отдельно для ссылки на контакт или параметр. Если pin_name или parameter_name представляет собой массив, макрос имеет форму pin_name(idx) или param_name(idx), где idx — это индекс в массиве контактов. Когда массив представляет собой массив переменного размера, разрешено ссылаться только на элементы до его condsize.

    Если элемент является условным элементом, ссылаться на него можно только в том случае, если его условие оценено как ненулевое значение.

  • variable_name — для каждой переменной variable_name существует макрос, который позволяет использовать имя отдельно для ссылки на переменную. Когда variable_name представляет собой массив, используется обычный индекс в стиле C: variable_name[idx].

  • data — если указаны "option data", этот макрос разрешает доступ к данным экземпляра.

  • fperiod — число секунд с плавающей запятой между вызовами этой функции реального времени.

  • FOR_ALL_INSTS() {} — для компонентов, не работающих в режиме реального времени. Этот макрос перебирает все определенные экземпляры. Внутри тела цикла макросы pin_name, parameter_name и data работают так же, как и в функциях реального времени.

10. Компоненты с одной функцией

Если компонент имеет только одну функцию и строка "FUNCTION" не появляется нигде после ;;, то часть после ;; все это считается телом единственной функции компонента. Пример этого см. в Simple Comp.

11. Индивидуальность компонента

Если компонент имеет какие-либо контакты или параметры с условием "if" или "[maxsize : condsize]", он называется компонентом с "индивидуальностью". Индивидуальность каждого экземпляра определяется при загрузке модуля. Индивидуальность можно использовать для создания контактов только при необходимости. Например, в логическом компоненте используется индивидуальность, чтобы обеспечить переменное количество входных контактов для каждого логического элемента и обеспечить выбор любой из основных логических функций и, или и xor.

Число разрешенных элементов индивидуальности по умолчанию устанавливается во время компиляции (64). Значение по умолчанию применяется к многочисленным компонентам, включенным в дистрибутив, созданным с использованием halcompile.

Чтобы изменить разрешенное количество элементов индивидуальности для пользовательских компонентов, используйте опцию --personality с halcompile. Например, чтобы разрешить до 128 индивидуальных раз:

  [sudo] halcompile --personalities=128 --install ...

При использовании компонентов с индивидуальностью обычно указывается элемент индивидуальности для каждого указанного экземпляра компонента. Пример для 3 экземпляров логического компонента:

loadrt logic names=and4,or3,nand5, personality=0x104,0x203,0x805
Note
Если в строке loadrt указано больше экземпляров, чем индивидуальностей, экземплярам с неуказанными индивидуальностями присваивается индивидуальность 0. Если запрошенное количество экземпляров превышает количество разрешенных индивидуальностей, индивидуальность назначаются путем индексации по модулю количества разрешенных индивидуальностей. Печатается сообщение, обозначающее такие назначения.

12. Компиляция

Поместите файл «.comp» в исходный каталог linuxcnc/src/hal/components и повторно запустите make. Файлы Comp автоматически обнаруживаются системой сборки.

Если файл .comp является драйвером для оборудования, его можно поместить в linuxcnc/src/hal/drivers и он будет создан, если только LinuxCNC не настроен как симулятор не в реальном времени.

13. Компиляция компонентов реального времени вне дерева исходного кода

halcompile может обрабатывать, компилировать и устанавливать компонент реального времени за один шаг, помещая rtexample.ko в каталог модулей реального времени LinuxCNC:

[sudo] halcompile --install rtexample.comp
Note
sudo (для прав root) необходим при использовании LinuxCNC из установки пакета deb. При использовании сборки Run-In-Place (RIP) права root не требуются.

Или он может обработать и скомпилировать за один шаг, оставив example.ko (или example.so для симулятора) в текущем каталоге:

halcompile --compile rtexample.comp

Или он может просто обработать, оставив example.c в текущем каталоге:

halcompile rtexample.comp

halcompile также может скомпилировать и установить компонент, написанный на C, используя параметры --install и --compile, показанные выше:

[sudo] halcompile --install rtexample2.c

Документация в формате man также может быть создана на основе информации в разделе объявлений:

halcompile --document rtexample.comp

Полученную справочную страницу example.9 можно просмотреть с помощью

man ./example.9

или скопировать в стандартное место для страниц руководства.

14. Компиляция компонентов, не работающих в реальном времени, вне дерева исходного кода

halcompile может обрабатывать, компилировать, устанавливать и документировать компоненты, не работающие в реальном времени:

halcompile non-rt-example.comp
halcompile --compile non-rt-example.comp
[sudo] halcompile --install non-rt-example.comp
halcompile --document non-rt-example.comp

Для некоторых библиотек (например, modbus) может потребоваться добавить дополнительные аргументы компилятора и компоновщика, чтобы компилятор мог найти и связать библиотеки. В случае файлов .comp это можно сделать с помощью операторов "option" в файле .comp. Для файлов .c это невозможно, поэтому вместо этого можно использовать параметры --extra-compile-args и --extra-link-args. Например, эту командную строку можно использовать для компиляции компонента vfdb_vfd.c вне дерева.

halcompile --userspace --install --extra-compile-args="-I/usr/include/modbus" --extra-link-args="-lm -lmodbus -llinuxcncini" vfdb_vfd.c
Note
Эффект от использования дополнительных аргументов как в командной строке, так и в файле не определен.

15. Examples

15.1. constant

Обратите внимание, что объявление "function _" создает функции с именем "constant.0" и т. д. Имя файла должно совпадать с именем компонента.

component constant;
pin out float out;
param r float value = 1.0;
function _;
license "GPL"; // indicates GPL v2 or later
;;
FUNCTION(_) { out = value; }

15.2. sincos

Этот компонент вычисляет синус и косинус входного угла в радианах. Он имеет другие возможности, чем выходы "sine" и "cosine" siggen, потому что вход представляет собой угол, а не работает свободно на основе параметра "frequency".

В исходном коде выводы объявлены с именами sin_ и cos_, чтобы они не мешали функциям sin() и cos(). Контакты HAL по-прежнему называются sincos.<num>.sin.

component sincos;
pin out float sin_;
pin out float cos_;
pin in float theta;
function _;
license "GPL"; // indicates GPL v2 or later
;;
#include <rtapi_math.h>
FUNCTION(_) { sin_ = sin(theta); cos_ = cos(theta); }

15.3. out8

Этот компонент является драйвером вымышленной карты под названием out8, которая имеет 8 контактов цифрового выхода, которые обрабатываются как одно 8-битное значение. Таких карт в системе может быть разное количество и они могут находиться по разным адресам. Вывод называется out_, поскольку out — это идентификатор, используемый в <asm/io.h>. Он иллюстрирует использование EXTRA_SETUP и EXTRA_CLEANUP для запроса области ввода-вывода и последующего ее освобождения в случае ошибки или при выгрузке модуля.

component out8;
pin out unsigned out_ "Output value; only low 8 bits are used";
param r unsigned ioaddr;

function _;

option count_function;
option extra_setup;
option extra_cleanup;
option constructable no;

license "GPL"; // indicates GPL v2 or later
;;
#include <asm/io.h>

#define MAX 8
int io[MAX] = {0,};
RTAPI_MP_ARRAY_INT(io, MAX, "I/O addresses of out8 boards");

int get_count(void) {
    int i = 0;
    for(i=0; i<MAX && io[i]; i++) { /* Nothing */ }
    return i;
}

EXTRA_SETUP() {
    if(!rtapi_request_region(io[extra_arg], 1, "out8")) {
        // set this I/O port to 0 so that EXTRA_CLEANUP does not release the IO
        // ports that were never requested.
        io[extra_arg] = 0;
        return -EBUSY;
    }
    ioaddr = io[extra_arg];
    return 0;
}

EXTRA_CLEANUP() {
    int i;
    for(i=0; i < MAX && io[i]; i++) {
        rtapi_release_region(io[i], 1);
    }
}

FUNCTION(_) { outb(out_, ioaddr); }

15.4. hal_loop

component hal_loop;
pin out float example;

Этот фрагмент компонента иллюстрирует использование префикса hal_ в имени компонента.

loop — это общее имя, а префикс hal_ позволяет избежать потенциальных конфликтов имен с другим несвязанным программным обеспечением. Например, в системах реального времени RTAI код реального времени выполняется в ядре, поэтому, если бы компонент назывался просто loop, он мог бы легко конфликтовать со стандартным модулем ядра loop.

При загрузке halcmd show comp покажет компонент под названием hal_loop. Однако вывод, отображаемый halcmd show pin, будет loop.0.example, а не hal-loop.0.example.

15.5. arraydemo

Этот компонент реального времени иллюстрирует использование массивов фиксированного размера:

component arraydemo "4-bit Shift register";
pin in bit in;
pin out bit out-# [4];
function _ nofp;
license "GPL"; // indicates GPL v2 or later
;;
int i;
for(i=3; i>0; i--) out(i) = out(i-1);
out(0) = in;

15.6. rand

Этот компонент, работающий не в реальном времени, меняет значение на своем выходном контакте на новое случайное значение в диапазоне (0,1) примерно раз в 1 мс.

component rand;
option userspace;

pin out float out;
license "GPL"; // indicates GPL v2 or later
;;
#include <unistd.h>

void user_mainloop(void) {
    while(1) {
        usleep(1000);
        FOR_ALL_INSTS() out = drand48();
    }
}

15.7. logic

Этот компонент реального времени показывает, как использовать "индивидуальность" для создания массивов переменного размера и дополнительных выводов.

component logic "LinuxCNC HAL component providing experimental logic functions";
pin in bit in-##[16 : personality & 0xff];
pin out bit and if personality & 0x100;
pin out bit or if personality & 0x200;
pin out bit xor if personality & 0x400;
function _ nofp;
description """
Experimental general 'logic function' component.  Can perform 'and', 'or'
and 'xor' of up to 16 inputs.  Determine the proper value for 'personality'
by adding:
.IP \\(bu 4
The number of input pins, usually from 2 to 16
.IP \\(bu
256 (0x100)  if the 'and' output is desired
.IP \\(bu
512 (0x200)  if the 'or' output is desired
.IP \\(bu
1024 (0x400)  if the 'xor' (exclusive or) output is desired""";
license "GPL"; // indicates GPL v2 or later
;;
FUNCTION(_) {
    int i, a=1, o=0, x=0;
    for(i=0; i < (personality & 0xff); i++) {
        if(in(i)) { o = 1; x = !x; }
        else { a = 0; }
    }
    if(personality & 0x100) and = a;
    if(personality & 0x200) or = o;
    if(personality & 0x400) xor = x;
}

Типичная линия нагрузки для этого компонента может быть такой

loadrt logic count=3 personality=0x102,0x305,0x503

который создает следующие контакты:

  • Элемент И с двумя входами: logic.0.and, logic.0.in-00, logic.0.in-01

  • Элемент И с 5-ю входами и элементы ИЛИ: logic.1.and, logic.1.or, logic.1.in-00, logic.1.in-01, logic.1.in-02, logic.1.in-03, logic.1.in-04,

  • Элементы И с тремя входами и элементы Исключающее ИЛИ: logic.2.and, logic.2.xor, logic.2.in-00, logic.2.in-01, logic.2.in-02

15.8. Общие функции

В этом примере показано, как вызывать функции из основной функции. Он также показывает, как передать ссылку на контакты HAL этим функциям.

component example;
pin in s32 in;
pin out bit out1;
pin out bit out2;

function _;
license "GPL";
;;

// general pin set true function
void set(hal_bit_t *p){
    *p = 1;
}

// general pin set false function
void unset(hal_bit_t *p){
    *p = 0;
}

//main function
FUNCTION(_) {
    if (in < 0){
        set(&out1);
        unset(&out2);
    }else if (in >0){
        unset(&out2);
        set(&out2);
    }else{
        unset(&out1);
        unset(&out2);
    }
}

Этот компонент использует две общие функции для управления связанным с ним битом контакта HAL.

16. Использование командной строки

Страница руководства halcompile содержит подробную информацию о вызове halcompile.

$ man halcompile

Краткое описание использования halcompile представлено следующим образом:

$ halcompile --help