Вы находитесь на странице: 1из 10

Топики Блоги Люди Форум Магазин Конкурс Справочная Войти или Зарегистрироваться

Сообщество EasyElectronics.ru

Все Коллективные Персональные TOP

Хорошие Плохие Поиск

Modbus RTU для Чайников


Блог им. khomin

Яндекс.Директ

• Бесплатная среда разработки плат


• Видео экскурсия по нашей фабрике
• Всего $2 За 10 печатных плат!
• Всего $7 за трафарет для пасты!

Купить ТВ Курс обучения Часы ROLEX


Антенну Siemens Tia- DAYTONA
со скидкой Portal купить 1550
53%! грн.
techkin.info  tia-portal.plcpro.ru  rolex-daytona.shop 

Modbus — протокол, работающий по принципу «клиент-сервер».


Широко применяется в промышленности.
Modbus может использоваться для передачи данных через последовательные линии
связи RS-485, RS-422, RS-232, а также сети TCP/IP.
В этой статье рассмотрим на примере линии RS-485.

И так, в основе интерфейса RS-485 лежит принцип дифференциальной (балансной)


передачи данных. Суть его заключается в передаче одного сигнала по двум Прямой эфир
проводам. Причем по одному проводу (условно A) идет оригинальный сигнал, а по
Комментарии Публикации
другому (условно B) — его инверсная копия. Другими словами, если на одном
проводе «1», то на другом «0» и наоборот. Таким образом, между двумя проводами
витой пары всегда есть разность потенциалов: при «1» она положительна, при «0» — toxin65 → Корректируем время на

отрицательна.

счётчике СОЭ-55 МЗЭП 23 Блог им. toxin65

anakost → Delphi. Определение


разрешения видеофайлов форматов AVI и MKV
прямым парсингом без использования

кодеков. Часть II. 1 Алгоритмы и
программные решения

kalobyte-ya → USB для AVR. Часть 2. HID



Class на V-USB 363 Связь железа с
компьютером.

011119xx →Управление светодиодной



лентой на WS2812B с STM32F10x 254 STM32

Vga → Две схемы таймера для



вентилятора ванной комнаты. 16 Блог им.
Technicum505SU

Именно этой разностью потенциалов и передается сигнал. Такой способ передачи trengtor → Макетирование на Veroboard
обеспечивает высокую устойчивость к синфазной помехе. Синфазной называют →
stripboard и на простых stripboard 105
Технологии
помеху, действующую на оба провода линии одинаково. К примеру,
электромагнитная волна, проходя через участок линии связи, наводит в обоих Vga → DDS синтезатор AD9833 83 → Блог
проводах потенциал. Если сигнал передается потенциалом в одном проводе им. grand1987

относительно общего, как в RS-232, то наводка на этот провод может исказить сигнал Vga → Delphi. Определение разрешения
относительно хорошо поглощающего наводки общего («земли»). Кроме того, на видеофайла формата MP4 прямым парсингом
сопротивлении длинного общего провода будет падать разность потенциалов →
без использования кодеков. 20 Алгоритмы и
программные решения
земель — дополнительный источник искажений. А при дифференциальной передаче
искажения не происходит. В самом деле, если два провода пролегают близко друг к __bl__→ Передача данных в устройство
другу, да еще перевиты, то наводка на оба провода одинакова. Потенциал в обоих →
через COM-порт 5 LabView

одинаково нагруженных проводах изменяется одинаково, при этом информативная


разность потенциалов остается без изменений. Technicum505SU →
Школьный
осциллограф Н3017, паспорт. 9 Инструмент→
Воплощение UR5SIX →
Очередная "blue pill" на RISC-V
Есть несколько вариантов. →
контроллере 8 Блог им. perry_moshkin
— Подешевле на известной MAX-ADM485.
Без изоляции, развязки, изолированного источника питания. Зато стоит не более 25
hudoykl →
Руководство по
проектированию устройств с операционными
рублей. усилителями. Впервые на русском языке! 17 →
— Подороже, сюда можно отнести монстра ADM2587, ADM2483 и пр. Компэл - Журнал "Новости Электроники"

