PDA

Просмотр полной версии : Как из REAL сделать 2 * WORD (для Modbus)?



SokolovA
13.07.2016, 21:19
Добрый вечер!

ПЛК 150 используется как модуль ввода-вывода для СПК 110 (зачем, не спрашивайте, рак мозга у одного товарища-погроммиста).

Протокол обмена - Modbus-RTU через RS485.

Модбас передает максимум переменные типа WORD. Аналоговые входы\выходы имеют тип REAL (по длине - 2хWORD).

Вопрос - как сделать в Codesys 2.3 из REAL два WORD.

MEMCPY, насколько я понял, оно не умеет.

Как еще?

Заранее спасибо!

lara197a
13.07.2016, 21:41
модбас передает все
4 байта
8 байт
флоат
файл
и т.д.
плк 150 тоже

_Mikhail
13.07.2016, 22:32
Для передачи REAL между ПЛК и СПК не надо его разбивать. нужно считывать оба регистра и составлять обратно REAL.Все это описано http://www.owen.ru/forum/showthread.php?t=23897

capzap
13.07.2016, 22:52
(зачем, не спрашивайте, рак мозга у одного товарища-погроммиста)

вполне нормальное решение, чтоб не закупать модули по отдельности

а вот по Вам
1) выше уже написано что конфигуратор плк150 позволяет иметь в слейве и реалы в том числе
2) memcpy может и не работает, только она для КДС3.5, а в КДС2.3 используется sysmemcpy
3) вариантов как еще масса

SokolovA
13.07.2016, 23:08
вполне нормальное решение, чтоб не закупать модули по отдельности

а вот по Вам
1) выше уже написано что конфигуратор плк150 позволяет иметь в слейве и реалы в том числе
2) memcpy может и не работает, только она для КДС3.5, а в КДС2.3 используется sysmemcpy
3) вариантов как еще масса

Неа, не нормальное, ни по деньгам - экономии в данном конкретном случае в минус рубль, ни по технологичности - вместо разработки я трачу время на разбивку\склеивание переменных.

ПЛК 150 передает REAL. А вот на стороне 3.6 у меня такая http://i.piccy.info/i9/08796125136446202e0ae4ae0143f4ac/1468439960/201414/1051572/150.jpg (http1://i.piccy.info/i9/08796125136446202e0ae4ae0143f4ac/1468439960/201414/1051572/150.jpg).
Модбас больше чем WORD не принимает, тип сменить нельзя, он зависит от команды, а они только для BOOL (Функция 01), BYTE (02), WORD (03, 04).

2. За sysmemcpy спасибо.

3. А какой вариант наилучший, напишите плиз!

lara197a
13.07.2016, 23:43
преобразовывает в ворд
2байта тупо улетели
опять берете исходное
сдвигаете в право(могу путать право-лево) на 16
и преобразовываете в ворд.
получаете второй ворд

можно почитать что такое реал
упаковываете в один регистр мантису
в др. экспоненту.

у меня раз попадалась обратная задача с расходомера данные прочесть
создал фб. потребуется кину в личку.

capzap
14.07.2016, 07:48
Неа, не нормальное, ни по деньгам - экономии в данном конкретном случае в минус рубль, ни по технологичности - вместо разработки я трачу время на разбивку\склеивание переменных.

ПЛК 150 передает REAL. А вот на стороне 3.6 у меня такая http://i.piccy.info/i9/08796125136446202e0ae4ae0143f4ac/1468439960/201414/1051572/150.jpg (http1://i.piccy.info/i9/08796125136446202e0ae4ae0143f4ac/1468439960/201414/1051572/150.jpg).
Модбас больше чем WORD не принимает, тип сменить нельзя, он зависит от команды, а они только для BOOL (Функция 01), BYTE (02), WORD (03, 04).

2. За sysmemcpy спасибо.

3. А какой вариант наилучший, напишите плиз!

Т.е. у Вас нет сложностей опрашивать несколько устройств в сети, вместо одного?
У Вас нет проблем с габаритами шкафа для этой мини-задачи?
Из модуля аналоговых входов, Вам тоже пришлось бы "выуживать" РЕАЛ по модбас, не вижу разницы с текущим вариантом комплектации. Если Вы планировали забирать ИНТ-ы, то кто мешает делать это в плк?

