Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: AVR Modbus RTU
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Интерфейсы
vazz
В общем с AVRами дружу недавно, столкнулся с острой необходимостью использования протокола Modbus RTU (т.к. оборудование уже используют на многих объектах и нужно общение приборов с новыми прошивками работающими по тому же принципу). В общем, надеюсь, найдутся добрые люди, которые смогут мне простому смертному помочь в освоении этого протокола на базе ATMega16. Собственно готовых кодов не нужно, мне б полностью принцип понять применительно к моей задаче. Я кое чего нашел от прежнего программиста, вот все что есть:

-------------------------------
Протокол передачи данных ModBus RTU.
Функция для передачи данных: 04 - «read input registers».
Запрашивать 14 регистров начиная с нулевого. Каждый регистр – двухбайтное слово. Обращаю внимание, что в протоколе ModBus все слова передаются старшим байтом вперед. Были случаи что попадалась документация с разными таблицами CRC и разными полиномами. Ниже приведу свои таблицы.
Порядок передаваемых данных:

1. Значение A
2. Значение B
3. Значение C
4. Значение D
5. Значение E
6. Значение F
7. Значение G
8. Значение H
9. Значение I
10.Значение J
11.Значение K
12.Значение L
13.Значение M
14.Значение N

Запись параметров - 16 (10 Hex) Preset Multiple Registers
Порядок передаваемых данных:

1. Значение I
2. Значение J
3. Значение K
4. Значение L
5. Значение M
6. Значение N


const unsigned char CRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40};

const unsigned char CRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40};

Надеюсь на помощь!
MrYuran
А вопрос-то, собственно, в чём?
freemodbus.berlios.de - готовый модуль.
Встраивается буквально в 3 шага.
Если хотите разобраться с имеющейся у вас реализацией, задавайте конкретные вопросы.
vazz
Вопросов много.. За ссылку конечно спасибо, но видимо пока я там ничего понять не смогу. Про модбас знаю только что слово такое есть, узнать бы принцип протокола именно RTU (для моей задачи, глобально протокол осваивать нет необходимости пока). Также как высчитывать CRC16? (как я понял его нужно высчитывать используя две таблицы, которые приведены выше), т.е. полный алгоритм подсчета что с чем складывать куда что сдвигать и при каких условиях. Какие временные интервалы выдерживать при посылках/ожиданиях ответа. Пока только умею атмегой посылать/принимать байты с заданной скоростью без какого либо протокола..) Вобщем, у меня есть формат посылки, которую нужно отправить в прибор, в ответ на которую прибор должен прислать все необходимые данные.. Видимо аццкая жара которая неделю влияет на мозги (белок то сворачивается) и мне б "на пальцах для тупых" основные принципы протокола (по каким правилам что передавать и когда чего принимать) и принцип подсчета црц16 исходя из приведенных таблиц
MrYuran
Если тупо на пальцах именно про RTU, то основная фишка там - это таймаут 3.5 символа по приёму пакета.
То есть, если между двумя любыми байтами разрыв больше, то это считается концом пакета.
В freemodbus это реализовано с помощью таймера.
По приёму каждого символа заводится таймер на 3.5 символа вперёд, а символ кладётся в буфер.
Если сработал таймер, значит имеем тайм-аут и соответственно конец пакета.
Вызывается обработчик, который анализирует целостность пакета (CRC, заголовок, код команды, длина пакета) и выставляет соответствующие флаги, которые затем обрабатываются функцией eMBpoll(), которая, в свою очередь, вызывает CallBack-функции для обработки команд.
Вычисление CRC производится в функции usMBCRC16 (./RTU/mbcrc.c)
zltigo
QUOTE (vazz @ Aug 2 2010, 08:12) *
В общем с AVRами дружу недавно...

Admin:
Тема перенесена, как не имеющая отношения к AVR. Напомю, так-же, что на форуме есть раздел "Для начинающих" и поиск.
war4one
Почитайте это и вот это.

Контрольная сумма считается так:
Код
static __flash unsigned char aucCRCHi[] = {
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
    0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
    0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
    0x40
};

static __flash unsigned char aucCRCLo[] = {
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,
    0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD,
    0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7,
    0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE,
    0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2,
    0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB,
    0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91,
    0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88,
    0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,
    0x40
};

unsigned int
usMBCRC16(unsigned char * pucFrame, unsigned int usLen )
{
    unsigned char   ucCRCHi = 0xFF;
    unsigned char   ucCRCLo = 0xFF;
    int             iIndex;

    while( usLen-- )
    {
        iIndex = ucCRCLo ^ *( pucFrame++ );
        ucCRCLo = ucCRCHi ^ (aucCRCHi[iIndex] );
        ucCRCHi = ( aucCRCLo[iIndex] );
    }
    return ucCRCHi << 8 | ucCRCLo;
}

vazz
Цитата(war4one @ Aug 2 2010, 09:09) *
Почитайте это и вот это.

Контрольная сумма считается так:


Благодарю за пример, сегодня уже находил что-то подобное, я пишу на ассемблере пока только, с Си имею дело только когда чего-нить в visual-среде под винду пишу (ну это боловство пока только). В начале я как понимаю мы объявляем переменные а дальше крах мозга...)))) я заранее пршу прощения, так стыдно.....))) ну и собсно исходя из того, что я пишу на машинном уровне мне б понять как соотносится принятый байт со значениями в этих таблицах и как в итоге получается этот несчастный црц.. я не зря "на пальцах для тупых" попросил, но я не безнадежен....)) в последний раз я так мучался когда не знал с чего начать контроллеры осваивать))) ну вот освоил 51, AVR, хочу еще ARM, но тут без Си никак (последнее - енто лирическое отступление, мона пропустить). Вобщем, в идеале, я принимаю все байты посылки, считаю црц, если он правильный то проверяю 1й байт, если он совпадает с адресом прибора, то по функции выдаю ответ с данными, если во время приема возникла тишина в 3,5 байта (исходя из скорости) то полученные данные считаю полностью принятой посылкой (ну и дальше сначала как описал), правильно глаголю? осталос только с этими таблицами разобраться на машинном уровне..
MrYuran
Код
unsigned int
usMBCRC16(unsigned char * pucFrame, unsigned int usLen )  
// pucFrame - указатель на начало буфера (адрес)
// usLen - длина пакета
{
    unsigned char   ucCRCHi = 0xFF;  // старший байт црц
    unsigned char   ucCRCLo = 0xFF;  // младший
    int             iIndex;

    while( usLen-- )  // цикл, usLen уменьшается на 1 при каждой итерации
    {
        iIndex = ucCRCLo ^ *( pucFrame++ );  // ксорим ucCRCLo  и байт, на который указывает pucFrame.
        // полученное значение будет индексом iIndex в таблицах. pucFrame инкрементируется.
        ucCRCLo = ucCRCHi ^ (aucCRCHi[iIndex] );  // ксорим ucCRCHi и значение из таблицы aucCRCHi с индексом iIndex.
        ucCRCHi = ( aucCRCLo[iIndex] );  // ucCRCHi равно значению из таблицы aucCRCLo с индексом iIndex
    }
    return ucCRCHi << 8 | ucCRCLo;  // Возвращаем старший и младший байт CRC в виде 16-разрядного слова
}
vazz
Цитата(MrYuran @ Aug 2 2010, 10:57) *
Возвращаем старший и младший байт CRC в виде 16-разрядного слова


Вот спасибо хорошо положите на камод.. Епт, все оказывается не так сложно. Спасибо добрый человек за разжовывание..)) на бумажке посчитал по этому методу (через таблички) и все получается, ну пока для посылки всего из одного байта посчитал)) Только после подсчета получается какбы итоговое значение младшего байта CRC ставится старшим байтом, а старшее итоговое ставится младшим, круто блин
MrYuran
Цитата(vazz @ Aug 2 2010, 12:50) *
Вот спасибо хорошо положите на камод.. Епт, все оказывается не так сложно. Спасибо добрый человек за разжовывание..))

Да незачем.
Учите Си, пригодится в хозяйстве!
vazz
попробовал я с двухбайтной посылкой на бумажке посчитать.. может кому пригодится, с такими же проблемами как у меня с мосКом))) допустим я передаю два байта 01 04. В программке работы с com-портом CRC16 получилось равным 01 E3 т.е. в бинарном коде 00000001 11100011.

Изначальные условия у меня такие: ucCRCHi = 11111111, ucCRCLo = 11111111, iIndex = 0, pucFrame = 0, usLen = 2, таблицы вышеуказанные

выполняем действия пока usLen не обнулится:

---обрабатываем 1й байт (0x01)

iIndex = 11111111 xor 00000001 = 11111110 (т.е. дес. 254)
ucCRCLo = ucCRCHi xor aucCRCHi{iIndex=254} = 11111111 xor 0x81 (10000001) = 01111110
ucCRCHi = aucCRCLo{iIndex=254} = 10000000 (т.е. 0x80)

---обрабатываем 2й байт (0x04)

iIndex = 01111110 xor 00000100 = 01111010 (т.е. дес. 122)
ucCRCLo = ucCRCHi xor aucCRCHi{iIndex=122} = 10000000 xor 0x81 (10000001) = 00000001
ucCRCHi = aucCRCLo{iIndex=122} = 11100011 (т.е. 0xE3)

итого получается ucCRCLo + ucCRCHi = 01 E3, те самые что выдала программка в начале

всем большое СПАСИБО
rezident
Почему никто из отвечающих не дал ссылки на текст самой спецификации? Как я понял, вопрошающий ее не читал. Ссылка на сайт modbus.org.
MODBUS Protocol Specification
MODBUS Over Serial Line FOR LEGACY APPLICATIONS ONLY
Modbus Serial Line Protocol and Implementation Guide V1.02
MODBUS TCP/IP
Object Messaging Specification for the Modbus TCP Protocol
vazz
на железе сегодня полностью отладил прием 8ми байт (1й - адрес, 2й - функция 3й4й - начальный адрес регистров, 5й6й - кол-во регистров, 7й8й - CRC16), CRC16 считает, при скорости 57600 выжидаю 70мс (3.5/57.6 = 60.8мс, но я решил для верности немножко с запасом) после приема очередного байта, если время прошло считаю как новый фрэйм, также сделал что если приходит больше 8ми байт в одном фрэйме, то каждый 9тый воспринимается как начало нового 8ми байтого фрэйма, короче радости полные штаны))
MrYuran
Цитата(vazz @ Aug 5 2010, 12:40) *
также сделал что если приходит больше 8ми байт в одном фрэйме, то каждый 9тый воспринимается как начало нового 8ми байтого фрэйма, короче радости полные штаны))

Это неправильно, в RTU должен быть разрыв 3,5 символа.
Если его нет, то это будет ошибка формата фрейма.
vazz
Цитата(MrYuran @ Aug 5 2010, 11:51) *
Это неправильно, в RTU должен быть разрыв 3,5 символа.
Если его нет, то это будет ошибка формата фрейма.


сделал, теперь молчит если больше 8ми байт. У меня вопрос созрел по самому модбасу. Вобщем как я и говорил у меня ни исходников предыдущей версии прошивки нету ни связи с тем программистом который ее создавал. Так вот, значит посылаю я прибору со старой прошивкой строку:

01 04 00 00 00 01 31 CA

а он мне в ответ

01 04 08 00 00 00 00 00 00 FF FF 25 BD

Возникает у меня как минимум 2 вопроса

