|
Новичок
Регистрация: 09.03.2026
Сообщений: 10
Репутация: 10
![]() |
Тут надысь в припадке работоспособности изваял одну штуку, и ношусь с ней, с целью куда-нибудь пристроить или хотя бы получить обратную связь.
В общем, если заинтересует – продолжу. zeroParsing Modbus – на 48-ми байтах, без парсинга и ошибок. В общем, это алгоритм приёма пакетов Modbus на паттерновом (или же абстрактном) принципе. Сначала сразу код с необходимыми, как мне кажется, комментариями. Код:
#define PRESSURE_KEY 0x5AA5000A1C2D0405 //Ключ - полная копия ожидаемого запроса мастера.
//Эта функция вызывается там, где байт материализуется из линии.
//У меня это в isr
int mbNewByte(uint8_t byte)
{
static volatile uint64_t shiftBuf; //Это самая главная переменная.
shiftBuf = (ShiftBuf >> 8) | byte;
if(shiftBuf == PRESSURE_KEY)
{
//Всё, разборка пакета успешно заверщена, можно отвечать.
//Разумеется, актуальный ответ должен быть готов всегда в виде, пригодном для отправки.
return 1; //Пакет принят!
}
return 0; //Процесс приёма не заверщён.
}
Примечательные особенности: • полное отсутствие ошибок, и это самое главное • темпоральная инвариантность – алгоритм безразличен к временным промежуткам между байтами в линии. Они могут отсутствовать либо исчисляться веками. • 48 – байт – это минимальный контроллер, на котором алгоритм сможет работать. Но таких нет, так что начинаем с 64. • Это не какой-то там кастомный модбас. Мастер не отличит такого слейва от настоящего. Это просто другой принцип. Ограничения. Ну куда же без них. Этот вариант алгоритма для датчика - устройства, которое не выдаёт воздействия на объект и не конфигурируется. И первое, и второе можно победить, попутно потеряв красоту первоначального варианта, но практически без потери в эффективности. А, ну ещё в этой версии не сообщает об ошибках. Тоже устранимо. Будет интерес – продолжим. |
||
|
Эксперт
Регистрация: 17.10.2010
Адрес: Томск
Сообщений: 3,602
Репутация: 960
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
__________________
Никогда не спорьте с идиотами.Вы опуститесь до их уровня, где они задавят вас своим опытом. МАРК ТВЕН. ... |
||
|
Знаток
Регистрация: 13.07.2007
Сообщений: 712
Репутация: 148
![]() ![]() |
Уважаемый, Алопеций Шнифт,
Вы прошли первый квест в увлекательной игре Жизнь. По ходу дальнейших квестов и для их прохода вы вынуждены будете совершенствовать Навыки, приобретать опыт и покупать инструменты. По ходу этой игры, вы поймёте, чтобы проходить квесты надо создавать СВОИ ФУНКЦИОНАЛЬНО законченные устройства. Помощи особо не ждите, сопрут код и идею, как не прятатали бы. А следующий этап создать изделия, при виде которых только у единиц шевельнулась мысль повторить. |
||
|
Новичок
Регистрация: 09.03.2026
Сообщений: 10
Репутация: 10
![]() |
Цитата:
Только в данном случае речь идёт об общем принципе, алгоритме, который может быть применён широким классом устройств для самых разных целей. А в 'функционально законченных' устройствах этот алгоритм вполне себе бегает в поле с чужими мастерами. Жду этого с нетерпением. Не поможете? В смысле, не сопрёте ли? |
||
|
Новичок
Регистрация: 09.03.2026
Сообщений: 10
Репутация: 10
![]() |
Малодушно посчитаю это проявлением интереса (хоть и не понял суть поправки), и на этом основании разрешу себе продолжить.
Итак, в прошлый раз я предоставил полный код Модбас для датчика с серьёзным нарушением стандарта. А именно - отсутствуют ответные сообщения об ошибках запроса. Нужно ли? Разработчику - нет. А вот наладчику... В общем-то, и ему ненадолго, до первого ответа. Но если ответа нет, а в ответ глухое молчание, работа становится очень нервной. Поэтому Прикручиваем к ZPM ошибки. Точнее, сообщения о них. При этом пока никак не отступаемся от главного принципа: никакого парсинга. Тут в скобках замечу, что вообще Модбас - не самый удобный для этого алгоритма протокол. ODSP или 101-й подходят ничуть не хуже, а значительно лучше. Но Модбас и значительно попроще, и пораспространённей. Это я к тому, что сравнение с ключом, по размеру равным пакету, отнюдь не единственный вариант паттерн-матчинга (сорри за матерщину) для этого алгоритма, чем мы сейчас с удовольствием и воспользуемся. Код:
union x64_Union
{
uint64_t u64; // всё 64-битное значение
uint16_t words[4]; // 4 слова по 16 бит
uint8_t bytes[8]; // 8 байт
};
#define PRESSURE_KEY 0x5AA5000A1C2D0405 //Ключ - полная копия ожидаемого запроса мастера.
int mbNewByte(uint8_t byte)
{
static volatile uint64_t shiftBuf;
uint64_t diff;
shiftBuf = (ShiftBuf >> 8) | byte;
diff = shiftBuf ^ PRESSURE_KEY; //теперь в diff битовая разница между ключом и пакетом
if(!diff) //Если отличий вообще нет - наш пакет
{
//Всё, разборка пакета успешно заверщена, можно отвечать.
//Разумеется, актуальный ответ должен быть готов всегда в виде, пригодном для отправки.
return 1; //Пакет принят!
}
if(bytes[7])
{
//но адрес-то наш! поэтому
if(((x64_Union *)&diff)->words[1])
{
return -1; //обнаружена ошибка, требующая ответа
}
if(((x64_Union *)&diff)->words[2])
{
return -2;//обнаружена другая ошибке
}
return 0; //продолжаем приём
}
return 0; //Продолжаем приём.
}
После этих проделок мы получили тот же датчик, но уже полностью соответствующий стандарту. Никто не придерётся. Данные отдаёт, об ошибках сигнализирует. Не умеет принимать пока данные от мастера? Тоже исправимо, позже продолжу. Ранее обещал, что продолжу только в случае проявления интереса, но попутно получил столько чрезвычайно полезных советов, что уже чувствую себя обязанным. Так что продолжу в любом случае. А, там я ещё не вполне корректно обошёлся с crc (просто наплевав на него), но пониманию алгоритма это не мешает. А полный вариант уж точно по запросу.
Последний раз редактировалось Алопеций Шнифт; 11.03.2026 в 16:49.
|
||
|
Новичок
Регистрация: 09.03.2026
Сообщений: 10
Репутация: 10
![]() |
Можно сдвигать в любую сторону. Другое дело, что ключ должен быть развёрнут в ту же сторону.
Я же не конкретное решение (библиотеку или набор макросов) предлагаю, а алгоритм с кучей вариантов. Которые между тем уже устарели. Причешу - опубликую актуальную версию. Нет, всё ранее выложенное вполне себе работает. Просто уже есть (будет) получше. |
||
|
Новичок
Регистрация: 09.03.2026
Сообщений: 10
Репутация: 10
![]() |
А чего ждать-то?
Ниже последняя версия второго варианта алгоритма. Это консольное приложение VS - тот случай, когда эмуляция ничем не хуже, чем отладка на целевой платформе - алгоритм чисто математический и работает везде. Код:
//ВНИМАНИЕ!!! Мат в данных. Строго 18+
#include <stdint.h>
#include <stdio.h>
union ModbusFrame
{
uint64_t slider;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8[8];
};
uint16_t updcrc(uint16_t crc, uint8_t data) {
crc ^= data;
for (uint8_t i = 0; i < 8; i++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xA001;
}
else {
crc >>= 1;
}
}
return crc;
}
uint16_t crc16(void *buf,int sz)
{
uint16_t crc = 0xFFFF; // начальное значение
for (int i = 0; i < sz; i++)
{
crc = updcrc(crc, ((uint8_t*)buf)[i]);
//printf("%04x\n", crc);
}
return crc;
}
uint32_t Key = 0x5AA50607; // ключ сократился до 32 бит.
uint16_t hdr_crc; // должно быть предрассчитано там же, где и ключ
static volatile union ModbusFrame frame;
void printState(union ModbusFrame* mf)
{
printf("\n 0 1 2 3 4 5 6 7");
printf("\nslider:|");
for (int i = 0; i < 8; i++) printf("%02x |", frame.u8[i]);
}
int mbNewByte(uint8_t byte)
{
union ModbusFrame diff;
frame.slider >>= 8;
frame.u8[7] = byte;
diff.u32[0] = frame.u32[0] ^ Key; //тепреь в diff битывая разница со slider
printState((ModbusFrame *)&frame);
if (!diff.u8[0]) //ноль - наш адрес
{
uint16_t crc;
crc = updcrc(hdr_crc, frame.u8[4]); //досчитываем crc для 2-х оставшихся байт
crc = updcrc(crc, frame.u8[5]);
//crc = crc16((void *)frame.u8, 6); //ну или считаем сразу.
if (crc == frame.u16[3]) //сравниваем.
{
//Пакет корректен и адресован нам, обязаны что-то отвечать. Выясняем, что именно
if (diff.u8[1])
{
printf(" error -1\n");
return -1;//Мастер запросил не ту функцию
}
if (diff.u16[1])
{
printf(" error -2\n");
return -2;//Мастер запросил не тот регистр
}
printf(" *** OK! ***\n");//рабочий запрос принят!
return 1;
}
}
return 0; // приём в процессе
}
static uint8_t emu[] = {
'Х', 'У', 'Й',//Есть мнение, что без подобного паттерна отечественный софт
//работать не будет. Мнение ошибочное, но на всяк. случАй.
0xff, //короткий шум в линии
0x07, 0x06, 0xA5, 0x5A, 0x12, 0x34, 0x86, 0x04, //рабочий пакет
0x07, 0x06, 0xA6, 0x5A, 0x12, 0x34, 0x86, 0x04, //ошибка в регистре
0x55, 0xAA, 0xff, //шум в линии
0x07, 0x06, 0xA5, 0x5A, 0x12, 0x34, 0x86, 0x04, //рабочий пакет
0x07, 0x05, 0xA6, 0x5A, 0x12, 0x34, 0x86, 0x04 //ошибка в функции
};
uint8_t Emulator()
{
static int cnt;
uint8_t ret = emu[cnt++];
cnt = cnt % sizeof(emu);
return ret;
}
void main()
{
uint8_t byte;
hdr_crc = crc16(&Key, 4);
for (;;)
{
byte = Emulator();
mbNewByte(byte);
}
}
Сразу сообщаю: помещение ключа в поток собьёт алгоритм. Другое дело, как он там окажется в реальной-то линии?
Последний раз редактировалось Алопеций Шнифт; 17.03.2026 в 12:05.
|
||
| Обратная связь РадиоЛоцман Вверх |