Разводить пп желательно очень вдумчиво. DVF → Фоторезист + ЛУТ = ... 18 →


Узел RS-485 хорошо вынести подальше от точных и измерительных цепей, узлов и Технологии
т.п.
Fahivec →
Двухполярный маломощный
На обычную сигнальную линию проложенную вдоль силовых установок и мощных регулируемый стабилизатор 27 Силовая →
потребителей, воздействует огромное количество наводок и помех. электроника
В некоторых случаях, их потенциал может достигнуть нескольких тысяч вольт!
skelet → Беспроводные наушники ТДС-6 8
→ Аудиотехника
GYUR22 → VREFINT_CAL и иже с ним 7 →
STM32

Contemplator →
Все о модуле
распознавания голоса EasyVR (ex VRbot) 81 →
Деталька

amaora →Контроллер BLDC финальная



версия (rev4c) 14 Блог им. amaora

Mihail →
Стек для W5200 без циклов
задержек + STM32F103 14 STM32 →
Vga →
Доработка напильником

мультиметра UT61E 139 Блог им. ACE

Весь эфир | RSS


Так выглядит типичная посылка, от Ведущего — Ведомому.

1-Wire Altera arduino ARM Assembler

Atmel AVR C++ compel DIY enc28j60


ethernet FPGA gcc I2C IAR KEIL
LaunchPad LCD led linux LPCXpresso
MSP430 nxp PCB PIC pinboard2 RTOS
STM32 STM8 STM8L TI UART
USB алгоритм ассемблер АЦП
библиотека блок питания вопрос
Так выглядит ответ Ведомого — Ведущему
деталька дисплей идея
инструмент конкурс конкурс2
ЛУТ микроконтроллеры
начинающим обзор
Отладочная плата паяльник
ID — Адрес ведомого устройства. Он может иметь значения от 1 до 247. Адрес 0 печатная плата плата ПЛИС поделки
используется для широковещательной передачи, его распознаёт каждое устройство, покупки программатор
адреса в диапазоне 248…255 — зарезервированы. программирование светодиод софт
Команда(код функции):
схема схемотехника Технологии
в данном примере одна, на чтение 0x03.
Но в действительности их намного больше.
умный дом фоторезист халява хрень
Все коды функций делятся на: Часы юмор

— Публичные коды, описанные в стандарте MODBUS-IDA. Их список включает уже


назначенные и используемые коды, а также коды для будущего использования;
— User-Defined Function Codes (65-72, 100-110) — коды, которые могут использоваться
Блоги
компаниями для собственных функций, и не описаны в спецификации;
— Reserved Function Codes (9, 10, 13, 14, 41, 42, 43, 90, 91, 125, 126 и 127) — Топ

зарезервированы коды, которые не доступны для общего использования.


(0x02) — чтение значений из нескольких дискретных входов (Read Discrete Inputs). AVR 38.98
(0x03) — чтение значений из нескольких регистров хранения (Read Holding Registers).
STM8 37.92
(0x04) — чтение значений из нескольких регистров ввода (Read Input Registers).
(0x05) — запись значения одного флага (Force Single Coil). Мусоровоз 29.53
(0x06) — запись значения в один регистр хранения (Preset Single Register).
STM32 28.46
(0x07) — Чтение сигналов состояния (Read Exception Status)
(0x0F) — запись значений в несколько регистров флагов (Force Multiple Coils) Связь железа с компьютером. 24.04

(0x10) — запись значений в несколько регистров хранения (Preset Multiple Registers) Деталька 23.24
(0x16) — запись в один регистр хранения с использованием маски «И» и маски «ИЛИ»
(Mask Write Register). Схемотехника 18.15

