Просмотр полной версии : Целочисленные в формате u32 и u64
Здравствуйте!
Подключаю к ПР205 устройство по шине Modbus. Целочисленные переменные типа u16 передаются без проблем. А вот с некоторыми регистрами которые имеют формат u32 или u64 возникает проблема.
Как можно считать данные регистры и потом математически их привести к нормальной форме. Есть ли у кого нибудь мысли. Мои знания в данной области, к сожалению ограничены.
к u64 никак. ПР ограничен u32. Разделить на два регистра, потом собрать.
Кажется я сам нашел ответ на свой вопрос. Может кому нибудь пригодится. Поправьте меня если я ошибаюсь.
при считывании числа например U64 который занимает 4 регистра нужно считать их в отдельные переменные и потом:
Чтобы получить итоговое число:
читаем регистр 0x2200
младшая часть — значение оставляем «как есть» : 0хb4dfhex = 46303dec
далее регистр 0x2201
значение умножаем на 2^16 : 0x0dffhex = 3583dec, 3583 * 65536 = 234815488
затем регистр 0x2202
значение умножаем на 2^32 : 0 * 4294967296 = 0
и в конце 0x2203
значение умножаем на 2^48 : 0 * 281474976710656 = 0
Суммируем результаты : 46303 + 234815488 + 0 + 0 = 234861791. Возможно потребуется преобразовать в число с плавающей точкой.
Может есть более изящное решение?
kondor3000
16.10.2024, 12:57
Небось опять со счётчиком, где 64 битные регистры. Это только на СПК1хх и ПЛК 2хх можно сделать
Это нормальное решение из РЭ, только это не для ПР, а для ПЛК
и 0 там только в начале, потом дикие цифры пойдут.
Два 16-битных в одни 32-битный еще можно собрать.
А дальше?! 64-бита придется хранить в 2-х разных переменных.
Потом придется делить на 10 несколько раз, чтобы отделить по цифре и вывести.
Я делаю с помощью макроса rDIV https://owen.ru/forum/showthread.php?t=26216&p=431974&viewfull=1#post431974
Отделяю каждую цифру https://owen.ru/forum/showthread.php?t=26216&p=432009&viewfull=1#post432009
float не переварит преобразование из u64, он и u32 не переварит в принципе. Там где u32 продолжит считать с 1,2 знаками после запятой, float уже загнется.
А толку от ухищрений с u64? только показать, математика будет недоступна с этим числом.
..Подключаю к ПР205 устройство по шине Modbus. ... типа u16 передаются без проблем. А вот с некоторыми ... имеют формат u32 или u64..
.
WB-MAP какой-нить?
Да, Измеритель параметров электрической сети WB-MAP3E.
Но там конечно можно использовать только регистры u32. Еще есть регистры s32 (у которых есть знак). С ними как быть?
Что Вы хотите с ними сделать?
float не переварит преобразование из u64, он и u32 не переварит в принципе. Там где u32 продолжит считать с 1,2 знаками после запятой, float уже загнется.
А толку от ухищрений с u64? только показать, математика будет недоступна с этим числом.
Я, наверное, опять не так понял?!
Взял максимальный u32 и преобразовал его во float и обратно - вроде все биты на месте?!
79464
Да, Измеритель параметров электрической сети WB-MAP3E.
Но там конечно можно использовать только регистры u32. Еще есть регистры s32 (у которых есть знак). С ними как быть?
Есть раздел алгоритмов - длинная арифметика. Сложение/вычитание просто поразрядно, умножение можно тоже поразрядно (как умножение на бумаге в столбик), а есть умножение Карацубы - сверхбыстрое, но при реализации потребуется рекурсия, деление - вот это совсем сложный вариант.
В данном случае арифметика будет хоть и "длинной" но весьма ограниченной 64 разрядами.
Эта длинная тирада к тому, что если эти данные нужно просто отображать, то можно использовать самостоятельно реализованные или взятые с форума алгоритмы.
несмотря на то, что в документации по Modbus регистрам может быть указан множитель (к примеру, 1e-5) это означает всего лишь, что нужно при выводе целого числа отделить последние 5 символов (цифр) символом точка, т.е. реального деления не будет - такой формат чисел называется десятичные дроби с фиксированной точкой.
Чуть выше приведённой ранее ссылки с делением есть вывод принятого 64 разрядного числа с множителем 1e-5.
https://owen.ru/forum/showthread.php?t=26216&p=431901&viewfull=1#post431901
Собственно, скажите, что предполагает заказчик и алгоритм от этих чисел?
kondor3000
16.10.2024, 15:23
Я, наверное, опять не так понял?!
Взял максимальный u32 и преобразовал его во float и обратно - вроде все биты на месте?!
79464
float только 5-6 знаков поддерживает, остальное откидывает, при чём не только после запятой, но и перед, если знаков больше 5,
а 32 битное целочисленное считает все 10 знаков.
FPavel, вроде уже решили, что на ПР это не сделать, вы опять за своё)) Он тупо 4 регистра разом прочитать не может, не говоря про расчёты.
Я, наверное, опять не так понял?!
Взял максимальный u32 и преобразовал его во float и обратно - вроде все биты на месте?!
79464
Случайность. На конце входного поставьте 0 заместо 5
Точнее - особенность чисто ОЛ-преобразования reaL_to_dword. Все что больше FFFF_FFFF - приводит к FFFF_FFFF
float только 5-6 знаков поддерживает, остальное откидывает, при чём не только после запятой, но и перед, если знаков больше 5,
а 32 битное целочисленное считает все 10 знаков.
FPavel, вроде уже решили, что на ПР это не сделать, вы опять за своё)) Он тупо 4 регистра разом прочитать не может, не говоря про расчёты.
Понятно, что в мантиссу float 23 бита не впихнуть 32 бита. Однако костыли есть и для 64 бита - это 2 по 32. Работает же?!
Если очень захотеть можно приделать костыли для работы с double float.
FPavel, вроде уже решили, что на ПР это не сделать, вы опять за своё)) Он тупо 4 регистра разом прочитать не может, не говоря про расчёты.
Изрядно забыл, чем тогда дело закончилось... А у самого не на чем проверить...
Если прибор не отдаёт 4 регистра по-отдельности, а требует чтения в один запрос, то тогда ничего не поделать.
Но если
- регистры можно прочитать,
- значение в этом 64 разрядном регистре меняется медленнее, чем несколько запросов,
- от этих данных требуется просто вывод на экран
То можно же прочитать и вывести. Для достоверности можно отбрасывать те результаты, которые отличаются более, чем на некоторое значение (возникшее при переполнении младшего регистра без своевременного прочтения старшего).
Да, Измеритель параметров электрической сети WB-MAP3E.
Но там конечно можно использовать только регистры u32. Еще есть регистры s32 (у которых есть знак). С ними как быть?
Про 64 на ПР проще забыть. Или купить ошейник и плетку - тот же эффект, но быстрее
А со 32 signed - а ПР позволяет 2 регистра сразу прочитать? А по частям - слепить их именно как udint, посмотреть на признак знака и с учетом этого преобразовать в real. Всё просто.
Фигня может быть из-за не одномоментности считывания 2 регистров.
от этих данных требуется просто вывод на экран
тогда не страшно. А что выводить-то надо?
Или купите ПЛК. ПР - не для такого
- от этих данных требуется просто вывод на экран
Вот это тут основное. И стоит ли оно затрат ПР для вывода.
Кстати на анализаторах сети часто есть double по Modbus. Учитывая, что это просто набор регистров хранения, а что туда запихнули не важно. Важно позволяет ли конечное устройство не только отображение, но еще и обработку.
А никто ведь не мешает прочитать как float два соседних регистра. У ПР то это один запрос будет. Дальше на биты и в число. Или там что-то может потеряться? как-то за ненадобностью таким не баловался.
На ПК это легко все делает, а вот на ПР?
kondor3000
16.10.2024, 16:01
Про 64 на ПР проще забыть.
Фигня может быть из-за не одномоментности считывания 2 регистров.
Купите ПЛК. ПР - не для такого
2 регистра читать можно и как 32 битное знаковое целочисленное пересчитать во Foat тут выложено
Функции FLOAT_TO_DINT и DINT_TO_FLOAT на ST____ https://owen.ru/forum/showthread.php?t=22915&page=311&p=429821#post429821
А вот можно ли прочитать 4 регистра, по 2 шт. за 2 раза, никто так и не написал, потому что в РЭ написано читать 4 регистра разом.
Изрядно забыл, чем тогда дело закончилось... А у самого не на чем проверить...
Если прибор не отдаёт 4 регистра по-отдельности, а требует чтения в один запрос, то тогда ничего не поделать.
Но если
- регистры можно прочитать,
- значение в этом 64 разрядном регистре меняется медленнее, чем несколько запросов,
- от этих данных требуется просто вывод на экран
То можно же прочитать и вывести. Для достоверности можно отбрасывать те результаты, которые отличаются более, чем на некоторое значение (возникшее при переполнении младшего регистра без своевременного прочтения старшего).
Я согласен с Вами! Накопительный счетчик сохраняю в 2-х u32 и сравниваю со следующими. Если младшие u32 изменились, а старшие u32 - нет, то значению можно верить.
2 регистра читать можно и как 32 битное знаковое целочисленное пересчитать во Foat тут выложено.
Нет никаких проблем пересчитать 2 регистра (из внешнего dint) в real. А они одновременно пришли?
kondor3000
16.10.2024, 16:19
Нет никаких проблем пересчитать 2 регистра (из внешнего dint) в real. А они одновременно пришли?
Одновременно, в ПР есть выбор читать 2 регистра, целое или Float. 4 регистра читать нельзя.
А вот можно ли прочитать 4 регистра, по 2 шт. за 2 раза, никто так и не написал, потому что в РЭ написано читать 4 регистра разом.
В РЭ возможно имелось ввиду для достоверности данных, так как меняются. Если там Modbus такой, что не позволяет прочитать эти 4 регистра за два запроса, то это уже претензии к реализации Modbus конкретного прибора.
А вот можно ли прочитать 4 регистра, по 2 шт. за 2 раза, никто так и не написал, потому что в РЭ написано читать 4 регистра разом.
Прочитать - можно. Только обрабатывать - творчески.
Считать со счетчика, отобразить на экране контроллера. Возможно сделать какие то расчеты, пока до конца не решил.
Возможно сделать какие то расчеты вот про это лучше забудьте. Там такая каша получится, вычислять все пополам, потом творчески отображать :)
Друзья, из всего того, что вы тут обсуждали я понял практически ничего.
Но хотелось бы закончить вопрос практического применения полученных знаний. Касательно этого прибора я для себя понял следущюее.
1. С переменными в формате u64 я прощаюсь. Получать, выводить и анализировать данные по накопленной энергии не получится ну и ладно.
2. Большинство переменных находятся в формате u16 или u32 big endian для считывания которых в настройках контроллера Мстера установил две галки "старшим байтом вперед" (она стояла по умолчанию) "старшим регистром вперед" ее я поставил для корректного считывания big endian. Все значения читаются корректно.
Но вот с некоторыми переменными возникли сложности. в частности с теми которые в формате s32 little endian. Они, при таких настройках читаются не корректно.
Можете ли вы подсказать, как их правильно считать, преобразовать и использовать. Может макрос уже какой есть.
Заранее спасибо.
Небольшое уточнение. Я задал вопрос производителям счетчиков, они сначала предложили читать регистры отдельно по одному, но потом сами же предложили этого не делать, опасаясь за то, что в период считывания регистры могут поменяться и значения будут не корректные.
Потом они предложили сделать следующее:
" Я бы предложила немного иначе. Чтобы не возникало вышеописанной проблемы при считывании двух регистров разными запросами.
Я бы считала число из двух регистров, а потом математически поменяла их местами."
Но моих знаний в этой области не достаточно для совершения подобных манипуляций.
kondor3000
17.10.2024, 21:45
Небольшое уточнение. Я задал вопрос производителям счетчиков, они сначала предложили читать регистры отдельно по одному, но потом сами же предложили этого не делать, опасаясь за то, что в период считывания регистры могут поменяться и значения будут не корректные.
Потом они предложили сделать следующее:
" Я бы предложила немного иначе. Чтобы не возникало вышеописанной проблемы при считывании двух регистров разными запросами.
Я бы считала число из двух регистров, а потом математически поменяла их местами."
Но моих знаний в этой области не достаточно для совершения подобных манипуляций.
32 битное целочисленное занимает 2 регистра, ПР позволяет их читать вместе за один раз.
Если я правильно перевёл, u32 big endian - обратный порядок байт
s32 little endian - прямой порядок байт, для этого как раз и нужны галки старшим байтом и старшим регистром вперёд, перестановка регистров и байт.
Так же регистры и байты можно переставить в программе. Но для начала надо проверить правильность предположений.
Если имеются ввиду 32 битные знаковые целочисленные, то ответ тут
Функции FLOAT_TO_DINT и DINT_TO_FLOAT на ST____ https://owen.ru/forum/showthread.php?t=22915&page=311&p=429821#post429821
Пробуйте читать так и так и смотрите результат. Скрины выложите тут.
ПР не понимает напрямую отрицательные числа s32. Если оно будет приходить с минусом, то вы по умолчанию будете получать непонятное значение.
Вроде были макросы, которые позволяли работать со знаковыми числами, но при переводе через float возможно будут возникать ошибки.
Я возможно делаю, что-то неправильно:
Sign := s32.31; // Sign = false - число положительное, Sign = true - число отрицательное
if Sign then
u32 := 0 - s32; // Модуль числа s32, т.е. без знака
else
u32 := s32;
end_if
Можно ли считать, что если ValH(старшие 32 бита) и ValL(младшие 32 бита) считанные 2 раза подряд и давшие один и тот же результат,
дали результат верный shl(ValH, 32) + ValL?
EFrol дальше в математику ПР это все пихните. когда отрицательное число будет -2 100 999 например
Речь же не всегда об отображении.
Понятно, что будут сложности, но они ведь решаемы!!
Прочитать - можно. Только обрабатывать - творчески.
Давайте добавим макросы для работы c s32, u64, s64 и даже с double float.
Можем же ведь?!
EFrol дело не в "можем ведь" а в том, что это начнет отжирать ресурсы ПР и увеличивать время цикла. Зачем насильничать над ПР? :)
EFrol дело не в "можем ведь" а в том, что это начнет отжирать ресурсы ПР и увеличивать время цикла. Зачем насильничать над ПР? :)
Ну это пусть каждый решает сам для себя.
Я, например, перехожу на Arduino. Мне как-то всё равно.
kondor3000
18.10.2024, 13:37
Понятно, что будут сложности, но они ведь решаемы!!
Давайте добавим макросы для работы c s32, u64, s64 и даже с double float.
Можем же ведь?!
Для S32 уже всё обсудили и выложили, только в этой теме уже 2 раза ссылку давал.
Про 64 бита сделали только сложение, до умножения так и не дошло. Ссылку давал FPavel в начале.
Опять я, наверно, не те макросы использую?!
function_block u64_MUL
var_input
x1H, x1L : udint;
x2H, x2L : udint;
end_var
var_output
qH, qL : udint;
end_var
var
cnt : udint;
end_var
qH := 0; qL := 0;
for cnt := 0 to 63 do
if x2L.0 then
qL := qL + x1L;
qH := qH + x1H;
if (qL < x1L) then qH := qH + 1; end_if
end_if
x2L := shr(x2L, 1); x2L.31 := x2H.0; x2H := shr(x2H, 1);
x1H := shl(x1H, 1); x1H.0 := x1L.31; x1L := shl(x1L, 1);
end_for
end_function_block
function_block u64_DIV
var_input
x1h, x1l : udint; // Делимое
x2h, x2l : udint; // Делитель
end_var
var_output
QH, QL : udint; // Частное
MH, ML : udint; // Остаток
end_var
var
cnt : udint;
end_var
if x2h = 0 and x2l = 0 then return; end_if
cnt := 1; QH := 0; QL := 0;
while x2h < 2147483648 do
x2h := shl(x2h, 1);
if x2l > 2147483647 then x2h := x2h + 1; end_if
x2l := shl(x2l, 1); cnt := cnt + 1;
end_while
while cnt > 0 do
QH := shl(QH, 1);
if QL > 2147483647 then QH := QH + 1; end_if
QL := shl(QL, 1);
if x1h > x2h then
x1h := x1h - x2h; QL := QL + 1;
if x1l >= x2L then
x1l := x1l - x2l;
else
x1l := x2l - x1l; x1l := 0 - x1l;
x1h := x1h - 1;
end_if
elsif x1h = x2h then
if x1l >= x2l then
x1h := 0; x1l := x1l - x2l; QL := QL + 1;
end_if
end_if
x2l := shr(x2l, 1);
if x2h > shl(shr(x2h, 1), 1) then x2l := x2l + 2147483648; end_if
x2h := shr(x2h, 1);
cnt := cnt - 1;
end_while
MH := x1h; ML := x1l;
end_function_block
kondor3000
18.10.2024, 18:47
Это всё конечно хорошо, но почему никто не смотрит, какие числа надо перемножать и складывать ?
число 2 в 32 и в 48 спепени перемножить с числом в 3 и 4 регистре, а потом всё сложить.
То есть умножить надо 2 раза 16 битное на 64 битное, а потом всё сложить. Сложение нужно 1 число 32 битное + 2 числа 64 битных.
2 регистр можно посчитать просто умножением в Лоджике 65535*65536=4294901760 .
Еще раз, что надо посчитать, вот скрин 74299
1 регистр 0x2200
младшая часть — значение оставляем «как есть» (может быть до 65535)
2 регистр 0x2201
значение умножаем на 2^16 (может быть до 4294901760)
3 регистр 0x2202
значение умножаем на 2^32 (сейчас 0, а будет число до 5 знаков, например 65535 * 4294967296=281470681743360 )
4 регистр 0x2203
значение умножаем на 2^48 (сейчас 0, а будет число до 5 знаков, например 65535 * 281474976710656=18446462598732840960 )
65535+4294901760+281470681743360+18446462598732840 960=18446744073709551615 - максимально возможное 20-значное число
умноженное на 0.00001=184467440737095.51615
Деление делать не надо, просто откинуть 5 последних знаков.
kondor3000, Вы несколько запутались в формате принимаемого числа.
В том РЭ приведён пример, как из 4-х принятых регистров составить 64-разрядное число.
На самом деле при приёме ничего умножать не нужно - для подобного, обычно, пользуются или сдвигами или приведением типа результата к массиву слов (или рассыпухе из 4 слов).
Если бы дело происходило в CODESYS 3.5, можно было бы поступить следующими способами (забыл только обозначение там 64-разрядного типа, пусть будет u64)
type TConvert:
union
My64: u64;
x0, x1, x2, x3: uint;
a: array [0..3] of uint;
end_union
end_type
v: TConvert; // промежуточная переменная
r0, r1, r2, r3: uint; //регистры из Modbus
v64: u64; // это и есть итоговая переменная
// заполним её регистрами при помощи вспомогательной переменной
v.x0 := r0; v.x1 := r1; v.x2 := r2; v.x3 := r3;
v64 := v.My64;
// можно аналогично через массив
x.a[0] := r0; // ....... и так далее
v64 := v.My64;
// можно через сдвиги
v64 := r0 + shl(r1, 16) + shl(r2, 32) + shl(r3, 48);
// можно через умножение
v64 := r0 + r1 * 65536 + r2 * ........ // не помню дальше константы, но смысл понятен
А EFrol приводит макросы арифметических операций над уже полученными 64-разрядными числами, он "уже их принял" и ведёт дальнейшую обработку - считает деньги по тарифу ;)
Да, открывал, видел, но при наборе примера не стал забивать голову константами - именно эти константы вместе с умножением - исключительно бумажный пример без практического применения, т.к. сдвиг всегда быстрее умножения, а прямое размещение данных сразу в нужном месте (привет, union!) - ещё быстрее.
Powered by vBulletin® Version 4.2.3 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot