468 lines
16 KiB
C
468 lines
16 KiB
C
/**
|
||
*******************************************************************************
|
||
* @file modbus_slave.c
|
||
* @brief Модуль для реализации слейв MODBUS.
|
||
*******************************************************************************
|
||
* @details
|
||
Файл содержит реализацию функций для работы Modbus в режиме слейва.
|
||
|
||
@section slave Функции и макросы
|
||
|
||
- MB_Slave_Response() — Ответ на запрос
|
||
- MB_Slave_Collect_Message() — Сбор сообщения в режиме слейва.
|
||
- MB_Slave_Parse_Message() — Парс сообщения в режиме слейва.
|
||
******************************************************************************/
|
||
#include "modbus.h"
|
||
|
||
#ifdef MODBUS_ENABLE_SLAVE
|
||
/**
|
||
* @brief Ответ на сообщение в режиме слейва.
|
||
* @param hmodbus Указатель на хендлер RS.
|
||
* @param modbus_msg Указатель на структуру сообщения.
|
||
* @return RS_RES Статус о результате ответа на комманду.
|
||
*/
|
||
RS_StatusTypeDef MB_Slave_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
|
||
{
|
||
RS_StatusTypeDef MB_RES = 0;
|
||
hmodbus->f.MessageHandled = 0;
|
||
hmodbus->f.EchoResponse = 0;
|
||
RS_Reset_TX_Flags(hmodbus); // reset flag for correct transmit
|
||
|
||
MB_Diagnostics_BusMessageCnt();
|
||
if(hmodbus->ID == 0 || modbus_msg->DeviceAddr == 0)
|
||
{
|
||
MB_Diagnostics_SlaveNoResponseCnt(); // <-- Устройство не отвечает на широковещательные сообщения
|
||
hmodbus->RS_STATUS = RS_SKIP;
|
||
return RS_Handle_Receive_Start(hmodbus, modbus_msg);
|
||
}
|
||
MB_Diagnostics_SlaveMessageCnt();
|
||
|
||
if(modbus_msg->FuncCode < FC_ERR_VALUES_START)// if no errors after parsing
|
||
{
|
||
switch (modbus_msg->FuncCode)
|
||
{
|
||
// Read Coils
|
||
case FC_R_COILS:
|
||
hmodbus->f.MessageHandled = MB_Process_Read_Coils(hmodbus->pMessagePtr);
|
||
break;
|
||
|
||
// Read Hodling Registers
|
||
case FC_R_HOLD_REGS:
|
||
hmodbus->f.MessageHandled = MB_Process_Read_Hold_Regs(hmodbus->pMessagePtr);
|
||
break;
|
||
case FC_R_IN_REGS:
|
||
hmodbus->f.MessageHandled = MB_Process_Read_Input_Regs(hmodbus->pMessagePtr);
|
||
break;
|
||
|
||
|
||
// Write Single Coils
|
||
case FC_W_COIL:
|
||
hmodbus->f.MessageHandled = MB_Process_Write_Single_Coil(hmodbus->pMessagePtr);
|
||
if(hmodbus->f.MessageHandled)
|
||
{
|
||
hmodbus->f.DataUpdated = 1;
|
||
hmodbus->f.EchoResponse = 1;
|
||
hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes)
|
||
}
|
||
break;
|
||
|
||
case FC_W_HOLD_REG:
|
||
hmodbus->f.MessageHandled = MB_Process_Write_Single_Reg(hmodbus->pMessagePtr);
|
||
if(hmodbus->f.MessageHandled)
|
||
{
|
||
hmodbus->f.DataUpdated = 1;
|
||
hmodbus->f.EchoResponse = 1;
|
||
hmodbus->RS_Message_Size -= 2; // echo response if write ok (minus 2 cause of two CRC bytes)
|
||
}
|
||
break;
|
||
|
||
// Write Multiple Coils
|
||
case FC_W_COILS:
|
||
hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Coils(hmodbus->pMessagePtr);
|
||
if(hmodbus->f.MessageHandled)
|
||
{
|
||
hmodbus->f.DataUpdated = 1;
|
||
hmodbus->f.EchoResponse = 1;
|
||
hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes)
|
||
}
|
||
break;
|
||
|
||
// Write Multiple Registers
|
||
case FC_W_HOLD_REGS:
|
||
hmodbus->f.MessageHandled = MB_Process_Write_Miltuple_Regs(hmodbus->pMessagePtr);
|
||
if(hmodbus->f.MessageHandled)
|
||
{
|
||
hmodbus->f.DataUpdated = 1;
|
||
hmodbus->f.EchoResponse = 1;
|
||
hmodbus->RS_Message_Size = 6; // echo response if write ok (withous data bytes)
|
||
}
|
||
break;
|
||
|
||
case FC_R_DEVICE_ID:
|
||
hmodbus->f.MessageHandled = MB_Process_Read_Device_Identifications(hmodbus->pMessagePtr);
|
||
break;
|
||
|
||
// Добавить в switch-case после других case:
|
||
case FC_R_DIAGNOSTICS:
|
||
hmodbus->f.MessageHandled = MB_Process_Diagnostics(hmodbus->pMessagePtr);
|
||
break;
|
||
|
||
/* unknown func code */
|
||
default:
|
||
modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */
|
||
}
|
||
|
||
|
||
// Проверяем режим устройства - если Listen Only, не обрабатываем команды
|
||
if (MB_GetDeviceMode() == MODBUS_LISTEN_ONLY_MODE)
|
||
{
|
||
MB_Diagnostics_SlaveNoResponseCnt();
|
||
hmodbus->RS_STATUS = RS_SKIP;
|
||
return RS_Handle_Receive_Start(hmodbus, modbus_msg);;
|
||
}
|
||
|
||
// Проверяем статус обработки запроса
|
||
if(hmodbus->f.MessageHandled == 0)
|
||
{
|
||
MB_Diagnostics_ExceptionErrorCnt();
|
||
TrackerCnt_Warn(hmodbus->rs_err);
|
||
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
|
||
}
|
||
else
|
||
{
|
||
TrackerCnt_Ok(hmodbus->rs_err);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
// if we need response - check that transmit isnt busy
|
||
if( RS_Is_TX_Busy(hmodbus) )
|
||
RS_Abort(hmodbus, ABORT_TX); // if tx busy - set it free
|
||
|
||
// Transmit right there, or sets (fDeferredResponse) to transmit response in main code
|
||
if(hmodbus->f.DeferredResponse == 0)
|
||
{
|
||
MB_RES = RS_Handle_Transmit_Start(hmodbus, modbus_msg);
|
||
}
|
||
else
|
||
{
|
||
RS_Handle_Receive_Start(hmodbus, modbus_msg);
|
||
hmodbus->f.DeferredResponse = 0;
|
||
}
|
||
|
||
hmodbus->RS_STATUS = MB_RES;
|
||
return MB_RES;
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* @brief Сбор сообщения в буфер UART в режиме слейв (фрейм слейва из msg -> uart).
|
||
* @param hmodbus Указатель на хендлер RS.
|
||
* @param modbus_msg Указатель на структуру сообщения.
|
||
* @param modbus_uart_buff Указатель на буффер UART.
|
||
* @return RS_RES Статус о результате заполнения буфера.
|
||
*/
|
||
RS_StatusTypeDef MB_Slave_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
|
||
{
|
||
int ind = 0; // ind for modbus-uart buffer
|
||
|
||
if(hmodbus->f.EchoResponse && hmodbus->f.MessageHandled) // if echo response need
|
||
ind = hmodbus->RS_Message_Size;
|
||
else
|
||
{
|
||
//------INFO ABOUT DATA/MESSAGE------
|
||
#ifdef MODBUS_PROTOCOL_TCP
|
||
modbus_uart_buff[ind++] = modbus_msg->TransactionID >> 8;
|
||
modbus_uart_buff[ind++] = modbus_msg->TransactionID& 0xFF;
|
||
|
||
modbus_uart_buff[ind++] = modbus_msg->ProtocolID >> 8;
|
||
modbus_uart_buff[ind++] = modbus_msg->ProtocolID& 0xFF;
|
||
|
||
modbus_uart_buff[ind++] = modbus_msg->PDULength >> 8;
|
||
modbus_uart_buff[ind++] = modbus_msg->PDULength& 0xFF;
|
||
#endif
|
||
//-----------[first bytes]-----------
|
||
// set ID of message/user
|
||
modbus_uart_buff[ind++] = modbus_msg->DeviceAddr;
|
||
|
||
// set dat or err response
|
||
modbus_uart_buff[ind++] = modbus_msg->FuncCode;
|
||
|
||
if (modbus_msg->FuncCode < FC_ERR_VALUES_START) // if no error occur
|
||
{
|
||
// fill modbus header
|
||
if(0) {}
|
||
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // devide identifications header
|
||
{
|
||
modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type;
|
||
modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId;
|
||
modbus_uart_buff[ind++] = modbus_msg->DevId.Conformity;
|
||
modbus_uart_buff[ind++] = modbus_msg->DevId.MoreFollows;
|
||
modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId;
|
||
modbus_uart_buff[ind++] = modbus_msg->DevId.NumbOfObj;
|
||
|
||
if (modbus_msg->ByteCnt > MbData_size*2) // if ByteCnt less than MbData_size
|
||
{
|
||
TrackerCnt_Err(hmodbus->rs_err);
|
||
return RS_COLLECT_MSG_ERR;
|
||
}
|
||
|
||
|
||
//---------------DATA----------------
|
||
//-----------[data bytes]------------
|
||
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->MbData;
|
||
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
|
||
{ // set data
|
||
modbus_uart_buff[ind++] = *tmp_data_addr;
|
||
tmp_data_addr++;
|
||
}
|
||
|
||
}
|
||
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
#ifdef MODBUS_ENABLE_DIAGNOSTICS
|
||
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
|
||
{
|
||
// Diagnostics special format: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[0] >> 8; // Sub-function HI
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[0] & 0xFF; // Sub-function LO
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[1] >> 8; // Data HI
|
||
modbus_uart_buff[ind++] = modbus_msg->MbData[1] & 0xFF; // Data LO
|
||
}
|
||
#endif //MODBUS_ENABLE_DIAGNOSTICS
|
||
else // modbus data header
|
||
{
|
||
// set size of received data
|
||
if (modbus_msg->ByteCnt <= MbData_size*2) // if ByteCnt less than MbData_size
|
||
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
|
||
else // otherwise return data_size err
|
||
{
|
||
TrackerCnt_Err(hmodbus->rs_err);
|
||
return RS_COLLECT_MSG_ERR;
|
||
}
|
||
|
||
//---------------DATA----------------
|
||
//-----------[data bytes]------------
|
||
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->MbData;
|
||
for(int i = 0; i < modbus_msg->ByteCnt; i++) // filling buffer with data
|
||
{ // set data
|
||
if (i%2 == 0) // HI byte
|
||
modbus_uart_buff[ind++] = (*tmp_data_addr)>>8;
|
||
else // LO byte
|
||
{
|
||
modbus_uart_buff[ind++] = *tmp_data_addr;
|
||
tmp_data_addr++;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
else // if some error occur
|
||
{ // send expection code
|
||
modbus_uart_buff[ind++] = modbus_msg->Except_Code;
|
||
}
|
||
}
|
||
if(ind < 0)
|
||
return RS_COLLECT_MSG_ERR;
|
||
|
||
//---------------CRC----------------
|
||
//---------[last 16 bytes]----------
|
||
#ifndef MODBUS_PROTOCOL_TCP
|
||
// calc crc of received data
|
||
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
|
||
// write crc to message structure and modbus-uart buffer
|
||
modbus_msg->MbCRC = CRC_VALUE;
|
||
modbus_uart_buff[ind++] = CRC_VALUE;
|
||
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
|
||
#endif
|
||
|
||
hmodbus->RS_Message_Size = ind;
|
||
|
||
return RS_OK; // returns ok
|
||
}
|
||
|
||
/**
|
||
* @brief Определить размер модбас запроса (СЛЕЙВ версия).
|
||
* @param hRS Указатель на хендлер RS.
|
||
* @param rx_data_size Указатель на переменную для записи кол-ва байт для принятия.
|
||
* @return RS_RES Статус о корректности рассчета кол-ва байт для принятия.
|
||
* @details Определение сколько байтов надо принять по протоколу.
|
||
*/
|
||
static int MB_Define_Size_of_Function(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
|
||
{
|
||
RS_StatusTypeDef MB_RES = 0;
|
||
int mb_func_size = 0;
|
||
|
||
if (modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
|
||
{
|
||
mb_func_size = 1;
|
||
}
|
||
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID)
|
||
{
|
||
mb_func_size = 0;
|
||
}
|
||
else if ((modbus_msg->FuncCode & ~FC_ERR_VALUES_START) < 0x0F)
|
||
{
|
||
mb_func_size = 1;
|
||
}
|
||
else
|
||
{
|
||
mb_func_size = modbus_msg->ByteCnt + 2;
|
||
}
|
||
|
||
|
||
mb_func_size = RS_RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
|
||
return mb_func_size;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Парс сообщения в режиме слейв (фрейм мастера из uart -> msg).
|
||
* @param hmodbus Указатель на хендлер RS.
|
||
* @param modbus_msg Указатель на структуру сообщения.
|
||
* @param modbus_uart_buff Указатель на буффер UART.
|
||
* @return RS_RES Статус о результате заполнения структуры.
|
||
* @details Заполнение структуры сообщения из буффера UART.
|
||
*/
|
||
RS_StatusTypeDef MB_Slave_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
|
||
{
|
||
uint32_t check_empty_buff;
|
||
int ind = 0; // ind for modbus-uart buffer
|
||
hmodbus->f.RX_Continue = 0;
|
||
int expected_size = 0;
|
||
//-----INFO ABOUT DATA/MESSAGE-------
|
||
#ifdef MODBUS_PROTOCOL_TCP
|
||
modbus_msg->TransactionID =modbus_uart_buff[ind++]<<8;
|
||
modbus_msg->TransactionID |=modbus_uart_buff[ind++];
|
||
|
||
modbus_msg->ProtocolID =modbus_uart_buff[ind++]<<8;
|
||
modbus_msg->ProtocolID |=modbus_uart_buff[ind++];
|
||
|
||
modbus_msg->PDULength =modbus_uart_buff[ind++]<<8;
|
||
modbus_msg->PDULength |=modbus_uart_buff[ind++];
|
||
#endif
|
||
//-----------[first bits]------------
|
||
// get ID of message/user
|
||
if(modbus_uart_buff[ind] != hmodbus->ID)
|
||
{
|
||
modbus_msg->DeviceAddr = 0;
|
||
ind++;
|
||
}
|
||
else
|
||
{
|
||
modbus_msg->DeviceAddr = modbus_uart_buff[ind++];
|
||
}
|
||
|
||
// get func code
|
||
modbus_msg->FuncCode = modbus_uart_buff[ind++];
|
||
if(modbus_msg->FuncCode & FC_ERR_VALUES_START) // явная херня
|
||
{
|
||
MB_Diagnostics_SlaveNAKCnt();
|
||
modbus_msg->DeviceAddr = 0;
|
||
return RS_SKIP;
|
||
}
|
||
|
||
if(0) {}
|
||
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // if it device identifications request
|
||
{
|
||
modbus_msg->DevId.MEI_Type = modbus_uart_buff[ind++];
|
||
modbus_msg->DevId.ReadDevId = modbus_uart_buff[ind++];
|
||
modbus_msg->DevId.NextObjId = modbus_uart_buff[ind++];
|
||
modbus_msg->ByteCnt = 0;
|
||
}
|
||
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
|
||
#ifdef MODBUS_ENABLE_DIAGNOSTICS
|
||
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
|
||
{
|
||
// Diagnostics: читаем 4 байта в MbData[0] и MbData[1]
|
||
// Sub-function
|
||
modbus_msg->MbData[0] = modbus_uart_buff[ind++] << 8;
|
||
modbus_msg->MbData[0] |= modbus_uart_buff[ind++];
|
||
// Data
|
||
modbus_msg->MbData[1] = modbus_uart_buff[ind++] << 8;
|
||
modbus_msg->MbData[1] |= modbus_uart_buff[ind++];
|
||
modbus_msg->Addr = 0; // не использует Addr
|
||
modbus_msg->Qnt = 0; // не использует Qnt
|
||
}
|
||
#endif //MODBUS_ENABLE_DIAGNOSTICS
|
||
else // if its classic modbus request
|
||
{
|
||
// get address from CMD
|
||
modbus_msg->Addr = modbus_uart_buff[ind++] << 8;
|
||
modbus_msg->Addr |= modbus_uart_buff[ind++];
|
||
|
||
// get address from CMD
|
||
modbus_msg->Qnt = modbus_uart_buff[ind++] << 8;
|
||
modbus_msg->Qnt |= modbus_uart_buff[ind++];
|
||
}
|
||
|
||
if((hmodbus->pMessagePtr->FuncCode == 0x0F) || (hmodbus->pMessagePtr->FuncCode == 0x10))
|
||
hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++];
|
||
else
|
||
hmodbus->pMessagePtr->ByteCnt = 0;
|
||
|
||
expected_size = MB_Define_Size_of_Function(hmodbus, modbus_msg);
|
||
// если размер меньше ожидаемого - продолжаем принимать
|
||
if(hmodbus->RS_Message_Size < expected_size)
|
||
{
|
||
hmodbus->f.RX_Continue = 1;
|
||
return RS_SKIP;
|
||
}
|
||
// если больше Ошибка
|
||
else if (hmodbus->RS_Message_Size > expected_size)
|
||
{
|
||
MB_Diagnostics_CommunicationErrorCnt();
|
||
return RS_PARSE_MSG_ERR;
|
||
}
|
||
|
||
//---------------DATA----------------
|
||
// (optional)
|
||
if (modbus_msg->ByteCnt != 0)
|
||
{
|
||
//check that data size is correct
|
||
if (modbus_msg->ByteCnt > MbData_size*2)
|
||
{
|
||
TrackerCnt_Err(hmodbus->rs_err);
|
||
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
|
||
MB_Diagnostics_CommunicationErrorCnt();
|
||
return RS_PARSE_MSG_ERR;
|
||
}
|
||
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->MbData;
|
||
for(int i = 0; i < modbus_msg->ByteCnt; i++)
|
||
{ // set data
|
||
if (i%2 == 0)
|
||
*tmp_data_addr = ((uint16_t)modbus_uart_buff[ind++] << 8);
|
||
else
|
||
{
|
||
*tmp_data_addr |= modbus_uart_buff[ind++];
|
||
tmp_data_addr++;
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------CRC----------------
|
||
//----------[last 16 bits]----------
|
||
#ifndef MODBUS_PROTOCOL_TCP
|
||
// calc crc of received data
|
||
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
|
||
// get crc of received data
|
||
modbus_msg->MbCRC = modbus_uart_buff[ind++];
|
||
modbus_msg->MbCRC |= modbus_uart_buff[ind++] << 8;
|
||
// compare crc
|
||
if (modbus_msg->MbCRC != CRC_VALUE)
|
||
{
|
||
MB_Diagnostics_CommunicationErrorCnt();
|
||
TrackerCnt_Err(hmodbus->rs_err);
|
||
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
|
||
}
|
||
#endif
|
||
|
||
return RS_OK;
|
||
}
|
||
|
||
#endif //MODBUS_ENABLE_SLAVE
|