SokolovA
14.07.2016, 12:32
Нормальное решение программиста который не занимается хренью в виде разбивки/склеивания мифических регистров, а пересылает целиком структуры и пьет пиво пока вы пытаетесь "экономить рубль".

Модбас вообще не работает c BOOL, BYTE, WORD.
Функции 1,2 - атавизм. Функции 3,4 позволяют прочитать объем абстрактных данных от 2 до 250 байт куда можно пихнуть практически все что угодно с точки зрения автоматизации.

Рубль тут никто не экономит, написал, чтобы не спрашивали про финансовую составляющую.

Ок, и как же их переслать, эти структуры, со стороны 2.3, если аналоговый вход имеетр тип REAL, а в 3.6 я не могу задать произвольный тип переменной, которая мне приходит по модбасу, она только WORD или меньше. То есть 250 байт он может и могет, но они будут разбиты на N переменных типа WORD. Которые нужно резать на источнике (ПЛК 150) и склеивать на приемнике (СПК 110).

Еще раз, скрин (http://i.piccy.info/i9/08796125136446202e0ae4ae0143f4ac/1468439960/201414/1051572/150.jpg).

Тип WORD в переменной не меняется.



Т.е. у Вас нет сложностей опрашивать несколько устройств в сети, вместо одного?
У Вас нет проблем с габаритами шкафа для этой мини-задачи?
Из модуля аналоговых входов, Вам тоже пришлось бы "выуживать" РЕАЛ по модбас, не вижу разницы с текущим вариантом комплектации. Если Вы планировали забирать ИНТ-ы, то кто мешает делать это в плк?


А в чем проблема опрашивать 5 модулей? Они это сами делают, мне ни строчки кода писать не надо, только раздать адреса и настройки залить в модули.
Проблемы, наверное, будут, когда один из модулей отвалится, но тогда и вся система станет неработоспособной.

Габариты - это не мои проблемы, в данной задаче. Но, в общем и целом, шкафы производства РФ пока еще есть и они стоят копейки.

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

Либо у меня не тот кодесис, либо я что-то делаю не так. Еще раз, тип аналогового входа на ПЛК 150 - REAL. В модбас-слейв на ПЛК я могу сделать переменную REAL. Но на 3.6 мой канал имеет тип ARRAY [0...4] OF WORD, и изменить ни тип канала, ни, соответственно, тип переменных в канале я не могу руками.
В этом-то вся и проблема.
Значит, нужно на ПЛК запихивать REAL в два WORD, а на СПК наоборот их склеивать.

Что не так? :)

capzap
14.07.2016, 12:42
Что не так? :)
да много чего не так, не ликбез же проводить

переходите в тему http://www.owen.ru/forum/showthread.php?t=23897 скачивайте документ SPK_Modbus_v.0.ХХ до страницы 120 раздел 6.4.2. Описание реализации на языке ST ну и дальше по описанию

bogd67
18.02.2026, 12:34
//==========
//==========

kondor3000
18.02.2026, 14:29
//================================================== =//
//================================================== =//
FUNCTION_BLOCK REAL_TO_WW

//================================================== =//

FUNCTION_BLOCK WW_TO_REAL___________(*тут ФБ вместо функции*)

_____________________________________ (* Нет присвоения выхода фунции*)

//================================================== =//


У вас ошибки во втором блоке, , это вообще функция, а не ФБ и нет присвоения выхода у функции.
Кроме того это блоки больше для Овен лоджика подходят, в Codesys все делается через указатели в 3 строки
или вообще без кода, проcто положив REAL в 2 последовательных WORD

kondor3000
19.02.2026, 15:15
Дополняю, в первом блоке тоже ошибки, не верно работает на пределах от -0.99999 до -0.00001 и от 0.00001 до 0.99999

bogd67
20.02.2026, 10:21
//-----------------------