1. Что за 08 и откуда оно берется? Эт явно что-то с модбасом связано
2. Почему так много данных выдает (пускай они даже 00 00), судя по всему он выдает четыре 2хбайтных параметра 00 00 00 00 00 00 FF FF, хотя я в исходящей посылке даю прибору инструкцию на передачу всего одного слова данных начиная с 00 00 адреса. Я предполагал, что ответ будет примерно таким:

01 04 xx xx CRC16

Поможите люди опытныя..)
rezident
Цитата(vazz @ Aug 5 2010, 14:40) *
при скорости 57600 выжидаю 70мс (3.5/57.6 = 60.8мс, но я решил для верности немножко с запасом) после приема очередного байта, если время прошло считаю как новый фрэйм, также сделал что если приходит больше 8ми байт в одном фрэйме, то каждый 9тый воспринимается как начало нового 8ми байтого фрэйма, короче радости полные штаны))

Вы упорно не желаете читать документацию? По ссылкам которые я привел выше нужно прочитать по крайней мере
MODBUS Protocol Specification и Modbus Serial Line Protocol and Implementation Guide V1.02. Если бы вы прочитали вторую, то могли заметить ремарку про паузы. Да и про формат ответа вопросов не возникало.
vazz
Цитата(rezident @ Aug 5 2010, 14:09) *
Вы упорно не желаете читать документацию? По ссылкам которые я привел выше нужно прочитать по крайней мере
MODBUS Protocol Specification и Modbus Serial Line Protocol and Implementation Guide V1.02. Если бы вы прочитали вторую, то могли заметить ремарку про паузы. Да и про формат ответа вопросов не возникало.


Справедливо с точки зрения человека который постоянно этим протоколом пользуется. Для меня же задача эта одноразовая и в самом простом ее "формате". Я не ленив, но врядли мне потребуется изучать этот протокол глубже чем "Компьютер - мастер, прибор - slave, 1 запрос - 1 ответ раз в 2-5 секунд". Из рациональных побуждений в плане затрат своего времени, которое дороже денег, я решил спросить у умудренных опытом людей совета в своей простейшей задаче. Мне не трудно изучить всю документацию по этому вопросу, но смысла в досканальном изучении совсем нет и не придвидится. Сегодня почитаю указанные вами документы. Большое спасибо всем за помощь, извините если кого напряг.
demiurg_spb
Цитата(vazz @ Aug 5 2010, 14:14) *
Так вот, значит посылаю я прибору

01 04 00 00 00 01 31 CA

а он мне в ответ

01 04 08 00 00 00 00 00 00 FF FF 25 BD

Возникает у меня как минимум 2 вопроса

1. Что за 08 и откуда оно берется? Эт явно что-то с модбасом связано

request: slave_address[1byte] func[1byte] address[2bytes] reg_qty[2bytes] crc[2bytes]
response: slave_address[1byte] func[1byte] bytes_qty[1byte] data[1-250bytes] crc[2bytes]
Цитата
2. Почему так много данных выдает (пускай они даже 00 00), судя по всему он выдает четыре 2хбайтных параметра 00 00 00 00 00 00 FF FF, хотя я в исходящей посылке даю прибору инструкцию на передачу всего одного слова данных начиная с 00 00 адреса. Я предполагал, что ответ будет примерно таким:
01 04 xx xx CRC16
Предположения тут неуместны.
Что за прибор-то? Вероятно бага в приборе...
vazz
Цитата(demiurg_spb @ Aug 13 2010, 14:49) *
request: slave_address[1byte] func[1byte] address[2bytes] reg_qty[2bytes] crc[2bytes]
response: slave_address[1byte] func[1byte] bytes_qty[1byte] data[1-250bytes] crc[2bytes]


bytes_qty - как я понимаю ответ прибора типа "данные приняты без проблем, вот вам ответ", другими словами в случае если ошибок при приеме посылки нет и адрес с функцией совпадают я тупо втыкаю в ответную посылку байт 08 после адреса и функции..

Цитата(demiurg_spb @ Aug 13 2010, 14:49) *
Предположения тут неуместны.
Что за прибор-то? Вероятно бага в приборе...


Вероятно, но дело в том, что прежде чем мне передавать в ответ какие-либо данные мне нужно знать их последовательность, в данном случае какая понимаю ответная посылка имеет фиксированную длину независимо от кол-ва запрошенных данных, но указываемое кол-во данных влияет на заполнение этих фиксированных "вакантных" 4 слов данных... Хотя эт тож догадки.. Связи с программистом этим нет, молчит как рыба, обидно
XVR
Цитата
bytes_qty - как я понимаю ответ прибора типа "данные приняты без проблем, вот вам ответ"
Неверно понимаете - это количество байтов в блоке данных ответа (поле data)
demiurg_spb
Цитата(vazz @ Aug 17 2010, 15:54) *
в данном случае какая понимаю ответная посылка имеет фиксированную длину независимо от кол-ва запрошенных данных, но указываемое кол-во данных влияет на заполнение этих фиксированных "вакантных" 4 слов данных...

