[Промышленное программирование, Разработка робототехники, Программирование микроконтроллеров, Разработка для интернета вещей, Производство и разработка электроники] ModBus Slave RTU/ASCII без смс и регистрации
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
В открытом доступе присутствует множество библиотек для реализации ModBus Slave устройства, но они зачастую, содержат избыточный функционал, сложны в освоении и содержат грубые ошибки. В данной статье рассматривается библиотека, по скромному мнению автора, лишенная этих недостатков.
Программное обеспечение библиотеки поставляется в виде открытого исходного кода на языке Си.
modbus.h
SPL
////////////////////////////////////////////////////////////////////
// Асинхронная обработка сообщение ModBus v2 //
// Автор - Iван //
///////////////////////////////////////////////////////////////////
#ifndef __MODBUS_H
#define __MODBUS_H
#include "main.h"
///////////////////////////////////////////////////////////////////////////////
//Настройки МодБас
//Данные, регистры Модбас
#define ModBusUseGlobal (0) //Использовать глобальные входы/выходы, входные/выходные регистры
//Функции протокола Модбас
#define ModBusUseFunc1 (0) //Использовать функцию 1 - чтение статуса Coils (дискретных выходных битов)
#define ModBusUseFunc2 (0) //Использовать функцию 2 - чтение статуса дискретных входов
#define ModBusUseFunc3 (1) //Использовать функцию 3 - чтение значения выходных регистров
#define ModBusUseFunc4 (0) //Использовать функцию 4 - чтение значения входных регистров
#define ModBusUseFunc5 (0) //Использовать функцию 5 - запись выходного бита
#define ModBusUseFunc6 (1) //Использовать функцию 6 - запись выходного регистра
#define ModBusUseFunc15 (0) //Использовать функцию 15 - запись нескольких выходных битов
#define ModBusUseFunc16 (1) //Использовать функцию 16 - запись нескольких выходных регистров
//Адрес устройства
#define ModBusID (1) //Адрес на шине МодБас
#define ModBusID_FF (255) //Адрес на шине МодБас, на который отвечает всегда
//Таймауты
#define ModBusMaxPause (5)//Пауза между символами, для определения начала пакета [mS],
#define ModBusMaxPauseResp (2) //Пауза между запросом Мастера и ответом Слайва [mS]
//Длинна пакетов
#define ModBusMaxPaketRX (96)//Максимальный размер принимаемого пакета <127
//Дискретные входы
#define ModBusMaxInBit (0) //Количество дискретных входов
#define ModBusMaxInBitTX (8) //Максимальное количество дискретных входов при передаче пакета
#define ModBusMaxInByte ((ModBusMaxInBit+7)/8) //Количество дискретных входов в байтах
//Дискретные выходы
#define ModBusMaxOutBit (0) //Количество дискретных выходов
#define ModBusMaxOutByte ((ModBusMaxOutBit+7)/8) //Количество дискретных выходов в байтах
#define ModBusMaxOutBitTX (8) //Максимальное количество дискретных выходов в передаваемом пакете
#define ModBusMaxOutBitRX (8) //Максимальное количество дискретных выходов доступное для груповой установки
//Регистры доступные для чтения
#define ModBusMaxInReg (0) //Количество входных регистров (регистры только для чтения)
#define ModBusMaxInRegTX (24) //Максимальное количество входных регистров в передаваемом пакете
//Регистры доступные для чтения-записи
#define ModBusMaxOutReg (48) //Количество выходных регистров
#define ModBusMaxOutRegTX (32)//Максимальное количество выходных регистров в передаваемом пакете
#define ModBusMaxOutRegRX (32)//Максимальное количество выходных регистров доступное для груповой установки
////////////////////////////////////////////////////////////////////////////////
//Опорные функции, связь с системой
//Системный таймер, инкрементируется каждую миллисекунду
#define ModBusSysTimer TimingDelay
//Запись байта в поток последовательного порта - void ModBusPUT(unsigned char A)
#define ModBusPUT(A) PutFifo0(A)
//Чтение байта из потока последовательного порта, - unsigned short ModBusGET(void)
//Если нет данных возвращает 0х0000, иначе возвращает 0х01ХХ
#define ModBusGET() Inkey16Fifo0()
////////////////////////////////////////////////////////////////////////////////
//Инициализация
void ModBusIni(void);
//Функция обработки Сообщений модбас RTU
//Работает совместно с системным таймером
//Использует макросы ModbusPUT(A) ModbusGET()
void ModBusRTU(void);
//Функция обработки Сообщений модбас ASCII
//Работает совместно с системным таймером
//Использует макросы ModbusPUT(A) ModbusGET()
void ModBusASCII(void);
//Заполнение регистров Модбас
//перенос данных из программных переменных в регистры МодБас
void Prg2ModBusOutBit(void);
void Prg2ModBusInBit(void);
void Prg2ModBusOutReg(void);
void Prg2ModBusInReg(void);
//Считывание регистров Модбас
//перенос данных из регистров МодБас в программные переменные
void ModBus2PrgOutBit(void);
void ModBus2PrgOutReg(void);
#pragma pack(push,1)
//Тип данных для работы с дискретными входами/выходами
typedef union
{
unsigned char byte;
struct
{
unsigned char bit0:1;
unsigned char bit1:1;
unsigned char bit2:1;
unsigned char bit3:1;
unsigned char bit4:1;
unsigned char bit5:1;
unsigned char bit6:1;
unsigned char bit7:1;
};
}
ModBusBit_t;
#pragma pack(pop)
#ifdef __MODBUS2PRG_C
#if ModBusMaxInBit!=0
ModBusBit_t ModBusInBit[ModBusMaxInByte]; //массив дискретных входов
#endif
#if ModBusMaxOutBit!=0
ModBusBit_t ModBusOutBit[ModBusMaxOutByte]; //массив дискретных выходов
#endif
#if ModBusMaxInReg!=0
unsigned short ModBusInReg[ModBusMaxInReg]; //массив входных регистров
#endif
#if ModBusMaxOutReg!=0
unsigned short ModBusOutReg[ModBusMaxOutReg]; //массив выходных регистров
#endif
#else
#if ModBusUseGlobal!=0 || defined(__MODBUS_C)
#if ModBusMaxInBit!=0
extern ModBusBit_t ModBusInBit[ModBusMaxInByte]; //массив дискретных входов
#endif
#if ModBusMaxOutBit!=0
extern ModBusBit_t ModBusOutBit[ModBusMaxOutByte]; //массив дискретных выходов
#endif
#if ModBusMaxInReg!=0
extern unsigned short ModBusInReg[ModBusMaxInReg]; //массив входных регистров
#endif
#if ModBusMaxOutReg!=0
extern unsigned short ModBusOutReg[ModBusMaxOutReg]; //массив выходных регистров
#endif
#endif//#if ModBusUseGlobal!=0
#endif//#ifdef __MODBUS2PRG_C
#endif//#ifndef __MODBUS_H
modbus.c
SPL
#define __MODBUS_C
#include "modbus.h"
static unsigned char PaketRX[ModBusMaxPaketRX];//массив для сохранения пакета
static unsigned char UkPaket;//указатель в массиве, текущий принятый символ
static unsigned long TimModbus; //время приема пакета по системному таймеру
static unsigned short CRCmodbus;//текущий CRC
static unsigned char Sost;//состояние 0/1 прием/передача
//Инициализация
void ModBusIni(void)
{
TimModbus=ModBusSysTimer;//запомнить таймер
UkPaket=0;//сбросить указатель пакета
CRCmodbus=0xFFFF; //установить начальное значение CRC
//Инициализация регистров МодБас
#if ModBusMaxOutBit!=0
Prg2ModBusOutBit();
#endif
#if ModBusMaxInBit!=0
Prg2ModBusInBit();
#endif
#if ModBusMaxOutReg!=0
Prg2ModBusOutReg();
#endif
#if ModBusMaxInReg!=0
Prg2ModBusInReg();
#endif
return;
}
//Функция вычисления CRC
static inline unsigned short CRCfunc(unsigned short inCRC, unsigned char in)
{
inCRC=inCRC^in;
for(int j=0;j<8;j++){if(inCRC&1){inCRC=(inCRC>>1)^0xA001U;}else {inCRC=inCRC>>1;}}
return inCRC;
}
//Функция обработки Сообщений модбас
void ModBusRTU(void)
{
if(Sost==0)
{//Состояние прием
while(!0)
{//Цикл приема символов
unsigned short Tmp=ModBusGET(); //читаем символ из входного потока
if(Tmp==0) return; //если нет данных - возврат
//символ принят
Tmp=Tmp&0xFF;//отбрасываем признак приема байта
//Проверка временного интервала между символами
if((ModBusSysTimer-TimModbus)>ModBusMaxPause)
{//превышен таймаут, начинаем прием нового пакета
PaketRX[0]=Tmp;//сохранить принятый символ в буфер приема
UkPaket=1;//установить указатель пакета
TimModbus=ModBusSysTimer;//сбросить таймер
//вычисление CRC
CRCmodbus=CRCfunc(0xFFFF,Tmp);
continue;//повторный запрос символа
}
else
{//таймаут не превышен, принимаем уже начатый пакет
TimModbus=ModBusSysTimer;//сбросить таймер
PaketRX[UkPaket]=Tmp;//сохранить принятый символ
UkPaket++;//инкремент указателя пакета
if(UkPaket==ModBusMaxPaketRX)//проверяем на длину пакета
{//буфер пакета переполнился
UkPaket=0;//сбросить указатель пакета
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//ошибка, повторный запрос символа требуется
}
//вычисление CRC
CRCmodbus=CRCfunc(CRCmodbus,Tmp);
}
//Если принято мало данных
if(UkPaket<8) continue; //повторный запрос символа
//проверка на принятие пакета
if(CRCmodbus==0)
{//проверка на длинные пакеты
if(PaketRX[1]==15 || PaketRX[1]==16)
{//если длинные команды (15,16) , проверяем "Счетчик байт"
if((PaketRX[6]+9)!=UkPaket) continue;
}
break; //Ура! Пакет принят!!!
}
}
//////////////////////////////////////////////////////////////////////////////
// Ура! Пакет принят!!!
/////////////////////////////////////////////////////////////////////////////
UkPaket=0;//сбросить указатель пакета
//проверка адреса
if((PaketRX[0]!=ModBusID)&&(PaketRX[0]!=ModBusID_FF))
{//Не наш адрес
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
//переходим в состояние передача ответа
Sost=!0;
#if ModBusMaxPauseResp!=0
return;//повторный запрос не требуется
#endif
}
/////////////////////////////////////////////////////////////////////////////
if(Sost!=0
#if ModBusMaxPauseResp!=0
&& (ModBusSysTimer-TimModbus)>=ModBusMaxPauseResp
#endif
)
{//Состояние передача ответа
Sost=0;
/////////////////////////////////////////////////////////////////////////////
// обработка команд //
/////////////////////////////////////////////////////////////////////////////
//Код функции 01 - чтение статуса Coils (дискретных выходных битов).
/*Сообщение-запрос содержит адрес начального бита и количество битов для чтения.
Биты нумеруются начиная с 0.
В сообщении-ответе каждое значение переменной передается одним битом,
то есть в одном байте пакуется статус 8 битов переменных.
Если количество их не кратно восьми, остальные биты в байте заполняются нулями.
Счетчик вмещает количество байт в поле данных.
01 Чтение статуса выходов
ОПИСАНИЕ
Читает статуса ON/OFF дискретных выходов в подчиненном.
ЗАПРОС
Запрос содержит адрес начального выхода и количество выходов для чтения.
Выхода адресуются начиная с нуля: выхода 1-16 адресуются как 0-15.
Ниже приведен пример запроса на чтение выходов 20-56 с подчиненного устройства 17.
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 01 1
Начальный адрес Hi 00 2
Начальный адрес Lo 13 3
Количество Hi 00 4
Количество Lo 25 5
Контрольная сумма (CRC или LRC) --
ОТВЕТ
Статус выходов в ответном сообщении передается как один выход на бит.
Если возвращаемое количество выходов не кратно восьми, то оставшиеся биты в последнем байте сообщения будут установлены в 0.
Счетчик байт содержит количество байт передаваемых в поле данных.
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 01 1
Счетчик байт 05 2
Данные(Выхода 27-20) CD 3
Данные(Выхода 35-28) 6B 4
Данные(Выхода 43-36) B2 5
Данные(Выхода 51-44) 0E 6
Данные(Выхода 56-52) 1B 7
Контрольная сумма (CRC или LRC) --
*/
#if ModBusUseFunc1!=0
if(PaketRX[1]==0x01)
{
//вычисление адреса запрашиваемых бит
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества запрашиваемых бит
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if((AdresBit+KolvoBit)>(ModBusMaxOutBit) || KolvoBit>ModBusMaxOutBitTX || KolvoBit==0)
{//неправильный адрес и количество
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
Prg2ModBusOutBit();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
//адрес
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]);
//код команды
ModBusPUT(1);
CRCmodbus=CRCfunc(CRCmodbus,1);
//количества полных байт
ModBusPUT((KolvoBit+7)>>3);
CRCmodbus=CRCfunc(CRCmodbus,((KolvoBit+7)>>3));
//копирование битов в пакет ответа
unsigned char TxByte=0;//текущий байт
unsigned char Bit=AdresBit&7;//указатель бит в ModBusOutBit[]
AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[]
//копирование из регистра ModBusOutBit[] в пакет
int i=0;
while(!0)
{
if((ModBusOutBit[AdresBit].byte)&(1<<Bit))
{
TxByte=TxByte|(1<<(i&7));
}
//инкрементруем указатели
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
break;
}
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//Код функции 2 - чтение статуса дискретных входов
/*02 Read Input Status
ОПИСАНИЕ
Чтение ON/OFF состояния дискретных входов (ссылка 1Х) в пдчиненном.
ЗАПРОС
Запрос содержит номер начального входа и количество входов для чтения. Входа адресуются начиная с 0.
Ниже приведен пример запроса на чтение входов 10197-10218 с подчиненного устройства 17.
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 02 1
Начальный адрес ст. 00 2
Начальный адрес мл. C4 3
Кол-во входов ст. 00 4
Кол-во входов мл. 16 5
Контрольная сумма --
ОТВЕТ
Статус входов в ответном сообщении передается как один выход на бит.
Если возвращаемое количество входов не кратно восьми, то оставшиеся биты в последнем байте сообщения будут установлены в 0.
Счетчик байт содержит количество байт передаваемых в поле данных.
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 01 1
Счетчик байт 03 2
Данные(Входы 10204-10197) AC 3
Данные(Входы 10212-10205) DB 4
Данные(Входы 10218-10213) 35 5
Контрольная сумма (CRC или LRC) --
*/
#if ModBusUseFunc2!=0
if(PaketRX[1]==0x02)
{
//вычисление адреса запрашиваемых бит
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества запрашиваемых бит
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if((AdresBit+KolvoBit)>(ModBusMaxInBit) || KolvoBit>ModBusMaxInBitTX || KolvoBit==0)
{//неправильный адрес и количество
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
Prg2ModBusInBit();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
//адрес
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]);
//код команды
ModBusPUT(2);
CRCmodbus=CRCfunc(CRCmodbus,2);
//количества полных байт
ModBusPUT((KolvoBit+7)>>3);
CRCmodbus=CRCfunc(CRCmodbus,((KolvoBit+7)>>3));
//копирование битов в пакет ответа
unsigned char TxByte=0;//текущий байт
unsigned char Bit=AdresBit&7;//указатель бит
AdresBit=AdresBit>>3;//указатель байт
//копирование из регистра ModBusInBit[] в пакет
int i=0;
while(!0)
{
if((ModBusInBit[AdresBit].byte)&(1<<Bit))
{//устанавливаем бит в пакете
TxByte=TxByte|(1<<(i&7));
}
//инкрементруем указатели
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(TxByte);
CRCmodbus=CRCfunc(CRCmodbus,TxByte);
break;
}
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//Код функции 03 - чтение значения выходных/внутренних регистров.
/*Сообщение-запрос содержит адрес начального исходного/внутреннего регистра (двухбайтовое слово),
и количество регистров для чтения. Регистры нумеруются начиная с 0.
03 Read Holding Registers
ОПИСАНИЕ
Чтение двоичного содержания регистров (ссылка 4Х) в подчиненном.
ЗАПРОС
Сообщение запроса специфицирует начальный регистр и количество регистров для чтения.
Регистры адресуются начина с 0: регистры 1-16 адресуются как 0-15.
Ниже приведен пример чтения регистров 40108-40110 с подчиненного устройства 17.
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 03 1
Начальный адрес ст. 00 2
Начальный адрес мл. 6B 3
Кол-во регистров ст. 00 4
Кол-во регистров мл. 03 5
Контрольная сумма --
ОТВЕТ
Данные регистров в ответе передаются как два бйта на регистр.
Для каждого регистра, первый байт содержит старшие биты второй байт содержит младшие биты.
За одно обращение может считываться 125 регистров для контроллеров 984-Х8Х (984-685 и т.д.),
и 32 регистра для других контроллеров. Ответ дается когда все данные укомплектованы.
Это пример ответа на запрос представленный выше:
Ответ
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 03 1
Счетчик байт 06 2
Данные (регистр 40108) ст. 02 3
Данные (регистр 40108) мл. 2B 4
Данные (регистр 40109) ст. 00 5
Данные (регистр 40109) мл. 00 6
Данные (регистр 40110) ст. 00 7
Данные (регистр 40110) мл. 64 8
Контрольная сумма --
*/
#if ModBusUseFunc3!=0
if(PaketRX[1]==0x03)
{
//вычисление адреса запрашиваемых слов
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление адреса количества запрашиваемых слов
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || (KolvoWord>ModBusMaxOutRegTX))
{//тады конец
CRCmodbus=0xFFFF;//установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
Prg2ModBusOutReg();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
//адрес
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]);
//код команды
ModBusPUT(3);
CRCmodbus=CRCfunc(CRCmodbus,3);
//количества полных байт
ModBusPUT(KolvoWord<<1);
CRCmodbus=CRCfunc(CRCmodbus,(KolvoWord<<1));
//Копирование из регистра ModBusOutReg[] в пакет ответа
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(ModBusOutReg[AdresWord+i]>>8);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusOutReg[AdresWord+i]>>8));
ModBusPUT(ModBusOutReg[AdresWord+i]>>0);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusOutReg[AdresWord+i]>>0));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//Код функции 04 - чтение значения входных регистров
/*04 Read Input Registers
СОДЕРЖАНИЕ
Чтение двоичного содержания входных регистров (ссылка 3Х) в подчиненном.
ЗАПРОС
Запрос содержит номер начального регистра и количество регистров для чтения.
Ниже приведен пример запроса для чтения регистра 30009 с подчиненного устройства 17.
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 03 1
Начальный адрес ст. 00 2
Начальный адрес мл. 6B 3
Кол-во регистров ст. 00 4
Кол-во регистров мл. 03 5
Контрольная сумма --
ОТВЕТ
Данные регистров в ответе передаются как два бйта на регистр.
Для каждого регистра, первый байт содержит старшие биты второй байт содержит младшие биты.
За одно обращение может считываться 125 регистров для контроллеров 984-Х8Х (984-685 и т.д.),
и 32 регистра для других контроллеров. Ответ дается когда все данные укомплектованы.
Это пример ответа на запрос представленный выше:
Ответ
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 03 1
Счетчик байт 02 2
Данные (регистр 30009) ст. 00 3
Данные (регистр 30009) мл. 2A 4
Контрольная сумма --
*/
#if ModBusUseFunc4!=0
if(PaketRX[1]==0x04)
{
//вычисление адреса запрашиваемых слов
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление адреса количества запрашиваемых слов
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if(((AdresWord+KolvoWord)>ModBusMaxInReg) || (KolvoWord>ModBusMaxInRegTX))
{//тады конец
CRCmodbus=0xFFFF;//установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
Prg2ModBusInReg();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
//адрес
ModBusPUT(PaketRX[0]);
CRCmodbus=CRCfunc(0xFFFF,(PaketRX[0]));
//код команды
ModBusPUT(4);
CRCmodbus=CRCfunc(CRCmodbus,4);
//количества полных байт
ModBusPUT(KolvoWord<<1);
CRCmodbus=CRCfunc(CRCmodbus,(KolvoWord<<1));
//Копирование из регистра ModBusInReg[] в пакет ответа
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(ModBusInReg[AdresWord+i]>>8);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusInReg[AdresWord+i]>>8));
ModBusPUT(ModBusInReg[AdresWord+i]>>0);
CRCmodbus=CRCfunc(CRCmodbus,(ModBusInReg[AdresWord+i]>>0));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//Код функции 05 - запись выходного/внутреннего бита
/*05 Force Single Coil
ОПИСАНИЕ
Установка единичного выхода (ссылка 0Х) в ON или OFF.
При широковещательной передаче функция устанавливает все выходы с данным адресом во всех подчиненных контроллерах.
ЗАМЕЧАНИЕ Функция может пересекаться с установкой защиты
памяти и установкой недоступности выходов.
ЗАПРОС
Запрос содержит номер выхода для установки. Выходы адресуются начиная с 0. Выход 1 адресуется как 0.
Состояние, в которое необходимо установить выход (ON/OFF) описывается в поле данных.
Величина FF00 Hex - ON. Величина 0000 - OFF. Любое другое число неверно и не влияет на выход.
В приведенном ниже примере устанавливается выход 173 в состояние ON в подчиненном устройстве 17.
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 05 1
Адрес выхода мл. 00 2
Адрес выхода ст. AC 3
Данные ст. FF 4
Данные мл. 00 5
Контрольная сумма --
ОТВЕТ
Нормальный ответ повторяет запрос.
Ответ
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 05 1
Адрес выхода мл. 00 2
Адрес выхода ст. AC 3
Данные ст. FF 4
Данные мл. 00 5
Контрольная сумма --
*/
#if ModBusUseFunc5!=0
if(PaketRX[1]==0x05)
{
//вычисление адреса записываемого выхода
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//проверка на допустимый адрес
if(AdresBit>=ModBusMaxOutBit)
{//если неправильный адрес
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
//установка сброс бита
switch (((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])))
{
case 0xFF00:
//установка бита
ModBusOutBit[(AdresBit>>3)].byte|=(1<<(AdresBit&7));
break;
case 0x0000:
//сброс бита
ModBusOutBit[(AdresBit>>3)].byte&=(~(1<<(AdresBit&7)));
break;
default:
{//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
}
//Ответ
for(int i=0;i<8;i++) ModBusPUT(PaketRX[i]);//запускаем передачу пакета ответа
ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//Код функции 06 - запись выходного/внутреннего регистра.
/*Функция аналогична 05, но оперирует с регистрами (словами).
В запросе указывается номер выходного/внутреннего регистра и его значение.
06 Preset Single Register
ОПИСАНИЕ. Записывает величину в единичный регистр (ссылка 4Х).
При щироковезательной передаче на всех подчиненных устройствах устанавливается один и тот же регистр.
ЗАМЕЧАНИЕ
Функция может пересекаться с установленной защитой памяти.
ЗАПРОС
Запрос содержит ссылку на регистр, который необходимо установить. Регистры адресуются с 0.
Величина, в которую необходимо установить регистр передается в поле данных.
Контроллеры M84 и 484 используют 10-ти битную величину, старшие шесть бит заполняются 0.
Все другие контроллерыиспользуют 16 бит.
В приведенном ниже примере в регистр 40002 записывается величина 0003 Hex в подчиненном устройстве 17.
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 06 1
Адрес регистра мл. 00 2
Адрес регистра ст. 01 3
Данные ст. 00 4
Данные мл. 03 5
Контрольная сумма --
ОТВЕТ
Нормальный ответ повторяет запрос.
Ответ
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 06 1
Адрес регистра мл. 00 2
Адрес регистра ст. 01 3
Данные ст. 00 4
Данные мл. 03 5
Контрольная сумма --
*/
#if ModBusUseFunc6!=0
if(PaketRX[1]==0x06)
{
//вычисление адреса записываемого выхода
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//проверка на допустимый адрес
if(AdresWord>=(ModBusMaxOutReg))
{//если неправильный адрес
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
//запись слова
ModBusOutReg[AdresWord]=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//Ответ
for(int i=0;i<8;i++) ModBusPUT(PaketRX[i]);//запускаем передачу пакета ответа
ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//Код функции 0x0F - запись нескольких выходных/внутренних битов.
/*В запросе указывается начальный адрес бита, количество бит для записи, счетчик байтов и непосредственно значения.
15 (0F Hex) Force Multiple Coils
ОПИСАНИЕ
Устанавливает каждый выход (ссылка 0Х) последовательности выходов в одно из состояний ON или OFF.
При широковещательной передаче функция устанавливает подобные выходы на всех подчиненных.
ЗАМЕЧАНИЕ Функция может пересекаться с установкой защиты памяти и установкой недоступности выходов.
ЗАПРОС
Запрос специфицирует выходы для установки. Выходы адресуются начиная с 0.
Ниже показан пример запроса на установку последовательности выходов начиная с 20 (адресуется как 19)
в подчиненном устройстве 17.
Поле данных запроса содержит 2 байта: CD 01 Hex (1100 1101 0000 0001 двоичное).
Соответствие битов и выходов представлено ниже:
Бит: 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1
Выход: 27 26 25 24 23 22 21 20 - - - - - - 29 28
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 0F 1
Адрес выхода ст. 00 2
Адрес выхода мл. 13 3
Кол-во выходов ст. 00 4
Кол-во выходов мл. 0A 5
Счетчик байт 02 6
Данные для установки (Выходы 27-20) CD 7
Данные для установки (Выходы 29-28) 01 8
Контрольная сумма -- 9
ОТВЕТ
Нормальный ответ возвращает адрес подчиненного, код функции, начальный адрес, и количество установленных выходов.
Это пример ответа на представленный выше запрос.
Ответ
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 0F 1
Адрес выхода ст. 00 2
Адрес выхода мл. 13 3
Кол-во выходов ст. 00 4
Кол-во выходов мл. 0A 5
Контрольная сумма --
*/
#if ModBusUseFunc15!=0
if(PaketRX[1]==0x0F)
{
//вычисление адреса записываемых бит
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества записываемых бит
unsigned short KolvoBit=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//если неправильный адрес и количество
if(((AdresBit+KolvoBit)>ModBusMaxOutBit) || (KolvoBit>ModBusMaxOutBitRX))
{//тады конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
//установка битов
unsigned char Bit=(AdresBit&7);//указатель бит в ModBusOutBit[]
AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[]
//цикл по битам
for(int i=0;i<KolvoBit;i++)
{
if(PaketRX[7+(i>>3)]&(1<<(i&7)))//если текущий бит PaketRX равен 1
{//устанавливаем бит в ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)|((unsigned char)(1<<Bit));
}
else
{//сбрасываем бит ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)&((unsigned char)(~(1<<Bit)));
}
//инкрементруем указатели
Bit++;if(Bit==8){Bit=0;AdresBit++;}
}
//вычисляем CRC пакета передачи и передаем
CRCmodbus=0xFFFF;
for(int i=0;i<6;i++)
{
ModBusPUT(PaketRX[i]);
CRCmodbus=CRCfunc(CRCmodbus,(PaketRX[i]));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
//Код функции 0x10 запись нескольких выходных/внутренних регистров.
/*16 (10 Hex) Preset Multiple Regs
ОПИСАНИЕ
Запись данных в последовательность регистров (ссылка 4Х).
При широковещательной передаче, функция устанавливает подобные регистры во всех подчиненных устройствах.
ЗАМЕЧАНИЕ
Функция может пересекаться с установленной защитой памяти.
ЗАПРОС
Запрос специфицирует регистры для записи. Регистры адресуются начиная с 0.
Данные для записи в регистры содержатся в поле данных запроса.
Контроллеры M84 и 484 используют 10-битовую величину, со старшими шестью битами установленными в 0.
Все остальные контроллеры используют 16 бит.
Ниже приведен пример запроса на установку двух регистров начиная с 40002 в 00 0A и 01 02 Hex,
в подчиненном устройстве 17:
Запрос
Имя поля Пример
(Hex)
Адрес подчиненного 11 0
Функция 10 1
Начальный адрес 00 2
Начальный адрес 01 3
Кол-во регистров ст. 00 4
Кол-во регистров мл. 02 5
Счетчик байт 04 6
Данные ст. 00 7
Данные мл. 0A 8
Данные ст. 01 9
Данные мл. 02 10
Контрольная сумма --
ОТВЕТ
Нормальный ответ содержит адрес подчиненного, код функции, начальный адрес, и количество регистров.
*/
#if ModBusUseFunc16!=0
if(PaketRX[1]==0x10)
{
//вычисление адреса записываемых слов
unsigned short b=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества записываемых слов
unsigned short c=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//если неправильный адрес и количество
if(((b+c)>ModBusMaxOutReg) || c>ModBusMaxOutRegRX || c==0)
{//тады конец
CRCmodbus=0xFFFF;//установить начальное значение CRC
return;//Ошибка, повторный запрос не требуется
}
//Копирование из пакета в регистр ModBusOutReg[]
for(int i=0;i<c;i++)
{
ModBusOutReg[b+i]=(((unsigned short)PaketRX[7+(i<<1)])<<8)|(PaketRX[8+(i<<1)]);
}
//вычисляем CRC пакета передачи и передаем
CRCmodbus=0xFFFF;
for(int i=0;i<6;i++)
{
ModBusPUT(PaketRX[i]);
CRCmodbus=CRCfunc(CRCmodbus,(PaketRX[i]));
}
ModBusPUT(CRCmodbus);
ModBusPUT(CRCmodbus>>8);
ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;//повторный запрос не требуется
}
#endif
/////////////////////////////////////////////////////////////////////////////
//полный конец
CRCmodbus=0xFFFF; //установить начальное значение CRC
return;////Ошибка, нераспознана команда, повторный запрос не требуется
}
return;//повторный запрос не требуется
}
//Функция конвертация шеснадцатиричных символов в число
static inline unsigned char Hex2Dig(unsigned char h)
{
if((h>='0')&&(h<='9')) return (h -'0');
if((h>='A')&&(h<='F')) return (h -'A'+10);
return 0;
}
static unsigned char LRCmodbus;//тукущий LRC
static unsigned char Simvol0;//предидущий принятвй символ
#define ASCII_CR (0x0D)//возврат каретки
#define ASCII_LF (0x0A)//перевод строки
static const unsigned char BCD[]="0123456789ABCDEF";//строка для конвертации числа в символ
//Функция обработки Сообщений модбас ASCII
void ModBusASCII(void)
{
if(Sost==0)
{//Состояние прием
while(!0)
{//Цикл приема символов
unsigned short Tmp=ModBusGET(); //читаем символ из входного потока
if(Tmp==0) return; //если нет данных повторный запрос не требуется
//Символ принят
Tmp=Tmp&0xFF;//отбрасываем признак приема байта
//проверка на начало пакета
if(Tmp==':')
{//начало пакета
LRCmodbus=0;//обнуляем LRC
UkPaket=0;//указатель в массиве, текущий принятый символ
continue;//запускаем повторный запрос символа
}
//проверка на алфавит сообщения
if(!(
((Tmp>='0')&&(Tmp<='9'))||
((Tmp>='A')&&(Tmp<='F'))||
(Tmp==ASCII_CR)||
(Tmp==ASCII_LF)
))
{
return;//Ошибка, повторный запрос не требуется
}
//сохраняем принятый символ
if((UkPaket&1)==0)
{//указатель принятых данных четный 0,2,4,6...
Simvol0=Tmp; //сохраняем первый символ пакета
UkPaket++; //икреметируем указатель пакета
continue;//запускаем повторный запрос
}
else
{//указатель принятых данных нечетный 1,3,5,7...
if(Tmp!=ASCII_LF)
{//не достигнут конец
PaketRX[UkPaket>>1]=(Hex2Dig(Simvol0)<<4)|(Hex2Dig(Tmp));//сохраняем байт пакета
LRCmodbus=LRCmodbus-PaketRX[UkPaket>>1];//считаем LRC
UkPaket++;//икреметируем указатель пакета
if(UkPaket>(ModBusMaxPaketRX<<1))//проверка на переполнение
{//Буфер приема переполнился
UkPaket=0;//сбросить указатель пакета
return;//ошибка, повторный запрос не требуется
}
}
else break;
}
}
//Проверка LCR
if(LRCmodbus!=0) return;//Ошибка, повторный запрос не требуется
//Провекка адреса
if((PaketRX[0]!=ModBusID)&&(PaketRX[0]!=ModBusID_FF))
{//Не наш адрес
return;//повторный запрос не требуется
}
//преходим в состояние передача
Sost=!0;
TimModbus=ModBusSysTimer;//запомнить таймер
#if ModBusMaxPauseResp!=0
return;//повторный запрос не требуется
#endif
}
/////////////////////////////////////////////////////////////////////////////
if(Sost!=0
#if ModBusMaxPauseResp!=0
&& (ModBusSysTimer-TimModbus)>=ModBusMaxPauseResp
#endif
)
{//Состояние передача ответа
Sost=0;
/////////////////////////////////////////////////////////////////////////////
// обработка команд //
/////////////////////////////////////////////////////////////////////////////
#if ModBusUseFunc1!=0
//01 Чтение статуса выходов
if(PaketRX[1]==0x01)
{
//вычисление адреса запрашиваемых бит
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества запрашиваемых бит
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if((AdresBit+KolvoBit)>(ModBusMaxOutBit) || KolvoBit>ModBusMaxOutBitTX || KolvoBit==0)
{//конец
return;//Ошибка, повторный запрос не требуется
}
Prg2ModBusOutBit();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
ModBusPUT(':');
//адрес
ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший
LRCmodbus=0-PaketRX[0];//считаем LRC
//код команды
ModBusPUT(BCD[1>>4]);//Передаем старший
ModBusPUT(BCD[1&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-1;//считаем LRC
//количества полных байт
ModBusPUT(BCD[((KolvoBit+7)>>3)>>4]);//Передаем старший
ModBusPUT(BCD[((KolvoBit+7)>>3)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-((KolvoBit+7)>>3);//считаем LRC
//копирование битов в пакет ответа
unsigned char TxByte=0;//текущий байт
unsigned char Bit=AdresBit&7;//указатель бит в ModBusOutBit[]
AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[]
//копирование из регистра ModBusOutBit[] в пакет
int i=0;
while(!0)
{
if((ModBusOutBit[AdresBit].byte)&(1<<Bit))//если текущий бит ModBusOutBit[] равен 1
{//устанавливаем бит в пакете
TxByte=TxByte|(1<<(i&7));
}
//инкрементруем указатели
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(BCD[TxByte>>4]);//Передаем старший
ModBusPUT(BCD[TxByte&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-TxByte;//считаем LRC
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(BCD[TxByte>>4]);//Передаем старший
ModBusPUT(BCD[TxByte&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-TxByte;//считаем LRC
break;
}
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc2!=0
//02 Read Input Status
if(PaketRX[1]==0x02)
{
//вычисление адреса запрашиваемых бит
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества запрашиваемых бит
unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if((AdresBit+KolvoBit)>(ModBusMaxInBit) || KolvoBit>ModBusMaxInBitTX || KolvoBit==0)
{//конец
return;//Ошибка, повторный запрос не требуется
}
Prg2ModBusInBit();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
ModBusPUT(':');
//адрес
ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший
LRCmodbus=0-PaketRX[0];//считаем LRC
//код команды
ModBusPUT(BCD[2>>4]);//Передаем старший
ModBusPUT(BCD[2&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-2;//считаем LRC
//количества полных байт
ModBusPUT(BCD[((KolvoBit+7)>>3)>>4]);//Передаем старший
ModBusPUT(BCD[((KolvoBit+7)>>3)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-((KolvoBit+7)>>3);//считаем LRC
//копирование битов в пакет ответа
unsigned char TxByte=0;//текущий байт
unsigned char Bit=AdresBit&7;//указатель бит в ModBusOutBit[]
AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[]
//копирование из регистра ModBusOutBit[] в пакет
int i=0;
while(!0)
{
if((ModBusInBit[AdresBit].byte)&(1<<Bit))//если текущий бит ModBusOutBit[] равен 1
{//устанавливаем бит в пакете
TxByte=TxByte|(1<<(i&7));
}
//инкрементруем указатели
Bit++;
if(Bit==8){Bit=0;AdresBit++;}
i++;
if((i&7)==0)
{
ModBusPUT(BCD[TxByte>>4]);//Передаем старший
ModBusPUT(BCD[TxByte&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-TxByte;//считаем LRC
TxByte=0;
if(i==KolvoBit) break; else continue;
}
if(i==KolvoBit)
{
ModBusPUT(BCD[TxByte>>4]);//Передаем старший
ModBusPUT(BCD[TxByte&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-TxByte;//считаем LRC
break;
}
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc3!=0
//03 Read Holding Registers
if(PaketRX[1]==0x03)
{
//вычисление адреса запрашиваемых слов
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление адреса количества запрашиваемых слов
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || KolvoWord>ModBusMaxOutRegTX)
{//тады конец
return;//Ошибка, повторный запрос не требуется
}
Prg2ModBusOutReg();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
ModBusPUT(':');
//адрес
ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший
LRCmodbus=0-PaketRX[0];//считаем LRC
//код команды
ModBusPUT(BCD[3>>4]);//Передаем старший
ModBusPUT(BCD[3&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-3;//считаем LRC
//количества полных байт
ModBusPUT(BCD[(KolvoWord<<1)>>4]);//Передаем старший
ModBusPUT(BCD[(KolvoWord<<1)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-(KolvoWord<<1);//считаем LRC
//Копирование из регистра ModBusOutReg[] в пакет ответа
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(BCD[((ModBusOutReg[AdresWord+i])>>8)>>4]);//Передаем старший
ModBusPUT(BCD[((ModBusOutReg[AdresWord+i])>>8)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-((ModBusOutReg[AdresWord+i])>>8);//считаем LRC
ModBusPUT(BCD[(((ModBusOutReg[AdresWord+i])>>0)>>4)&0x0F]);//Передаем старший
ModBusPUT(BCD[(((ModBusOutReg[AdresWord+i])>>0)>>0)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-((ModBusOutReg[AdresWord+i])>>0);//считаем LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc4!=0
//04 Read Input Registers
if(PaketRX[1]==0x04)
{
//вычисление адреса запрашиваемых слов
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление адреса количества запрашиваемых слов
unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]));
//если неправильный адрес и количество
if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || KolvoWord>ModBusMaxOutRegTX)
{//тады конец
return;//Ошибка, повторный запрос не требуется
}
Prg2ModBusInReg();//Заполнение регистров Модбас (GlobalDate->ModBus)
//формирование пакета ответа
ModBusPUT(':');
//адрес
ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший
LRCmodbus=0-PaketRX[0];//считаем LRC
//код команды
ModBusPUT(BCD[4>>4]);//Передаем старший
ModBusPUT(BCD[4&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-4;//считаем LRC
//количества полных байт
ModBusPUT(BCD[(KolvoWord<<1)>>4]);//Передаем старший
ModBusPUT(BCD[(KolvoWord<<1)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-(KolvoWord<<1);//считаем LRC
//Копирование из регистра ModBusOutReg[] в пакет ответа
for(int i=0;i<KolvoWord;i++)
{
ModBusPUT(BCD[((ModBusInReg[AdresWord+i])>>8)>>4]);//Передаем старший
ModBusPUT(BCD[((ModBusInReg[AdresWord+i])>>8)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-((ModBusInReg[AdresWord+i])>>8);//считаем LRC
ModBusPUT(BCD[(((ModBusInReg[AdresWord+i])>>0)>>4)&0x0F]);//Передаем старший
ModBusPUT(BCD[(((ModBusInReg[AdresWord+i])>>0)>>0)&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-((ModBusInReg[AdresWord+i])>>0);//считаем LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc5!=0
//05 Force Single Coil
if(PaketRX[1]==0x05)
{
//вычисление адреса записываемого выхода
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//проверка на допустимый адрес
if(AdresBit>=ModBusMaxOutBit)//если неправильный адрес
{//тады конец
return;//Ошибка, повторный запрос не требуется
}
//установка сброс бита
switch (((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])))
{
case 0xFF00:
//установка бита
ModBusOutBit[(AdresBit>>3)].byte|=(1<<(AdresBit&7));
break;
case 0x0000:
//сброс бита
ModBusOutBit[(AdresBit>>3)].byte&=(~(1<<(AdresBit&7)));
break;
default:
{ //конец
return;//Ошибка, повторный запрос не требуется
}
}
//Ответ
ModBusPUT(':');
for(int i=0;i<7;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший
}
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc6!=0
//06 Preset Single Register
if(PaketRX[1]==0x06)
{
//вычисление адреса записываемого выхода
unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//проверка на допустимый адрес
if(AdresWord>=(ModBusMaxOutReg))//если неправильный адрес
{//тады конец
return;//Ошибка, повторный запрос не требуется
}
//запись слова
ModBusOutReg[AdresWord]=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//Ответ
ModBusPUT(':');
for(int i=0;i<7;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший
}
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc15!=0
//15 (0F Hex) Force Multiple Coils
if(PaketRX[1]==0x0F)
{
//вычисление адреса записываемых бит
unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества записываемых бит
unsigned short KolvoBit=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//если неправильный адрес и количество
if(((AdresBit+KolvoBit)>ModBusMaxOutBit) || (KolvoBit>ModBusMaxOutBitRX))
{//тады конец
return;//Ошибка, повторный запрос не требуется
}
//установка битов
unsigned char Bit=(AdresBit&7);//указатель бит в ModBusOutBit[]
AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[]
//цикл по битам
for(int i=0;i<KolvoBit;i++)
{
if(PaketRX[7+(i>>3)]&(1<<(i&7)))//если текущий бит PaketRX равен 1
{//устанавливаем бит в ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)|((unsigned char)(1<<Bit));
}
else
{//сбрасываем бит ModBusOutBit[]
ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)&((unsigned char)(~(1<<Bit)));
}
//инкрементруем указатели
Bit++;if(Bit==8){Bit=0;AdresBit++;}
}
//вычисляем LRC пакета передачи и передаем
LRCmodbus=0;
ModBusPUT(':');
for(int i=0;i<6;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-PaketRX[i];//считаем LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
return;//повторный запрос не требуется
}
#endif
#if ModBusUseFunc16!=0
//16 (10 Hex) Preset Multiple Regs
if(PaketRX[1]==0x10)
{
//вычисление адреса записываемых слов
unsigned short b=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3])));
//вычисление количества записываемых слов
unsigned short c=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])));
//если неправильный адрес и количество
if(((b+c)>ModBusMaxOutReg) || c>ModBusMaxOutRegRX)
{
//тады конец
return;//Ошибка, повторный запрос не требуется
}
//Копирование из пакета в регистр ModBusOutReg[]
for(int i=0;i<c;i++)
{
ModBusOutReg[b+i]=(((unsigned short)PaketRX[7+(i<<1)])<<8)|(PaketRX[8+(i<<1)]);
}
//вычисляем LRC пакета передачи и передаем
LRCmodbus=0;
ModBusPUT(':');
for(int i=0;i<6;i++)
{
ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший
ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший
LRCmodbus=LRCmodbus-PaketRX[i];//считаем LRC
}
ModBusPUT(BCD[LRCmodbus>>4]);
ModBusPUT(BCD[LRCmodbus&0x0F]);
ModBusPUT(ASCII_CR);
ModBusPUT(ASCII_LF);
ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate)
//конец
return;//повторный запрос не требуется
}
#endif
}
//конец
return;//Ошибка, нераспознана команда, повторный запрос не требуется
}
ModBus2Prg.c
SPL
#define __MODBUS2PRG_C
#include "modbus.h"
//Заполнение регистров Модбас
//перенос данных из программных переменных в регистры МодБас
void Prg2ModBusOutBit(void)
{//заполнение регистров дискретных выходов
return;
}
void Prg2ModBusInBit(void)
{//заполнение регистров дискретных входов
//ModBusInBit[0].bit0=1;
return;
}
void Prg2ModBusOutReg(void)
{//заполнение регистров 4Х регистры для чтения/записи
return;
}
void Prg2ModBusInReg(void)
{//заполнение регистов 3Х регистры для чтения
return;
}
//Считывание регистров Модбас
//Перенос данных из регистров МодБас в программные переменные
void ModBus2PrgOutBit(void)
{//чтение регистров дискретных выходов
return;
}
void ModBus2PrgOutReg(void)
{//чтение регистров 4Х регистры для чтения/записи
return;
}
В файле modbus.h содержатся требуемые объявления, опции компиляции и настроечные константы. Кратко опишем основные опции и настроечные параметры.
ModBusUseFunc1 — ModBusUseFunc15 – опция компиляции, определяющая использование функций протокола ModBus. Практические реализации устройств ModBus работают с ограниченным набором функций протокола, наиболее часто, функции 3,6 и 16. Нет необходимости включать в проект лишний код.
ModBusID, ModBusID_FF – Адреса на шине ModBus. Данный реализация протокола поддерживает два адреса. Это может быть удобно для ввода в эксплуатацию устройств, адрес ModBusID является настраиваемым адресом устройства, а адрес ModBusID_FF адресом для индивидуальной настройки устройства.
ModBusMaxPause – Пауза между символами, для определения начала пакета, задается в квантах ModBusSysTimer. Как правило квант ModBusSysTimer равен 1мС. Для большинства приложений соблюдение таймаутов описанных в стандарте протокола просто невозможно. Например, ModBus Master работающий на Win-машине никогда не сможет обеспечить требуемые протоколом таймауты. Поэтому задавать квант времени менее 1мС можно считать нецелесообразным. Практические наблюдения показывают, что величина ModBusMaxPause должна быть порядка 5-10мС.
ModBusMaxPauseResp — Пауза между запросом Master и ответом Slave. Многие ModBus Master устройства имеют задержку переключения с передачи на прием, эту задержку можно скомпенсировать этой константой.
ModBusMaxInBit, ModBusMaxOutBit, ModBusMaxInReg, ModBusMaxOutReg — Количество дискретных входов, выходов, регистров для чтения, регистров для чтения/записи. В программе резервируется память под регистры ModBus. Если определенный тип регистров не используется значение необходимо указать равное нулю.
ModBusMaxInBitTX, ModBusMaxOutBitTX, ModBusMaxInRegTX, ModBusMaxOutRegTX — Максимальное количество дискретных входов, выходов, регистров для чтения, регистров для чтения/записи выходных регистров в передаваемом пакете. Эта настройка должна совпадать с соответствующей настройкой ModBus Master.
Для портирования библиотеки на любую платформу необходимо указать через макросы три функций.
ModBusSysTimer — Системный таймер, переменная, инкрементирующаяся каждую миллисекунду в отдельном потоке выполнения. В качестве этой переменной может выступать uwTick, из библиотеки HAL STM32, или стандартная функция языка Си clock().
void ModBusPUT(unsigned char A) — Запись байта в последовательный поток.
unsigned short ModBusGET(void) — Чтение байта из последовательного потока. Если в последовательном потоке нет данных, то функция возвращает 0, если данные есть, то возвращаемое значение — старший байт 0х01, младший байт — прочитанные данные.
Для использования библиотеки необходимо заполнить тело функций Prg2ModBusOutBit(), Prg2ModBusInBit(), Prg2ModBusOutReg(), Prg2ModBusInReg(), отвечающие за копирование переменных пользователя в регистры ModBus. Так же, необходимо заполнить тело функций ModBus2PrgOutBit(), ModBus2PrgOutReg(), отвечающие за копирование регистров ModBus в переменные пользователя. В теле этих функций можно выполнить некоторые действия связанные с изменением регистров, например, осуществить проверку на допустимые значения.
Например:
void Prg2ModBusOutReg(void)
{//заполнение регистров, 4Х регистры для чтения/записи
ModBusOutReg[0]=A;
ModBusOutReg[1]=B;
ModBusOutReg[2]=C;
return;
}
void ModBus2PrgOutReg(void)
{ //чтение регистров 4Х, регистры для чтения/записи
if(ModBusOutReg[0] < MaxA) A= ModBusOutReg[0];
B=ModBusOutReg[1];
C=ModBusOutReg[2];
return;
}
Допускается не заполнять тело указанных функций, а работать с регистрами напрямую, при этом надо использовать опцию ModBusUseGlobal.
Для инициализации ModBus устройства необходимо вызвать функцию ModBusIni(). Функции ModBusRTU() или ModBusASCII() обеспечивающей работу устройства по протоколам RTU и ASCII соответственно. Их необходимо вызывать в главном цикле программы:
ModBusIni();
while(!0)
{
if(ModBusTip==RTU) ModBusRTU(); else ModBusASCII();
}
Не стоит забывать, что перед инициализацией и вызовом функции, обеспечивающей работу ModBus устройства, необходимо позаботится об инициализации последовательного потока (UART). Программные решения, связанные с организацией поточного ввода/вывода, зависят от аппаратной платформы, и их рассмотрение выходит за рамки данной статьи.
Данная библиотека была протестирована с OPC сервером Kepware, панелями SIMATIC и Wientek, также другими ModBus Masterами, во множестве устройств на микроконтроллерах семейства PIC и STM32, и показала свою 142% работоспособность. Простота портирования данной библиотеки позволит легко адаптировать ее под другие типы 8-16-32разрядных микроконтроллеров.
===========
Источник:
habr.com
===========
Похожие новости:
- [Интерфейсы, IT-стандарты, Стандарты связи, Разработка для интернета вещей, Интернет вещей] Что такое СПОДЭС и причём здесь прибор учёта электрической энергии
- [Разработка робототехники, Робототехника] Disney показала робота, который смотрит и реагирует подобно человеку
- [Информационная безопасность, IT-стандарты, Промышленное программирование, SCADA] Assurance Case: аргументированное обоснование безопасности
- [Разработка робототехники, Учебный процесс в IT, Робототехника, Электроника для начинающих] Как мы готовим победителей олимпиад по молодежной робототехнике в Университете ИТМО
- [Беспроводные технологии, Разработка систем связи, Стандарты связи, Разработка для интернета вещей, Интернет вещей] Интернет вещей по-русски. Минимализм и открытость OpenUNB
- [Разработка робототехники, Робототехника] Роботы-шары FreeBOT научились создавать структуры разной формы
- [Программирование микроконтроллеров, Схемотехника, Производство и разработка электроники, DIY или Сделай сам, Электроника для начинающих] STM32 DoomBoy SDRAM ILI9341
- [Производство и разработка электроники, DIY или Сделай сам] Создание устройства качания детских кроваток с дугообразным полозом
- [Разработка робототехники, Учебный процесс в IT, Робототехника, Электроника для начинающих] Робототехнические лаборатории, фаблаб и победы молодежных команд на профильных олимпиадах — дайджест Университета ИТМО
- [Программирование, Разработка мобильных приложений, Программирование микроконтроллеров, Умный дом, DIY или Сделай сам] Свой путь в мире, где есть готовое решение для всего
Теги для поиска: #_promyshlennoe_programmirovanie (Промышленное программирование), #_razrabotka_robototehniki (Разработка робототехники), #_programmirovanie_mikrokontrollerov (Программирование микроконтроллеров), #_razrabotka_dlja_interneta_veschej (Разработка для интернета вещей), #_proizvodstvo_i_razrabotka_elektroniki (Производство и разработка электроники), #_modbus, #_promyshlennoe_programmirovanie (
Промышленное программирование
), #_razrabotka_robototehniki (
Разработка робототехники
), #_programmirovanie_mikrokontrollerov (
Программирование микроконтроллеров
), #_razrabotka_dlja_interneta_veschej (
Разработка для интернета вещей
), #_proizvodstvo_i_razrabotka_elektroniki (
Производство и разработка электроники
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:01
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
В открытом доступе присутствует множество библиотек для реализации ModBus Slave устройства, но они зачастую, содержат избыточный функционал, сложны в освоении и содержат грубые ошибки. В данной статье рассматривается библиотека, по скромному мнению автора, лишенная этих недостатков. Программное обеспечение библиотеки поставляется в виде открытого исходного кода на языке Си. modbus.hSPL////////////////////////////////////////////////////////////////////
// Асинхронная обработка сообщение ModBus v2 // // Автор - Iван // /////////////////////////////////////////////////////////////////// #ifndef __MODBUS_H #define __MODBUS_H #include "main.h" /////////////////////////////////////////////////////////////////////////////// //Настройки МодБас //Данные, регистры Модбас #define ModBusUseGlobal (0) //Использовать глобальные входы/выходы, входные/выходные регистры //Функции протокола Модбас #define ModBusUseFunc1 (0) //Использовать функцию 1 - чтение статуса Coils (дискретных выходных битов) #define ModBusUseFunc2 (0) //Использовать функцию 2 - чтение статуса дискретных входов #define ModBusUseFunc3 (1) //Использовать функцию 3 - чтение значения выходных регистров #define ModBusUseFunc4 (0) //Использовать функцию 4 - чтение значения входных регистров #define ModBusUseFunc5 (0) //Использовать функцию 5 - запись выходного бита #define ModBusUseFunc6 (1) //Использовать функцию 6 - запись выходного регистра #define ModBusUseFunc15 (0) //Использовать функцию 15 - запись нескольких выходных битов #define ModBusUseFunc16 (1) //Использовать функцию 16 - запись нескольких выходных регистров //Адрес устройства #define ModBusID (1) //Адрес на шине МодБас #define ModBusID_FF (255) //Адрес на шине МодБас, на который отвечает всегда //Таймауты #define ModBusMaxPause (5)//Пауза между символами, для определения начала пакета [mS], #define ModBusMaxPauseResp (2) //Пауза между запросом Мастера и ответом Слайва [mS] //Длинна пакетов #define ModBusMaxPaketRX (96)//Максимальный размер принимаемого пакета <127 //Дискретные входы #define ModBusMaxInBit (0) //Количество дискретных входов #define ModBusMaxInBitTX (8) //Максимальное количество дискретных входов при передаче пакета #define ModBusMaxInByte ((ModBusMaxInBit+7)/8) //Количество дискретных входов в байтах //Дискретные выходы #define ModBusMaxOutBit (0) //Количество дискретных выходов #define ModBusMaxOutByte ((ModBusMaxOutBit+7)/8) //Количество дискретных выходов в байтах #define ModBusMaxOutBitTX (8) //Максимальное количество дискретных выходов в передаваемом пакете #define ModBusMaxOutBitRX (8) //Максимальное количество дискретных выходов доступное для груповой установки //Регистры доступные для чтения #define ModBusMaxInReg (0) //Количество входных регистров (регистры только для чтения) #define ModBusMaxInRegTX (24) //Максимальное количество входных регистров в передаваемом пакете //Регистры доступные для чтения-записи #define ModBusMaxOutReg (48) //Количество выходных регистров #define ModBusMaxOutRegTX (32)//Максимальное количество выходных регистров в передаваемом пакете #define ModBusMaxOutRegRX (32)//Максимальное количество выходных регистров доступное для груповой установки //////////////////////////////////////////////////////////////////////////////// //Опорные функции, связь с системой //Системный таймер, инкрементируется каждую миллисекунду #define ModBusSysTimer TimingDelay //Запись байта в поток последовательного порта - void ModBusPUT(unsigned char A) #define ModBusPUT(A) PutFifo0(A) //Чтение байта из потока последовательного порта, - unsigned short ModBusGET(void) //Если нет данных возвращает 0х0000, иначе возвращает 0х01ХХ #define ModBusGET() Inkey16Fifo0() //////////////////////////////////////////////////////////////////////////////// //Инициализация void ModBusIni(void); //Функция обработки Сообщений модбас RTU //Работает совместно с системным таймером //Использует макросы ModbusPUT(A) ModbusGET() void ModBusRTU(void); //Функция обработки Сообщений модбас ASCII //Работает совместно с системным таймером //Использует макросы ModbusPUT(A) ModbusGET() void ModBusASCII(void); //Заполнение регистров Модбас //перенос данных из программных переменных в регистры МодБас void Prg2ModBusOutBit(void); void Prg2ModBusInBit(void); void Prg2ModBusOutReg(void); void Prg2ModBusInReg(void); //Считывание регистров Модбас //перенос данных из регистров МодБас в программные переменные void ModBus2PrgOutBit(void); void ModBus2PrgOutReg(void); #pragma pack(push,1) //Тип данных для работы с дискретными входами/выходами typedef union { unsigned char byte; struct { unsigned char bit0:1; unsigned char bit1:1; unsigned char bit2:1; unsigned char bit3:1; unsigned char bit4:1; unsigned char bit5:1; unsigned char bit6:1; unsigned char bit7:1; }; } ModBusBit_t; #pragma pack(pop) #ifdef __MODBUS2PRG_C #if ModBusMaxInBit!=0 ModBusBit_t ModBusInBit[ModBusMaxInByte]; //массив дискретных входов #endif #if ModBusMaxOutBit!=0 ModBusBit_t ModBusOutBit[ModBusMaxOutByte]; //массив дискретных выходов #endif #if ModBusMaxInReg!=0 unsigned short ModBusInReg[ModBusMaxInReg]; //массив входных регистров #endif #if ModBusMaxOutReg!=0 unsigned short ModBusOutReg[ModBusMaxOutReg]; //массив выходных регистров #endif #else #if ModBusUseGlobal!=0 || defined(__MODBUS_C) #if ModBusMaxInBit!=0 extern ModBusBit_t ModBusInBit[ModBusMaxInByte]; //массив дискретных входов #endif #if ModBusMaxOutBit!=0 extern ModBusBit_t ModBusOutBit[ModBusMaxOutByte]; //массив дискретных выходов #endif #if ModBusMaxInReg!=0 extern unsigned short ModBusInReg[ModBusMaxInReg]; //массив входных регистров #endif #if ModBusMaxOutReg!=0 extern unsigned short ModBusOutReg[ModBusMaxOutReg]; //массив выходных регистров #endif #endif//#if ModBusUseGlobal!=0 #endif//#ifdef __MODBUS2PRG_C #endif//#ifndef __MODBUS_H modbus.cSPL#define __MODBUS_C
#include "modbus.h" static unsigned char PaketRX[ModBusMaxPaketRX];//массив для сохранения пакета static unsigned char UkPaket;//указатель в массиве, текущий принятый символ static unsigned long TimModbus; //время приема пакета по системному таймеру static unsigned short CRCmodbus;//текущий CRC static unsigned char Sost;//состояние 0/1 прием/передача //Инициализация void ModBusIni(void) { TimModbus=ModBusSysTimer;//запомнить таймер UkPaket=0;//сбросить указатель пакета CRCmodbus=0xFFFF; //установить начальное значение CRC //Инициализация регистров МодБас #if ModBusMaxOutBit!=0 Prg2ModBusOutBit(); #endif #if ModBusMaxInBit!=0 Prg2ModBusInBit(); #endif #if ModBusMaxOutReg!=0 Prg2ModBusOutReg(); #endif #if ModBusMaxInReg!=0 Prg2ModBusInReg(); #endif return; } //Функция вычисления CRC static inline unsigned short CRCfunc(unsigned short inCRC, unsigned char in) { inCRC=inCRC^in; for(int j=0;j<8;j++){if(inCRC&1){inCRC=(inCRC>>1)^0xA001U;}else {inCRC=inCRC>>1;}} return inCRC; } //Функция обработки Сообщений модбас void ModBusRTU(void) { if(Sost==0) {//Состояние прием while(!0) {//Цикл приема символов unsigned short Tmp=ModBusGET(); //читаем символ из входного потока if(Tmp==0) return; //если нет данных - возврат //символ принят Tmp=Tmp&0xFF;//отбрасываем признак приема байта //Проверка временного интервала между символами if((ModBusSysTimer-TimModbus)>ModBusMaxPause) {//превышен таймаут, начинаем прием нового пакета PaketRX[0]=Tmp;//сохранить принятый символ в буфер приема UkPaket=1;//установить указатель пакета TimModbus=ModBusSysTimer;//сбросить таймер //вычисление CRC CRCmodbus=CRCfunc(0xFFFF,Tmp); continue;//повторный запрос символа } else {//таймаут не превышен, принимаем уже начатый пакет TimModbus=ModBusSysTimer;//сбросить таймер PaketRX[UkPaket]=Tmp;//сохранить принятый символ UkPaket++;//инкремент указателя пакета if(UkPaket==ModBusMaxPaketRX)//проверяем на длину пакета {//буфер пакета переполнился UkPaket=0;//сбросить указатель пакета CRCmodbus=0xFFFF; //установить начальное значение CRC return;//ошибка, повторный запрос символа требуется } //вычисление CRC CRCmodbus=CRCfunc(CRCmodbus,Tmp); } //Если принято мало данных if(UkPaket<8) continue; //повторный запрос символа //проверка на принятие пакета if(CRCmodbus==0) {//проверка на длинные пакеты if(PaketRX[1]==15 || PaketRX[1]==16) {//если длинные команды (15,16) , проверяем "Счетчик байт" if((PaketRX[6]+9)!=UkPaket) continue; } break; //Ура! Пакет принят!!! } } ////////////////////////////////////////////////////////////////////////////// // Ура! Пакет принят!!! ///////////////////////////////////////////////////////////////////////////// UkPaket=0;//сбросить указатель пакета //проверка адреса if((PaketRX[0]!=ModBusID)&&(PaketRX[0]!=ModBusID_FF)) {//Не наш адрес CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } //переходим в состояние передача ответа Sost=!0; #if ModBusMaxPauseResp!=0 return;//повторный запрос не требуется #endif } ///////////////////////////////////////////////////////////////////////////// if(Sost!=0 #if ModBusMaxPauseResp!=0 && (ModBusSysTimer-TimModbus)>=ModBusMaxPauseResp #endif ) {//Состояние передача ответа Sost=0; ///////////////////////////////////////////////////////////////////////////// // обработка команд // ///////////////////////////////////////////////////////////////////////////// //Код функции 01 - чтение статуса Coils (дискретных выходных битов). /*Сообщение-запрос содержит адрес начального бита и количество битов для чтения. Биты нумеруются начиная с 0. В сообщении-ответе каждое значение переменной передается одним битом, то есть в одном байте пакуется статус 8 битов переменных. Если количество их не кратно восьми, остальные биты в байте заполняются нулями. Счетчик вмещает количество байт в поле данных. 01 Чтение статуса выходов ОПИСАНИЕ Читает статуса ON/OFF дискретных выходов в подчиненном. ЗАПРОС Запрос содержит адрес начального выхода и количество выходов для чтения. Выхода адресуются начиная с нуля: выхода 1-16 адресуются как 0-15. Ниже приведен пример запроса на чтение выходов 20-56 с подчиненного устройства 17. Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 01 1 Начальный адрес Hi 00 2 Начальный адрес Lo 13 3 Количество Hi 00 4 Количество Lo 25 5 Контрольная сумма (CRC или LRC) -- ОТВЕТ Статус выходов в ответном сообщении передается как один выход на бит. Если возвращаемое количество выходов не кратно восьми, то оставшиеся биты в последнем байте сообщения будут установлены в 0. Счетчик байт содержит количество байт передаваемых в поле данных. Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 01 1 Счетчик байт 05 2 Данные(Выхода 27-20) CD 3 Данные(Выхода 35-28) 6B 4 Данные(Выхода 43-36) B2 5 Данные(Выхода 51-44) 0E 6 Данные(Выхода 56-52) 1B 7 Контрольная сумма (CRC или LRC) -- */ #if ModBusUseFunc1!=0 if(PaketRX[1]==0x01) { //вычисление адреса запрашиваемых бит unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества запрашиваемых бит unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if((AdresBit+KolvoBit)>(ModBusMaxOutBit) || KolvoBit>ModBusMaxOutBitTX || KolvoBit==0) {//неправильный адрес и количество CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } Prg2ModBusOutBit();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа //адрес ModBusPUT(PaketRX[0]); CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]); //код команды ModBusPUT(1); CRCmodbus=CRCfunc(CRCmodbus,1); //количества полных байт ModBusPUT((KolvoBit+7)>>3); CRCmodbus=CRCfunc(CRCmodbus,((KolvoBit+7)>>3)); //копирование битов в пакет ответа unsigned char TxByte=0;//текущий байт unsigned char Bit=AdresBit&7;//указатель бит в ModBusOutBit[] AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[] //копирование из регистра ModBusOutBit[] в пакет int i=0; while(!0) { if((ModBusOutBit[AdresBit].byte)&(1<<Bit)) { TxByte=TxByte|(1<<(i&7)); } //инкрементруем указатели Bit++; if(Bit==8){Bit=0;AdresBit++;} i++; if((i&7)==0) { ModBusPUT(TxByte); CRCmodbus=CRCfunc(CRCmodbus,TxByte); TxByte=0; if(i==KolvoBit) break; else continue; } if(i==KolvoBit) { ModBusPUT(TxByte); CRCmodbus=CRCfunc(CRCmodbus,TxByte); break; } } ModBusPUT(CRCmodbus); ModBusPUT(CRCmodbus>>8); //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //Код функции 2 - чтение статуса дискретных входов /*02 Read Input Status ОПИСАНИЕ Чтение ON/OFF состояния дискретных входов (ссылка 1Х) в пдчиненном. ЗАПРОС Запрос содержит номер начального входа и количество входов для чтения. Входа адресуются начиная с 0. Ниже приведен пример запроса на чтение входов 10197-10218 с подчиненного устройства 17. Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 02 1 Начальный адрес ст. 00 2 Начальный адрес мл. C4 3 Кол-во входов ст. 00 4 Кол-во входов мл. 16 5 Контрольная сумма -- ОТВЕТ Статус входов в ответном сообщении передается как один выход на бит. Если возвращаемое количество входов не кратно восьми, то оставшиеся биты в последнем байте сообщения будут установлены в 0. Счетчик байт содержит количество байт передаваемых в поле данных. Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 01 1 Счетчик байт 03 2 Данные(Входы 10204-10197) AC 3 Данные(Входы 10212-10205) DB 4 Данные(Входы 10218-10213) 35 5 Контрольная сумма (CRC или LRC) -- */ #if ModBusUseFunc2!=0 if(PaketRX[1]==0x02) { //вычисление адреса запрашиваемых бит unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества запрашиваемых бит unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if((AdresBit+KolvoBit)>(ModBusMaxInBit) || KolvoBit>ModBusMaxInBitTX || KolvoBit==0) {//неправильный адрес и количество CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } Prg2ModBusInBit();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа //адрес ModBusPUT(PaketRX[0]); CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]); //код команды ModBusPUT(2); CRCmodbus=CRCfunc(CRCmodbus,2); //количества полных байт ModBusPUT((KolvoBit+7)>>3); CRCmodbus=CRCfunc(CRCmodbus,((KolvoBit+7)>>3)); //копирование битов в пакет ответа unsigned char TxByte=0;//текущий байт unsigned char Bit=AdresBit&7;//указатель бит AdresBit=AdresBit>>3;//указатель байт //копирование из регистра ModBusInBit[] в пакет int i=0; while(!0) { if((ModBusInBit[AdresBit].byte)&(1<<Bit)) {//устанавливаем бит в пакете TxByte=TxByte|(1<<(i&7)); } //инкрементруем указатели Bit++; if(Bit==8){Bit=0;AdresBit++;} i++; if((i&7)==0) { ModBusPUT(TxByte); CRCmodbus=CRCfunc(CRCmodbus,TxByte); TxByte=0; if(i==KolvoBit) break; else continue; } if(i==KolvoBit) { ModBusPUT(TxByte); CRCmodbus=CRCfunc(CRCmodbus,TxByte); break; } } ModBusPUT(CRCmodbus); ModBusPUT(CRCmodbus>>8); //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //Код функции 03 - чтение значения выходных/внутренних регистров. /*Сообщение-запрос содержит адрес начального исходного/внутреннего регистра (двухбайтовое слово), и количество регистров для чтения. Регистры нумеруются начиная с 0. 03 Read Holding Registers ОПИСАНИЕ Чтение двоичного содержания регистров (ссылка 4Х) в подчиненном. ЗАПРОС Сообщение запроса специфицирует начальный регистр и количество регистров для чтения. Регистры адресуются начина с 0: регистры 1-16 адресуются как 0-15. Ниже приведен пример чтения регистров 40108-40110 с подчиненного устройства 17. Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 03 1 Начальный адрес ст. 00 2 Начальный адрес мл. 6B 3 Кол-во регистров ст. 00 4 Кол-во регистров мл. 03 5 Контрольная сумма -- ОТВЕТ Данные регистров в ответе передаются как два бйта на регистр. Для каждого регистра, первый байт содержит старшие биты второй байт содержит младшие биты. За одно обращение может считываться 125 регистров для контроллеров 984-Х8Х (984-685 и т.д.), и 32 регистра для других контроллеров. Ответ дается когда все данные укомплектованы. Это пример ответа на запрос представленный выше: Ответ Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 03 1 Счетчик байт 06 2 Данные (регистр 40108) ст. 02 3 Данные (регистр 40108) мл. 2B 4 Данные (регистр 40109) ст. 00 5 Данные (регистр 40109) мл. 00 6 Данные (регистр 40110) ст. 00 7 Данные (регистр 40110) мл. 64 8 Контрольная сумма -- */ #if ModBusUseFunc3!=0 if(PaketRX[1]==0x03) { //вычисление адреса запрашиваемых слов unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление адреса количества запрашиваемых слов unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || (KolvoWord>ModBusMaxOutRegTX)) {//тады конец CRCmodbus=0xFFFF;//установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } Prg2ModBusOutReg();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа //адрес ModBusPUT(PaketRX[0]); CRCmodbus=CRCfunc(0xFFFF,PaketRX[0]); //код команды ModBusPUT(3); CRCmodbus=CRCfunc(CRCmodbus,3); //количества полных байт ModBusPUT(KolvoWord<<1); CRCmodbus=CRCfunc(CRCmodbus,(KolvoWord<<1)); //Копирование из регистра ModBusOutReg[] в пакет ответа for(int i=0;i<KolvoWord;i++) { ModBusPUT(ModBusOutReg[AdresWord+i]>>8); CRCmodbus=CRCfunc(CRCmodbus,(ModBusOutReg[AdresWord+i]>>8)); ModBusPUT(ModBusOutReg[AdresWord+i]>>0); CRCmodbus=CRCfunc(CRCmodbus,(ModBusOutReg[AdresWord+i]>>0)); } ModBusPUT(CRCmodbus); ModBusPUT(CRCmodbus>>8); //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //Код функции 04 - чтение значения входных регистров /*04 Read Input Registers СОДЕРЖАНИЕ Чтение двоичного содержания входных регистров (ссылка 3Х) в подчиненном. ЗАПРОС Запрос содержит номер начального регистра и количество регистров для чтения. Ниже приведен пример запроса для чтения регистра 30009 с подчиненного устройства 17. Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 03 1 Начальный адрес ст. 00 2 Начальный адрес мл. 6B 3 Кол-во регистров ст. 00 4 Кол-во регистров мл. 03 5 Контрольная сумма -- ОТВЕТ Данные регистров в ответе передаются как два бйта на регистр. Для каждого регистра, первый байт содержит старшие биты второй байт содержит младшие биты. За одно обращение может считываться 125 регистров для контроллеров 984-Х8Х (984-685 и т.д.), и 32 регистра для других контроллеров. Ответ дается когда все данные укомплектованы. Это пример ответа на запрос представленный выше: Ответ Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 03 1 Счетчик байт 02 2 Данные (регистр 30009) ст. 00 3 Данные (регистр 30009) мл. 2A 4 Контрольная сумма -- */ #if ModBusUseFunc4!=0 if(PaketRX[1]==0x04) { //вычисление адреса запрашиваемых слов unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление адреса количества запрашиваемых слов unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if(((AdresWord+KolvoWord)>ModBusMaxInReg) || (KolvoWord>ModBusMaxInRegTX)) {//тады конец CRCmodbus=0xFFFF;//установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } Prg2ModBusInReg();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа //адрес ModBusPUT(PaketRX[0]); CRCmodbus=CRCfunc(0xFFFF,(PaketRX[0])); //код команды ModBusPUT(4); CRCmodbus=CRCfunc(CRCmodbus,4); //количества полных байт ModBusPUT(KolvoWord<<1); CRCmodbus=CRCfunc(CRCmodbus,(KolvoWord<<1)); //Копирование из регистра ModBusInReg[] в пакет ответа for(int i=0;i<KolvoWord;i++) { ModBusPUT(ModBusInReg[AdresWord+i]>>8); CRCmodbus=CRCfunc(CRCmodbus,(ModBusInReg[AdresWord+i]>>8)); ModBusPUT(ModBusInReg[AdresWord+i]>>0); CRCmodbus=CRCfunc(CRCmodbus,(ModBusInReg[AdresWord+i]>>0)); } ModBusPUT(CRCmodbus); ModBusPUT(CRCmodbus>>8); //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //Код функции 05 - запись выходного/внутреннего бита /*05 Force Single Coil ОПИСАНИЕ Установка единичного выхода (ссылка 0Х) в ON или OFF. При широковещательной передаче функция устанавливает все выходы с данным адресом во всех подчиненных контроллерах. ЗАМЕЧАНИЕ Функция может пересекаться с установкой защиты памяти и установкой недоступности выходов. ЗАПРОС Запрос содержит номер выхода для установки. Выходы адресуются начиная с 0. Выход 1 адресуется как 0. Состояние, в которое необходимо установить выход (ON/OFF) описывается в поле данных. Величина FF00 Hex - ON. Величина 0000 - OFF. Любое другое число неверно и не влияет на выход. В приведенном ниже примере устанавливается выход 173 в состояние ON в подчиненном устройстве 17. Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 05 1 Адрес выхода мл. 00 2 Адрес выхода ст. AC 3 Данные ст. FF 4 Данные мл. 00 5 Контрольная сумма -- ОТВЕТ Нормальный ответ повторяет запрос. Ответ Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 05 1 Адрес выхода мл. 00 2 Адрес выхода ст. AC 3 Данные ст. FF 4 Данные мл. 00 5 Контрольная сумма -- */ #if ModBusUseFunc5!=0 if(PaketRX[1]==0x05) { //вычисление адреса записываемого выхода unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //проверка на допустимый адрес if(AdresBit>=ModBusMaxOutBit) {//если неправильный адрес CRCmodbus=0xFFFF; //установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } //установка сброс бита switch (((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))) { case 0xFF00: //установка бита ModBusOutBit[(AdresBit>>3)].byte|=(1<<(AdresBit&7)); break; case 0x0000: //сброс бита ModBusOutBit[(AdresBit>>3)].byte&=(~(1<<(AdresBit&7))); break; default: {//конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } } //Ответ for(int i=0;i<8;i++) ModBusPUT(PaketRX[i]);//запускаем передачу пакета ответа ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate) //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //Код функции 06 - запись выходного/внутреннего регистра. /*Функция аналогична 05, но оперирует с регистрами (словами). В запросе указывается номер выходного/внутреннего регистра и его значение. 06 Preset Single Register ОПИСАНИЕ. Записывает величину в единичный регистр (ссылка 4Х). При щироковезательной передаче на всех подчиненных устройствах устанавливается один и тот же регистр. ЗАМЕЧАНИЕ Функция может пересекаться с установленной защитой памяти. ЗАПРОС Запрос содержит ссылку на регистр, который необходимо установить. Регистры адресуются с 0. Величина, в которую необходимо установить регистр передается в поле данных. Контроллеры M84 и 484 используют 10-ти битную величину, старшие шесть бит заполняются 0. Все другие контроллерыиспользуют 16 бит. В приведенном ниже примере в регистр 40002 записывается величина 0003 Hex в подчиненном устройстве 17. Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 06 1 Адрес регистра мл. 00 2 Адрес регистра ст. 01 3 Данные ст. 00 4 Данные мл. 03 5 Контрольная сумма -- ОТВЕТ Нормальный ответ повторяет запрос. Ответ Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 06 1 Адрес регистра мл. 00 2 Адрес регистра ст. 01 3 Данные ст. 00 4 Данные мл. 03 5 Контрольная сумма -- */ #if ModBusUseFunc6!=0 if(PaketRX[1]==0x06) { //вычисление адреса записываемого выхода unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //проверка на допустимый адрес if(AdresWord>=(ModBusMaxOutReg)) {//если неправильный адрес CRCmodbus=0xFFFF; //установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } //запись слова ModBusOutReg[AdresWord]=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))); //Ответ for(int i=0;i<8;i++) ModBusPUT(PaketRX[i]);//запускаем передачу пакета ответа ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate) //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //Код функции 0x0F - запись нескольких выходных/внутренних битов. /*В запросе указывается начальный адрес бита, количество бит для записи, счетчик байтов и непосредственно значения. 15 (0F Hex) Force Multiple Coils ОПИСАНИЕ Устанавливает каждый выход (ссылка 0Х) последовательности выходов в одно из состояний ON или OFF. При широковещательной передаче функция устанавливает подобные выходы на всех подчиненных. ЗАМЕЧАНИЕ Функция может пересекаться с установкой защиты памяти и установкой недоступности выходов. ЗАПРОС Запрос специфицирует выходы для установки. Выходы адресуются начиная с 0. Ниже показан пример запроса на установку последовательности выходов начиная с 20 (адресуется как 19) в подчиненном устройстве 17. Поле данных запроса содержит 2 байта: CD 01 Hex (1100 1101 0000 0001 двоичное). Соответствие битов и выходов представлено ниже: Бит: 1 1 0 0 1 1 0 1 0 0 0 0 0 0 0 1 Выход: 27 26 25 24 23 22 21 20 - - - - - - 29 28 Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 0F 1 Адрес выхода ст. 00 2 Адрес выхода мл. 13 3 Кол-во выходов ст. 00 4 Кол-во выходов мл. 0A 5 Счетчик байт 02 6 Данные для установки (Выходы 27-20) CD 7 Данные для установки (Выходы 29-28) 01 8 Контрольная сумма -- 9 ОТВЕТ Нормальный ответ возвращает адрес подчиненного, код функции, начальный адрес, и количество установленных выходов. Это пример ответа на представленный выше запрос. Ответ Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 0F 1 Адрес выхода ст. 00 2 Адрес выхода мл. 13 3 Кол-во выходов ст. 00 4 Кол-во выходов мл. 0A 5 Контрольная сумма -- */ #if ModBusUseFunc15!=0 if(PaketRX[1]==0x0F) { //вычисление адреса записываемых бит unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества записываемых бит unsigned short KolvoBit=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))); //если неправильный адрес и количество if(((AdresBit+KolvoBit)>ModBusMaxOutBit) || (KolvoBit>ModBusMaxOutBitRX)) {//тады конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } //установка битов unsigned char Bit=(AdresBit&7);//указатель бит в ModBusOutBit[] AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[] //цикл по битам for(int i=0;i<KolvoBit;i++) { if(PaketRX[7+(i>>3)]&(1<<(i&7)))//если текущий бит PaketRX равен 1 {//устанавливаем бит в ModBusOutBit[] ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)|((unsigned char)(1<<Bit)); } else {//сбрасываем бит ModBusOutBit[] ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)&((unsigned char)(~(1<<Bit))); } //инкрементруем указатели Bit++;if(Bit==8){Bit=0;AdresBit++;} } //вычисляем CRC пакета передачи и передаем CRCmodbus=0xFFFF; for(int i=0;i<6;i++) { ModBusPUT(PaketRX[i]); CRCmodbus=CRCfunc(CRCmodbus,(PaketRX[i])); } ModBusPUT(CRCmodbus); ModBusPUT(CRCmodbus>>8); ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate) //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif //Код функции 0x10 запись нескольких выходных/внутренних регистров. /*16 (10 Hex) Preset Multiple Regs ОПИСАНИЕ Запись данных в последовательность регистров (ссылка 4Х). При широковещательной передаче, функция устанавливает подобные регистры во всех подчиненных устройствах. ЗАМЕЧАНИЕ Функция может пересекаться с установленной защитой памяти. ЗАПРОС Запрос специфицирует регистры для записи. Регистры адресуются начиная с 0. Данные для записи в регистры содержатся в поле данных запроса. Контроллеры M84 и 484 используют 10-битовую величину, со старшими шестью битами установленными в 0. Все остальные контроллеры используют 16 бит. Ниже приведен пример запроса на установку двух регистров начиная с 40002 в 00 0A и 01 02 Hex, в подчиненном устройстве 17: Запрос Имя поля Пример (Hex) Адрес подчиненного 11 0 Функция 10 1 Начальный адрес 00 2 Начальный адрес 01 3 Кол-во регистров ст. 00 4 Кол-во регистров мл. 02 5 Счетчик байт 04 6 Данные ст. 00 7 Данные мл. 0A 8 Данные ст. 01 9 Данные мл. 02 10 Контрольная сумма -- ОТВЕТ Нормальный ответ содержит адрес подчиненного, код функции, начальный адрес, и количество регистров. */ #if ModBusUseFunc16!=0 if(PaketRX[1]==0x10) { //вычисление адреса записываемых слов unsigned short b=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества записываемых слов unsigned short c=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))); //если неправильный адрес и количество if(((b+c)>ModBusMaxOutReg) || c>ModBusMaxOutRegRX || c==0) {//тады конец CRCmodbus=0xFFFF;//установить начальное значение CRC return;//Ошибка, повторный запрос не требуется } //Копирование из пакета в регистр ModBusOutReg[] for(int i=0;i<c;i++) { ModBusOutReg[b+i]=(((unsigned short)PaketRX[7+(i<<1)])<<8)|(PaketRX[8+(i<<1)]); } //вычисляем CRC пакета передачи и передаем CRCmodbus=0xFFFF; for(int i=0;i<6;i++) { ModBusPUT(PaketRX[i]); CRCmodbus=CRCfunc(CRCmodbus,(PaketRX[i])); } ModBusPUT(CRCmodbus); ModBusPUT(CRCmodbus>>8); ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate) //конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;//повторный запрос не требуется } #endif ///////////////////////////////////////////////////////////////////////////// //полный конец CRCmodbus=0xFFFF; //установить начальное значение CRC return;////Ошибка, нераспознана команда, повторный запрос не требуется } return;//повторный запрос не требуется } //Функция конвертация шеснадцатиричных символов в число static inline unsigned char Hex2Dig(unsigned char h) { if((h>='0')&&(h<='9')) return (h -'0'); if((h>='A')&&(h<='F')) return (h -'A'+10); return 0; } static unsigned char LRCmodbus;//тукущий LRC static unsigned char Simvol0;//предидущий принятвй символ #define ASCII_CR (0x0D)//возврат каретки #define ASCII_LF (0x0A)//перевод строки static const unsigned char BCD[]="0123456789ABCDEF";//строка для конвертации числа в символ //Функция обработки Сообщений модбас ASCII void ModBusASCII(void) { if(Sost==0) {//Состояние прием while(!0) {//Цикл приема символов unsigned short Tmp=ModBusGET(); //читаем символ из входного потока if(Tmp==0) return; //если нет данных повторный запрос не требуется //Символ принят Tmp=Tmp&0xFF;//отбрасываем признак приема байта //проверка на начало пакета if(Tmp==':') {//начало пакета LRCmodbus=0;//обнуляем LRC UkPaket=0;//указатель в массиве, текущий принятый символ continue;//запускаем повторный запрос символа } //проверка на алфавит сообщения if(!( ((Tmp>='0')&&(Tmp<='9'))|| ((Tmp>='A')&&(Tmp<='F'))|| (Tmp==ASCII_CR)|| (Tmp==ASCII_LF) )) { return;//Ошибка, повторный запрос не требуется } //сохраняем принятый символ if((UkPaket&1)==0) {//указатель принятых данных четный 0,2,4,6... Simvol0=Tmp; //сохраняем первый символ пакета UkPaket++; //икреметируем указатель пакета continue;//запускаем повторный запрос } else {//указатель принятых данных нечетный 1,3,5,7... if(Tmp!=ASCII_LF) {//не достигнут конец PaketRX[UkPaket>>1]=(Hex2Dig(Simvol0)<<4)|(Hex2Dig(Tmp));//сохраняем байт пакета LRCmodbus=LRCmodbus-PaketRX[UkPaket>>1];//считаем LRC UkPaket++;//икреметируем указатель пакета if(UkPaket>(ModBusMaxPaketRX<<1))//проверка на переполнение {//Буфер приема переполнился UkPaket=0;//сбросить указатель пакета return;//ошибка, повторный запрос не требуется } } else break; } } //Проверка LCR if(LRCmodbus!=0) return;//Ошибка, повторный запрос не требуется //Провекка адреса if((PaketRX[0]!=ModBusID)&&(PaketRX[0]!=ModBusID_FF)) {//Не наш адрес return;//повторный запрос не требуется } //преходим в состояние передача Sost=!0; TimModbus=ModBusSysTimer;//запомнить таймер #if ModBusMaxPauseResp!=0 return;//повторный запрос не требуется #endif } ///////////////////////////////////////////////////////////////////////////// if(Sost!=0 #if ModBusMaxPauseResp!=0 && (ModBusSysTimer-TimModbus)>=ModBusMaxPauseResp #endif ) {//Состояние передача ответа Sost=0; ///////////////////////////////////////////////////////////////////////////// // обработка команд // ///////////////////////////////////////////////////////////////////////////// #if ModBusUseFunc1!=0 //01 Чтение статуса выходов if(PaketRX[1]==0x01) { //вычисление адреса запрашиваемых бит unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества запрашиваемых бит unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if((AdresBit+KolvoBit)>(ModBusMaxOutBit) || KolvoBit>ModBusMaxOutBitTX || KolvoBit==0) {//конец return;//Ошибка, повторный запрос не требуется } Prg2ModBusOutBit();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа ModBusPUT(':'); //адрес ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший LRCmodbus=0-PaketRX[0];//считаем LRC //код команды ModBusPUT(BCD[1>>4]);//Передаем старший ModBusPUT(BCD[1&0x0F]);//передаем младший LRCmodbus=LRCmodbus-1;//считаем LRC //количества полных байт ModBusPUT(BCD[((KolvoBit+7)>>3)>>4]);//Передаем старший ModBusPUT(BCD[((KolvoBit+7)>>3)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-((KolvoBit+7)>>3);//считаем LRC //копирование битов в пакет ответа unsigned char TxByte=0;//текущий байт unsigned char Bit=AdresBit&7;//указатель бит в ModBusOutBit[] AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[] //копирование из регистра ModBusOutBit[] в пакет int i=0; while(!0) { if((ModBusOutBit[AdresBit].byte)&(1<<Bit))//если текущий бит ModBusOutBit[] равен 1 {//устанавливаем бит в пакете TxByte=TxByte|(1<<(i&7)); } //инкрементруем указатели Bit++; if(Bit==8){Bit=0;AdresBit++;} i++; if((i&7)==0) { ModBusPUT(BCD[TxByte>>4]);//Передаем старший ModBusPUT(BCD[TxByte&0x0F]);//передаем младший LRCmodbus=LRCmodbus-TxByte;//считаем LRC TxByte=0; if(i==KolvoBit) break; else continue; } if(i==KolvoBit) { ModBusPUT(BCD[TxByte>>4]);//Передаем старший ModBusPUT(BCD[TxByte&0x0F]);//передаем младший LRCmodbus=LRCmodbus-TxByte;//считаем LRC break; } } ModBusPUT(BCD[LRCmodbus>>4]); ModBusPUT(BCD[LRCmodbus&0x0F]); ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc2!=0 //02 Read Input Status if(PaketRX[1]==0x02) { //вычисление адреса запрашиваемых бит unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества запрашиваемых бит unsigned short KolvoBit=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if((AdresBit+KolvoBit)>(ModBusMaxInBit) || KolvoBit>ModBusMaxInBitTX || KolvoBit==0) {//конец return;//Ошибка, повторный запрос не требуется } Prg2ModBusInBit();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа ModBusPUT(':'); //адрес ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший LRCmodbus=0-PaketRX[0];//считаем LRC //код команды ModBusPUT(BCD[2>>4]);//Передаем старший ModBusPUT(BCD[2&0x0F]);//передаем младший LRCmodbus=LRCmodbus-2;//считаем LRC //количества полных байт ModBusPUT(BCD[((KolvoBit+7)>>3)>>4]);//Передаем старший ModBusPUT(BCD[((KolvoBit+7)>>3)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-((KolvoBit+7)>>3);//считаем LRC //копирование битов в пакет ответа unsigned char TxByte=0;//текущий байт unsigned char Bit=AdresBit&7;//указатель бит в ModBusOutBit[] AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[] //копирование из регистра ModBusOutBit[] в пакет int i=0; while(!0) { if((ModBusInBit[AdresBit].byte)&(1<<Bit))//если текущий бит ModBusOutBit[] равен 1 {//устанавливаем бит в пакете TxByte=TxByte|(1<<(i&7)); } //инкрементруем указатели Bit++; if(Bit==8){Bit=0;AdresBit++;} i++; if((i&7)==0) { ModBusPUT(BCD[TxByte>>4]);//Передаем старший ModBusPUT(BCD[TxByte&0x0F]);//передаем младший LRCmodbus=LRCmodbus-TxByte;//считаем LRC TxByte=0; if(i==KolvoBit) break; else continue; } if(i==KolvoBit) { ModBusPUT(BCD[TxByte>>4]);//Передаем старший ModBusPUT(BCD[TxByte&0x0F]);//передаем младший LRCmodbus=LRCmodbus-TxByte;//считаем LRC break; } } ModBusPUT(BCD[LRCmodbus>>4]); ModBusPUT(BCD[LRCmodbus&0x0F]); ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc3!=0 //03 Read Holding Registers if(PaketRX[1]==0x03) { //вычисление адреса запрашиваемых слов unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление адреса количества запрашиваемых слов unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || KolvoWord>ModBusMaxOutRegTX) {//тады конец return;//Ошибка, повторный запрос не требуется } Prg2ModBusOutReg();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа ModBusPUT(':'); //адрес ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший LRCmodbus=0-PaketRX[0];//считаем LRC //код команды ModBusPUT(BCD[3>>4]);//Передаем старший ModBusPUT(BCD[3&0x0F]);//передаем младший LRCmodbus=LRCmodbus-3;//считаем LRC //количества полных байт ModBusPUT(BCD[(KolvoWord<<1)>>4]);//Передаем старший ModBusPUT(BCD[(KolvoWord<<1)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-(KolvoWord<<1);//считаем LRC //Копирование из регистра ModBusOutReg[] в пакет ответа for(int i=0;i<KolvoWord;i++) { ModBusPUT(BCD[((ModBusOutReg[AdresWord+i])>>8)>>4]);//Передаем старший ModBusPUT(BCD[((ModBusOutReg[AdresWord+i])>>8)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-((ModBusOutReg[AdresWord+i])>>8);//считаем LRC ModBusPUT(BCD[(((ModBusOutReg[AdresWord+i])>>0)>>4)&0x0F]);//Передаем старший ModBusPUT(BCD[(((ModBusOutReg[AdresWord+i])>>0)>>0)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-((ModBusOutReg[AdresWord+i])>>0);//считаем LRC } ModBusPUT(BCD[LRCmodbus>>4]); ModBusPUT(BCD[LRCmodbus&0x0F]); ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc4!=0 //04 Read Input Registers if(PaketRX[1]==0x04) { //вычисление адреса запрашиваемых слов unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление адреса количества запрашиваемых слов unsigned short KolvoWord=((((unsigned short)PaketRX[4])<<8)|(PaketRX[5])); //если неправильный адрес и количество if(((AdresWord+KolvoWord)>ModBusMaxOutReg) || KolvoWord>ModBusMaxOutRegTX) {//тады конец return;//Ошибка, повторный запрос не требуется } Prg2ModBusInReg();//Заполнение регистров Модбас (GlobalDate->ModBus) //формирование пакета ответа ModBusPUT(':'); //адрес ModBusPUT(BCD[PaketRX[0]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[0]&0x0F]);//передаем младший LRCmodbus=0-PaketRX[0];//считаем LRC //код команды ModBusPUT(BCD[4>>4]);//Передаем старший ModBusPUT(BCD[4&0x0F]);//передаем младший LRCmodbus=LRCmodbus-4;//считаем LRC //количества полных байт ModBusPUT(BCD[(KolvoWord<<1)>>4]);//Передаем старший ModBusPUT(BCD[(KolvoWord<<1)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-(KolvoWord<<1);//считаем LRC //Копирование из регистра ModBusOutReg[] в пакет ответа for(int i=0;i<KolvoWord;i++) { ModBusPUT(BCD[((ModBusInReg[AdresWord+i])>>8)>>4]);//Передаем старший ModBusPUT(BCD[((ModBusInReg[AdresWord+i])>>8)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-((ModBusInReg[AdresWord+i])>>8);//считаем LRC ModBusPUT(BCD[(((ModBusInReg[AdresWord+i])>>0)>>4)&0x0F]);//Передаем старший ModBusPUT(BCD[(((ModBusInReg[AdresWord+i])>>0)>>0)&0x0F]);//передаем младший LRCmodbus=LRCmodbus-((ModBusInReg[AdresWord+i])>>0);//считаем LRC } ModBusPUT(BCD[LRCmodbus>>4]); ModBusPUT(BCD[LRCmodbus&0x0F]); ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc5!=0 //05 Force Single Coil if(PaketRX[1]==0x05) { //вычисление адреса записываемого выхода unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //проверка на допустимый адрес if(AdresBit>=ModBusMaxOutBit)//если неправильный адрес {//тады конец return;//Ошибка, повторный запрос не требуется } //установка сброс бита switch (((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))) { case 0xFF00: //установка бита ModBusOutBit[(AdresBit>>3)].byte|=(1<<(AdresBit&7)); break; case 0x0000: //сброс бита ModBusOutBit[(AdresBit>>3)].byte&=(~(1<<(AdresBit&7))); break; default: { //конец return;//Ошибка, повторный запрос не требуется } } //Ответ ModBusPUT(':'); for(int i=0;i<7;i++) { ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший } ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate) //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc6!=0 //06 Preset Single Register if(PaketRX[1]==0x06) { //вычисление адреса записываемого выхода unsigned short AdresWord=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //проверка на допустимый адрес if(AdresWord>=(ModBusMaxOutReg))//если неправильный адрес {//тады конец return;//Ошибка, повторный запрос не требуется } //запись слова ModBusOutReg[AdresWord]=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))); //Ответ ModBusPUT(':'); for(int i=0;i<7;i++) { ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший } ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate) //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc15!=0 //15 (0F Hex) Force Multiple Coils if(PaketRX[1]==0x0F) { //вычисление адреса записываемых бит unsigned short AdresBit=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества записываемых бит unsigned short KolvoBit=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))); //если неправильный адрес и количество if(((AdresBit+KolvoBit)>ModBusMaxOutBit) || (KolvoBit>ModBusMaxOutBitRX)) {//тады конец return;//Ошибка, повторный запрос не требуется } //установка битов unsigned char Bit=(AdresBit&7);//указатель бит в ModBusOutBit[] AdresBit=AdresBit>>3;//указатель байт ModBusOutBit[] //цикл по битам for(int i=0;i<KolvoBit;i++) { if(PaketRX[7+(i>>3)]&(1<<(i&7)))//если текущий бит PaketRX равен 1 {//устанавливаем бит в ModBusOutBit[] ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)|((unsigned char)(1<<Bit)); } else {//сбрасываем бит ModBusOutBit[] ModBusOutBit[AdresBit].byte=(ModBusOutBit[AdresBit].byte)&((unsigned char)(~(1<<Bit))); } //инкрементруем указатели Bit++;if(Bit==8){Bit=0;AdresBit++;} } //вычисляем LRC пакета передачи и передаем LRCmodbus=0; ModBusPUT(':'); for(int i=0;i<6;i++) { ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший LRCmodbus=LRCmodbus-PaketRX[i];//считаем LRC } ModBusPUT(BCD[LRCmodbus>>4]); ModBusPUT(BCD[LRCmodbus&0x0F]); ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); ModBus2PrgOutBit();//Считывание регистров Модбас (ModBus->GlobalDate) //конец return;//повторный запрос не требуется } #endif #if ModBusUseFunc16!=0 //16 (10 Hex) Preset Multiple Regs if(PaketRX[1]==0x10) { //вычисление адреса записываемых слов unsigned short b=(((((unsigned short)PaketRX[2])<<8)|(PaketRX[3]))); //вычисление количества записываемых слов unsigned short c=(((((unsigned short)PaketRX[4])<<8)|(PaketRX[5]))); //если неправильный адрес и количество if(((b+c)>ModBusMaxOutReg) || c>ModBusMaxOutRegRX) { //тады конец return;//Ошибка, повторный запрос не требуется } //Копирование из пакета в регистр ModBusOutReg[] for(int i=0;i<c;i++) { ModBusOutReg[b+i]=(((unsigned short)PaketRX[7+(i<<1)])<<8)|(PaketRX[8+(i<<1)]); } //вычисляем LRC пакета передачи и передаем LRCmodbus=0; ModBusPUT(':'); for(int i=0;i<6;i++) { ModBusPUT(BCD[PaketRX[i]>>4]);//Передаем старший ModBusPUT(BCD[PaketRX[i]&0x0F]);//передаем младший LRCmodbus=LRCmodbus-PaketRX[i];//считаем LRC } ModBusPUT(BCD[LRCmodbus>>4]); ModBusPUT(BCD[LRCmodbus&0x0F]); ModBusPUT(ASCII_CR); ModBusPUT(ASCII_LF); ModBus2PrgOutReg();//Считывание регистров Модбас (ModBus->GlobalDate) //конец return;//повторный запрос не требуется } #endif } //конец return;//Ошибка, нераспознана команда, повторный запрос не требуется } ModBus2Prg.cSPL#define __MODBUS2PRG_C
#include "modbus.h" //Заполнение регистров Модбас //перенос данных из программных переменных в регистры МодБас void Prg2ModBusOutBit(void) {//заполнение регистров дискретных выходов return; } void Prg2ModBusInBit(void) {//заполнение регистров дискретных входов //ModBusInBit[0].bit0=1; return; } void Prg2ModBusOutReg(void) {//заполнение регистров 4Х регистры для чтения/записи return; } void Prg2ModBusInReg(void) {//заполнение регистов 3Х регистры для чтения return; } //Считывание регистров Модбас //Перенос данных из регистров МодБас в программные переменные void ModBus2PrgOutBit(void) {//чтение регистров дискретных выходов return; } void ModBus2PrgOutReg(void) {//чтение регистров 4Х регистры для чтения/записи return; } В файле modbus.h содержатся требуемые объявления, опции компиляции и настроечные константы. Кратко опишем основные опции и настроечные параметры. ModBusUseFunc1 — ModBusUseFunc15 – опция компиляции, определяющая использование функций протокола ModBus. Практические реализации устройств ModBus работают с ограниченным набором функций протокола, наиболее часто, функции 3,6 и 16. Нет необходимости включать в проект лишний код. ModBusID, ModBusID_FF – Адреса на шине ModBus. Данный реализация протокола поддерживает два адреса. Это может быть удобно для ввода в эксплуатацию устройств, адрес ModBusID является настраиваемым адресом устройства, а адрес ModBusID_FF адресом для индивидуальной настройки устройства. ModBusMaxPause – Пауза между символами, для определения начала пакета, задается в квантах ModBusSysTimer. Как правило квант ModBusSysTimer равен 1мС. Для большинства приложений соблюдение таймаутов описанных в стандарте протокола просто невозможно. Например, ModBus Master работающий на Win-машине никогда не сможет обеспечить требуемые протоколом таймауты. Поэтому задавать квант времени менее 1мС можно считать нецелесообразным. Практические наблюдения показывают, что величина ModBusMaxPause должна быть порядка 5-10мС. ModBusMaxPauseResp — Пауза между запросом Master и ответом Slave. Многие ModBus Master устройства имеют задержку переключения с передачи на прием, эту задержку можно скомпенсировать этой константой. ModBusMaxInBit, ModBusMaxOutBit, ModBusMaxInReg, ModBusMaxOutReg — Количество дискретных входов, выходов, регистров для чтения, регистров для чтения/записи. В программе резервируется память под регистры ModBus. Если определенный тип регистров не используется значение необходимо указать равное нулю. ModBusMaxInBitTX, ModBusMaxOutBitTX, ModBusMaxInRegTX, ModBusMaxOutRegTX — Максимальное количество дискретных входов, выходов, регистров для чтения, регистров для чтения/записи выходных регистров в передаваемом пакете. Эта настройка должна совпадать с соответствующей настройкой ModBus Master. Для портирования библиотеки на любую платформу необходимо указать через макросы три функций. ModBusSysTimer — Системный таймер, переменная, инкрементирующаяся каждую миллисекунду в отдельном потоке выполнения. В качестве этой переменной может выступать uwTick, из библиотеки HAL STM32, или стандартная функция языка Си clock(). void ModBusPUT(unsigned char A) — Запись байта в последовательный поток. unsigned short ModBusGET(void) — Чтение байта из последовательного потока. Если в последовательном потоке нет данных, то функция возвращает 0, если данные есть, то возвращаемое значение — старший байт 0х01, младший байт — прочитанные данные. Для использования библиотеки необходимо заполнить тело функций Prg2ModBusOutBit(), Prg2ModBusInBit(), Prg2ModBusOutReg(), Prg2ModBusInReg(), отвечающие за копирование переменных пользователя в регистры ModBus. Так же, необходимо заполнить тело функций ModBus2PrgOutBit(), ModBus2PrgOutReg(), отвечающие за копирование регистров ModBus в переменные пользователя. В теле этих функций можно выполнить некоторые действия связанные с изменением регистров, например, осуществить проверку на допустимые значения. Например: void Prg2ModBusOutReg(void)
{//заполнение регистров, 4Х регистры для чтения/записи ModBusOutReg[0]=A; ModBusOutReg[1]=B; ModBusOutReg[2]=C; return; } void ModBus2PrgOutReg(void) { //чтение регистров 4Х, регистры для чтения/записи if(ModBusOutReg[0] < MaxA) A= ModBusOutReg[0]; B=ModBusOutReg[1]; C=ModBusOutReg[2]; return; } Допускается не заполнять тело указанных функций, а работать с регистрами напрямую, при этом надо использовать опцию ModBusUseGlobal. Для инициализации ModBus устройства необходимо вызвать функцию ModBusIni(). Функции ModBusRTU() или ModBusASCII() обеспечивающей работу устройства по протоколам RTU и ASCII соответственно. Их необходимо вызывать в главном цикле программы: ModBusIni();
while(!0) { if(ModBusTip==RTU) ModBusRTU(); else ModBusASCII(); } Не стоит забывать, что перед инициализацией и вызовом функции, обеспечивающей работу ModBus устройства, необходимо позаботится об инициализации последовательного потока (UART). Программные решения, связанные с организацией поточного ввода/вывода, зависят от аппаратной платформы, и их рассмотрение выходит за рамки данной статьи. Данная библиотека была протестирована с OPC сервером Kepware, панелями SIMATIC и Wientek, также другими ModBus Masterами, во множестве устройств на микроконтроллерах семейства PIC и STM32, и показала свою 142% работоспособность. Простота портирования данной библиотеки позволит легко адаптировать ее под другие типы 8-16-32разрядных микроконтроллеров. =========== Источник: habr.com =========== Похожие новости:
Промышленное программирование ), #_razrabotka_robototehniki ( Разработка робототехники ), #_programmirovanie_mikrokontrollerov ( Программирование микроконтроллеров ), #_razrabotka_dlja_interneta_veschej ( Разработка для интернета вещей ), #_proizvodstvo_i_razrabotka_elektroniki ( Производство и разработка электроники ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 22:01
Часовой пояс: UTC + 5