Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: FreeModbus
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Интерфейсы
Страницы: 1, 2
andrewlekar
Кто-нибудь использовал такую штуку? Пробую скомпилировать - какая-то лабуда в заголовках. Ругается, что то одного определения не видит, то другого. Что происходит то? Как побороть?
MrYuran
Цитата(andrewlekar @ Feb 3 2011, 12:13) *
Что происходит то? Как побороть?

Как обычно.
Включить все модули в проект или в пути поиска, если свой мэйкфайл.

У меня так сделано:
Код
...
MODULES +=  modbus
MODULES +=  modbus/RTU
MODULES +=  modbus/Port
MODULES +=  modbus/Functions
MODULES +=  modbus/system

...

SRCDIRS = $(PROJECT_ROOT)/
SRCDIRS += $(addprefix $(PROJECT_ROOT)/,$(MODULES))

#INCLUDES  = -I$(PROJECT_ROOT)/
INCLUDES  = $(addprefix -I$(PROJECT_ROOT)/,$(MODULES))

CSRC    = $(wildcard $(addsuffix /*.c,$(SRCDIRS)))
CPPSRC  = $(wildcard $(addsuffix /*.cpp,$(SRCDIRS)))
ASRC    = $(wildcard $(addsuffix /*.s,$(SRCDIRS)))


Ну и не забыть подключить:
Код
/* ----------------------- Modbus includes ----------------------------------*/
#include "../modbus/include/mb.h"
#include "../modbus/include/mbport.h"
#include "../modbus/port/port.h"
andrewlekar
Попробую, хотя вроде все так же делаю. Я версию модбаса использовал 1.3 - может с 1.5 лучше покатит. И ещё, сами модули собрались, а вот в программе пытаюсь использовать их тайпдефы и вызовы из mb.h - ругается на то, что они не объявлены. Ничего не пойму - сколько на си пишу, такого безобразия не встречал.
MrYuran
Цитата(andrewlekar @ Feb 3 2011, 12:59) *
Ничего не пойму - сколько на си пишу, такого безобразия не встречал.

Нет там никакого безобразия, библиотека сделана очень чётко и грамотно.
Подключается буквально в три шага, в документации расписано.
Возьмите для начала готовый пример.

Чтобы не гадать, выкладывайте, что именно не получается и какие ошибки.
andrewlekar
Ну вот, стало сильно лучше. Помогло волшебное добавление именно в таком порядке
Код
/* ----------------------- Modbus includes ----------------------------------*/
#include "../modbus/include/mb.h"
#include "../modbus/include/mbport.h"
#include "../modbus/port/port.h"


Причем добавление во все без исключения модули модбаса - иначе нет-нет да поругивалось.
MrYuran
Цитата(andrewlekar @ Feb 3 2011, 13:40) *
Причем добавление во все без исключения модули модбаса

Это идеологически неправильно...
Надо просто пути поиска к этим папкам прописать.
andrewlekar
Пути поиска просто скажут где находится тот или иной заголовочный файл, но добавлять всё равно нужно при помощи #include. Проблема не в путях, а в том, что каждый модуль модбаса хочет именно эту последовательность инклудов. Для меня это оказалось несколько странным. Хотя я не отрицаю, что тут я что-то не понимаю, но до этого успешно добавлял в проект и ucOS, и lwIp, и PolarSSL - такой странной реакции не наблюдал.
MrYuran
Цитата(andrewlekar @ Feb 3 2011, 14:52) *
Проблема не в путях, а в том, что каждый модуль модбаса хочет именно эту последовательность инклудов. Для меня это оказалось несколько странным.

Действительно странно...
Хотя, может я тоже добавлял, просто не помню. Давно это было, а отлаженная библиотека кочует из проекта в проект без изменений.
andrewlekar
А ещё подскажите, где код для мастера искать? В упор не вижу мастера во этой библиотечке.
Dimoza
Цитата(andrewlekar @ Feb 3 2011, 12:59) *
версию модбаса использовал 1.3 - может с 1.5 лучше покатит.

Собирал недавно проект ModbusTCP на Freemodbus 1.5 - с заголовками такой фигни не было, зато обнаружился другой небольшой глючок: не для всех функций корректно вычислялась длина возвращаемого пакета. Для большинства modbus-мастеров это не принципиально (там длина в пакете двух местах присутствует), но товарищ делал мастера на питоне под линуксом и у него мастер, собранный из готовой библиотеки, ругался на некорректность данных.
alux
При сборке проекта были ошибки линкера, пока не подключил #include "mbfunc.h" в некоторых модулях.
Проект скомпилировал в IAR v.5.50.0.
Безуспешно пытаюсь запустить freemodbus-v1.5. Пробую на разных контроллерах (сейчас на Меге324P 20MHz).
Код
int
main( void )
{
    eMBErrorCode    eStatus;

    eStatus = eMBInit( MB_RTU, 0x01, 0, 9600UL, MB_PAR_NONE );

    __enable_interrupt();

    /* Enable the Modbus Protocol Stack. */
    eStatus = eMBEnable(  );

    for(;; )
    {
        ( void )eMBPoll(  );

        /* Here we simply count the number of poll cycles. */
        usRegInputBuf[0]++;
    }
На запрос чтения одного регистра по адресу 1000 отвечает ошибкой. В чем может быть проблема?

Dimoza
Цитата(alux @ Mar 3 2011, 00:45) *
На запрос чтения одного регистра по адресу 1000 отвечает ошибкой. В чем может быть проблема?

В возвращаемом пакете что передаёт? Там ведь пусть небогатая , но есть информация об ошибке.
alux
Код
01 83 02 C0

Это ответ на запрос чтения (код 0x03) с устройства №1 одного регистра по адресу 1000.
Почему, никак не могу понять? Проект компилируется, правда в IARе, без ошибок.
Dimoza
Цитата(alux @ Mar 3 2011, 09:31) *
Код
01 83 02 C0

То есть возвращает код ошибки - 2. А это означает (см. рисунок), что вы пытаетесь прочитать несуществующий регистр. Обработчик у вас этот адрес обрабатывает?
alux
Это из примера freemodbus
Код
#define REG_INPUT_START 1000
#define REG_INPUT_NREGS 4

static USHORT   usRegInputStart = REG_INPUT_START;
static USHORT   usRegInputBuf[REG_INPUT_NREGS];

Со стороны мастера (ПК) периодически даю запрос на чтение:
Код
wError = mb.ReadOutputRegisters(1, 1000, 1, wTempData);
//адр. устройства, адрес считывания, кол-во считываемых регистров, буфер

Так где я не прав?
MrYuran
Цитата(alux @ Mar 3 2011, 10:44) *
Так где я не прав?

Покажите ещё СВ-функцию чтения регистров
Далее, ReadOutputRegisters - это какая функция? (код функции)
Вообще-то, при чтении Input регистров как бы нужна функция ReadInput, но точно можно определить по коду.
Ну или пакет запроса приведите.
Dimoza
В разных реализациях по разному отсчитываются адреса. Holding начинаются с 40000 по терминологии modbus, а в вызове функции может быть отсчёт и от 0, и от 1. Попробуйте вот этой утилитой потестить:
http://www.chipkin.com/cas-modbus-scanner
В ней, например, для чтения holding register 9 нужно задать такие настройки (см аттач).
alux
Со стороны мастера (ПК)
Код
//Modbus Functions
const BYTE CModbus::READ_OUTPUT_REGISTERS      = 3;


из примера freemodbus:
Код
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }

    return eStatus;
}


пакет запроса:
Код
01 03 03 E8 00 01 04 7A

ответ:
Код
01 83 04 40
MrYuran
Сдаётся мне, вы холдинги хотите прочитать вместо инпутов.
Проверьте тогда ещё константы
REG_HOLDING_START и REG_HOLDING_NREGS

Для чтения Input регистров функция 04
alux
со стороны мастера изменил запрос на
Код
01 04 03 E8 00 01 B1 BA


ответ уже без ошибки:
Код
01 04 02 00 00 B9 30


но это не то, что я ожидаю получить. Я должен получить инкрементированное значение usRegInputBuf[0]++;
Что еще не так?
MrYuran
Цитата(alux @ Mar 3 2011, 12:59) *
но это не то, что я ожидаю получить. Я должен получить инкрементированное значение usRegInputBuf[0]++;
Что еще не так?

А вот тут уже возможно играет смещение на 1.
Попробуйте ещё соседний регистр поменять.
Dimoza
Если я ничего не путаю, в ответе должно быть два байта ячейки usRegInputBuf[0]. Если вы её никак не инициализируете, то будет возвращаться неопределённое значение. Скорее всего именно нули.
alux
Цитата(Dimoza @ Mar 3 2011, 14:17) *
в ответе должно быть два байта ячейки usRegInputBuf[0]. Если вы её никак не инициализируете, то будет возвращаться неопределённое значение. Скорее всего именно нули.

Глобальная переменная инициализируется нулем. Дальше в цикле она инкрементируется:
Код
usRegInputBuf[0]++;

Т.е. я должен увидеть на приеме 0,1,2,3...

Цитата(MrYuran @ Mar 3 2011, 14:10) *
А вот тут уже возможно играет смещение на 1.
Попробуйте ещё соседний регистр поменять.

а вот так:
Код
usRegInputBuf[1]++;

уже отвечает то, что нужно!

А где необходимо добавить это смещение на 1 ?
MrYuran
Цитата(alux @ Mar 3 2011, 13:27) *
А где необходимо добавить это смещение на 1 ?

Ну, чтобы не лезть в глубины, можно в CB-функции подправить вычисление iRegIndex
Хотя, идеологически более правильно залезть внутрь и разобраться, почему так.
alux
Вот ответ автора freemodbus на проблему смещения адреса:
Код
MODBUS protocol address (Base0) is Address 1 in the FreeMODBUS stack. The term PLC address is no longer correct.
The MODBUS PDU addresses registers at 0. The MODBUS application protocol start address at 1.
For MODBUS POLL testing simply add 1 to the register you set when you use Base 0 and you should get the address which FreeMODBUS uses.


т.е. нужно добавить 1 в определении
#define REG_HOLDING_START 1001

PS. А вопрос был такой:
Код
I’m using Modbus Poll to debug he FreeModbus programs. If REG_HOLDING_START is set to be 1000, the valid address of Modbus Poll looks like this:
PLC Addresses (Base1) = 31000
Protocol Addresses (Base0) = 00999
But if REG_HOLDING_START is set to be 0, the first address of Protocol Addresses cannot be found because the address cannot be -1.
Do you recommend not to use REG_HOLDING_START = 0 in this case?
Dimoza
Насколько я уяснил это для себя, "по-простому", в modbus нумерация регистров и койлов начинается с 1. Так для пользователя оборудования будет проще: сложно обычному человеку понять, что такое нулевой датчик. А вот 1, 2, 3 и так далее - уже проще. НО! В передаваемом пакете modbus этому самому 1-му датчику будет соответствовать адрес 0.
alux
Как правильно передать код ошибки MB_EX_SLAVE_BUSY = 0х06 , в случае, если SLAVE занят вычислениями, т.е. результат еще не готов?
Функцию опроса ModBus я поместил в другую функцию, которую периодически вызываю по таймеру:
Код
void MBPoll()
{
  (void)eMBPoll();
}

Я вижу выход такой:
в функции eMBErrorCode eMBRegHoldingCB(...) по глобальному флагу Busy,
Код
if(Busy) eStatus = MB_ETIMEDOUT;
else
...
, возвращаемое значение из этой функции преобразуется в исключение MB_EX_SLAVE_BUSY;
MrYuran
Цитата(alux @ Mar 5 2011, 13:26) *
Я вижу выход такой:

Ну да, как-то так.
Я, кстати, смещение на 1 тоже задал в дефайне, не долго думая sm.gif
(заглянул по случаю, освежил память)
alux
Еще для полного счастья нужно использовать функции для записи\чтения битов ForceSingleCoil (0x05), ReadSingleCoil(0x01).
Приведите, пожалуйста, пример с использованием этих функций. Флаги, битовое поле в области ввода-вывода ниже 0x1f.
Код
// Flags
typedef union
{
    unsigned char Complete;   // Access all 8 bits
    
    struct
    {
        bool IsTestCompleted   : 1;    
        bool IsDataReady      : 1;      
        bool IsRelayOn          : 1;  
        bool IsReset          : 1;  
        bool IsOverheat          : 1;  
        bool IsKeyPressed      : 1;
        bool IsOpenoff              : 1;  
        bool IsShortcut          : 1;  
    };
} FLAGS;
__no_init volatile FLAGS Flags@0x28; // Флаги, битовое поле в области ввода-вывода ниже 0x1f (ACSR Register)
MrYuran
Цитата(alux @ Mar 7 2011, 14:47) *
Еще для полного счастья нужно использовать функции для записи\чтения битов ForceSingleCoil (0x05), ReadSingleCoil(0x01).

Не пробовал, т.к. особого смысла не вижу.
Не проще ли все флаги оптом передать в регистре статуса, например?
_Pasha
Цитата(alux @ Mar 7 2011, 14:47) *
Еще для полного счастья нужно использовать функции для записи\чтения битов ForceSingleCoil (0x05), ReadSingleCoil(0x01).
Приведите, пожалуйста, пример с использованием этих функций. Флаги, битовое поле в области ввода-вывода ниже 0x1f.

А какой гешефт их так описывать?
При адресации можно использовать offsetof()/8, биты задавать перечислениями. Получится
Код
extern
#ifdef CORE_32
#define CORE_WD 32
char
#else
#define CORE_WD 8
int
#endif
mb_coils[];
enum coils {foo,bar};
static inline bool coil_r(enum coils coil);
static inline void coil_w(enum coils coil , bool val);
bool coil_r(enum coils coil)
{
  return (mb_coils[coil / CORE_WD] & (1 << coil & (CORE_WD-1)))?true:false;
}
alux
При использовании функции 0x10 PresetMultipleRegisters запрос от мастера идет как положено (проверяю в Serial Port Monitor), а ответа от слева не поступает. Функция 0x06 PresetSingleRegister работает как положено. В mbconfig.h функция 0x10 разрешена :
Код
#define MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED  (  1 )

В чем может быть проблема?
andrewlekar
Callback написали для этой функции?
alux
как выяснилось, проблема не в этой функции (0x10), а в адресе регистров, в которые нужно писать. У меня регистры (переменные типа int) упакованы в структуру :
Код
typedef struct
{
    int NumSensorsDS;            
    int TemperatureTC[NUM_SENSORS];
    int Parameter[2][NUM_SENSORS];
    int Temperature[2];      
    int NumSensorsTC;    
    int Cable;          
    int Average;              
    int Data;              
    long Accum;    
}TValue;

В регистры Cable, Average записываются значения и отдается нормальный ответ от устройства при помощи ф-ций PresetSingleRegister,PresetMultipleRegisters, а если указать адрес записываемого регистра в NumSensorsTC, то запрос остается без ответа. Почему, не пойму никак?..

PS. Callback-функции написаны и работают.

PS2. Вроде нашел... В Callback-функции использую ф-ции записи во флеш.
Код
                            WriteFlashByte(FLASH_PARAMETER_LOCATION + 2 + 1, *(pucRegBuffer));
                            WriteFlashByte(FLASH_PARAMETER_LOCATION + 2, *(pucRegBuffer + 1));

Если закоментировать эти строки, то ответ нормальный появляется. Наверно, правильно сделать так : в колбэк-функции устанавливать флаг, а в главном цикле по этому флагу писать во флеш.
ISK2010
Доброго времени суток!

Занимаюсь изучением МК STM32F105. У меня есть два устройства с RS485 полудуплексом. Хочу их связать, чтобы управлять первым и получать от него измерения с АЦП. Почитал и понял, что нужно применить ModBus как широко распространенный протокол. Велосипед изобретать не хочу, поэтому гляжу в сторону freeModBus. Скачал версию 1.5, но...без поллитра сока не разобраться. А сока нет...

Правильно ли я понял, что нужно свои функции приема-посылки написать?

Не могу понять, вот допустим я в фоне второго устройства хочу получить температуру с первого устройства, что делать? Какую функцию использовать?

Пожалуйста помогите разобраться. Может у кого-нибудь есть простенький пример приклеивания FreeModBus к STM32F10X?

MrYuran
Цитата(ISK2010 @ Apr 19 2011, 08:45) *
Не могу понять, вот допустим я в фоне второго устройства хочу получить температуру с первого устройства, что делать? Какую функцию использовать?
Пожалуйста помогите разобраться. Может у кого-нибудь есть простенький пример приклеивания FreeModBus к STM32F10X?

Это нужно писать мастер. (FreeModbus - это Slave)
То есть, необходимы функции, которые будут отправлять мастер-пакеты и ожидать ответа.
ISK2010
А как slave должен реагировать на прием пустого ADU? Т.е. который адресован ему и имеет правильный CRC, но не имеет ни функции, ни данных.
yashok
Цитата
который адресован ему и имеет правильный CRC, но не имеет ни функции, ни данных


Если вместо функции и данных просто нули, то при проверки кода функции, если он данную ф-цию не поодерживает, должен вернуть ошибку с номером 1 (ILLEGAL FUNCTION).
ISK2010
А CRC всех принятых пакетов рассчитывается? Или только тех, которые адресованы нам?

В FreeModbus, я посмотрел, для всех принятых фреймов проверяется минимальная длина и CRC. Но не могу понять зачем. Ведь можно сначала проверять адрес, и тратить время на расчет CRC только "своих" фреймов.
yashok
По хорошему проверка должна идти в следующей очередности:
1. Длина пакета
2. CRC
3. Адрес
4. Функция
5. Данные
ISK2010
Цитата(yashok @ May 17 2011, 09:04) *
По хорошему проверка должна идти в следующей очередности:
1. Длина пакета
2. CRC
3. Адрес
4. Функция
5. Данные


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


yashok
Для простых случаев может и можно не проверять. А если сеть протяженная, с большим количеством слейвов, помеховая обстановка серьезная - вероятность ошибок в пакете приличная, проверка crc становится обязательной.
ISK2010
Бесспорно проверка CRC обязательна. Но только для тех фреймов, которые нам адресованы. А если уже для этих фреймов не сошелся crc, то игнорируем их и инкрементируем счетчик ошибок crc.
yashok
Да согласен

Хотя у некторых есть счетчики которые считают ошибки в линии т.к. если есть ошибка, то не известно где именно и кому предназначен пакет.
leshij
Доброго дня всем.
Пытаюсь поднять FreeRTOS(7.2)+lwip(1.4)+FreeModbus(1.5)(TCP), FreeRTOS+lwip на LM3S9B95(board EVB-9B95) худо-бедно справился, пинги идут, httpserver_raw запустился, пробную страничку получил.
А вот с modbus справится не могу. Для теста взял qModMaster, т.к. работать легко с ней(основные шаги connect->read data->disconnect я выполняю по кнопками, что упрощает тест), и параллельно для надежности нечто более сложное NI OPC Server.
Connect, Disconnect работают отлично, вызываются prvxMBTCPPortAccept() и prvxMBTCPPortReceive()(здесь он уходит в prvvMBPortReleaseClient и закрывает соединение).
А вот с чтением данных у меня возникли проблемы, при чтении попадаю я в prvxMBTCPPortReceive(), далее по функции доходит до (void)xMBPortEventPost(EV_FRAME_RECEIVED); и из нее не возвращается, ползу по ней дебагером
Код
//portevent.c
BOOL xMBPortEventPost( eMBEventType eEvent )
{
    eMailBoxEvent = eEvent;
    sys_mbox_post( xMailBox, &eMailBoxEvent );
    return TRUE;
}

он уходит в sys_mbox_post();
Код
//sys_arch.c
void sys_mbox_post( sys_mbox_t *pxMailBox, void *pxMessageToPost )
{
    while( xQueueSendToBack( *pxMailBox, &pxMessageToPost, portMAX_DELAY ) != pdTRUE );
}

и на сколько я понимаю крутится внутри xQueueGenericSend().
После чего отваливается ethernet, т.е. пинги не проходят вообще, да и походу роутер не видит плату, FreeRTOS в это время работает Task'и переключает исправно.
Я уже даже не знаю в какую сторону копать.
Порт modbus взят из демки MCF5235TCP, а LWIP где-то на просторах интернета

Нажмите для просмотра прикрепленного файла
Нажмите для просмотра прикрепленного файла
leshij
в sys_mbox_post() убрал цикл, заменил portMAX_DELAY на 1000(т.е. на 1 секунду). Ethernet перестал отваливаться, обнаружил, что функция xQueueSendToBack возвращает ошибку errQUEUE_FULL.
leshij
обнаружил, что структура pxQueue не подгружалась при работе с модбасом
у меня еще не пришло полное понимание & и *, поэтому решил поиграться с ними в порте freemodbus (portevent.c)
Код
sys_mbox_post( xMailBox, &eMailBoxEvent );

заменил на
Код
sys_mbox_post( &xMailBox, &eMailBoxEvent );

структура подгрузилась и функция xQueueSendToBack перестала выдавать ошибку errQUEUE_FULL, но что-то я сильно сомневаюсь, что в порте была ошибка

Больше не сомневаюсь, это реально ошибка в порте, изменил на &xMailBox во всех функциях portevent.c и freemodbus заработал. Большое спасибо разработчикам freemodbus, так и без работы можно оставить, яж на испытательном сроке ещеsm.gif но все равно им спасибо за трудsm.gif
veskon
Помогите реализовать Modbus-slave на базе Atmega32A. Условие: применяемый на freemodbus 16-разрядный таймер1 использовать нельзя - он нужен мне для реализации 16-разрядного аппаратного ШИМа.

Что уже сделано:
1) Материалы на freemodbus.org изучил (хотя и не во всем разобрался).
2) Нашел http://forum.easyelectronics.ru/viewtopic....mp;hilit=modbus - во втором посте выложено решение для 168й атмеги по замене таймера 1 на таймер 0. Скачал, собрал в свой проект, откомпилировал для 168й атмеги.
2) Заменил ссылки на таймер 0А (для 168й меги) на таймер 0 для 32й меги.
3) 2) Заменил ссылки на таймер 0В (для 168й меги) на таймер 2 для 32й меги.
4) Откомпилировал в AtmelStudio6 без ошибок.
5) Подключил контроллер через мах485 и конвертер i-7561 (485/usb) к компьютеру.
6) ОРС-сервером Lectus отправляю запрос 01 03 00 01 00 01 D5 CA, осциллографом наблюдаю соответствующую последовательность на ножке микроконтроллера, НО сам контроллер молчит и в ответ никаких сообщений не выдает.

Предполагаю два варианта проблемы:
1) Простая замена таймеров оказалась некорректной.
2) Неверно "привязаны" регистры usRegInputBuf и usRegHoldingBuf.

Помогите пожалуйста разобраться
MrYuran
Цитата(veskon @ Sep 30 2013, 14:02) *
Помогите пожалуйста разобраться

Для начала, RTFM - Related Pages -> Porting for RTU/ASCII

Ну а дальше тщательная трассировка.
Самое простое, это банальный бит четности (проверка на четность по стандарту Modbus RTU/ASCII).
Если прерывания ловятся и пакет принимается, должен выставляться эвент и функция поллинга перенаправляет в нужный коллбэк.
Дальше убеждаемся, что формируется ответный пакет, и вот тут может всплыть 485-й с переключением потока.
Lagman
Цитата(veskon @ Sep 30 2013, 15:02) *
Помогите пожалуйста разобраться

А может слейв адрес не тот?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.