Такого быть не должно. Сколько запросили регистров (N) столько байт (N*2) и получить должны.
Цитата
Хотя эт тож догадки..
Не нужно гадать. Ещё раз Вам настоятельно рекомендую читать стандарт.
vazz
я снова тут! вобщем делюсь своими успехами, изучил особенности модбаса касательно своей задачи основательно и вобщем сделал всем моим приборам поддержку этого протокола, всего одной функции (чтения), но больше и не надо было, зато качественно, выдают ответы как положено, в т.ч. выдают ошибки типа функция не поддерживается, ошибка поля данных и т.д. и т.п. (с модификацией старшего бита функции в случае ошибок, усё как положено). Вобщем сейчас задача встала передо мной, никак не могу сообразить как это решить. Создал я ужо почти некую систему измерения температуры с 16разрядным АЦП, ИОНом крутым, вобщем все оч качественно, с компенсацией цифровых шумов и т.д. и т.п. Так вот система эта состоит из 4 компонентов: 1. комп 2.радиомодуль с usb (к компу) 3. радиомодуль с rs485 (к измерительным блокам) 4.измерительные модули (до 256 на одной шине, подключаются к п.3), все это уже собрано и отлажено многочисленным испытаниями, как точности измерения тк и дальностью взаимодействия, осталось ток дописать под винду программулину, которая всем этим ворочает. Так вот каждый измерительный модуль имеет 72 измерительных входа для датчиков (ну и дополнительные входы для измерения напряжения на проводах компенсации, 3хпроводная схема). Каждый модуль измерительный способен хранить в своей EEPROM 1 полное измерение (с датой и временем). В этом полном измерении (т.е. измерено 72 датчика), для каждого там сохранено 4 байта информации для последующей обработки на компе. Структура записи измерения в EEPROM: [5 байт на дату и время] + [1 байт кол-ва датчиков в отчете] + [5 байт на датчик, 1 на номер датчика, 4 на результаты измерения]x72. В итоге получается массив размером 5+1+5х72 = 366 байт, плюс к этому прибавляется 4 байта служебно информации для передачи по интерфейсу. Итого: 370 байт. Моя процедура подсчета CRC16 корректно считает сумму для пакета длиной не более 256 байт. КАК ПОСЧИТАТЬ CRC16 ДЛЯ ПАКЕТА БОЛЬШЕ 256 БАЙТ? :-) извините если вопрос неуместен и глуп.. ПОМОЖИТЕ, а то кроме этого мне ничо не мешает эту систему довести до конца
Ruslan1
Цитата(vazz @ Apr 1 2011, 17:41) *
КАК ПОСЧИТАТЬ CRC16 ДЛЯ ПАКЕТА БОЛЬШЕ 256 БАЙТ? :-)

Вероятно Вы уперлись в разрядность счетчика, который байты считает, абсолютно никаких других ограничений и придумать не получается. Выделите 2-байтовую переменную под счетчик и вперед к новым победам....
Если еще что-то смущает, то код функции подсчета crc приведите, тогда проще будет советы давать sm.gif
rezident
Цитата(vazz @ Apr 1 2011, 20:41) *
В итоге получается массив размером 5+1+5х72 = 366 байт, плюс к этому прибавляется 4 байта служебно информации для передачи по интерфейсу. Итого: 370 байт. Моя процедура подсчета CRC16 корректно считает сумму для пакета длиной не более 256 байт. КАК ПОСЧИТАТЬ CRC16 ДЛЯ ПАКЕТА БОЛЬШЕ 256 БАЙТ? :-) извините если вопрос неуместен и глуп.. ПОМОЖИТЕ, а то кроме этого мне ничо не мешает эту систему довести до конца
Ну если проблема только в подсчете CRC16, то нужно видимо изменить тип переменной которая задает размер пакета. Вместо unsigned char использовать unsigned int. Если вы используете функцию пример которой приведен в приложении к спецификации MODBUS over Serial Line, то там используется типа unsigned short, который нужно заменить на unsigned int.
Но я хотел бы задать встречный вопрос: как вы посредством протокола ModBus собираетесь передавать данные больше, чем 252 байта? Стандартными командами ModBus это не получится сделать. Ведь длина пакета не может быть больше, чем 256 байт, а у вас запись 370 байт.
vazz
Цитата(Ruslan1 @ Apr 1 2011, 19:08) *
Вероятно Вы уперлись в разрядность счетчика, который байты считает


Добрый день , Ruslan1! Вобщем считаю я по таблицам, которые приведены в начале темы, а алгоритм следующий (он слегка кривенький в плане написания, но до 256 байт работает):

; +-------------------------------------------------------------------------+
; | подпрограмма проверки контрольной суммы CRC16 |
; | |
; |temp1,temp10 - кол-во байт для подсчета, temp8,temp6 - начальный адрес 1го байта|
; | выход: temp3 - младший байт CRC16, temp2 - старший байт CRC16 |
; +-------------------------------------------------------------------------+
checkCRC16:
ldi temp2,0xFF ;загружаем начальное значение CRCLo
ldi temp3,0xFF ;загружаем начальное значение CRCLh
ldi temp4,0x00 ;загружаем начальное значение CRCindex
add temp6,temp1
adc temp8,temp10
mov temp5,temp6
mov temp9,temp8
loopXORbyte:
mov temp6,temp5
mov temp8,temp9
sub temp6,temp1 ;в temp6,temp8 остается адрес текущего байта принятого фрэйма
sbc temp8,temp10
mov ZH,temp8
mov ZL,temp6
ld temp6,Z ;в temp6 остается значение байта
eor temp2,temp6
mov temp4,temp2 ;получаем новое значение CRCIndex (CRCLo при этом теряется)
ldi ZH,0x02 ;выбираем старший массив
mov ZL,temp4 ;выбираем элемент массива (по CRCIndex)
lpm temp6,Z ;в temp6 получаем значение элемента массива
eor temp3,temp6 ;получаем новое значение CRCLo (CRCHi при этом теряется)
mov temp2,temp3
ldi ZH,0x01 ;выбираем младший массив
mov ZL,temp4 ;выбираем элемент массива (по CRCIndex)
lpm temp3,Z ;в temp3 получаем значение элемента массива (что и есть новое CRCHi)
subi temp1,1
sbci temp10,0
cpi temp1,0
brne loopXORbyte
cpi temp10,0
brne loopXORbyte
ret

Не клюйте за ассемблер, пока до Си руки не дошли (но скоро дойдут, т.к. скоро приедтся с arm работать и 7дюймовым tft)

кстати это алгоритм считает и после 256 байт, я пробовал добавить просто 2ой байт для счетчика, но видимо шо-то не то или с таблицами такой метод не работает. Свыше 256 байт этот алгорит выдает некое значение CRC16, но оно не совпадает к примеру со значеним которое выдает программка для работы с портами при досчете для того же самого пакета

