Вход

Просмотр полной версии : Преобразовать ответ



IIeroniux
05.10.2015, 10:52
День добрый! Прошу помочь мне в решении задачи.

Суть - подключить датчик перемещения Рифтек к ОВЕН ПЛК-160 по RS-485. У датчика нестандартный протокол.
Взял готовый пример на форуме (открытие порта, передача и получение данных). Но не могу интерпретировать ответ.

Запрос результата - два байта, 01h 86h. Именно в таком виде отравляю команду на датчик. С него приходит ответ длиною 4 байта. Ответ собирается в массив (на картинке оранжевая рамка).
Вручную я могу его расшифровать, а вот написать код в проекте... не хватает соображения.

Пример ручной расшифровки ответа:

В массиве (левая оранжевая рамка на вложении) числа 193 198 201 192 (как я понимаю в десятичной системе исчисления). Сначала их нужно перевести в 16ию систему. Каждую переводим по отдельности
193(DEC)=C1h; 198(DEC)=C6h; 201(DEC)=C9h; 192(DEC)=C0h. Теперь нужно изменить их последовательность (согласно РЭ на датчик).
Получим 0961h. Переводим в десятичную систему 0961h=2410(DEC). Это и есть искомый результат.
Эту последовательность операций необходимо автоматизировать.

P.S. Цифры в оранжевой рамке меняются хаотично. Но в любой момент времени (если не перемещать датчик) рассчитанный таким образом ответ будет одинаков. Для цифр в правой рамке, после перевода, получяется D1h D6h D9h D0h. Естественно ответ от датчика будет такой же - 0961. Почему если датчик неподвижен по линии связи приходят разные байты я не знаю.

Yegor
05.10.2015, 11:25
SHL(BYTE_TO_WORD(otvet[3]) AND 15, 12) +
SHL(BYTE_TO_WORD(otvet[2]) AND 15, 8) +
SHL(BYTE_TO_WORD(otvet[1]) AND 15, 4) +
SHL(BYTE_TO_WORD(otvet[0]) AND 15, 0);
Почему если датчик неподвижен по линии связи приходят разные байты я не знаю.А там написано вроде. Читайте внимательно.

P.S. Можно чуть короче написать:
(otvet[3] AND 15) * 16#1000 +
(otvet[2] AND 15) * 16#100 +
(otvet[1] AND 15) * 16#10 +
(otvet[0] AND 15) * 16#1;Можно без поразрядных операций:
otvet[3] * 16#1000 +
otvet[2] * 16#100 +
otvet[1] * 16#10 +
otvet[0] -
16#11110 * (otvet[0] / 16#10);

20260

IIeroniux
05.10.2015, 14:14
Огромное спасибо, попробовал оба последних примера и всё получилось!!! Если напишите в личку свой телефон отправлю вам небольшую благодарность.

Если у вас будет время не могли бы вы объяснить код (для чего то или иное действие выполняется, дальше буду сам разбираться) - сам никогда не работал с битовыми/байтовыми операциями, а тупо переписать код без понимания я бы не хотел.



SHL(BYTE_TO_WORD(otvet[3]) AND 15, 12) +
SHL(BYTE_TO_WORD(otvet[2]) AND 15, 8) +
SHL(BYTE_TO_WORD(otvet[1]) AND 15, 4) +
SHL(BYTE_TO_WORD(otvet[0]) AND 15, 0);А там написано вроде. Читайте внимательно.
Тут вообще ничего не понятно, прочитал в букваре что shl - это сдвиг.


P.S. Можно чуть короче написать:
(otvet[3] AND 15) * 16#1000 +
(otvet[2] AND 15) * 16#100 +
(otvet[1] AND 15) * 16#10 +
(otvet[0] AND 15) * 16#1;
Тут выполняется побитовое И. Зачем? Потом умножается на 16#100 - эти операции выполняются в десятичной системе?