(0x18) — Чтение данных из очереди (Read FIFO Queue) Умный дом 17.75
(0x14) — Чтение из файла (Read File Record)
MSP430 17.13
(0x15) — Запись в файл (Write File Record)
(0x08) — Диагностика (Diagnostic) LPC1xxx 14.79
(0x0B) — Чтение счетчика событий (Get Com Event Counter)
Все блоги
(0x0C) — Чтение журнала событий (Get Com Event Log)
(0x11) — Чтение информации об устройстве (Report Slave ID)
(0x2B) — Encapsulated Interface Transport
Обработка ошибок
Ведущий отправляет запрос к Ведомому, в котором в поле «код функции» указывает
ему на необходимое действие.
Байты данных содержат информацию, необходимую для выполнения данной
функции.
Ведомый, в случае удачного выполнения этой функции, повторяет код функции в
ответе.
При возникновении ошибки, код функции в ответе модифицируется — старший бит
выставляется в 1.
В байтах данных передается причина ошибки. Например при исполнении Ведомым
функции 0x0F возникла ошибка, тогда он ответит Ведущему полем функции равным
0x8F.
В дополнении к изменению кода функции, Ведомый размещает в поле данных
уникальный код, который указывает на тип и причину ошибки.

CRC-16, циклически избыточный код.


Полином:

Для расчета есть два метода:


Простой
uint16_t GetCRC16(byte *buf, uint8_t len)
{
 uint16_t crc;
 crc = 0xFFFF;
 while(len--)
  {
   crc = crctable[((crc>>8)^*buf++)&0xFF] ^ (crc<<8);
  }
 crc ^= 0xFFFF;
 return crc;
}

и Табличный
const uint8_t auchCRCHi[256]=
{
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0
        0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
        0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0
        0x40
};

 const uint8_t auchCRCLo[256]=


{
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06
        0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A
        0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3
        0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29
        0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60
        0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68
        0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71
        0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B
        0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42
        0x40
};        
uint16_t CRC16(uint8_t *p, uint16_t len)
{
        uint8_t crc_hi;
        uint8_t crc_lo;
        uint8_t n;

        if (len>256U)
        {
                return (0);
        }

        n = (uint8_t)len;

        crc_hi = 0xFF;   // high byte of CRC initialized


        crc_lo = 0xFF;   // low byte of CRC initialized

        do
        {
           uint8_t i = crc_hi ^ *p++;        // will index into CR
           crc_hi = crc_lo ^ (uint8_t)(&auchCRCHi[i]);    // calcu
           crc_lo =          (uint8_t)(&auchCRCLo[i]);
        }
        while (--n);         // pass through message buffer (max 2
       
        return ((crc_hi << 8) | crc_lo);
}

Использование табличной функции


unsigned char mess[3] = {1,108,8};
volatile unsigned short res1 = CRC16(&mess,3);
res1 будет равен 0x0СС6 при подстановке в конце команды менять местами
старший и младший байты не надо. Эта функция при занесении значения в
res1 автоматически меняет местами старший и младший байты.

Как указано в даташите на ADM485, для работы на прием выводы RE-DE-DI должны
быть в 0,
тогда на выводе RO появляются принятые данные.
Для работы на передачу — все противоположно, но данные следует слать на DI.
Простая функция приема