kondor3000
20.02.2026, 10:53
Поправил, теперь считает корректно)) да, такой вариант это путь самурая, в третьем кодесис это делают через UNION или через указатели во втором, но не во всяком ST есть указатели и UNION. Сейчас, например, ОВЕН предлагает линейку ПЛК c российской средой ПОЛИГОН. Среда разработки очень перспективная, оперативная техподдержка, удобный интерфейс, есть возможность работаь на С++ и на ST. НО среда пока в процессе развития, и указатели пока не реализованы. К тому же пусть и давний вопрос в первом посте, но он так и висел без ответа))

Опять ошибки в первом блоке, мало того, ещё и LINT, туда добавили, а в CDS 2.3 нет LINT.
Вот ссылка на готовые решения в Овен Лоджик https://owen.ru/forum/showthread.php?t=40405&p=450146&viewfull=1#post450146
без указателей и прочего.

bogd67
20.02.2026, 11:09
//-------------------------------

capzap
20.02.2026, 11:37
Поправил, теперь считает корректно))
видимо надо еще поправить, если спрсить ИИ

1) Неполное выполнение алгоритма
Код обрывается после блока ELSE в основной ветке. Отсутствует финальная сборка результата из компонентов (dwSign, dwExp, dwMant) и распределение по выходам wLow и wHigh.

2) Проблемы с точностью вычислений
Использование логарифмов (LOG) и степеней (EXPT) для вычисления экспоненты и мантиссы может привести к ошибкам округления. Это критично для точного преобразования в формат IEEE 754.

3) Некорректная обработка субнормальных чисел
Формула rAbs / 1.401298E-45 может давать значения вне диапазона DWORD.
Не учитывается, что мантисса субнормальных чисел не имеет неявной единицы.

4) Отсутствие обработки особых случаев
Не обработаны:
бесконечности (+INF, -INF);
NaN (Not a Number).

5) Потенциальное переполнение
При преобразовании REAL_TO_DWORD может произойти переполнение, если результат выходит за диапазон 0…4294967295.

6) Некорректное вычисление экспоненты
Смещение экспоненты (bias) должно применяться строго по стандарту IEEE 754: значение экспоненты кодируется со смещением 127. В коде это реализовано неточно.

7) Отсутствие инициализации
Переменные wLow и wHigh не инициализированы. При ошибках преобразования они могут содержать неопределённые значения.

8) Отсутствие маскировки битов
При сборке dwResult не контролируется, что:
экспонента занимает биты 30–23;
мантисса — биты 22–0;
знак — бит 31.


Тестовые кейсы для валидации
0.0 16#00000000 +0.0
-0.0 16#80000000 -0.0 (знак сохранён)
1.0 16#3F800000 Эталонное значение
-2.5 16#C0200000 Отрицательное нормальное
1.1754944e-38 16#00800000 Минимальное нормальное
1.0E-40 субнормал Проверка ветки subnormal
3.4028235E+38 16#7F7FFFFF MAX_REAL
> MAX_REAL 16#7F800000 +Infinity

bogd67
20.02.2026, 12:00
//----------------------------

bogd67
20.02.2026, 12:26
//-----------------------

kondor3000
20.02.2026, 13:34
Ошибки во втором кодесис? У меня, к сожалению второго нет, есть третий и полигон. На них всё работает, ошибок нет. если не сложно, поправьте.


Вы при копировании потеряли END_IF в конце кода.
Просто 1 вариант был для CDS2.3, а второй вариант уже под 3.5 с использованием LINT, что удивительно.
А рабочий код тут, примеры ниже и работают везде, даже где нет указателей, логарифмов и 64 битных переменных, типа LINT https://owen.ru/forum/showthread.php?t=40405&p=450141&viewfull=1#post450141

bogd67
20.02.2026, 14:20
//------------------------------

kondor3000
20.02.2026, 14:27
Первый вариант я достал из загашника, он был написан мной лет пять назад для БЕРЕМИЗ, как есть в ПОЛИГОНЕ он не работал, ПОЛИГОН не понимает неявного преобразования типов, поэтому появился LINT_TO_INT. второго кодесис у меня нет, я уже писал. Я понял, что Вам скучно, просто хочется поговорить)

Дело не в поговорить.
Интересно когда код улучшают, исправляют, вы дважды выложили не рабочий код, Зачем?
Тема ПЛК (среда CoDeSys V2.3)ПЛК1хх при чём тут Полигон?

