Как создать указатель на функцию в CodeSy v3
Можете ли вы дать мне пример того, как я могу объявить указатель на функцию в моей библиотеке? И как я могу передать указатель на функцию к моей внешней библиотеке?
4 ответа
TL;DR: Сумки возможны и очень легко в CoDeSys v3.
В CoDeSys "функции" - это на самом деле указатели на функции, хранящиеся в таблице функций.
В CodeSys v2, чтобы получить адрес функции, вы должны были использовать INDEXOF(F_MyFunction)
, и это обеспечило индекс указателя функции в таблице функций. Получение адреса стола было, ммм, упражнением для читателя (его можно было найти с помощью некоторой гимнастики).
В CoDeSys v3, ADR
работает вместо INDEXOF
, и поэтому ADR(F_MyFunction)
дает вам адрес указателя функции, который указывает на F_MyFunction
код!
И угадайте, что: вы можете установить этот указатель на функцию и F_MyFunction(...)
это просто вызов через указатель на функцию.
Это так просто.
Итак, чтобы выполнять косвенные вызовы функций, все, что вам нужно сделать, это объявить фиктивную "функцию", которая в действительности работает как устанавливаемый указатель на функцию!
Предположим, у нас есть две целевые функции, которые мы хотим вызвать косвенно:
FUNCTION F_Add1: INT
VAR_INPUT
param : INT;
END_VAR
F_Add1:= param + 1;
END_FUNCTION
FUNCTION F_Add2: INT
VAR_INPUT
param : INT;
END_VAR
F_Add2:= param + 2;
END_FUNCTION
Затем мы определяем "указатель на функцию", который должен иметь ту же сигнатуру, что и косвенно вызываемые функции:
FUNCTION FPTR_Add : INT
VAR_INPUT
param : INT;
END_VAR
END_FUNCTION
У нас также может быть помощник, который назначает указатели на функции:
F_FPTR_Assign
VAR_INPUT
dst : POINTER TO PVOID;
src : POINTER TO PVOID;
END_VAR
dst^ := src^;
END_FUNCTION
Наконец, давайте сделаем несколько косвенных вызовов:
// should return 3 if indirect calls work
FUNCTION F_Test : INT
VAR
val : INT;
END_VAR
F_FPTR_Assign(ADR(FPTR_Add), ADR(F_Add1));
// FPTR_Add points to F_Add1
val := FPTR_Add(val);
// here val has value 1
F_FPTR_Assign(ADR(FPTR_Add), ADR(F_Add2));
// FPTR_Add points to F_Add2
val := FPTR_Add(val);
// here val has value 3
F_Test := val;
END_FUNCTION
Единственным недостатком этого метода является то, что отладчик не проверяет динамические значения указателей на функции и, таким образом, шаг внутрь ведет себя как шаг вперед. Обходной путь состоит в том, чтобы установить точку останова в целевых функциях, после чего оба шага и переход остановятся на этом.
Есть и другие способы достижения этого эффекта, например, путем прямого управления фреймами стека, поэтому даже если этот точный метод перестанет работать из-за некоторых изменений в CoDeSys, есть другие способы сделать это.
Это старая тема, но, похоже, есть другой (намного лучший) способ обойти отсутствующую функцию указателя функции. Т.е. используя указатель на функциональный блок и вызывая его метод, см. код ниже.
FUNCTION_BLOCK TButtonHandlers
//with
METHOD Click : BOOL
METHOD LongClick : BOOL
и
PROGRAM FOO
VAR
Handlers: TButtonHandlers;
pHdl: POINTER TO TButtonHandlers;
END_VAR
-
pHdl:= ADR(Handlers); //Assign
pHdl^.Click(); //Function Call
Надеюсь, это поможет таким людям, как я, наткнуться на эту (старую) тему.
Указатели возможны в codesys
Создать указатель в codesys
ты бы сделал
VAR
pVar : POINTER TO BYTE;
tempVar : BYTE;
derefereceVar : BYTE;
END_VAR
//get a pointer to the byte variable
pVar := ADR(tempVar);
разыменовать этот указатель вы бы
derefereceVar := tempVar^;
Поэтому, если вы хотите иметь указатель в качестве аргумента вашей функции, вы должны передать pVar
или же ADR(tempVar)
в приведенном выше примере для параметра вашей функции, который принимает POINTER_TO_BYTE
как тип.
На платформах Codesys можно создать указатель на тип данных или функциональный блок. В TwinCAT 3 также возможно создать указатель на функцию, но он не может быть вызван в программе ПЛК. Указатель функции может быть задан только как параметр внешнего компонента библиотеки.
Проверьте это: https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/136447627.html&id=4296747249216071915