Можно без поразрядных операций:
otvet[3] * 16#1000 +
otvet[2] * 16#100 +
otvet[1] * 16#10 +
otvet[0] -
16#11110 * (otvet[0] / 16#10);
Вот здесь мне интересно откуда взялось 16#11110 и почему otvet[0] необходимо делить на 16#10.

Yegor
05.10.2015, 17:45
Представьте себе байт в двоичном виде: 11001001. В шестнадцатеричной системе это C9, причём C это та часть, которая 1100, а 9 это 1001. С десятичной системой так красиво не получается, если что.

Так вот перед вами стоит задача из 4 таких байтов получить по 4 разряда с правой стороны, а затем собрать эти 16 бит в двухбайтовое слово, т.е. из C9 C9 C9 C9 сделать 9999.

Начнём с конца — собрать. Как в десятичной системе из цифр 1, 5, 0, 8 сделать число 1508? Очевидно: 1×1000+5×100+0×10+8×1. Прикол в том, что абсолютно так же это делается в любой системе счисления, хоть в шестнадцатеричной, хоть в двоичной, хоть в скольки-угодно-ичной. То есть имея A16, 316, 616, C16 для получения A36C нам нужно сделать то же самое: A×100016+3×10016+6×1016+С×116. Это видно во втором и третьем способах. Шутка в тему: существует 10 типов людей — те, кто не знает про двоичную систему счисления; те, кто про неё знает и те, кто не ожидал, что это шутка про троичную систему.

В первом способе вместо умножения используется сдвиг влево. Опять же как в десятичной системе если вам надо из 56 получить 5600, то вы сдвигаете на два разряда влево. А если сдвинуть на два разряда влево в двоичной системе, то получится умножение не на 100, а на 4 (на 2, а потом ещё на 2). Помня, как красиво шестнадцатеричная ложится на двоичную, мы видим, что сдвиг на четыре разряда в двоичной даёт нам сдвиг на один разряд в шестнадцатеричной, а это и есть умножение на 1016. SHL это двоичный сдвиг влево, то есть умножение на 2 в N-й степени.

Теперь о том, как убрать левые четыре разряда, которые мешают нам просто всё помножить и сложить. Если вы переведёте десятичное 15 в двоичной вид, то получится 1111, ну или 00001111, т.к. в байте 8 бит. Если теперь взять поразрядное «И» между 11001001 и 00001111, то выйдет 00001001. От C9 остаётся 09 — что нам и нужно. Дальше мы спокойно умножаем и складываем — получается второй способ. В первом кроме сдвига вместо умножения по сравнению со вторым способом ещё приходится делать байты шире через BYTE_TO_WORD — чтобы было куда сдвигать. При умножении такой проблемы не возникает, т.к. компилятор берёт размерность большего из операндов, но не меньше INT, если вы явно не указали иного.

Наконец в третьем способе мы не обнуляем лишние разряды заранее, а просто вычитаем то, что из них получается после суммирования и умножения. Деление C9 на 1016 даёт нам просто "C" — можете считать эту операцию обратным сдвигом (собственно, есть SHR — сдвиг вправо, можно было использовать его). Дальше мы пользуемся тем фактом, что во всех четырёх байтах лишняя тетрада одинаковая, а значит достаточно умножить любую из лишних тетрад на 1111016, чтобы получить излишек. Скажем C×1111016=CCCC0. Попробуем: С9 + С90 + С900 + С9000 = D6659. D6659 - CCCC0 = 9999. Ура.

Вроде ответил на все вопросы кроме одного, который не совсем правильно поставлен:
Потом умножается на 16#100 - эти операции выполняются в десятичной системе?К операциям неприменимо понятие системы счисления. Причём не только в кодесисе или в программировании, а вообще в принципе. Система счисления это всего лишь способ записи самих чисел-операндов. Есть ещё всем известная римская нотация, и арифметика там даёт те же результаты. Никто не мешает сложить, например, IV + 9. Вместо otvet[3] * 16#1000 я бы мог написать otvet[2#11] * 4096, и результат был бы тот же.