Цитата(rezident @ Apr 1 2011, 19:15) *
Но я хотел бы задать встречный вопрос: как вы посредством протокола ModBus собираетесь передавать данные больше, чем 252 байта? Стандартными командами ModBus это не получится сделать. Ведь длина пакета не может быть больше, чем 256 байт, а у вас запись 370 байт.


Извините что сразу не уточнил, я после своего поста ток понял что этот вопрос последует неминуемо) в этой системе было решено создать свой протокол "засекреченный" типа, он оч похож на модбас, но не совсем, этот протокол позволяет мне значительно сократить время общения главного с подчиненными (вдаваться в подробности думаю нет смысла, хотя если это интересно я могу и рассказать). Сейчас проблема только с подсчетом црц для пакета больше 256 длиной. Система имеет свой собственный радиоканал + локальный физ.интерфейс RS485, к которому больше никакое оборудование никогда не будет цепляться, ну есть еще кое какие маркетинговые причины почему свой протокол.
rezident
Цитата(vazz @ Apr 1 2011, 21:25) *
Извините что сразу не уточнил, я после своего поста ток понял что этот вопрос последует неминуемо) в этой системе было решено создать свой протокол "засекреченный" типа, он оч похож на модбас, но не совсем, этот протокол позволяет мне значительно сократить время общения главного с подчиненными (вдаваться в подробности думаю нет смысла, хотя если это интересно я могу и рассказать).
Ну меня не особенно интересует этот ваш "засекреченный" протокол biggrin.gif У нас тоже есть свой протокол (правда он нисколько не секретный), который был создан на базе протоколов ModBus и PiNet. Общая длина пакета ограничена 65536 (2^16) байт. Но с помощью него можно адресовать 2^32 байт. Делается это очень просто. Есть отдельная команда "чтение со смещением". При исполнении этой команды первые четыре байта поля Data интерпретируются как смещение от адреса, задаваемого полем Starting Address. Если Starting Address будет указывать на начало вашей записи EEPROM, то с помощью смещения вы сможете почитать всю вашу запись (370 байт) за два запроса. Но для этого нужно использовать код "нестандартной" (пользовательской) функции в ModBus. Соответственно поддержать в приборе ответ на запрос с таким кодом функции.
vazz
Цитата(rezident @ Apr 1 2011, 19:48) *
Ну меня не особенно интересует этот ваш "засекреченный" протокол


"засекреченный" эт не всмысле что он супер крутой какой-то и принципиально новый и "тока за отдельные деньги", сам по себе он не представляет никакой ценности)) он только для этой системы, например сейчас если сказать обычному пользователю что для мышки тож дрова нужны он на тя как на дурачка посмотрит, тож самое и тут, у всего есть свои заморочки с сопряжением, но конечному пользователю собсно это даж знать не нужно, тем более система полностью самостоятельна и не предполагает быть интегрированна в некую другую систему сбора данных. Я эту процедуру подсчета crc как тогда еще написал так больше и не трогал, а тут понадобилось больше 256 байт передавать.. и чо делать хз. Я даж не понимаю как наращивание счетчика еще одним байтом помогает, если значений в массивах таблиц всего по 256 в каждой. Т.е. при переполнении счетчика что с чем должно ксориться если массив таблицы закончился. Вот этот бы момент уточнить. Мож другие таблицы нужны и принцип несколько иной..

Вот например я прям ща запрашиваю полное измерение из модуля, получаю от него:

3E 01 01 01 0B 00 00 48 01 00 53 FF FF 02 00 63 FF FF 03 00 68 FF FF 04 00 78 FF FF 05 00 55 FF FF 06 00 73 FF FF 07 00 81 FF FF 08 00 88 FF FF 09 00 68 FF FF 0A 00 7C FF FF 0B 00 7A FF FF 0C 00 84 FF FF 0D 00 64 FF FF 0E 00 77 FF FF 0F 00 75 FF FF 10 00 87 FF FF 11 00 55 FF FF 12 00 67 FF FF 13 00 61 FF FF 14 00 6A FF FF 15 00 63 FF FF 16 00 66 FF FF 17 00 6D FF FF 18 00 7C FF FF 19 00 47 FF FF 1A 00 57 FF FF 1B 00 55 FF FF 1C 00 67 FF FF 1D 00 47 FF FF 1E 00 5E FF FF 1F 00 52 FF FF 20 00 68 FF FF 21 00 44 FF FF 22 00 4B FF FF 23 00 4B FF FF 24 00 57 FF FF 25 00 5B FF FF 26 00 58 FF FF 27 00 50 FF FF 28 00 4B FF FF 29 00 43 FF FF 2A 00 3F FF FF 2B 00 28 FF FF 2C 00 38 FF FF 2D 00 3A FF FF 2E 00 41 FF FF 2F 00 3A FF FF 30 00 42 FF FF 31 00 28 FF FF 32 00 36 FF FF 33 00 2D FF FF 34 00 3D FF FF 35 00 2B FF FF 36 00 38 FF FF 37 00 1A FF FF 38 00 1E FF FF 39 00 1D FF FF 3A 00 1D FF FF 3B 00 1D FF FF 3C 00 1F FF FF 3D 00 37 FF FF 3E 00 2C FF FF 3F 00 2C FF FF 40 00 25 FF FF 41 00 2D FF FF 42 00 23 FF FF 43 00 44 FF FF 44 00 41 FF FF 45 00 46 FF FF 46 00 39 FF FF 47 00 30 FF FF 48 00 29 FF FF DC FB

DC FB - эт по его мнению CRC16