void USART1_IRQHandler(void)
{       uint16_t temp;
        if((Modbus_Progress_Status == M_STAT_REC_ON)|| (Modbus_Pro
           {
        Modbus_Data[Modbus_Count_Byte] = USART1->DR;
        Modbus_Count_Byte ++;
            if(Modbus_Count_Byte  >= LENGTH_PACK)
                {
                if(Modbus_Data[0] == MY_ID)
                 {
                    temp = Modbus_Data[7];
                    temp = temp<<8;
                    temp |= Modbus_Data[6];
                                               
                     if(CRC_calc(Modbus_Data,9)==temp)
                        {                                          
                        Modbus_Progress_Status = M_STAT_TRAN_ON;
                        Modbus_Count_Byte =0;
                        USART1->CR1 &=~ USART_CR1_RE;
                        USART1->CR1 &=~ USART_CR1_RXNEIE;
                        Modbus_Send();
                        };
}}}}

Ответ выглядит примерно так

void Modbus_Send(void)
{
        uint16_t crc;
        uint8_t i;
        USART1->CR1 &=~ USART_CR1_RE;
        USART1->CR1 &=~ USART_CR1_RXNEIE;
       
        Modbus_DataTX[0]= ID;
        Modbus_DataTX[1]= COMMAND_READ;
        Modbus_DataTX[2]= LENGTH_REDE;
       
        Modbus_DataTX[3]= Param1L;
        Modbus_DataTX[4]= Param1H;
       
        Modbus_DataTX[5]= Param2L;
        Modbus_DataTX[6]= Param1H;
        ....
        Modbus_DataTX[21]= Param21L;
        Modbus_DataTX[22]= Param21H;
       
        crc = CRC(Modbus_DataTX, 23);
       
        Modbus_DataTX[23]= (crc & 0x00FF);
        Modbus_DataTX[24]= (crc >>8);
       
        Modbus_Transmit_Select();
       
        for(i=0; i<LENGTH_PACK_REDE; i++)
                {
                        USART_Transmit(Modbus_DataTX[i]);
                }
       
                USART1->CR1 &=~ USART_CR1_TE;
                Modbus_Receive_Select();
                // Тут очищаем флаги если есть
                Modbus_Data_TransLength = 0;
                Modbus_Progress_Status = M_STAT_CLEAR;
                Modbus_Progress_Status = 0;
                Modbus_Count_Byte = 0;
                Modbus_Count_TransByte =0;
               
                USART1->CR1 |=  USART_CR1_RE;
                USART1->CR1 |= USART_CR1_RXNEIE;
}

Все интервалы организованы на прерываниях.


Сообщение должно начинаться и заканчиваться интервалом тишины,
длительностью не менее 3,5 символов.
Во время передачи сообщения не должно быть пауз длительностью более 1,5
символов.
Для скоростей более 19200 бод допускается использовать интервалы 1,75 и 0,75 мс,
соответственно.

Для отладки удобно использовать что-то вроде Modbus_Poll.

К сожалению он не бесплатный, триальная версия работает 25 дней, ограничивает


работу 10 минутами и всячески достает сообщениями…

Файл логанализатора, с общением по Modbus Яндекс диск

Рекомендуется к прочтению:
Спецификация Modbus Link
RS-485 для чайников — Link
Modbus в Википедии Link
Modbus протокол Link

Отдельное спасибо товарищу Papandopala, за функцию табличного расчета CRC.

modbus, RS-485

+4 25 октября 2013, 03:34 khomin

Комментарии (27)
RSS свернуть / развернуть

Данный код на работоспобность не проверял, набросал за 20 минут ) 0


Есть проверенный проект, осторожно! Присутствует быдлокод и нет комментариев ))
yadi.sk/d/RdEcc94LBY9c7

khomin
25 октября 2013, 03:41

Все интервалы организованы на прерываниях. 0


Сообщение должно начинаться и заканчиваться интервалом тишины, длительностью
не менее 3,5 символов.
Во время передачи сообщения не должно быть пауз длительностью более 1,5
символов.
Для скоростей более 19200 бод допускается использовать интервалы 1,75 и 0,75 мс,
соответственно.
Адрес может изменяться от 1 до 247. Адрес 0 используется для широковещательной
передачи, его распознаёт каждое устройство, адреса в диапазоне 248…255 —
зарезервированы.

khomin
25 октября 2013, 08:14

Есть более приятная бесплатная софтина — Modbus Tester 0

GYUR22
25 октября 2013, 12:19 ↑
вообще то да, но с учетом старых трансиверов на полную нагрузку — 32 0
устройства на сегмент (а есть 1/2, 1/4) и 3 последовательных сегмента (но можно
древовидную стрктуру), но много нодов и длинные шланги это ад и сотона в поле,
поэтому надо подходить разумно.

GYUR22
25 октября 2013, 12:51 ↑

32 устройства на один сегмент сети. Потом ставится репитер/повторитель, и 0