bogd67
20.02.2026, 14:32
Думаю, что я последний раз тут что-то выложил) сейчас есть нейросети, на форуме можно туда ссылку сделать. там дадут любой ответ на любой вопрос. к тому же нейросеть не станет хамить, цепляться, и пытаться самоутверждаться за счет других))

kondor3000
20.02.2026, 17:03
Чат GPT5 четыре раза пытался исправить код и каждый раз код не работал на пределах от -0.99999 до -0.00001 и от 0.00001 до 0.99999, при чём именно это я и просил исправить, потом кончилось время.
Тупые ИИ в действии.
Используйте рабочий код, ссылка выше, это всё о чём хотел сказать ТС, только он не понял).

capzap
20.02.2026, 23:51
Чат GPT5 четыре раза пытался исправить код и каждый раз код не работал на пределах от -0.99999 до -0.00001 и от 0.00001 до 0.99999, при чём именно это я и просил исправить, потом кончилось время.
Тупые ИИ в действии.
Используйте рабочий код, ссылка выше, это всё о чём хотел сказать ТС, только он не понял).

что конкретно не работало? 88195 мой ИИ утверждает что все совпадает из этого кода



FUNCTION_BLOCK FB_RealToWords_IEEE754
VAR_INPUT
InputReal : REAL;
END_VAR

VAR_OUTPUT
WordHigh : WORD; // Биты 31-16
WordLow : WORD; // Биты 15-0
StatusCode : INT; // 0: Normal, 1: Zero, 2: Infinity, 3: NaN
END_VAR

VAR
Sign : BOOL;
AbsValue : REAL;
Exponent : INT;
Mantissa : DINT;
BiasedExp : BYTE;
SignBit : DINT;
TempDword : DINT;
i : INT;
Fraction : REAL;
ScaledMant : REAL;
END_VAR

// ================================================== ==========================
// ШАГ 1: Обработка особых случаев (до математических операций)
// ================================================== ==========================

// Проверка на NaN (NaN != NaN по стандарту IEEE 754)
IF (InputReal <> InputReal) THEN
// NaN: Exponent = 255 (16#FF), Mantissa != 0
SignBit := 0; // Для NaN знак обычно игнорируем
BiasedExp := 255;
Mantissa := 16#00400000; // Quiet NaN (старший бит мантиссы = 1)
StatusCode := 3;

// Сборка DWORD
TempDword := SHL(SignBit, 31) OR SHL(BiasedExp, 23) OR Mantissa;
WordHigh := DWORD_TO_WORD(SHR(TempDword, 16));
WordLow := DWORD_TO_WORD(TempDword);
RETURN;
END_IF;

// Проверка на бесконечность (через сравнение с максимальным REAL)
// Примечание: в некоторых реализациях потребуется флаг от переполнения
IF (InputReal > 3.40282347E+38) OR (InputReal < -3.40282347E+38) THEN
// Infinity: Exponent = 255, Mantissa = 0
SignBit := 1;
BiasedExp := 255;
Mantissa := 0;
StatusCode := 2;

TempDword := SHL(SignBit, 31) OR SHL(BiasedExp, 23) OR Mantissa;
WordHigh := DWORD_TO_WORD(SHR(TempDword, 16));
WordLow := DWORD_TO_WORD(TempDword);
RETURN;
END_IF;

// ================================================== ==========================
// ШАГ 2: Извлечение знака
// ================================================== ==========================

IF InputReal < 0.0 THEN
Sign := TRUE;
SignBit := 1;
AbsValue := -InputReal; // Сохраняем абсолютное значение
ELSIF InputReal > 0.0 THEN
Sign := FALSE;
SignBit := 0;
AbsValue := InputReal;
ELSE
// Случай нуля (+0.0 или -0.0)
// Для сохранения знака -0.0 проверяем 1.0/InputReal
IF (1.0 / InputReal) < 0.0 THEN
Sign := TRUE;
SignBit := 1;
ELSE
Sign := FALSE;
SignBit := 0;
END_IF;

// Zero: Exponent = 0, Mantissa = 0
BiasedExp := 0;
Mantissa := 0;
StatusCode := 1;

TempDword := SHL(SignBit, 31) OR SHL(BiasedExp, 23) OR Mantissa;
WordHigh := DWORD_TO_WORD(SHR(TempDword, 16));
WordLow := DWORD_TO_WORD(TempDword);
RETURN;
END_IF;

// ================================================== ==========================
// ШАГ 3: Нормализация числа (находим экспоненту без LOG)
// ================================================== ==========================

Exponent := 0;
Fraction := AbsValue;

// Если число >= 2.0, делим на 2 пока не попадём в диапазон [1.0, 2.0)
WHILE Fraction >= 2.0 DO
Fraction := Fraction * 0.5; // Деление на 2
Exponent := Exponent + 1;
END_WHILE;

// Если число < 1.0, умножаем на 2 пока не попадём в диапазон [1.0, 2.0)
WHILE Fraction < 1.0 DO
Fraction := Fraction * 2.0;
Exponent := Exponent - 1;
END_WHILE;

// Теперь Fraction в диапазоне [1.0, 2.0), Exponent - порядок числа

// ================================================== ==========================
// ШАГ 4: Вычисление смещённой экспоненты (Bias = 127)
// ================================================== ==========================

BiasedExp := BYTE_OF(Exponent + 127);

// Проверка на переполнение/исчезновение экспоненты
IF BiasedExp >= 255 THEN
// Переполнение -> Infinity
BiasedExp := 255;
Mantissa := 0;
StatusCode := 2;
TempDword := SHL(SignBit, 31) OR SHL(BiasedExp, 23) OR Mantissa;
WordHigh := DWORD_TO_WORD(SHR(TempDword, 16));
WordLow := DWORD_TO_WORD(TempDword);
RETURN;
ELSIF BiasedExp <= 0 THEN
// Исчезновение -> Denormal или Zero
BiasedExp := 0;
// Для денормализованных чисел нужна дополнительная обработка
Mantissa := 0;
StatusCode := 1;
TempDword := SHL(SignBit, 31) OR SHL(BiasedExp, 23) OR Mantissa;
WordHigh := DWORD_TO_WORD(SHR(TempDword, 16));
WordLow := DWORD_TO_WORD(TempDword);
RETURN;
END_IF;

// ================================================== ==========================
// ШАГ 5: Извлечение мантиссы (23 бита)
// ================================================== ==========================

// Fraction сейчас в диапазоне [1.0, 2.0)
// Убираем ведущую 1 (она неявная в IEEE 754)
Fraction := Fraction - 1.0; // Теперь [0.0, 1.0)

// Преобразуем дробную часть в 23-битную мантиссу
// Умножаем на 2^23 = 8388608
ScaledMant := Fraction * 8388608.0;
Mantissa := DINT_OF(ScaledMant + 0.5); // Округление

// Ограничиваем мантиссу 23 битами (маска 16#007FFFFF)
Mantissa := Mantissa AND 16#007FFFFF;

// ================================================== ==========================
// ШАГ 6: Сборка 32-битного представления
// ================================================== ==========================

// Формат: [Sign:1][Exponent:8][Mantissa:23]
// Бит 31: Знак
// Биты 30-23: Экспонента
// Биты 22-0: Мантисса

TempDword := 0;

// Добавляем бит знака (бит 31)
IF SignBit = 1 THEN
TempDword := TempDword OR 16#80000000;
END_IF;

// Добавляем экспоненту (биты 30-23)
TempDword := TempDword OR SHL(DINT_OF(BiasedExp), 23);

// Добавляем мантиссу (биты 22-0)
TempDword := TempDword OR Mantissa;

// ================================================== ==========================
// ШАГ 7: Разделение на два WORD
// ================================================== ==========================

WordHigh := DWORD_TO_WORD(SHR(TempDword, 16)); // Старшие 16 бит
WordLow := DWORD_TO_WORD(TempDword); // Младшие 16 бит

StatusCode := 0; // Normal

END_FUNCTION_BLOCK

kondor3000
21.02.2026, 09:36
что конкретно не работало? мой ИИ утверждает что все совпадает из этого кода