Для этой же последовательности (кроме DC FB) программка для работы с портами выдает crc16 = 4A C3. Модуль не прав, т.к. даж если всю последовательность прогнать через программку (вместе с crc), то нулей не получается((



блин, я решил попробовать прогнать через программку эту последовательность с ее же мыданной crc16 с полной уверенносью, что она мне ща в конце нули напишет и о ужас:

3E 01 01 01 0B 00 00 48 01 00 53 FF FF 02 00 63 FF FF 03 00 68 FF FF 04 00 78 FF FF 05 00 55 FF FF 06 00 73 FF FF 07 00 81 FF FF 08 00 88 FF FF 09 00 68 FF FF 0A 00 7C FF FF 0B 00 7A FF FF 0C 00 84 FF FF 0D 00 64 FF FF 0E 00 77 FF FF 0F 00 75 FF FF 10 00 87 FF FF 11 00 55 FF FF 12 00 67 FF FF 13 00 61 FF FF 14 00 6A FF FF 15 00 63 FF FF 16 00 66 FF FF 17 00 6D FF FF 18 00 7C FF FF 19 00 47 FF FF 1A 00 57 FF FF 1B 00 55 FF FF 1C 00 67 FF FF 1D 00 47 FF FF 1E 00 5E FF FF 1F 00 52 FF FF 20 00 68 FF FF 21 00 44 FF FF 22 00 4B FF FF 23 00 4B FF FF 24 00 57 FF FF 25 00 5B FF FF 26 00 58 FF FF 27 00 50 FF FF 28 00 4B FF FF 29 00 43 FF FF 2A 00 3F FF FF 2B 00 28 FF FF 2C 00 38 FF FF 2D 00 3A FF FF 2E 00 41 FF FF 2F 00 3A FF FF 30 00 42 FF FF 31 00 28 FF FF 32 00 36 FF FF 33 00 2D FF FF 34 00 3D FF FF 35 00 2B FF FF 36 00 38 FF FF 37 00 1A FF FF 38 00 1E FF FF 39 00 1D FF FF 3A 00 1D FF FF 3B 00 1D FF FF 3C 00 1F FF FF 3D 00 37 FF FF 3E 00 2C FF FF 3F 00 2C FF FF 40 00 25 FF FF 41 00 2D FF FF 42 00 23 FF FF 43 00 44 FF FF 44 00 41 FF FF 45 00 46 FF FF 46 00 39 FF FF 47 00 30 FF FF 48 00 29 FF FF 4A C3 B7 0F

в конце B7 0F!(( эт получается что моя процедура мож и арбайтен?) блин посчитайте кто нить для этой последовательности crc16, а то чо-то я уже ничо не понимаю.. вот для этого же пакета что последнирислал тока без 4 последних байт (это суммы crc две подряд)

посчитал тут http://webnet77.com/cgi-bin/helpers/crc.pl вообще 3 результат) во хрень

в экспериментах пошел дальше взял эту же последовательность с црц который посчитан самим модулем и прогнал через прогрммку, в и тоге тоже получил не 00 00, а B7 0F, как и в случае с предыдущим примером. шо то в этом есть.. или совпадение?

3E 01 01 01 0B 00 00 48 01 00 53 FF FF 02 00 63 FF FF 03 00 68 FF FF 04 00 78 FF FF 05 00 55 FF FF 06 00 73 FF FF 07 00 81 FF FF 08 00 88 FF FF 09 00 68 FF FF 0A 00 7C FF FF 0B 00 7A FF FF 0C 00 84 FF FF 0D 00 64 FF FF 0E 00 77 FF FF 0F 00 75 FF FF 10 00 87 FF FF 11 00 55 FF FF 12 00 67 FF FF 13 00 61 FF FF 14 00 6A FF FF 15 00 63 FF FF 16 00 66 FF FF 17 00 6D FF FF 18 00 7C FF FF 19 00 47 FF FF 1A 00 57 FF FF 1B 00 55 FF FF 1C 00 67 FF FF 1D 00 47 FF FF 1E 00 5E FF FF 1F 00 52 FF FF 20 00 68 FF FF 21 00 44 FF FF 22 00 4B FF FF 23 00 4B FF FF 24 00 57 FF FF 25 00 5B FF FF 26 00 58 FF FF 27 00 50 FF FF 28 00 4B FF FF 29 00 43 FF FF 2A 00 3F FF FF 2B 00 28 FF FF 2C 00 38 FF FF 2D 00 3A FF FF 2E 00 41 FF FF 2F 00 3A FF FF 30 00 42 FF FF 31 00 28 FF FF 32 00 36 FF FF 33 00 2D FF FF 34 00 3D FF FF 35 00 2B FF FF 36 00 38 FF FF 37 00 1A FF FF 38 00 1E FF FF 39 00 1D FF FF 3A 00 1D FF FF 3B 00 1D FF FF 3C 00 1F FF FF 3D 00 37 FF FF 3E 00 2C FF FF 3F 00 2C FF FF 40 00 25 FF FF 41 00 2D FF FF 42 00 23 FF FF 43 00 44 FF FF 44 00 41 FF FF 45 00 46 FF FF 46 00 39 FF FF 47 00 30 FF FF 48 00 29 FF FF DC FB B7 0F
rezident
Как вы можете доверять непонятно какому калькулятору, про который неизвестно даже по какому полиному он считает CRC16? Не говоря уже о том формате данных, в котором требуется копипастить в его окно.
Вот этот он-лайн калькулятор правильно считает CRC16 для ModBus RTU (проверял на коротких пакетах). Но к сожалению, он не может обработать слишком длинную (нестандартную) строку данных.
vazz
к сожалению ничего не могу найти в сети по моему вопросу... может опыта не хватает.. поэтому могу ток с бубном прыгать пока, и это кое что дает, но я не понял пока о чем это говорит, попробую объяснить, к примеру есть массив данных:

Код
3E 01 01 01 0B 00 00 48 01 00 50 FF FF 02 00 65 FF FF 03 00 61 FF FF 04 00 77 FF FF 05 00 57 FF FF 06 00 6E FF FF 07 00 84 FF FF 08 00 87 FF FF 09 00 66 FF FF 0A 00 7A FF FF 0B 00 73 FF FF 0C 00 84 FF FF 0D 00 65 FF FF 0E 00 71 FF FF 0F 00 76 FF FF 10 00 84 FF FF 11 00 56 FF FF 12 00 68 FF FF 13 00 61 FF FF 14 00 6B FF FF 15 00 5D FF FF 16 00 6C FF FF 17 00 65 FF FF 18 00 78 FF FF 19 00 49 FF FF 1A 00 57 FF FF 1B 00 53 FF FF 1C 00 64 FF FF 1D 00 4F FF FF 1E 00 5C FF FF 1F 00 55 FF FF 20 00 65 FF FF 21 00 46 FF FF 22 00 45 FF FF 23 00 49 FF FF 24 00 56 FF FF 25 00 5A FF FF 26 00 50 FF FF 27 00 4D FF FF 28 00 47 FF FF 29 00 40 FF FF 2A 00 3C FF FF 2B 00 2B FF FF 2C 00 32 FF FF 2D 00 39 FF FF 2E 00 40 FF FF 2F 00 36 FF FF 30 00 40 FF FF 31 00 27 FF FF 32 00 34 FF FF 33 00 2A FF FF 34 00 3A FF FF 35 00 2A FF FF 36 00 32 FF FF 37 00 1D FF FF 38 00 1D FF FF 39 00 15 FF FF 3A 00 20 FF FF 3B 00 1B FF FF 3C 00 20 FF FF 3D 00 37 FF FF 3E 00 2D FF FF 3F 00 2D FF FF 40 00 20 FF FF 41 00 28 FF FF 42 00 1E FF FF 43 00 3C FF FF 44 00 41 FF FF 45 00 41 FF FF 46 00 34 FF FF 47 00 39 FF FF 48 00 2A FF FF


мой модуль по приведенному выше алгоритму (на асме) считает для него crc16 = F4 17. Программка для работы с портами (IODump называется) считает для этого же массива crc16 = 84 8D. Это разные результаты, НО... Если взять этот массив и в конце добавить к нему F4 17 и прогнать через программку IODump в надежде получить 00 00, то я получу результат crc16 = 63 5B. Также если взять этот массив и в конце добавить к нему 84 8D и прогнать через программку IODump в надежде получить 00 00, то я опять же получаю результат crc16 = 63 5B. И это не совпадение, можно и еще несколько разных пакетов аналогично проверить и значения будут одинаковыми. Я немного в замешательстве, мож все таки у кого нить есть правильный алгоритм вычисления, для эталона, чтоб понять как дальше жить то..)
rezident
Цитата(vazz @ Apr 2 2011, 15:11) *
мож все таки у кого нить есть правильный алгоритм вычисления, для эталона, чтоб понять как дальше жить то..)
Правильный алгоритм в спецификации ModBus привден. И даже функция на языке Си имеется.
vazz
Цитата(rezident @ Apr 2 2011, 15:11) *
Правильный алгоритм в спецификации ModBus привден. И даже функция на языке Си имеется.


эт оттуда и таблицы и функция (просто на асм переделанная)
vazz
Короче переделал я алгоритм расчета crc из табличного в стандартный с тем же полиномом A001

Код
;  +-------------------------------------------------------------------------+
;  |              подпрограмма проверки контрольной суммы CRC16              |
;  |                                                                         |
;  |temp1,temp10 - кол-во байт для подсчета, temp8,temp6 - начальный адрес 1го байта|
;  |      выход: temp3 - младший байт CRC16, temp2 - старший байт CRC16      |
;  +-------------------------------------------------------------------------+
checkCRC16:
    ldi        temp2,0xFF        ;загружаем начальное значение CRC (МБ)
    ldi        temp3,0xFF        ;загружаем начальное значение CRC (СБ)
    ldi        temp4,0x01        ;загружаем значение полинома
    ldi        temp5,0xA0
    add        temp6,temp1        ;прибавляем к нач.адресу кол-во байт
    adc        temp8,temp10
    mov        temp7,temp6        ;копируем результат в temp7,temp9
    mov        temp9,temp8
loopCalcCRC16:
    mov        temp6,temp7        ;копируем результат в temp6,temp8
    mov        temp8,temp9
    sub        temp6,temp1        ;в temp6,temp8 остается адрес текущего байта принятого фрэйма
    sbc        temp8,temp10
    mov        ZH,temp8
    mov        ZL,temp6
    ld        temp6,Z            ;в temp6 остается значение байта
    eor        temp2,temp6        ;ксорим значение байта со значением CRC (МБ)
    ldi        temp6,8
loopXORbyte:
    sbrc    temp2,0            ;проверяем младший бит CRC (МБ)
    jmp        MB_CRCL_1
    jmp        MB_CRCL_0
MB_CRCL_1:
    clc                        ;сдвигаем вправо весь CRC
    ror        temp3
    ror        temp2
    eor        temp2,temp4        ;ксорим значение CRC с полиномом
    eor        temp3,temp5
    jmp        skip_MB_CRCL_0
MB_CRCL_0:
    clc                        ;сдвигаем вправо весь CRC
    ror        temp3
    ror        temp2
skip_MB_CRCL_0:
    dec        temp6
    brne    loopXORbyte
    subi    temp1,1            ;уменьшаем счетчик байт
    sbci    temp10,0
    cpi        temp1,0
    brne    loopCalcCRC16
    cpi        temp10,0
    brne    loopCalcCRC16    ;и проверяем достиг ли он 0
    ret


Этот код полностью повторяет С# код пользователя Linore с форума http://programmersforum.ru/showthread.php?t=82812&page=2

Правильность посчитанной суммы проверял программкой СRC Find (Подбор полинома) неизвестного автора, там можно ввести hex-пакет длиной больше 256 и получившуюся контрольную сумму, затем эта программка просчитывает все полиномы от 0x0000 до 0xFFFF, так вот для пакета:

Код
3E0101010B00004801005AFFFF02005FFFFF030062FFFF04004DFFFF050062FFFF06007CFFFF0700
6BFFFF08008AFFFF090062FFFF0A0079FFFF0B007BFFFF0C007FFFFF0D0056FFFF0E006AFFFF0F00
7DFFFF10007EFFFF110045FFFF120065FFFF130073FFFF14005FFFFF15005AFFFF160068FFFF1700
76
FFFF180076FFFF190057FFFF1A006BFFFF1B0061FFFF1C0069FFFF1D0039FFFF1E0072FFFF1F0058
FFFF200044FFFF210039FFFF220033FFFF230060FFFF240032FFFF25005FFFFF260048FFFF27005C
FF
FF280057FFFF290047FFFF2A0040FFFF2B003BFFFF2C0049FFFF2D0034FFFF2E0052FFFF2F0035FF
FF300035FFFF310033FFFF320041FFFF33004EFFFF340033FFFF350025FFFF36002DFFFF370007FF
FF
38002FFFFF390026FFFF3A0024FFFF3B0006FFFF3C0032FFFF3D0034FFFF3E0029FFFF3F0032FFFF
40003DFFFF41004CFFFF42000BFFFF43005CFFFF440036FFFF450055FFFF46002CFFFF470058FFFF
480019FFFF


мой модуль выдал crc16 = 0x403F, программка CRC Find при вычислении полинома выдала что совпал полином 0xA001

Программка для работы с портами IODump, которой я пользуюсь похоже считает crc16 корректно только для пакетов длиной не больше 256 и считает она тоже с полиномом 0xA001, например для этого же пакета она считает crc16 = 0x1604 и при проверке через прогу CRC Find ни с одним полиномом не совпадает. Вот такие дела собственно.

Кстати предыдущий способ (табличный) тоже корректно работал, все правильно считает, все дело было в кривой программке для работы с портами(((
ukpyr
Цитата
freemodbus.berlios.de - готовый модуль.
имхо сделано весьма неоптимально, тем более для АВР. Зачем-то включили настройку посл.порта в инициализацию, в результате пришлось лепить порты под все архитектуры. CRC считают для всего фрейма, тогда как можно обновлять прямо в прерывании приема/передачи для каждого байта.

vazz попробуйте такую функцию:
Код
#define CRC16_MODBUS_INIT 0xffff
#define CRC16_MODBUS_POLY 0xa001
U16 crc16_modbus_upd(U8 data, U16 crc_prev) {
    U8 flag_xor;
    crc_prev ^= data;
    for (U8 cnt_bits = 8; cnt_bits; cnt_bits--) {
        flag_xor = crc_prev & 1;
        crc_prev >>= 1;
        if (flag_xor) crc_prev ^= CRC16_MODBUS_POLY;
    }
}
vazz
Цитата(ukpyr @ Apr 3 2011, 12:19) *
vazz попробуйте такую функцию:
Код
#define CRC16_MODBUS_INIT 0xffff
#define CRC16_MODBUS_POLY 0xa001
U16 crc16_modbus_upd(U8 data, U16 crc_prev) {
    U8 flag_xor;
    crc_prev ^= data;
    for (U8 cnt_bits = 8; cnt_bits; cnt_bits--) {
        flag_xor = crc_prev & 1;
        crc_prev >>= 1;
        if (flag_xor) crc_prev ^= CRC16_MODBUS_POLY;
    }
}


благодарю, но обе функции которые я испытал работают замечательно, изначально моя ошибка была в том что ориентировался на свою программку для работы с портами как на эталон подсчета crc, оказалось все работало у меня изначально, зря паниковал, зато разобрался во всех этих заморочках досконально, эт плюс. ТУТ ОКАЗЫВАЕТСЯ ЕСТЬ ТЭГИ "CODE", а я блин понапихал тут кодов без соответствующего оформления..)) sorry
truppik
Добрый день!
Думаю правильно будет спросить в этой теме, а не создавать новую по схожему вопросу что обсуждалось выше. Ситуация:
Для проверки работоспособности, есть простая плата на atmega32 , кварц внешний 16МГц. Кабель UART TTL-232R-3V3 (собран на FT232), взял исходники freeModbus , чуть подредактировал (пишу на Atmel Studio 6.1), скомпилировалось все прекрасно. Залил в МК, запустил утилиту modpoll.exe с параметрами:
modpoll.exe -m rtu -a 10 -r 1000 -c 5 -t 3 -b 38400 -d 8 -p even COM3
pause>nul

МК находит, читает с него регистры и все вроде бы хорошо. но, беру любую другую программу для работы с ModBus RTU и ничего не коннектиться. типа ошибка связи. Вот остановился сейчас на этом http://modbustool.codeplex.com ибо наименее безглюченная при запуске и работе. Но связи так и не добился.

Что это может быть ?
truppik
Немного разобрался с тем, почему при чтении выдавало ошибку #2.
Подсказку нашел в соседней ветке http://electronix.ru/forum/index.php?showt...86005&st=15
так вот,там как я понял было дело в прошивке - надо было увеличивать на 1 (к примеру 1001) адрес к примеру Input регистра, и тогда он читался по адресу 1000 извне.
у меня схожая ситуация, но противоположно надо делать получается...


кто нибудь сталкивался с таким ? получается мастер на ПК по версии Modbus'a новее, чем в МК ? Пример реализации для AVR брал отсюда http://robot-develop.org/archives/125
Lagman
Скорей всего это особенность вашего мастера, в программе запрос идет по 999 ячейке как и указано пользователем. Есть программа modbus poll в ней можно задавать "protocol style addresses. BASE 0" или "PLC style addresses. Base 1" http://www.modbustools.com/base.asp
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.