709 lines
25 KiB
C
709 lines
25 KiB
C
/*!
|
||
Copyright 2017 АО "НИИЭТ" и ООО "НПФ ВЕКТОР"
|
||
|
||
Licensed under the Apache License, Version 2.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
You may obtain a copy of the License at
|
||
|
||
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
Unless required by applicable law or agreed to in writing, software
|
||
distributed under the License is distributed on an "AS IS" BASIS,
|
||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
\file V_ModBus.c
|
||
\brief Драйвер ModBus (см. TModBus)
|
||
24-03-2017 Перенос драйвера на К1921ВК01Т НИИЭТ. Сильный рефакторинг.
|
||
|
||
13-08-2012 Теперь поле type структуры MB_Record используется как признак записи/чтения
|
||
для не битовых HR/IR-переменных (раньше там был только признак записи).
|
||
|
||
24-01-2012 Теперь проще выбирать между SciaRegs, ScibRegs и ScicRegs. Это сделано
|
||
дефайном. Переименована структура RS в MODBUS_RS
|
||
|
||
|
||
26-02-2011 Исправлены какие-то баги, добавлена возможность отслеживать запись в объекты
|
||
типа "регистр" за счет изменения номера бита в словаре, поддержка большего
|
||
кол-ва процессоров.
|
||
|
||
|
||
27-11-2009 Сделана доработка драйвера для поддержки broadcast (широковещательных) сообщений. Естесственно,
|
||
что поддерживаются только команды записи (0x5 - write coils и 0x6 - write single register), т.к.
|
||
broadcast сообщения являются неподтверждаемыми сообщениями. Иными словами читать бесполезно, т.к.
|
||
ответа не придет... (Алямкин)
|
||
|
||
21-10-2008 Исправлена ошибка переполнения входного регистра - при переполнении выставлялась frame error (из драйвера SYS_VID800)
|
||
29-01-2008 Иправлена ошибка расчета таймаутов
|
||
28-01-2008 Иправлена ошибка в функции пойска нужного элемента словаря
|
||
|
||
29-11-2007 Драйвер переделан для работы с МК17.1
|
||
1.Исправлена ошибка при обнулении битовых регистров
|
||
2.Исправлен неправильный перебор элементов в словаре
|
||
3.Добавлена возможность автоматического перезапуска драйвера при большом количестве ошибок на линии
|
||
|
||
Замечание. Особенность реализации драйвера не предполагает наличия регистра № 0х0000 в словаре
|
||
Для устранения недостатка необходимо в инициализации переделать пойск последних элементов
|
||
массивов с поля номера регистра на поле адреса переменной регистра.
|
||
16-11-2006 Release Rev 1.0
|
||
|
||
\author Alecksey Anuchin
|
||
\version v 1.2 24/03/2017
|
||
|
||
*/
|
||
|
||
/** \addtogroup V_ModBus */
|
||
/*@{*/
|
||
#include "V_ModBus.h"
|
||
#include "main.h"
|
||
|
||
//Макросы для переключения RS485 между приемом и передачей
|
||
#define MODBUS_RX_RS485 GPIOM->DATAOUTCLR = (1 << 2) //M2
|
||
#define MODBUS_TX_RS485 GPIOM->DATAOUTSET = (1 << 2) //M2
|
||
#define MODBUS_GPIO_VALUE (GPIOK->DATA>>10) & 3
|
||
//Макрос для осциллографирования ножек UART (удобно для отладки, когда вообще ничего не работает)
|
||
|
||
//Частота тактирования блока UART (настраивается в инициализации микроконтроллера)
|
||
#define UART_CLOCK_FREQ 25000000
|
||
|
||
void ModBus_RS_Init(TModBus *p) {
|
||
|
||
p->MBInternals.NT_UART = UART3; //Используемый в драйвере номер UART (1,2,3)
|
||
p->MBInternals.NT_UART->CR_bit.UARTEN = 1; // Разрешить работу UART
|
||
|
||
//Текущая реализация драйвера не использует FIFO, обрабатывает данные по одному байту
|
||
//и вызывается в прерывании 10кГц. Поэтому частоты обмена больше 57600 не поддерживаются (будет терять байты)
|
||
if (p->BaudRate > 57600)
|
||
p->BaudRate = 57600;
|
||
|
||
// Настройка частоты в двух регистрах:
|
||
// задание делителя частоты для получения BaudRate
|
||
// Т.к. блок UART тактируется с частотой 25 МГц (так настроено в ините микроконтроллера),
|
||
// то для получения частоты, например, 57600 бит/с необходим
|
||
// делитель 25000000 / (16 * 57600) = 27,126736111111111111111111111111
|
||
// Целая часть I = 27
|
||
// Дробная часть F = (int)( 0.126736111*64 + 0.5 ) = 8
|
||
// Формулы см. в документации
|
||
float DivResFloat = (float) UART_CLOCK_FREQ / (16 * p->BaudRate);
|
||
long DivResInt = (long) DivResFloat;
|
||
long DivResFrac = (int) ((DivResFloat - DivResInt) * 64 + 0.5);
|
||
|
||
p->MBInternals.NT_UART->IBRD_bit.DIVINT = DivResInt;
|
||
p->MBInternals.NT_UART->FBRD_bit.DIVFRAC = DivResFrac;
|
||
|
||
// 8 бит данных, 1 стоп бит, без контроля четности
|
||
p->MBInternals.NT_UART->LCRH_bit.SPS = 0; // Нет проверки четности
|
||
p->MBInternals.NT_UART->LCRH_bit.WLEN = 3; // Длина посылки 8 бит
|
||
p->MBInternals.NT_UART->LCRH_bit.FEN = 0; // Не использовать FIFO
|
||
p->MBInternals.NT_UART->CR_bit.TXE = 1; // Разрешить приём
|
||
p->MBInternals.NT_UART->CR_bit.RXE = 1; // Разрешить передачу
|
||
p->MBInternals.NT_UART->LCRH_bit.STP2 = 0; // 1 стоп-бит
|
||
|
||
}
|
||
|
||
void ModBus_ResetError(TModBus *p) {
|
||
// Биты 0-3 - биты ошибок, сбрасываются записью в них "1"
|
||
p->MBInternals.NT_UART->RSR |= 0x000F;
|
||
}
|
||
|
||
int16 ModBus_CheckDataReady(TModBus *p) {
|
||
// return 1 - если буфер приёмника не пуст
|
||
return (!p->MBInternals.NT_UART->FR_bit.RXFE);
|
||
}
|
||
|
||
int16 ModBus_CheckTxEmpty(TModBus *p) //Если все отправлено (пуст и основной и теневой регистры)
|
||
{
|
||
// return 1 - если буфер передатчика пуст
|
||
return (p->MBInternals.NT_UART->FR_bit.TXFE);
|
||
}
|
||
|
||
int16 ModBus_CheckTxReady(TModBus *p) //В передатчик можно положить следующий символ (буфер свободен)
|
||
{
|
||
// "1" - если буфер передатчика пуст
|
||
return (p->MBInternals.NT_UART->FR_bit.TXFE);
|
||
}
|
||
|
||
void ModBus_Tx(TModBus *p, int16 data) // Передача данных
|
||
{
|
||
Uint8 tmp_data = data;
|
||
p->MBInternals.NT_UART->DR_bit.DATA = tmp_data;
|
||
}
|
||
|
||
//!Инициализация.
|
||
//!настройка таймаутов,
|
||
//!обработка словаря объектов для последующего быстрого поиска
|
||
|
||
//! \memberof TModBus
|
||
void ModBus_Init(TModBus *p) {
|
||
Uint16 x;
|
||
|
||
ModBus_RS_Init(p);
|
||
|
||
p->MBInternals.TimeOut1_5 = (int16) (((int32) p->ExecutionFreq * (int32) 17)
|
||
/ p->BaudRate + 2); // 17 ~ 11*1.5
|
||
p->MBInternals.TimeOut2_5 = (int16) (((int32) p->ExecutionFreq * (int32) 28)
|
||
/ p->BaudRate); // 28 ~ 11*2.5
|
||
|
||
p->MBInternals.RecievedCounter = -1;
|
||
|
||
////// HR
|
||
// поиск размера массива HR
|
||
x = 0;
|
||
while (mbodHR[x].index != 0)
|
||
x++;
|
||
p->MBInternals.NumOfHRs = x;
|
||
|
||
// поиск среднего элемента массива HR
|
||
p->MBInternals.MiddleOfHRs = x;
|
||
x = 0;
|
||
while ((p->MBInternals.MiddleOfHRs >> 1) != 0) {
|
||
p->MBInternals.MiddleOfHRs >>= 1;
|
||
x = x + x + 1;
|
||
}
|
||
p->MBInternals.MiddleOfHRs = x;
|
||
|
||
// поиск начального инкремента для метода последовательного
|
||
// приближения
|
||
p->MBInternals.InitialStepOfHRs = 1;
|
||
while (x != 0) {
|
||
x >>= 1;
|
||
p->MBInternals.InitialStepOfHRs <<= 1;
|
||
}
|
||
p->MBInternals.InitialStepOfHRs >>= 1;
|
||
|
||
////// Coils
|
||
// поиск размера массива C
|
||
x = 0;
|
||
while (mbodC[x].index != 0)
|
||
x++;
|
||
p->MBInternals.NumOfCs = x;
|
||
|
||
// поиск среднего элемента массива C
|
||
p->MBInternals.MiddleOfCs = x;
|
||
x = 0;
|
||
while ((p->MBInternals.MiddleOfCs >> 1) != 0) {
|
||
p->MBInternals.MiddleOfCs >>= 1;
|
||
x = x + x + 1;
|
||
}
|
||
p->MBInternals.MiddleOfCs = x;
|
||
|
||
// поиск начального инкремента для метода последовательного
|
||
// приближения
|
||
p->MBInternals.InitialStepOfCs = 1;
|
||
while (x != 0) {
|
||
x >>= 1;
|
||
p->MBInternals.InitialStepOfCs <<= 1;
|
||
}
|
||
p->MBInternals.InitialStepOfCs >>= 1;
|
||
|
||
// Discrete Inputs
|
||
// поиск размера массива DI
|
||
x = 0;
|
||
while (mbodDI[x].index != 0)
|
||
x++;
|
||
p->MBInternals.NumOfDIs = x;
|
||
|
||
// поиск среднего элемента массива DI
|
||
p->MBInternals.MiddleOfDIs = x;
|
||
x = 0;
|
||
while ((p->MBInternals.MiddleOfDIs >> 1) != 0) {
|
||
p->MBInternals.MiddleOfDIs >>= 1;
|
||
x = x + x + 1;
|
||
}
|
||
p->MBInternals.MiddleOfDIs = x;
|
||
|
||
// поиск начального инкремента для метода последовательного
|
||
// приближения
|
||
p->MBInternals.InitialStepOfDIs = 1;
|
||
while (x != 0) {
|
||
x >>= 1;
|
||
p->MBInternals.InitialStepOfDIs <<= 1;
|
||
}
|
||
p->MBInternals.InitialStepOfDIs >>= 1;
|
||
|
||
////// IR
|
||
// поиск размера массива HR
|
||
x = 0;
|
||
while (mbodIR[x].index != 0)
|
||
x++;
|
||
p->MBInternals.NumOfIRs = x;
|
||
|
||
// поиск среднего элемента массива HR
|
||
p->MBInternals.MiddleOfIRs = x;
|
||
x = 0;
|
||
while ((p->MBInternals.MiddleOfIRs >> 1) != 0) {
|
||
p->MBInternals.MiddleOfIRs >>= 1;
|
||
x = x + x + 1;
|
||
}
|
||
p->MBInternals.MiddleOfIRs = x;
|
||
|
||
// поиск начального инкремента для метода последовательного
|
||
// приближения
|
||
p->MBInternals.InitialStepOfIRs = 1;
|
||
while (x != 0) {
|
||
x >>= 1;
|
||
p->MBInternals.InitialStepOfIRs <<= 1;
|
||
}
|
||
p->MBInternals.InitialStepOfIRs >>= 1;
|
||
|
||
for (x = 0; x < 1000; x++)
|
||
;
|
||
}
|
||
|
||
//!Расчет
|
||
|
||
//!Фунция обработки посылок ModBus
|
||
|
||
//! \memberof TModBus
|
||
void ModBus_Execute(TModBus *p) {
|
||
Uint16 x;
|
||
volatile Uint16 RxReg;
|
||
if (ModBus.Enabled == 1) { // Если включен MODBUS
|
||
p->OfflineCounter += 1;
|
||
|
||
//долго нет собщений по сети?
|
||
if (p->OfflineCounter > p->OfflineCounterMax) {
|
||
p->isOnline = 0; //Мы не в сети
|
||
p->OfflineCounter = p->OfflineCounterMax + 1; //чтоб не убежал
|
||
} else
|
||
p->isOnline = 1; //в сети
|
||
|
||
//Контроль ошибок
|
||
if (p->errorCode) { //Если висит ошибка
|
||
p->error += 1;
|
||
if (p->AutoRestart == 1) //Если разрешен авто сброс ошибок
|
||
ModBus_ResetError(p);
|
||
}
|
||
if (p->clear_error == 1) //Если выставлен флаг однократного ручного сброса ошибки
|
||
{
|
||
ModBus_ResetError(p);
|
||
p->clear_error = 0;
|
||
p->error = 0;
|
||
}
|
||
//Текущее значение GPIO для отладки
|
||
p->GPIOsValue = MODBUS_GPIO_VALUE;
|
||
|
||
if (p->MBInternals.TxState < 1) //TxState==0 - передача не идет
|
||
if (ModBus_CheckDataReady(p)) //Если есть готовые принятные данные (байт)
|
||
{
|
||
p->MBInternals.TimeOut = p->MBInternals.TimeOut1_5; //Таймаут равный полтора символа
|
||
RxReg = p->MBInternals.NT_UART->DR; //Прием очередного символа
|
||
p->errorCode = (RxReg & 0x0F00) >> 8;
|
||
RxReg = RxReg & 0xFF;
|
||
p->received_data = RxReg; //Перекладывание принятых данных для отладки
|
||
p->ReceiveByteCounter++;
|
||
if (p->MBInternals.InCount == MODBUS_MAX_RX_COUNT) //Принятно слишком много байт
|
||
return;
|
||
p->MBInternals.In[p->MBInternals.InCount++] = RxReg; //Принятые данные в массив
|
||
p->MBInternals.RxCRC ^= RxReg; //Сразу же расчет контрольной суммы
|
||
for (RxReg = 0; RxReg < 8; RxReg++) //Это тоже продолжение расчета контрольной суммы
|
||
if (p->MBInternals.RxCRC & 0x01) {
|
||
p->MBInternals.RxCRC >>= 1;
|
||
p->MBInternals.RxCRC ^= p->MBInternals.MagicNumber;
|
||
} else
|
||
p->MBInternals.RxCRC >>= 1;
|
||
//больше ничего, если есть принятые данные, не делается
|
||
return;
|
||
}
|
||
if (p->MBInternals.TimeOut == 0) //Когда таймаут вышел, его обрабатывает условие p->MBInternals.TimeOut==1, а равен нулю он становится после приема
|
||
{
|
||
p->MBInternals.InCount = 0; //Обнуление счетчика массива
|
||
p->MBInternals.RxCRC = 0xFFFF; //Инициализация контрольной суммы на прием
|
||
if (ModBus_CheckTxEmpty(p)) //Если буфер на отправку пуст
|
||
{
|
||
MODBUS_RX_RS485; //Переключение RS485 на прием
|
||
p->MBInternals.TxState = 0; //TxState==0 - передача не идет
|
||
p->MBInternals.TxCRC = 0xFFFF; //Инициализация контрольной суммы на передачу
|
||
}
|
||
return;
|
||
}
|
||
if (p->MBInternals.TimeOut == 1) //; Сюда попадаем, когда межу символами появляется пауза в 1,5 символа
|
||
if (p->MBInternals.InCount != 0) //; Если количество входных данных не равно нулю, то рассматривем, что пришло
|
||
{
|
||
//поддержка broadcast осуществляется тут (по другому я незнаю как еще - Алямкин)
|
||
//далее переход в передачу - нам туда не нужно, потому обрабатываем здесь
|
||
//обрабатывать имеет смысл только команды на запись (0x5 - write coils и 0x6 - write single register).
|
||
if (p->MBInternals.In[0] == 0)//Входной массив, байт адреса равен 0 - широковещательный
|
||
{
|
||
//0x5 - write coils
|
||
if (p->MBInternals.In[1] == 5) {
|
||
x = (p->MBInternals.In[4] << 8) + p->MBInternals.In[5]; //Данные
|
||
//0 - если нашла, 3 - если запрос некорректен, 2 - ошибка в поиске
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodC,
|
||
p->MBInternals.NumOfCs, 1,
|
||
(p->MBInternals.In[2] << 8)
|
||
+ p->MBInternals.In[3], 1,
|
||
p->MBInternals.MiddleOfCs,
|
||
p->MBInternals.InitialStepOfCs);
|
||
if ((x != 0xFF00) && (x != 0x0000)) //Данные не валидны, для битовой переменной должны быть FF00 или 0000
|
||
p->MBInternals.temp = 3;//делается как будто ModBus_FindVocAddr вернула некорректный запрос
|
||
if (p->MBInternals.temp == 0)//если ModBus_FindVocAddr нашла адрес
|
||
{
|
||
if (p->MBInternals.In[4] == 0) // reset bit
|
||
*(Uint16 *) (p->MBInternals.Addr->addr) &=
|
||
(0xFFFF
|
||
- (1
|
||
<< p->MBInternals.Addr->type_or_acc));
|
||
else
|
||
// set bit
|
||
*(Uint16 *) (p->MBInternals.Addr->addr) |= (1
|
||
<< p->MBInternals.Addr->type_or_acc);
|
||
}
|
||
}
|
||
if (p->MBInternals.In[1] == 6) //0x6 - write HR
|
||
{
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodHR,
|
||
p->MBInternals.NumOfHRs, 16,
|
||
(p->MBInternals.In[2] << 8)
|
||
+ p->MBInternals.In[3], 1,
|
||
p->MBInternals.MiddleOfHRs,
|
||
p->MBInternals.InitialStepOfHRs);
|
||
if (p->MBInternals.temp == 0) //если ModBus_FindVocAddr нашла адрес
|
||
{
|
||
*(Uint16 *) (p->MBInternals.Addr->addr) =
|
||
(p->MBInternals.In[4] << 8)
|
||
+ p->MBInternals.In[5];
|
||
}
|
||
}
|
||
|
||
//делаем то же самое, что и ниже для правильной отбраковки сообщения и выходим
|
||
p->MBInternals.InCount = 0; //обнуление массива принятых байт
|
||
p->MBInternals.TimeOut = 0; //обнуление таймаута
|
||
p->MBInternals.RxCRC = 0xFFFF; //инициализация контрольной суммы
|
||
return;
|
||
}
|
||
|
||
if (p->MBInternals.In[0] != p->RSNodeID) //; Если номер узла не совпал с нашим, то выкидываем посылку.
|
||
{
|
||
p->MBInternals.InCount = 0;
|
||
p->MBInternals.TimeOut = 0;
|
||
p->MBInternals.RxCRC = 0xFFFF;
|
||
return;
|
||
}
|
||
if (p->MBInternals.RxCRC != 0) //; Если не сошлась контрольная сумма, то выкидываем посылку.
|
||
{
|
||
p->MBInternals.InCount = 0;
|
||
p->MBInternals.TimeOut = 0;
|
||
p->MBInternals.RxCRC = 0xFFFF;
|
||
return;
|
||
}
|
||
if (p->MBInternals.RecievedCounter < 0) //!!! //; Если еще не проинициализирован счетчик между фреймов (-1), то
|
||
{
|
||
//; инициализируем счетчик и выходим.
|
||
p->MBInternals.RecievedCounter = p->MBInternals.TimeOut2_5;
|
||
return;
|
||
}
|
||
if (p->MBInternals.RecievedCounter > 0) //!!! //; Если счетчик паузы между фреймов еще положительный, то уменьшаем его
|
||
{
|
||
//; и выходим
|
||
p->MBInternals.RecievedCounter--;
|
||
return;
|
||
}
|
||
//; Если счетчик RecievedCounter досчитал до нуля, то можно переключаться на передачу.
|
||
p->MBInternals.RecievedCounter = -1;
|
||
p->ReceivePacketCounter++;
|
||
MODBUS_TX_RS485; //RS485 на передачу
|
||
p->MBInternals.TxState = 1; //Передача
|
||
p->MBInternals.RxCRC = 0xFFFF;
|
||
p->MBInternals.TimeOut = p->MBInternals.TimeOut2_5;
|
||
ModBus_Tx(p, p->MBInternals.In[0]); //Передается первый байт - адрес устройства, почему-то взят из приема...
|
||
p->MBInternals.TxCRC ^= p->MBInternals.In[0]; //сразу расчет контрольноу суммы на передачу
|
||
//и это тоже расчет контрольной суммы
|
||
for (RxReg = 0; RxReg < 8; RxReg++)
|
||
if (p->MBInternals.TxCRC & 0x01) {
|
||
p->MBInternals.TxCRC >>= 1;
|
||
p->MBInternals.TxCRC ^= p->MBInternals.MagicNumber;
|
||
} else
|
||
p->MBInternals.TxCRC >>= 1;
|
||
return;
|
||
} else //количество принятых данных равно нулю.. почему-то
|
||
{
|
||
p->MBInternals.TimeOut = 0;
|
||
return;
|
||
}
|
||
p->MBInternals.TimeOut--; //отсчитывается таймаут
|
||
if (p->MBInternals.TxState < 1) //если TxState==0 - нет передачи, т выход, т.к. ниже - передача.
|
||
return;
|
||
|
||
if (ModBus_CheckTxReady(p) == 0) //если передатчик занят
|
||
return;
|
||
|
||
//Инициализация таймаута
|
||
p->MBInternals.TimeOut = p->MBInternals.TimeOut2_5;
|
||
|
||
// с этого момента включается дискретный автомат
|
||
// протокола
|
||
//p->TxState=0;
|
||
|
||
if (p->MBInternals.TxState == 1) //Состояние передачи 1
|
||
{
|
||
p->OfflineCounter = 0; //Счетчик для контроля пересылаемых пакетов ModBus
|
||
p->MBInternals.temp = p->MBInternals.In[1]; //Принятый массив, тут номер функции
|
||
if (p->MBInternals.temp == 1) // Функция read Coils
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp; //для отправки - номер функции
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodC,
|
||
p->MBInternals.NumOfCs, 1,
|
||
(p->MBInternals.In[2] << 8) + p->MBInternals.In[3],
|
||
(p->MBInternals.In[4] << 8) + p->MBInternals.In[5],
|
||
p->MBInternals.MiddleOfCs,
|
||
p->MBInternals.InitialStepOfCs);
|
||
if (p->MBInternals.temp == 0) //в словаре найден элемент
|
||
{
|
||
p->MBInternals.TxState = 10;
|
||
} else //ошибка, в словаре элемент не найден
|
||
{
|
||
p->MBInternals.ToSend |= 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = p->MBInternals.temp + 3;
|
||
}
|
||
} else if (p->MBInternals.temp == 2) // Код функции read DI
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp; //для отправки - номер функции
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodDI,
|
||
p->MBInternals.NumOfDIs, 1,
|
||
(p->MBInternals.In[2] << 8) + p->MBInternals.In[3],
|
||
(p->MBInternals.In[4] << 8) + p->MBInternals.In[5],
|
||
p->MBInternals.MiddleOfDIs,
|
||
p->MBInternals.InitialStepOfDIs);
|
||
if (p->MBInternals.temp == 0) //в словаре найден элемент
|
||
{
|
||
p->MBInternals.TxState = 10;
|
||
} else //ошибка, в словаре элемент не найден
|
||
{
|
||
p->MBInternals.ToSend |= 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = p->MBInternals.temp + 3;
|
||
}
|
||
} else if (p->MBInternals.temp == 3) // Функция read HR
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp;
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodHR,
|
||
p->MBInternals.NumOfHRs, 16,
|
||
(p->MBInternals.In[2] << 8) + p->MBInternals.In[3],
|
||
(p->MBInternals.In[4] << 8) + p->MBInternals.In[5],
|
||
p->MBInternals.MiddleOfHRs,
|
||
p->MBInternals.InitialStepOfHRs);
|
||
if (p->MBInternals.temp == 0) //в словаре найден элемент
|
||
{
|
||
p->MBInternals.TxState = 7;
|
||
} else //ошибка, в словаре элемент не найден
|
||
{
|
||
p->MBInternals.ToSend |= 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = p->MBInternals.temp + 3;
|
||
}
|
||
} else if (p->MBInternals.temp == 4) // Функция read IR
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp;
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodIR,
|
||
p->MBInternals.NumOfIRs, 16,
|
||
(p->MBInternals.In[2] << 8) + p->MBInternals.In[3],
|
||
(p->MBInternals.In[4] << 8) + p->MBInternals.In[5],
|
||
p->MBInternals.MiddleOfIRs,
|
||
p->MBInternals.InitialStepOfIRs);
|
||
if (p->MBInternals.temp == 0) //в словаре найден элемент
|
||
{
|
||
p->MBInternals.TxState = 7;
|
||
} else //ошибка, в словаре элемент не найден
|
||
{
|
||
p->MBInternals.ToSend |= 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = p->MBInternals.temp + 3;
|
||
}
|
||
} else if (p->MBInternals.temp == 5) // Функция write Coil
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp;
|
||
x = (p->MBInternals.In[4] << 8) + p->MBInternals.In[5]; //Данные
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodC,
|
||
p->MBInternals.NumOfCs, 1,
|
||
(p->MBInternals.In[2] << 8) + p->MBInternals.In[3], 1,
|
||
p->MBInternals.MiddleOfCs,
|
||
p->MBInternals.InitialStepOfCs);
|
||
if ((x != 0xFF00) && (x != 0x0000)) //Данные не валидны - можно только 0xFF00 и 0x0000
|
||
p->MBInternals.temp = 3; //Как будто бы ModBus_FindVocAddr вернула ошибку
|
||
if (p->MBInternals.temp == 0) //в словаре найден элемент
|
||
{
|
||
p->MBInternals.TxState = 12;
|
||
if (p->MBInternals.In[4] == 0) // Операция над словарем - reset bit
|
||
*(Uint16 *) (p->MBInternals.Addr->addr) &= (0xFFFF
|
||
- (1 << p->MBInternals.Addr->type_or_acc));
|
||
else
|
||
// set bit
|
||
*(Uint16 *) (p->MBInternals.Addr->addr) |= (1
|
||
<< p->MBInternals.Addr->type_or_acc);
|
||
p->Refresh = 1;
|
||
} else //ошибка, в словаре элемент не найден
|
||
{
|
||
p->MBInternals.ToSend |= 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = p->MBInternals.temp + 3;
|
||
}
|
||
} else if (p->MBInternals.temp == 6) // Функция write HR
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp;
|
||
p->MBInternals.temp = ModBus_FindVocAddr(p, mbodHR,
|
||
p->MBInternals.NumOfHRs, 16,
|
||
(p->MBInternals.In[2] << 8) + p->MBInternals.In[3], 1,
|
||
p->MBInternals.MiddleOfHRs,
|
||
p->MBInternals.InitialStepOfHRs);
|
||
if (p->MBInternals.temp == 0) //в словаре найден элемент
|
||
{
|
||
p->MBInternals.TxState = 12;
|
||
//Операция над словарем - запись регистра
|
||
*(Uint16 *) (p->MBInternals.Addr->addr) =
|
||
(p->MBInternals.In[4] << 8) + p->MBInternals.In[5];
|
||
p->MBInternals.Addr->type_or_acc = MB_VAR_WRITE;
|
||
} else //ошибка, в словаре элемент не найден
|
||
{
|
||
p->MBInternals.ToSend |= 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = p->MBInternals.temp + 3;
|
||
}
|
||
} else //никакая из поддерживаемых функций
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp | 0x80; //добавляется маска ошибки
|
||
p->MBInternals.TxState = 2;
|
||
}
|
||
} else if (p->MBInternals.TxState == 12) // send addr hi for write C
|
||
{
|
||
p->MBInternals.TxState = 13;
|
||
p->MBInternals.ToSend = p->MBInternals.In[2];
|
||
} else if (p->MBInternals.TxState == 13) // send addr low for write C
|
||
{
|
||
p->MBInternals.TxState = 14;
|
||
p->MBInternals.ToSend = p->MBInternals.In[3];
|
||
} else if (p->MBInternals.TxState == 14) // send data hi for write C
|
||
{
|
||
p->MBInternals.TxState = 15;
|
||
p->MBInternals.ToSend = p->MBInternals.In[4];
|
||
} else if (p->MBInternals.TxState == 15) // send data hi for write C
|
||
{
|
||
p->MBInternals.TxState = 3;
|
||
p->MBInternals.ToSend = p->MBInternals.In[5];
|
||
} else if (p->MBInternals.TxState == 10) // send length read Coils
|
||
{
|
||
p->MBInternals.TxState = 11;
|
||
p->MBInternals.In[5] = p->MBInternals.In[5]
|
||
+ (p->MBInternals.In[4] << 8);
|
||
p->MBInternals.ToSend = (p->MBInternals.In[5] + 7) >> 3;
|
||
} else if (p->MBInternals.TxState == 11) // send data read Coils
|
||
{
|
||
p->MBInternals.ToSend = 0;
|
||
for (x = 0; x < 8; x++) //Можно упаковать 8 битовых переменных за раз
|
||
{
|
||
if (p->MBInternals.In[5] != 0) //количество запрашиваемых элементов словаря
|
||
{
|
||
//Отправка элемента словаря
|
||
p->MBInternals.ToSend |=
|
||
(((*(Uint16 *) (p->MBInternals.Addr->addr))
|
||
>> (p->MBInternals.Addr->type_or_acc))
|
||
& 0x01) << x;
|
||
p->MBInternals.Addr++;
|
||
p->MBInternals.In[5]--;
|
||
}
|
||
if (p->MBInternals.In[5] == 0) //Если все отправили
|
||
p->MBInternals.TxState = 3;
|
||
}
|
||
} else if (p->MBInternals.TxState == 7) // send length read HR
|
||
{
|
||
p->MBInternals.TxState = 8;
|
||
p->MBInternals.ToSend = p->MBInternals.In[5] << 1;
|
||
} else if (p->MBInternals.TxState == 8) // send HR high word
|
||
{
|
||
p->MBInternals.TxState = 9;
|
||
//x= *p->MBInternals.Addr->addr;
|
||
p->MBInternals.temp = *(Uint16 *) (p->MBInternals.Addr->addr);
|
||
|
||
p->MBInternals.ToSend = p->MBInternals.temp >> 8;
|
||
} else if (p->MBInternals.TxState == 9) // send HR low word
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.temp & 0xFF;
|
||
p->MBInternals.Addr->type_or_acc = MB_VAR_READ;
|
||
p->MBInternals.Addr += 1;
|
||
p->MBInternals.In[5]--; //уменьшение кол-ва запрошенных переменных во входном массиве
|
||
if (p->MBInternals.In[5] == 0) //Если это количество равно нулю (все переменные отправили)
|
||
p->MBInternals.TxState = 3;
|
||
else
|
||
p->MBInternals.TxState = 8;
|
||
} else if (p->MBInternals.TxState == 2) // no such function
|
||
{
|
||
p->MBInternals.ToSend = 1;
|
||
p->MBInternals.TxState = 3;
|
||
} else if (p->MBInternals.TxState == 5) // send error 2
|
||
{
|
||
p->MBInternals.ToSend = 2;
|
||
p->MBInternals.TxState = 3;
|
||
} else if (p->MBInternals.TxState == 6) // send error 3
|
||
{
|
||
p->MBInternals.ToSend = 3;
|
||
p->MBInternals.TxState = 3;
|
||
} else if (p->MBInternals.TxState == 3) // tx crc low
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.TxCRC & 0x00FF;
|
||
p->MBInternals.TxState = 4;
|
||
} else if (p->MBInternals.TxState == 4) // tx crc high
|
||
{
|
||
p->MBInternals.ToSend = p->MBInternals.TxCRC & 0x00FF;
|
||
p->MBInternals.TxState = 0;
|
||
} else
|
||
p->MBInternals.TxState = 0;
|
||
|
||
p->MBInternals.TimeOut = p->MBInternals.TimeOut2_5;
|
||
|
||
ModBus_Tx(p, p->MBInternals.ToSend);
|
||
|
||
p->MBInternals.TxCRC ^= p->MBInternals.ToSend;
|
||
for (RxReg = 0; RxReg < 8; RxReg++)
|
||
if (p->MBInternals.TxCRC & 0x01) {
|
||
p->MBInternals.TxCRC >>= 1;
|
||
p->MBInternals.TxCRC ^= p->MBInternals.MagicNumber;
|
||
} else
|
||
p->MBInternals.TxCRC >>= 1;
|
||
return;
|
||
|
||
}
|
||
}
|
||
|
||
//!Вспомогательная функция поиска
|
||
|
||
//!функция ищет элемент в таблице и возвращает ссылку на него в p->MBInternals.Addr
|
||
//!возвращает:
|
||
//! 0 - если нашла
|
||
//! 3 - если запрос некорректен
|
||
//! 2 - ошибка в пойске
|
||
|
||
//! \memberof TModBus
|
||
int16 ModBus_FindVocAddr(TModBus *p, volatile MB_Record /*const*/Table[],
|
||
Uint16 TableSize, int16 Type, Uint16 Index, int16 NumOfIndexs,
|
||
int16 Mid, int16 Step) {
|
||
int16 x = 0;
|
||
if (Type == 16) {
|
||
if (NumOfIndexs == 0)
|
||
return (3);
|
||
if (NumOfIndexs > 0x7D)
|
||
return (3);
|
||
} else {
|
||
if (NumOfIndexs == 0)
|
||
return (3);
|
||
if (NumOfIndexs > 0x7D0)
|
||
return (3);
|
||
}
|
||
x += Mid;
|
||
Step <<= 1;
|
||
do {
|
||
Step >>= 1;
|
||
if (Index == Table[x].index) {
|
||
if ((x + NumOfIndexs) > TableSize)
|
||
return (2);
|
||
p->MBInternals.Addr = &Table[x];
|
||
return (0);
|
||
} else if (Index < Table[x].index)
|
||
x -= Step;
|
||
else {
|
||
x += Step;
|
||
if (x >= TableSize)
|
||
x = (TableSize - 1);
|
||
}
|
||
} while (Step != 0);
|
||
return (2);;
|
||
}
|
||
|
||
/*@}*/
|
||
|