Вот код для CDS 2.3, который просил исправить для работы в диапазоне от -0.99999 до -0.00001 и от 0.00001 до 0.99999
Он же не работает в Лоджике, с исправлениями конечно.


FUNCTION_BLOCK REAL_TO_WW
VAR_INPUT
rIn : REAL; (* Входное число *)
END_VAR
VAR_OUTPUT
wLow : WORD; (* Слово 2 для Modbus *)
wHigh : WORD; (* Слово 1 для Modbus *)
END_VAR
VAR
(* Промежуточные переменные *)
rAbs : REAL;
iSign : DWORD;
iExp : DINT;
rMantissa : REAL;
iMantissa : DWORD;
dwResult : DWORD; (* Итоговое 32-битное число *)
END_VAR


(* 1. Обработка нуля — особый случай в IEEE-754 *)
IF rIn = 0.0 THEN
dwResult := 0;
ELSE
(* 2. Определяем знак (бит 31) *)
IF rIn < 0.0 THEN
iSign := 16#80000000;
rAbs := ABS(rIn);
ELSE
iSign := 16#0;
rAbs := rIn;
END_IF
(* 3. Вычисляем экспоненту (биты 30..23) *)
(* Экспонента — это целая часть логарифма по основанию 10 *)
iExp := TRUNC(LOG(rAbs) / 0.30103); (* 0.30103 ? LOG(2) *)
(* 4. Вычисляем мантиссу (биты 22..0) *)
(* Убираем из числа степень двойки, оставляя значение [1.0 ... 2.0) *)
rMantissa := rAbs / EXPT(2.0, iExp);
(* В IEEE-754 первая единица мантиссы не записывается (скрытый бит) *)
(* Оставшуюся дробную часть растягиваем на 23 бита (2^23 = 8388608) *)
iMantissa := REAL_TO_DWORD((rMantissa - 1.0) * 8388608.0);
(* 5. Сборка итогового DWORD *)
(* Смещаем экспоненту на 127 (bias) и сдвигаем на 23 позиции влево *)
dwResult := iSign OR SHL(DINT_TO_DWORD(iExp + 127), 23) OR iMantissa;
END_IF