следующие 32 устройства и так до 255 устройств.

Papandopala
25 октября 2013, 12:57 ↑

Если кто то захочет использовать из статьи табличный метод рассчета CRC то он не 0


сможет, так как таблица в статье не выложена. Я бы рекомендовал это сделать.

Papandopala
25 октября 2013, 12:38

если подскажете где ее взять, разъясните как пользоваться — буду очень 0


благодарен.
Табличный метод не использовал, хоть и говорят что он более шустрый

khomin
25 октября 2013, 13:33 ↑

Таблицу пихаем во флеш память. 0

//Таблица для рассчета CRC16 Moudbus RTU.        


 const uint8_t PROGMEM auchCRCHi[256]=
{
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
        0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81,
        0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
        0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01,
        0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
        0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81,
        0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1,
        0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01,
        0x40
};

 const uint8_t PROGMEM auchCRCLo[256]=


{
        0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6,
        0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
        0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA,
        0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17,
        0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33,
        0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,
        0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9,
        0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24,
        0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0,
        0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
        0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8,
        0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD,
        0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1,
        0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
        0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B,
        0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E,
        0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82,
        0x40
};        

Ну и рассчет CRC
uint16_t CRC16(uint8_t *p, uint16_t len)
{
        uint8_t crc_hi;
        uint8_t crc_lo;
        uint8_t n;

        if (len>256U)
        {
                return (0);
        }

        n = (uint8_t)len;

        crc_hi = 0xFF;                    // high byte of CRC


        crc_lo = 0xFF;                    // low byte of CRC

        do
        {
                uint8_t i = crc_hi ^ *p++;        // will ind
                crc_hi = crc_lo ^ (uint8_t)pgm_read_byte(&auc
                crc_lo =          (uint8_t)pgm_read_byte(&auc
        }
        while (--n);                                    // pa
       
        return ((crc_hi << 8) | crc_lo);
}
/*
Вызов функции в программе.
unsigned char mess[3] = {1,108,8};
volatile unsigned short res1 = CRC16(&mess[0],3);
res1 будет равен 0СС6 при подстановке в конце команды менять
старший и младший байты не надо. Эта функция при занесении зн
res1 автоматически меняет местами старший и младший байты.

Этим методом пользовался в одном проекте, все четко работает.

Papandopala
25 октября 2013, 13:49 ↑

добавил, спасибо 0

khomin
25 октября 2013, 14:43 ↑

Так а зачем таблицу в озу перенес, убрав PROGMEM? Она ж у тебя вся 0
будет висеть в оперативке, памяти столько не напасешься.

Papandopala
25 октября 2013, 15:02 ↑

У STM32 единое адресное пространство, PROGMEM не нужен ) 0


Const говорит компилятору, что хранить нужно во Flash.

khomin
25 октября 2013, 15:07 ↑

А точно, статья ж про STM. 0

Papandopala
25 октября 2013, 15:09 ↑

А ты не заметил, что теперь у тебя в обоих пунктах один и тот же код, 0


только «табличный» подпорчен оптимизацией для восьмибиток и
потому кажется отличающимся?
GetCRC16 — типичный расчет CRC16 табличным методом. Таблица в
массиве crctable, его можно задать статично в флеше или же
объявить глобальной переменной в ОЗУ и заполнить вызовом
функции MakeCRC16Table, которую ты зачем-то убрал.

Vga
25 октября 2013, 16:12 ↑

Насколько я вижу, под видом двух методов расчета CRC в статье приведены два 0
куска одного, табличного. MakeCRC16Table считает табличку (uint16_t
crctable[256]), GetCRC16 считает CRC для блока. Первую функцию нужно вызвать
один раз для инициализации.
Vga
25 октября 2013, 16:07 ↑

Всё хорошо, но вот тут +2

Использование табличной функции


unsigned char mess[3] = {1,108,8};
volatile unsigned short res1 = CRC16(&mess[0],3);

по ушам резанул вызов функции — CRC16(&mess[0],3);

Коллега, имя массива — это есть адрес первого его элемента. Таким образом, можно и
лучше написать более прозрачный код — CRC16(mess,3);

И раз уж пошла такая пьянка, то вызов функции (uint8_t)pgm_read_byte(&auchCRCHi[i]) я


бы написал так (uint8_t) pgm_read_byte(auchCRCHi + i).

Это — классика (K&R), изящная арифметика указателей присущая языкам группы С.


Мне кажется, что так будет «красивее» код. Ну, типа «некрасивые самолеты плохо
летают» (с).

И примите мои извинения, за публично поучение.

zhevak
26 октября 2013, 00:36

И еще! +2

unsigned char mess[3] = {1,108,8};

У инициализированных массивов размер можно не указывать:

unsigned char mess[] = {1,108,8};

Это особенно актуально, когда приходится тестить массивы с разным


количеством элементов.

unsigned char mess[] = {1, 108, 8};


unsigned char mess[] = {1, 108, 8, 34, 342, 4545};
unsigned char mess[] = {1, 108, 8, 0, 0};

Чтобы каждый раз не пересчитывать размер, просто не указывайте его.

Еще раз, мои извинения!

zhevak
26 октября 2013, 00:42 ↑

0
У инициализированных массивов размер можно не указывать
В более общем случае это нужно:
unsigned char mess[100] = {1,108,8};

Logic
27 октября 2013, 18:46 ↑

Да, в общем случае — да. Но ведь мы рассматриваем не общий, а 0


конкретный случай. Поэтому, совершенно нет никакой необходимости
указывать размер массива.

Ограничивать размер массива в данном случае — это не очень хороший


ход. Если взять большой массив (с запасом), то будет напрасно
израсходовано пространство RAM. Если размер будет меньше, то
возникнут другие сложности. То есть в любом случае указывать размер
будет не совсем правильно (не оптимально).

Это очень хорошо, что Вы владеете этими знаниями, но применять их


бездумно — это не есть мастерство.

zhevak
27 октября 2013, 19:27 ↑

Товарищи, чего еще в статью можно добавить? :) 0

khomin
27 октября 2013, 12:54
+1
Шахматов и библиотекаршей.

EW1UA
27 октября 2013, 18:17 ↑

напиши еще одну статью, как сделать это на freemodbus 0


вчера смотрел его и его портировали на стм32 и желательно на примере ф103,
потому что на него портировали только, а он есть пинборде2

и я до сих пор не разобрался с типами регистров и номерами функций


что для чего нужно, где аналоговые, где дискретные
как это в коде запилить и чтобы с этим мог работать mach3 для чпу станков

3и сутки рою снова по этому делу и малость понял только

kalobyte-ya
27 октября 2013, 20:56 ↑

16-ти канальный логический анализатор прямо «из коробки» умеет парсить MODBUS? 0

Logic
27 октября 2013, 18:51

да, все Saleae распознают — Последовательный интерфейс, SPI, I2C, DMX, i-wire, 0
CAN

khomin
27 октября 2013, 19:45 ↑

Где прочитать про функцию 0x90? Ее использует (Модиконовское) ПО Concept и 0


(теперь уже Шнайдеровское) Unity Pro для работы с контроллером, видел эту функцию
в дампе вайршарка при работе по Modbus TCP.

romanetz
02 августа 2016, 22:45

День добрый. А что будет в посылке, отправляемой мастером, если он хочет не 0


считать, а записать значение в слейв? Где тогда будут находиться значение, которое
мастер хочет записать?

Dimaster
18 октября 2016, 15:09

Непонятно: как адрес (uint8_t)(&auchCRCHi[i]) становится значением из таблицы? 0


И где таблица crctable из GetCRC16?

EagleSha
29 ноября 2016, 20:21

Автор ошибся при переносе кода, в оригинале там было 0

(uint8_t)pgm_read_byte(&auchCRCHi[i]);

e_mc2
30 ноября 2016, 15:58 ↑

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.

Design by — Студия XeoArt © Powered by LiveStreet CMS

Вам также может понравиться