(* 6. Разрезаем на два WORD для Modbus *)
wHigh := DWORD_TO_WORD(SHR(dwResult, 16));
wLow := DWORD_TO_WORD(dwResult AND 16#FFFF);

capzap
21.02.2026, 13:23
Вот код для CDS 2.3, который просил исправить для работы в диапазоне от -0.99999 до -0.00001 и от 0.00001 до 0.99999
Он же не работает в Лоджике, с исправлениями конечно.

за свой вопрос к ИИ я сразу оговорил не использовать экспоненты и степени, а сейчас еще и про ограничения ОЛ и вроде считает, за исключением инфинити

function_block FB_RealToWords_IEEE754

VAR_INPUT
InputReal : REAL;
END_VAR

VAR_OUTPUT
WordHigh : udint; // Старшие 16 бит (биты 31–16)
WordLow : udint; // Младшие 16 бит (биты 15–0)
StatusCode : udint; // 0: Normal, 1: Zero, 2: Infinity, 3: NaN
END_VAR

VAR
Sign : BOOL;
AbsValue : REAL;
Exponent : udint;
Mantissa : udint;
BiasedExp : udint;
SignBit : udint;
TempDword : udint;
i : udint;
buff_val : udint;
Fraction : REAL;
ScaledMant : REAL;
END_VAR

// ================================================== ==========================
// ШАГ 1: Обработка особых случаев (до математических операций)
// ================================================== ==========================

// Проверка на NaN (NaN != NaN по стандарту IEEE 754)
IF (InputReal <> InputReal) THEN
// NaN: Exponent = 255, Mantissa != 0
SignBit := 0;
BiasedExp := 255;
Mantissa := 4194304; // Quiet NaN
StatusCode := 3;

// Сборка UDINT
TempDword := shl(SignBit, 31) + shl(BiasedExp, 23) + Mantissa;
WordHigh := shr(TempDword, 16) MOD 65536; // Старшие 16 бит
WordLow := TempDword MOD 65536; // Младшие 16 бит
RETURN;
END_IF;

// Проверка на бесконечность
IF (InputReal > 3.40282347E+38) OR (InputReal < -3.40282347E+38) THEN
// Infinity: Exponent = 255, Mantissa = 0
IF InputReal < 0.0 THEN
SignBit := 1;
ELSE
SignBit := 0;
END_IF;
BiasedExp := 255;
Mantissa := 0;
StatusCode := 2;

TempDword := shl(SignBit, 31) + shl(BiasedExp, 23) + Mantissa;
WordHigh := shr(TempDword, 16) MOD 65536; // Старшие 16 бит
WordLow := TempDword MOD 65536; // Младшие 16 бит
RETURN;
END_IF;

// ================================================== ==========================
// ШАГ 2: Извлечение знака
// ================================================== ==========================
IF InputReal < 0.0 THEN
Sign := TRUE;
SignBit := 1;
AbsValue := -InputReal;
ELSIF InputReal > 0.0 THEN
Sign := FALSE;
SignBit := 0;
AbsValue := InputReal;
ELSE
// Случай нуля
Sign := FALSE;
SignBit := 0;
BiasedExp := 0;
Mantissa := 0;
StatusCode := 1;

TempDword := shl(SignBit, 31) + shl(BiasedExp, 23) + Mantissa;
WordHigh := shr(TempDword, 16) MOD 65536; // Старшие 16 бит
WordLow := TempDword MOD 65536; // Младшие 16 бит
RETURN;
END_IF;

// ================================================== ==========================
// ШАГ 3: Нормализация числа
// ================================================== ==========================
Exponent := 0;
Fraction := AbsValue;

WHILE Fraction >= 2.0 DO
Fraction := Fraction * 0.5; // Деление на 2
Exponent := Exponent + 1;
END_WHILE;

WHILE Fraction < 1.0 DO
Fraction := Fraction * 2.0;
Exponent := Exponent - 1;
END_WHILE;

// ================================================== ==========================
// ШАГ 4: Вычисление смещённой экспоненты (Bias = 127)
// ================================================== ==========================
BiasedExp := Exponent + 127;

IF BiasedExp >= 255 THEN
// Переполнение -> Infinity
BiasedExp := 255;
Mantissa := 0;
StatusCode := 2;
TempDword := shl(SignBit, 31) + shl(BiasedExp, 23) + Mantissa;
WordHigh := shr(TempDword, 16) MOD 65536; // Старшие 16 бит
WordLow := TempDword MOD 65536; // Младшие 16 бит
RETURN;
ELSIF BiasedExp <= 0 THEN
// Исчезновение -> Zero
BiasedExp := 0;
Mantissa := 0;
StatusCode := 1;
TempDword := shl(SignBit, 31) + shl(BiasedExp, 23) + Mantissa;
WordHigh := shr(TempDword, 16) MOD 65536; // Старшие 16 бит
WordLow := TempDword MOD 65536; // Младшие 16 бит
RETURN;
END_IF;

// ================================================== ==========================
// ШАГ 5: Извлечение мантиссы (23 бита)
// ================================================== ==========================
Fraction := Fraction - 1.0; // Убираем ведущую 1
ScaledMant := Fraction * 8388608.0; // 2^23 = 8388608
Mantissa := real_to_udint(ScaledMant + 0.5); // Округление

// Ограничиваем мантиссу 23 битами
Mantissa := Mantissa MOD 8388607;

// ================================================== ==========================
// ШАГ 6: Сборка 32‑битного представления
// ================================================== ==========================
TempDword := 0;

// Добавляем бит знака (бит 31)
IF SignBit = 1 THEN
TempDword := TempDword + 2147483648;
END_IF;

// Добавляем экспоненту (биты 30–23)
TempDword := TempDword + shl(BiasedExp, 23);

// Добавляем мантиссу (биты 22–0)
TempDword := TempDword + Mantissa;

// ================================================== ==========================
// ШАГ 7: Разделение на два 16‑битных блока
// ================================================== ==========================
buff_val := shr(TempDword, 16);
WordHigh := shr(TempDword, 16) MOD 65536; // Старшие 16 бит
WordLow := TempDword MOD 65536; // Младшие 16 бит

StatusCode := 0; // Normal

end_function_block