Чет работает

This commit is contained in:
2025-12-16 17:57:59 +03:00
commit 260a6416ad
2906 changed files with 982301 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
#include "__crc_algs.h"
uint32_t CRC_calc;
uint32_t CRC_ref;
//uint16_t CRC_calc;
//uint16_t CRC_ref;
// left this global for debug
uint8_t uchCRCHi = 0xFF;
uint8_t uchCRCLo = 0xFF;
unsigned uIndex;
uint32_t crc32(uint8_t *data, uint32_t data_size)
{
static const unsigned int crc32_table[] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
unsigned int crc = 0xFFFFFFFF;
while (data_size--)
{
crc = (crc >> 8) ^ crc32_table[(crc ^ *data) & 255];
data++;
}
return crc^0xFFFFFFFF;
}
uint16_t crc16(uint8_t *data, uint32_t data_size)
{
/*Table of CRC values for high order byte*/
static unsigned char auchCRCHi[]=
{
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,
0x00,0xC1,0x81,0x40,0x01,0xC0,0x80,0x41,0x01,0xC0,0x80,0x41,0x00,0xC1,0x81,0x40,
};
/*Table of CRC values for low order byte*/
static char auchCRCLo[] =
{
0x00,0xC0,0xC1,0x01,0xC3,0x03,0x02,0xC2,0xC6,0x06,0x07,0xC7,0x05,0xC5,0xC4,0x04,
0xCC,0x0C,0x0D,0xCD,0x0F,0xCF,0xCE,0x0E,0x0A,0xCA,0xCB,0x0B,0xC9,0x09,0x08,0xC8,
0xD8,0x18,0x19,0xD9,0x1B,0xDB,0xDA,0x1A,0x1E,0xDE,0xDF,0x1F,0xDD,0x1D,0x1C,0xDC,
0x14,0xD4,0xD5,0x15,0xD7,0x17,0x16,0xD6,0xD2,0x12,0x13,0xD3,0x11,0xD1,0xD0,0x10,
0xF0,0x30,0x31,0xF1,0x33,0xF3,0xF2,0x32,0x36,0xF6,0xF7,0x37,0xF5,0x35,0x34,0xF4,
0x3C,0xFC,0xFD,0x3D,0xFF,0x3F,0x3E,0xFE,0xFA,0x3A,0x3B,0xFB,0x39,0xF9,0xF8,0x38,
0x28,0xE8,0xE9,0x29,0xEB,0x2B,0x2A,0xEA,0xEE,0x2E,0x2F,0xEF,0x2D,0xED,0xEC,0x2C,
0xE4,0x24,0x25,0xE5,0x27,0xE7,0xE6,0x26,0x22,0xE2,0xE3,0x23,0xE1,0x21,0x20,0xE0,
0xA0,0x60,0x61,0xA1,0x63,0xA3,0xA2,0x62,0x66,0xA6,0xA7,0x67,0xA5,0x65,0x64,0xA4,
0x6C,0xAC,0xAD,0x6D,0xAF,0x6F,0x6E,0xAE,0xAA,0x6A,0x6B,0xAB,0x69,0xA9,0xA8,0x68,
0x78,0xB8,0xB9,0x79,0xBB,0x7B,0x7A,0xBA,0xBE,0x7E,0x7F,0xBF,0x7D,0xBD,0xBC,0x7C,
0xB4,0x74,0x75,0xB5,0x77,0xB7,0xB6,0x76,0x72,0xB2,0xB3,0x73,0xB1,0x71,0x70,0xB0,
0x50,0x90,0x91,0x51,0x93,0x53,0x52,0x92,0x96,0x56,0x57,0x97,0x55,0x95,0x94,0x54,
0x9C,0x5C,0x5D,0x9D,0x5F,0x9F,0x9E,0x5E,0x5A,0x9A,0x9B,0x5B,0x99,0x59,0x58,0x98,
0x88,0x48,0x49,0x89,0x4B,0x8B,0x8A,0x4A,0x4E,0x8E,0x8F,0x4F,0x8D,0x4D,0x4C,0x8C,
0x44,0x84,0x85,0x45,0x87,0x47,0x46,0x86,0x82,0x42,0x43,0x83,0x41,0x81,0x80,0x40,
};
uchCRCHi = 0xFF;
uchCRCLo = 0xFF;
/* CRC Generation Function */
while( data_size--) /* pass through message buffer */
{
uIndex = uchCRCHi ^ *data++; /* calculate the CRC */
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
uchCRCLo = auchCRCLo[uIndex];
}
return uchCRCHi | uchCRCLo<<8;
}

View File

@@ -0,0 +1,7 @@
/**
*******************************************************************************
* @file __modbus_compat.c
* @brief Модуль для совместимости библиотеки MODBUS.
*******************************************************************************
******************************************************************************/
#include "modbus.h"

213
AllLibs/Modbus/Src/modbus.c Normal file
View File

@@ -0,0 +1,213 @@
/**
*******************************************************************************
* @file modbus.c
* @brief Модуль для реализации MODBUS.
*******************************************************************************
* @details
Файл содержит реализацию функций работы с Modbus.
@section mbapi Функции и макросы
### Инициализация:
- MODBUS_FirstInit() — Инициализация Modbus (подключение UART, TIM)
- MODBUS_Config() — Конфигурацмя Modbus (ID, Timeout).
- MODBUS_SlaveStart() — Запуск Modbus как Slave.
- MODBUS_MasterRequest() — Отправить запрос в MODBUS как Master.
### Функции для работы с RS (UART):
- RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения.
- RS_Response() — Отправка ответа.
******************************************************************************/
#include "modbus.h"
/* MODBUS HANDLES */
RS_HandleTypeDef hmodbus1; ///< Default Handle for Modbus
RS_MsgTypeDef MODBUS_MSG; ///< Default Message Struct for Modbus
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg);
//-------------------------------------------------------------------
//-----------------------------FOR USER------------------------------
/**
* @brief Инициализация периферии модбас.
* @param hmodbus Указатель на хендлер RS
* @param huart Указатель на хендлер UART
* @param htim Указатель на хендлер TIM
* @details Подключает хендлы периферии к hmodbus
* Конфигурация выставляется по умолчанию из modbus_config.h
*/
HAL_StatusTypeDef MODBUS_FirstInit(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim)
{
if((hmodbus == NULL) || (huart == NULL))
{
return HAL_ERROR;
}
MB_DeviceInentificationInit();
//-----------SETUP MODBUS-------------
// set up modbus: MB_RX_Size_NotConst and Timeout enable
hmodbus->ID = MODBUS_DEVICE_ID;
hmodbus->sRS_Timeout = MODBUS_TIMEOUT;
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
// INIT
hmodbus->RS_STATUS = RS_Init(hmodbus, huart, htim, 0);
RS_EnableReceive();
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
else
return HAL_ERROR;
}
/**
* @brief Программная конфигурация модбас.
* @param hmodbus указатель на хендлер RS
* @param Timeout Время тишины между двумя байтами после которых перезапускается прием
* @param master Режим мастер (пока не сделан)
* @details Конфигурирует ID, таймаут и режим hmodbus
*/
HAL_StatusTypeDef MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master)
{
if(hmodbus == NULL)
{
return HAL_ERROR;
}
if(!master)
{
if((ID < 1) || (ID > 247))
{
return HAL_ERROR;
}
hmodbus->ID = ID;
}
else
hmodbus->ID = 0;
hmodbus->sRS_Timeout = Timeout;
if(master)
hmodbus->sRS_Mode = RS_MASTER_REQUEST;
else
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
return HAL_OK;
}
/**
* @brief Запуск слейв модбас.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
(NULL чтобы использовать дефолтную)
* @details Конфигурирует ID, таймаут и режим hmodbus
*/
HAL_StatusTypeDef MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
if(hmodbus == NULL)
{
return HAL_ERROR;
}
if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
{
return HAL_ERROR;
}
MB_DiagnosticsInit();
if(modbus_msg)
hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, modbus_msg);
else
hmodbus->RS_STATUS = RS_Receive_IT(hmodbus, &MODBUS_MSG);
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
else
return HAL_ERROR;
}
/**
* @brief Реквест мастера модбас.
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения
* @details Конфигурирует ID, таймаут и режим hmodbus
*/
HAL_StatusTypeDef MODBUS_MasterRequest(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, void (*pClbk)(RS_HandleTypeDef*, RS_MsgTypeDef*))
{
if(hmodbus == NULL)
{
return HAL_ERROR;
}
if(modbus_msg == NULL)
{
return HAL_ERROR;
}
if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{
return HAL_ERROR;
}
if(hmodbus->f.RS_Busy)
return HAL_BUSY;
if(pClbk) // если задан используем пользовательский коллбек
hmodbus->pCallback = (void (*)(void*, void*))(pClbk);
else // иначе дефолтный
hmodbus->pCallback = (void (*)(void*, void*))(&MB_DefaultCallback);
hmodbus->RS_STATUS = RS_Transmit_IT(hmodbus, modbus_msg);
if(hmodbus->RS_STATUS == RS_OK)
return HAL_OK;
else
return HAL_ERROR;
}
//-------------------------------------------------------------------
//-----------------------------INTERNAL------------------------------
/**
* @brief Дефолтный коллбек для мастера.
* @param hmodbus Указатель на хендлер RS
* @param modbus_msg Указатель на структуру сообщения
* @details В этот коллбек попадут все запросы, с NULL-коллбеком
*/
static void MB_DefaultCallback(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
__NOP();
return;
}
/* Реализация функций из rs_message.c для протокола */
RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
{
if(hmodbus->sRS_Mode >= RS_MASTER_MODE_START)
{
return RS_ERR;
}
return MB_Slave_Response(hmodbus, modbus_msg);
}
RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{
return MB_Slave_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff);
}
else
{
return MB_Master_Collect_Message(hmodbus, modbus_msg, modbus_uart_buff);
}
}
RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
if(hmodbus->sRS_Mode < RS_MASTER_MODE_START)
{
return MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
}
else
{
return MB_Master_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
}
}

View File

@@ -0,0 +1,248 @@
/**
*******************************************************************************
* @file modbus_coils.c
* @brief Реализация работы с коилами Modbus
*******************************************************************************
* @details
Модуль для доступа к coils внутри программы:
- Функции для доступа к coils по глобальным адресам
- Макросы для доступа к coils по локальным адресам
Модуль обработки команд для coils (битовых данных):
- Чтение coils (0x01) - упаковка битов в байты для передачи
- Запись одиночного coil (0x05) - установка/сброс бита
- Запись множественных coils (0x0F) - распаковка битов из байтов
@section cvalid Валидация данных:
- Проверка соответствия количества байт и регистров
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
******************************************************************************/
#include "modbus_coils.h"
#ifdef MODBUS_ENABLE_COILS
/**
* @brief Выставить/сбросить коил по глобальному адресу.
* @param Addr Адрес коила.
* @param WriteVal Что записать в коил: 0 или 1.
* @return ExceptionCode Код исключения если коила по адресу не существует, и ET_NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
Вне зависимости от того как коилы размещены в памяти.
*/
MB_ExceptionTypeDef MB_Coil_Write_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
uint16_t *coils;
uint16_t start_shift = 0; // shift in coils register
//------------WRITE COIL-------------
Exception = MB_DefineCoilsAddress(&coils, Addr, 1, &start_shift, 1);
if(Exception == ET_NO_ERRORS)
{
switch(WriteVal)
{
case SET_COIL:
*coils |= (1<<start_shift);
break;
case RESET_COIL:
*coils &= ~(1<<start_shift);
break;
case TOOGLE_COIL:
*coils ^= (1<<start_shift);
break;
}
}
return Exception;
}
/**
* @brief Считать коил по глобальному адресу.
* @param Addr Адрес коила.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает весь регистр с маской на запрошенном коиле.
*
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
Вне зависимости от того как коилы размещены в памяти.
*/
uint16_t MB_Coil_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception_tmp;
if(Exception == NULL) // if exception is not given to func fill it
Exception = &Exception_tmp;
uint16_t *coils;
uint16_t start_shift = 0; // shift in coils register
//------------READ COIL--------------
*Exception = MB_DefineCoilsAddress(&coils, Addr, 1, &start_shift, 0);
if(*Exception == ET_NO_ERRORS)
{
return ((*coils)&(1<<start_shift));
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Coils (01 - 0x01).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Read Coils.
*/
uint8_t MB_Process_Read_Coils(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
uint16_t *coils;
uint16_t start_shift = 0; // shift in coils register
modbus_msg->Except_Code = MB_DefineCoilsAddress(&coils, modbus_msg->Addr, modbus_msg->Qnt, &start_shift, 0);
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------READING COIL------------
// setup output message data size
modbus_msg->ByteCnt = Divide_Up(modbus_msg->Qnt, 8);
// create mask for coils
uint16_t mask_for_coils = 0; // mask for coils that've been chosen
uint16_t setted_coils = 0; // value of setted coils
uint16_t temp_reg = 0; // temp register for saving coils that hasnt been chosen
uint16_t coil_cnt = 0; // counter for processed coils
// cycle until all registers with requered coils would be processed
int shift = start_shift; // set shift to first coil in first register
int ind = 0; // index for coils registers and data
for(; ind <= Divide_Up(start_shift + modbus_msg->Qnt, 16); ind++)
{
//----SET MASK FOR COILS REGISTER----
mask_for_coils = 0;
for(; shift < 0x10; shift++)
{
mask_for_coils |= 1<<(shift); // choose certain coil
if(++coil_cnt >= modbus_msg->Qnt)
break;
}
shift = 0; // set shift to zero for the next step
//-----------READ COILS--------------
modbus_msg->MbData[ind] = (*(coils+ind)&mask_for_coils) >> start_shift;
if(ind > 0)
modbus_msg->MbData[ind-1] |= ((*(coils+ind)&mask_for_coils) << 16) >> start_shift;
}
// т.к. MbData 16-битная, для 8-битной передачи, надо поменять местами верхний и нижний байты
for(; ind >= 0; --ind)
modbus_msg->MbData[ind] = ByteSwap16(modbus_msg->MbData[ind]);
return 1;
}
/**
* @brief Обработать функцию Write Single Coils (05 - 0x05).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Single Coils.
*/
uint8_t MB_Process_Write_Single_Coil(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00))
{
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
return 0;
}
// define position of coil
uint16_t *coils;
uint16_t start_shift = 0; // shift in coils register
modbus_msg->Except_Code = MB_DefineCoilsAddress(&coils, modbus_msg->Addr, 0, &start_shift, 1);
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//----------WRITTING COIL------------
if(modbus_msg->Qnt == 0xFF00)
*(coils) |= 1<<start_shift; // write flags corresponding to received data
else
*(coils) &= ~(1<<start_shift); // write flags corresponding to received data
return 1;
}
/**
* @brief Обработать функцию Write Multiple Coils (15 - 0x0F).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Multiple Coils.
*/
uint8_t MB_Process_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
if (modbus_msg->ByteCnt != Divide_Up(modbus_msg->Qnt, 8))
{ // if quantity too large OR if quantity and bytes count arent match
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
return 0;
}
// define position of coil
uint16_t *coils; // pointer to coils
uint16_t start_shift = 0; // shift in coils register
modbus_msg->Except_Code = MB_DefineCoilsAddress(&coils, modbus_msg->Addr, modbus_msg->Qnt, &start_shift, 1);
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//----------WRITTING COILS-----------
// create mask for coils
uint16_t mask_for_coils = 0; // mask for coils that've been chosen
uint32_t setted_coils = 0; // value of setted coils
uint16_t temp_reg = 0; // temp register for saving coils that hasnt been chosen
uint16_t coil_cnt = 0; // counter for processed coils
// cycle until all registers with requered coils would be processed
int shift = start_shift; // set shift to first coil in first register
for(int ind = 0; ind <= Divide_Up(start_shift + modbus_msg->Qnt, 16); ind++)
{
//----SET MASK FOR COILS REGISTER----
mask_for_coils = 0;
for(; shift < 0x10; shift++)
{
mask_for_coils |= 1<<(shift); // choose certain coil
if(++coil_cnt >= modbus_msg->Qnt)
break;
}
shift = 0; // set shift to zero for the next step
//-----------WRITE COILS-------------
// get current coils
temp_reg = *(coils+ind);
// set coils
setted_coils = ByteSwap16(modbus_msg->MbData[ind]) << start_shift;
if(ind > 0)
{
setted_coils |= ((ByteSwap16(modbus_msg->MbData[ind-1]) << start_shift) >> 16);
}
// write coils
*(coils+ind) = setted_coils & mask_for_coils;
// restore untouched coils
*(coils+ind) |= temp_reg&(~mask_for_coils);
if(coil_cnt >= modbus_msg->Qnt) // if all coils written - break cycle
break; // *kind of unnecessary
}
return 1;
}
#endif //MODBUS_ENABLE_COILS

View File

@@ -0,0 +1,10 @@
/**
*******************************************************************************
* @file modbus_core.c
* @brief Базовая реализация ядра Modbus
*******************************************************************************
* @details
В текущей реализации этот файл служит заглушкой для будущего расширения
функциональности ядра Modbus протокола.
******************************************************************************/
#include "modbus_core.h"

View File

@@ -0,0 +1,573 @@
/**
*******************************************************************************
* @file modbus_devid.c
* @brief Реализация идентификаторов устройства Modbus
*******************************************************************************
* @details
Модуль обработки запросов идентификации устройства через MEI-тип 0x0E:
- Формирование иерархии объектов идентификации
- Поддержка потоковой передачи при большом количестве объектов
- Автоматический расчет MoreFollows флагов
@section stream Потоковая передача:
При большом количестве объектов идентификация разбивается на несколько
сообщений с установкой флага MoreFollows и указанием NextObjId для
продолжения чтения в следующем запросе.
******************************************************************************/
#include "modbus_devid.h"
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
MB_DeviceIdentificationsTypeDef MB_DEVID; ///< Глобальная структура идентификаторов устройства
/**
* @brief Записать Один Объект Идентификатора в массив данных
* @param mbdata Указатель на массив данных в структуре RS_MsgTypeDef.
* @return obj Объект для записи.
*/
void MB_WriteSingleObjectToMessage(char *mbdata, unsigned *ind, MB_DeviceObjectTypeDef *obj)
{
mbdata[(*ind)++] = obj->length;
for (int i = 0; i < obj->length; i++)
{
mbdata[(*ind)++] = obj->name[i];
}
}
/**
* @brief Записать Массив Объектов Идентификатора в массив данных
* @param mbdata Указатель на массив данных в структуре RS_MsgTypeDef.
* @return obj Объект для записи.
*/
void MB_WriteObjectsToMessage(RS_MsgTypeDef *modbus_msg, unsigned maxidofobj)
{
MB_DeviceObjectTypeDef *obj = (MB_DeviceObjectTypeDef *)&MB_DEVID;
unsigned objidtmp = modbus_msg->DevId.NextObjId;
modbus_msg->Except_Code = ET_NO_ERRORS;
/* Define number of object in one message */
unsigned lastobjid = 0;
for(int i = 0; i < MbData_size*2;)
{
/* Если объект за пределами допутимого - выходим из цикла */
if(objidtmp >= 0xFF + MODBUS_NUMB_OF_USEROBJECTS)
break;
i += 2;
i += obj[objidtmp].length;
/* Если все еще помещается в массив переходим на следующий объект */
if(i < MbData_size*2)
{
objidtmp++;
}
/* Если объекты для записи закончились - выходим из цикла*/
if(objidtmp > maxidofobj)
break;
}
lastobjid = objidtmp-1;
/* Fill message with objects data */
char *mbdata = (char *)&modbus_msg->MbData;
unsigned ind = 0;
unsigned objid = modbus_msg->DevId.NextObjId;
for(; objid <= lastobjid; objid++)
{
mbdata[ind++] = objid;
MB_WriteSingleObjectToMessage(mbdata, &ind, &obj[objid]);
}
objid--;
if(modbus_msg->ByteCnt != 0)
{
modbus_msg->ByteCnt = ind;
modbus_msg->DevId.NextObjId = lastobjid+1;
if(objid == maxidofobj)
{
modbus_msg->DevId.MoreFollows = 0;
}
else
{
modbus_msg->DevId.MoreFollows = 0xFF;
}
}
else
{
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
}
}
/**
* @brief Обработать функцию Read Device Identifications (43/14 - 0x2B/0E).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Single Register.
*/
uint8_t MB_Process_Read_Device_Identifications(RS_MsgTypeDef *modbus_msg)
{
modbus_msg->DevId.Conformity = MODBUS_DEVICE_CONFORMITY;
switch(modbus_msg->DevId.ReadDevId)
{
case RID_BASIC_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 0;
}
MB_WriteObjectsToMessage(modbus_msg, 2);
modbus_msg->DevId.NumbOfObj = 3;
break;
case RID_REGULAR_IDENTIFICATIONS:
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 3;
}
MB_WriteObjectsToMessage(modbus_msg, 6);
modbus_msg->DevId.NumbOfObj = 4;
break;
case RID_EXTENDED_IDENTIFICATIONS:
if(MODBUS_NUMB_OF_USEROBJECTS <= 0 || MODBUS_NUMB_OF_USEROBJECTS > 128)
{
modbus_msg->Except_Code = ET_ILLEGAL_DATA_VALUE;
break;
}
if (modbus_msg->DevId.NextObjId == 0)
{
modbus_msg->DevId.NextObjId = 0x80;
}
MB_WriteObjectsToMessage(modbus_msg, 0x80+MODBUS_NUMB_OF_USEROBJECTS);
modbus_msg->DevId.NumbOfObj = MODBUS_NUMB_OF_USEROBJECTS;
break;
case RID_SPEDIFIC_IDENTIFICATIONS:
MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId);
modbus_msg->DevId.NumbOfObj = 1;
break;
default:
return 0;
}
if(modbus_msg->Except_Code != ET_NO_ERRORS)
{
return 0;
}
else
{
return 1;
}
}
/**
* @brief Инициализация идентификаторов.
*/
void MB_DeviceInentificationInit(void)
{
MB_ObjectInit(&MB_DEVID.VendorName, MODBUS_VENDOR_NAME);
MB_ObjectInit(&MB_DEVID.ProductCode, MODBUS_PRODUCT_CODE);
MB_ObjectInit(&MB_DEVID.Revision, MODBUS_REVISION);
MB_ObjectInit(&MB_DEVID.VendorUrl, MODBUS_VENDOR_URL);
MB_ObjectInit(&MB_DEVID.ProductName, MODBUS_PRODUCT_NAME);
MB_ObjectInit(&MB_DEVID.ModelName, MODBUS_MODEL_NAME);
#if defined(MODBUS_USEROBJECT_0_NAME) && MODBUS_NUMB_OF_USEROBJECTS>0
MB_ObjectInit(&MB_DEVID.User[0], MODBUS_USEROBJECT_0_NAME);
#endif
#if defined(MODBUS_USEROBJECT_1_NAME) && MODBUS_NUMB_OF_USEROBJECTS>1
MB_ObjectInit(&MB_DEVID.User[1], MODBUS_USEROBJECT_1_NAME);
#endif
#if defined(MODBUS_USEROBJECT_2_NAME) && MODBUS_NUMB_OF_USEROBJECTS>2
MB_ObjectInit(&MB_DEVID.User[2], MODBUS_USEROBJECT_2_NAME);
#endif
#if defined(MODBUS_USEROBJECT_3_NAME) && MODBUS_NUMB_OF_USEROBJECTS>3
MB_ObjectInit(&MB_DEVID.User[3], MODBUS_USEROBJECT_3_NAME);
#endif
#if defined(MODBUS_USEROBJECT_4_NAME) && MODBUS_NUMB_OF_USEROBJECTS>4
MB_ObjectInit(&MB_DEVID.User[4], MODBUS_USEROBJECT_4_NAME);
#endif
#if defined(MODBUS_USEROBJECT_5_NAME) && MODBUS_NUMB_OF_USEROBJECTS>5
MB_ObjectInit(&MB_DEVID.User[5], MODBUS_USEROBJECT_5_NAME);
#endif
#if defined(MODBUS_USEROBJECT_6_NAME) && MODBUS_NUMB_OF_USEROBJECTS>6
MB_ObjectInit(&MB_DEVID.User[6], MODBUS_USEROBJECT_6_NAME);
#endif
#if defined(MODBUS_USEROBJECT_7_NAME) && MODBUS_NUMB_OF_USEROBJECTS>7
MB_ObjectInit(&MB_DEVID.User[7], MODBUS_USEROBJECT_7_NAME);
#endif
#if defined(MODBUS_USEROBJECT_8_NAME) && MODBUS_NUMB_OF_USEROBJECTS>8
MB_ObjectInit(&MB_DEVID.User[8], MODBUS_USEROBJECT_8_NAME);
#endif
#if defined(MODBUS_USEROBJECT_9_NAME) && MODBUS_NUMB_OF_USEROBJECTS>9
MB_ObjectInit(&MB_DEVID.User[9], MODBUS_USEROBJECT_9_NAME);
#endif
#if defined(MODBUS_USEROBJECT_10_NAME) && MODBUS_NUMB_OF_USEROBJECTS>10
MB_ObjectInit(&MB_DEVID.User[10], MODBUS_USEROBJECT_10_NAME);
#endif
#if defined(MODBUS_USEROBJECT_11_NAME) && MODBUS_NUMB_OF_USEROBJECTS>11
MB_ObjectInit(&MB_DEVID.User[11], MODBUS_USEROBJECT_11_NAME);
#endif
#if defined(MODBUS_USEROBJECT_12_NAME) && MODBUS_NUMB_OF_USEROBJECTS>12
MB_ObjectInit(&MB_DEVID.User[12], MODBUS_USEROBJECT_12_NAME);
#endif
#if defined(MODBUS_USEROBJECT_13_NAME) && MODBUS_NUMB_OF_USEROBJECTS>13
MB_ObjectInit(&MB_DEVID.User[13], MODBUS_USEROBJECT_13_NAME);
#endif
#if defined(MODBUS_USEROBJECT_14_NAME) && MODBUS_NUMB_OF_USEROBJECTS>14
MB_ObjectInit(&MB_DEVID.User[14], MODBUS_USEROBJECT_14_NAME);
#endif
#if defined(MODBUS_USEROBJECT_15_NAME) && MODBUS_NUMB_OF_USEROBJECTS>15
MB_ObjectInit(&MB_DEVID.User[15], MODBUS_USEROBJECT_15_NAME);
#endif
#if defined(MODBUS_USEROBJECT_16_NAME) && MODBUS_NUMB_OF_USEROBJECTS>16
MB_ObjectInit(&MB_DEVID.User[16], MODBUS_USEROBJECT_16_NAME);
#endif
#if defined(MODBUS_USEROBJECT_17_NAME) && MODBUS_NUMB_OF_USEROBJECTS>17
MB_ObjectInit(&MB_DEVID.User[17], MODBUS_USEROBJECT_17_NAME);
#endif
#if defined(MODBUS_USEROBJECT_18_NAME) && MODBUS_NUMB_OF_USEROBJECTS>18
MB_ObjectInit(&MB_DEVID.User[18], MODBUS_USEROBJECT_18_NAME);
#endif
#if defined(MODBUS_USEROBJECT_19_NAME) && MODBUS_NUMB_OF_USEROBJECTS>19
MB_ObjectInit(&MB_DEVID.User[19], MODBUS_USEROBJECT_19_NAME);
#endif
#if defined(MODBUS_USEROBJECT_20_NAME) && MODBUS_NUMB_OF_USEROBJECTS>20
MB_ObjectInit(&MB_DEVID.User[20], MODBUS_USEROBJECT_20_NAME);
#endif
#if defined(MODBUS_USEROBJECT_21_NAME) && MODBUS_NUMB_OF_USEROBJECTS>21
MB_ObjectInit(&MB_DEVID.User[21], MODBUS_USEROBJECT_21_NAME);
#endif
#if defined(MODBUS_USEROBJECT_22_NAME) && MODBUS_NUMB_OF_USEROBJECTS>22
MB_ObjectInit(&MB_DEVID.User[22], MODBUS_USEROBJECT_22_NAME);
#endif
#if defined(MODBUS_USEROBJECT_23_NAME) && MODBUS_NUMB_OF_USEROBJECTS>23
MB_ObjectInit(&MB_DEVID.User[23], MODBUS_USEROBJECT_23_NAME);
#endif
#if defined(MODBUS_USEROBJECT_24_NAME) && MODBUS_NUMB_OF_USEROBJECTS>24
MB_ObjectInit(&MB_DEVID.User[24], MODBUS_USEROBJECT_24_NAME);
#endif
#if defined(MODBUS_USEROBJECT_25_NAME) && MODBUS_NUMB_OF_USEROBJECTS>25
MB_ObjectInit(&MB_DEVID.User[25], MODBUS_USEROBJECT_25_NAME);
#endif
#if defined(MODBUS_USEROBJECT_26_NAME) && MODBUS_NUMB_OF_USEROBJECTS>26
MB_ObjectInit(&MB_DEVID.User[26], MODBUS_USEROBJECT_26_NAME);
#endif
#if defined(MODBUS_USEROBJECT_27_NAME) && MODBUS_NUMB_OF_USEROBJECTS>27
MB_ObjectInit(&MB_DEVID.User[27], MODBUS_USEROBJECT_27_NAME);
#endif
#if defined(MODBUS_USEROBJECT_28_NAME) && MODBUS_NUMB_OF_USEROBJECTS>28
MB_ObjectInit(&MB_DEVID.User[28], MODBUS_USEROBJECT_28_NAME);
#endif
#if defined(MODBUS_USEROBJECT_29_NAME) && MODBUS_NUMB_OF_USEROBJECTS>29
MB_ObjectInit(&MB_DEVID.User[29], MODBUS_USEROBJECT_29_NAME);
#endif
#if defined(MODBUS_USEROBJECT_30_NAME) && MODBUS_NUMB_OF_USEROBJECTS>30
MB_ObjectInit(&MB_DEVID.User[30], MODBUS_USEROBJECT_30_NAME);
#endif
#if defined(MODBUS_USEROBJECT_31_NAME) && MODBUS_NUMB_OF_USEROBJECTS>31
MB_ObjectInit(&MB_DEVID.User[31], MODBUS_USEROBJECT_31_NAME);
#endif
#if defined(MODBUS_USEROBJECT_32_NAME) && MODBUS_NUMB_OF_USEROBJECTS>32
MB_ObjectInit(&MB_DEVID.User[32], MODBUS_USEROBJECT_32_NAME);
#endif
#if defined(MODBUS_USEROBJECT_33_NAME) && MODBUS_NUMB_OF_USEROBJECTS>33
MB_ObjectInit(&MB_DEVID.User[33], MODBUS_USEROBJECT_33_NAME);
#endif
#if defined(MODBUS_USEROBJECT_34_NAME) && MODBUS_NUMB_OF_USEROBJECTS>34
MB_ObjectInit(&MB_DEVID.User[34], MODBUS_USEROBJECT_34_NAME);
#endif
#if defined(MODBUS_USEROBJECT_35_NAME) && MODBUS_NUMB_OF_USEROBJECTS>35
MB_ObjectInit(&MB_DEVID.User[35], MODBUS_USEROBJECT_35_NAME);
#endif
#if defined(MODBUS_USEROBJECT_36_NAME) && MODBUS_NUMB_OF_USEROBJECTS>36
MB_ObjectInit(&MB_DEVID.User[36], MODBUS_USEROBJECT_36_NAME);
#endif
#if defined(MODBUS_USEROBJECT_37_NAME) && MODBUS_NUMB_OF_USEROBJECTS>37
MB_ObjectInit(&MB_DEVID.User[37], MODBUS_USEROBJECT_37_NAME);
#endif
#if defined(MODBUS_USEROBJECT_38_NAME) && MODBUS_NUMB_OF_USEROBJECTS>38
MB_ObjectInit(&MB_DEVID.User[38], MODBUS_USEROBJECT_38_NAME);
#endif
#if defined(MODBUS_USEROBJECT_39_NAME) && MODBUS_NUMB_OF_USEROBJECTS>39
MB_ObjectInit(&MB_DEVID.User[39], MODBUS_USEROBJECT_39_NAME);
#endif
#if defined(MODBUS_USEROBJECT_40_NAME) && MODBUS_NUMB_OF_USEROBJECTS>40
MB_ObjectInit(&MB_DEVID.User[40], MODBUS_USEROBJECT_40_NAME);
#endif
#if defined(MODBUS_USEROBJECT_41_NAME) && MODBUS_NUMB_OF_USEROBJECTS>41
MB_ObjectInit(&MB_DEVID.User[41], MODBUS_USEROBJECT_41_NAME);
#endif
#if defined(MODBUS_USEROBJECT_42_NAME) && MODBUS_NUMB_OF_USEROBJECTS>42
MB_ObjectInit(&MB_DEVID.User[42], MODBUS_USEROBJECT_42_NAME);
#endif
#if defined(MODBUS_USEROBJECT_43_NAME) && MODBUS_NUMB_OF_USEROBJECTS>43
MB_ObjectInit(&MB_DEVID.User[43], MODBUS_USEROBJECT_43_NAME);
#endif
#if defined(MODBUS_USEROBJECT_44_NAME) && MODBUS_NUMB_OF_USEROBJECTS>44
MB_ObjectInit(&MB_DEVID.User[44], MODBUS_USEROBJECT_44_NAME);
#endif
#if defined(MODBUS_USEROBJECT_45_NAME) && MODBUS_NUMB_OF_USEROBJECTS>45
MB_ObjectInit(&MB_DEVID.User[45], MODBUS_USEROBJECT_45_NAME);
#endif
#if defined(MODBUS_USEROBJECT_46_NAME) && MODBUS_NUMB_OF_USEROBJECTS>46
MB_ObjectInit(&MB_DEVID.User[46], MODBUS_USEROBJECT_46_NAME);
#endif
#if defined(MODBUS_USEROBJECT_47_NAME) && MODBUS_NUMB_OF_USEROBJECTS>47
MB_ObjectInit(&MB_DEVID.User[47], MODBUS_USEROBJECT_47_NAME);
#endif
#if defined(MODBUS_USEROBJECT_48_NAME) && MODBUS_NUMB_OF_USEROBJECTS>48
MB_ObjectInit(&MB_DEVID.User[48], MODBUS_USEROBJECT_48_NAME);
#endif
#if defined(MODBUS_USEROBJECT_49_NAME) && MODBUS_NUMB_OF_USEROBJECTS>49
MB_ObjectInit(&MB_DEVID.User[49], MODBUS_USEROBJECT_49_NAME);
#endif
#if defined(MODBUS_USEROBJECT_50_NAME) && MODBUS_NUMB_OF_USEROBJECTS>50
MB_ObjectInit(&MB_DEVID.User[50], MODBUS_USEROBJECT_50_NAME);
#endif
#if defined(MODBUS_USEROBJECT_51_NAME) && MODBUS_NUMB_OF_USEROBJECTS>51
MB_ObjectInit(&MB_DEVID.User[51], MODBUS_USEROBJECT_51_NAME);
#endif
#if defined(MODBUS_USEROBJECT_52_NAME) && MODBUS_NUMB_OF_USEROBJECTS>52
MB_ObjectInit(&MB_DEVID.User[52], MODBUS_USEROBJECT_52_NAME);
#endif
#if defined(MODBUS_USEROBJECT_53_NAME) && MODBUS_NUMB_OF_USEROBJECTS>53
MB_ObjectInit(&MB_DEVID.User[53], MODBUS_USEROBJECT_53_NAME);
#endif
#if defined(MODBUS_USEROBJECT_54_NAME) && MODBUS_NUMB_OF_USEROBJECTS>54
MB_ObjectInit(&MB_DEVID.User[54], MODBUS_USEROBJECT_54_NAME);
#endif
#if defined(MODBUS_USEROBJECT_55_NAME) && MODBUS_NUMB_OF_USEROBJECTS>55
MB_ObjectInit(&MB_DEVID.User[55], MODBUS_USEROBJECT_55_NAME);
#endif
#if defined(MODBUS_USEROBJECT_56_NAME) && MODBUS_NUMB_OF_USEROBJECTS>56
MB_ObjectInit(&MB_DEVID.User[56], MODBUS_USEROBJECT_56_NAME);
#endif
#if defined(MODBUS_USEROBJECT_57_NAME) && MODBUS_NUMB_OF_USEROBJECTS>57
MB_ObjectInit(&MB_DEVID.User[57], MODBUS_USEROBJECT_57_NAME);
#endif
#if defined(MODBUS_USEROBJECT_58_NAME) && MODBUS_NUMB_OF_USEROBJECTS>58
MB_ObjectInit(&MB_DEVID.User[58], MODBUS_USEROBJECT_58_NAME);
#endif
#if defined(MODBUS_USEROBJECT_59_NAME) && MODBUS_NUMB_OF_USEROBJECTS>59
MB_ObjectInit(&MB_DEVID.User[59], MODBUS_USEROBJECT_59_NAME);
#endif
#if defined(MODBUS_USEROBJECT_60_NAME) && MODBUS_NUMB_OF_USEROBJECTS>60
MB_ObjectInit(&MB_DEVID.User[60], MODBUS_USEROBJECT_60_NAME);
#endif
#if defined(MODBUS_USEROBJECT_61_NAME) && MODBUS_NUMB_OF_USEROBJECTS>61
MB_ObjectInit(&MB_DEVID.User[61], MODBUS_USEROBJECT_61_NAME);
#endif
#if defined(MODBUS_USEROBJECT_62_NAME) && MODBUS_NUMB_OF_USEROBJECTS>62
MB_ObjectInit(&MB_DEVID.User[62], MODBUS_USEROBJECT_62_NAME);
#endif
#if defined(MODBUS_USEROBJECT_63_NAME) && MODBUS_NUMB_OF_USEROBJECTS>63
MB_ObjectInit(&MB_DEVID.User[63], MODBUS_USEROBJECT_63_NAME);
#endif
#if defined(MODBUS_USEROBJECT_64_NAME) && MODBUS_NUMB_OF_USEROBJECTS>64
MB_ObjectInit(&MB_DEVID.User[64], MODBUS_USEROBJECT_64_NAME);
#endif
#if defined(MODBUS_USEROBJECT_65_NAME) && MODBUS_NUMB_OF_USEROBJECTS>65
MB_ObjectInit(&MB_DEVID.User[65], MODBUS_USEROBJECT_65_NAME);
#endif
#if defined(MODBUS_USEROBJECT_66_NAME) && MODBUS_NUMB_OF_USEROBJECTS>66
MB_ObjectInit(&MB_DEVID.User[66], MODBUS_USEROBJECT_66_NAME);
#endif
#if defined(MODBUS_USEROBJECT_67_NAME) && MODBUS_NUMB_OF_USEROBJECTS>67
MB_ObjectInit(&MB_DEVID.User[67], MODBUS_USEROBJECT_67_NAME);
#endif
#if defined(MODBUS_USEROBJECT_68_NAME) && MODBUS_NUMB_OF_USEROBJECTS>68
MB_ObjectInit(&MB_DEVID.User[68], MODBUS_USEROBJECT_68_NAME);
#endif
#if defined(MODBUS_USEROBJECT_69_NAME) && MODBUS_NUMB_OF_USEROBJECTS>69
MB_ObjectInit(&MB_DEVID.User[69], MODBUS_USEROBJECT_69_NAME);
#endif
#if defined(MODBUS_USEROBJECT_70_NAME) && MODBUS_NUMB_OF_USEROBJECTS>70
MB_ObjectInit(&MB_DEVID.User[70], MODBUS_USEROBJECT_70_NAME);
#endif
#if defined(MODBUS_USEROBJECT_71_NAME) && MODBUS_NUMB_OF_USEROBJECTS>71
MB_ObjectInit(&MB_DEVID.User[71], MODBUS_USEROBJECT_71_NAME);
#endif
#if defined(MODBUS_USEROBJECT_72_NAME) && MODBUS_NUMB_OF_USEROBJECTS>72
MB_ObjectInit(&MB_DEVID.User[72], MODBUS_USEROBJECT_72_NAME);
#endif
#if defined(MODBUS_USEROBJECT_73_NAME) && MODBUS_NUMB_OF_USEROBJECTS>73
MB_ObjectInit(&MB_DEVID.User[73], MODBUS_USEROBJECT_73_NAME);
#endif
#if defined(MODBUS_USEROBJECT_74_NAME) && MODBUS_NUMB_OF_USEROBJECTS>74
MB_ObjectInit(&MB_DEVID.User[74], MODBUS_USEROBJECT_74_NAME);
#endif
#if defined(MODBUS_USEROBJECT_75_NAME) && MODBUS_NUMB_OF_USEROBJECTS>75
MB_ObjectInit(&MB_DEVID.User[75], MODBUS_USEROBJECT_75_NAME);
#endif
#if defined(MODBUS_USEROBJECT_76_NAME) && MODBUS_NUMB_OF_USEROBJECTS>76
MB_ObjectInit(&MB_DEVID.User[76], MODBUS_USEROBJECT_76_NAME);
#endif
#if defined(MODBUS_USEROBJECT_77_NAME) && MODBUS_NUMB_OF_USEROBJECTS>77
MB_ObjectInit(&MB_DEVID.User[77], MODBUS_USEROBJECT_77_NAME);
#endif
#if defined(MODBUS_USEROBJECT_78_NAME) && MODBUS_NUMB_OF_USEROBJECTS>78
MB_ObjectInit(&MB_DEVID.User[78], MODBUS_USEROBJECT_78_NAME);
#endif
#if defined(MODBUS_USEROBJECT_79_NAME) && MODBUS_NUMB_OF_USEROBJECTS>79
MB_ObjectInit(&MB_DEVID.User[79], MODBUS_USEROBJECT_79_NAME);
#endif
#if defined(MODBUS_USEROBJECT_80_NAME) && MODBUS_NUMB_OF_USEROBJECTS>80
MB_ObjectInit(&MB_DEVID.User[80], MODBUS_USEROBJECT_80_NAME);
#endif
#if defined(MODBUS_USEROBJECT_81_NAME) && MODBUS_NUMB_OF_USEROBJECTS>81
MB_ObjectInit(&MB_DEVID.User[81], MODBUS_USEROBJECT_81_NAME);
#endif
#if defined(MODBUS_USEROBJECT_82_NAME) && MODBUS_NUMB_OF_USEROBJECTS>82
MB_ObjectInit(&MB_DEVID.User[82], MODBUS_USEROBJECT_82_NAME);
#endif
#if defined(MODBUS_USEROBJECT_83_NAME) && MODBUS_NUMB_OF_USEROBJECTS>83
MB_ObjectInit(&MB_DEVID.User[83], MODBUS_USEROBJECT_83_NAME);
#endif
#if defined(MODBUS_USEROBJECT_84_NAME) && MODBUS_NUMB_OF_USEROBJECTS>84
MB_ObjectInit(&MB_DEVID.User[84], MODBUS_USEROBJECT_84_NAME);
#endif
#if defined(MODBUS_USEROBJECT_85_NAME) && MODBUS_NUMB_OF_USEROBJECTS>85
MB_ObjectInit(&MB_DEVID.User[85], MODBUS_USEROBJECT_85_NAME);
#endif
#if defined(MODBUS_USEROBJECT_86_NAME) && MODBUS_NUMB_OF_USEROBJECTS>86
MB_ObjectInit(&MB_DEVID.User[86], MODBUS_USEROBJECT_86_NAME);
#endif
#if defined(MODBUS_USEROBJECT_87_NAME) && MODBUS_NUMB_OF_USEROBJECTS>87
MB_ObjectInit(&MB_DEVID.User[87], MODBUS_USEROBJECT_87_NAME);
#endif
#if defined(MODBUS_USEROBJECT_88_NAME) && MODBUS_NUMB_OF_USEROBJECTS>88
MB_ObjectInit(&MB_DEVID.User[88], MODBUS_USEROBJECT_88_NAME);
#endif
#if defined(MODBUS_USEROBJECT_89_NAME) && MODBUS_NUMB_OF_USEROBJECTS>89
MB_ObjectInit(&MB_DEVID.User[89], MODBUS_USEROBJECT_89_NAME);
#endif
#if defined(MODBUS_USEROBJECT_90_NAME) && MODBUS_NUMB_OF_USEROBJECTS>90
MB_ObjectInit(&MB_DEVID.User[90], MODBUS_USEROBJECT_90_NAME);
#endif
#if defined(MODBUS_USEROBJECT_91_NAME) && MODBUS_NUMB_OF_USEROBJECTS>91
MB_ObjectInit(&MB_DEVID.User[91], MODBUS_USEROBJECT_91_NAME);
#endif
#if defined(MODBUS_USEROBJECT_92_NAME) && MODBUS_NUMB_OF_USEROBJECTS>92
MB_ObjectInit(&MB_DEVID.User[92], MODBUS_USEROBJECT_92_NAME);
#endif
#if defined(MODBUS_USEROBJECT_93_NAME) && MODBUS_NUMB_OF_USEROBJECTS>93
MB_ObjectInit(&MB_DEVID.User[93], MODBUS_USEROBJECT_93_NAME);
#endif
#if defined(MODBUS_USEROBJECT_94_NAME) && MODBUS_NUMB_OF_USEROBJECTS>94
MB_ObjectInit(&MB_DEVID.User[94], MODBUS_USEROBJECT_94_NAME);
#endif
#if defined(MODBUS_USEROBJECT_95_NAME) && MODBUS_NUMB_OF_USEROBJECTS>95
MB_ObjectInit(&MB_DEVID.User[95], MODBUS_USEROBJECT_95_NAME);
#endif
#if defined(MODBUS_USEROBJECT_96_NAME) && MODBUS_NUMB_OF_USEROBJECTS>96
MB_ObjectInit(&MB_DEVID.User[96], MODBUS_USEROBJECT_96_NAME);
#endif
#if defined(MODBUS_USEROBJECT_97_NAME) && MODBUS_NUMB_OF_USEROBJECTS>97
MB_ObjectInit(&MB_DEVID.User[97], MODBUS_USEROBJECT_97_NAME);
#endif
#if defined(MODBUS_USEROBJECT_98_NAME) && MODBUS_NUMB_OF_USEROBJECTS>98
MB_ObjectInit(&MB_DEVID.User[98], MODBUS_USEROBJECT_98_NAME);
#endif
#if defined(MODBUS_USEROBJECT_99_NAME) && MODBUS_NUMB_OF_USEROBJECTS>99
MB_ObjectInit(&MB_DEVID.User[99], MODBUS_USEROBJECT_99_NAME);
#endif
#if defined(MODBUS_USEROBJECT_100_NAME) && MODBUS_NUMB_OF_USEROBJECTS>100
MB_ObjectInit(&MB_DEVID.User[100], MODBUS_USEROBJECT_100_NAME);
#endif
#if defined(MODBUS_USEROBJECT_101_NAME) && MODBUS_NUMB_OF_USEROBJECTS>101
MB_ObjectInit(&MB_DEVID.User[101], MODBUS_USEROBJECT_101_NAME);
#endif
#if defined(MODBUS_USEROBJECT_102_NAME) && MODBUS_NUMB_OF_USEROBJECTS>102
MB_ObjectInit(&MB_DEVID.User[102], MODBUS_USEROBJECT_102_NAME);
#endif
#if defined(MODBUS_USEROBJECT_103_NAME) && MODBUS_NUMB_OF_USEROBJECTS>103
MB_ObjectInit(&MB_DEVID.User[103], MODBUS_USEROBJECT_103_NAME);
#endif
#if defined(MODBUS_USEROBJECT_104_NAME) && MODBUS_NUMB_OF_USEROBJECTS>104
MB_ObjectInit(&MB_DEVID.User[104], MODBUS_USEROBJECT_104_NAME);
#endif
#if defined(MODBUS_USEROBJECT_105_NAME) && MODBUS_NUMB_OF_USEROBJECTS>105
MB_ObjectInit(&MB_DEVID.User[105], MODBUS_USEROBJECT_105_NAME);
#endif
#if defined(MODBUS_USEROBJECT_106_NAME) && MODBUS_NUMB_OF_USEROBJECTS>106
MB_ObjectInit(&MB_DEVID.User[106], MODBUS_USEROBJECT_106_NAME);
#endif
#if defined(MODBUS_USEROBJECT_107_NAME) && MODBUS_NUMB_OF_USEROBJECTS>107
MB_ObjectInit(&MB_DEVID.User[107], MODBUS_USEROBJECT_107_NAME);
#endif
#if defined(MODBUS_USEROBJECT_108_NAME) && MODBUS_NUMB_OF_USEROBJECTS>108
MB_ObjectInit(&MB_DEVID.User[108], MODBUS_USEROBJECT_108_NAME);
#endif
#if defined(MODBUS_USEROBJECT_109_NAME) && MODBUS_NUMB_OF_USEROBJECTS>109
MB_ObjectInit(&MB_DEVID.User[109], MODBUS_USEROBJECT_109_NAME);
#endif
#if defined(MODBUS_USEROBJECT_110_NAME) && MODBUS_NUMB_OF_USEROBJECTS>110
MB_ObjectInit(&MB_DEVID.User[110], MODBUS_USEROBJECT_110_NAME);
#endif
#if defined(MODBUS_USEROBJECT_111_NAME) && MODBUS_NUMB_OF_USEROBJECTS>111
MB_ObjectInit(&MB_DEVID.User[111], MODBUS_USEROBJECT_111_NAME);
#endif
#if defined(MODBUS_USEROBJECT_112_NAME) && MODBUS_NUMB_OF_USEROBJECTS>112
MB_ObjectInit(&MB_DEVID.User[112], MODBUS_USEROBJECT_112_NAME);
#endif
#if defined(MODBUS_USEROBJECT_113_NAME) && MODBUS_NUMB_OF_USEROBJECTS>113
MB_ObjectInit(&MB_DEVID.User[113], MODBUS_USEROBJECT_113_NAME);
#endif
#if defined(MODBUS_USEROBJECT_114_NAME) && MODBUS_NUMB_OF_USEROBJECTS>114
MB_ObjectInit(&MB_DEVID.User[114], MODBUS_USEROBJECT_114_NAME);
#endif
#if defined(MODBUS_USEROBJECT_115_NAME) && MODBUS_NUMB_OF_USEROBJECTS>115
MB_ObjectInit(&MB_DEVID.User[115], MODBUS_USEROBJECT_115_NAME);
#endif
#if defined(MODBUS_USEROBJECT_116_NAME) && MODBUS_NUMB_OF_USEROBJECTS>116
MB_ObjectInit(&MB_DEVID.User[116], MODBUS_USEROBJECT_116_NAME);
#endif
#if defined(MODBUS_USEROBJECT_117_NAME) && MODBUS_NUMB_OF_USEROBJECTS>117
MB_ObjectInit(&MB_DEVID.User[117], MODBUS_USEROBJECT_117_NAME);
#endif
#if defined(MODBUS_USEROBJECT_118_NAME) && MODBUS_NUMB_OF_USEROBJECTS>118
MB_ObjectInit(&MB_DEVID.User[118], MODBUS_USEROBJECT_118_NAME);
#endif
#if defined(MODBUS_USEROBJECT_119_NAME) && MODBUS_NUMB_OF_USEROBJECTS>119
MB_ObjectInit(&MB_DEVID.User[119], MODBUS_USEROBJECT_119_NAME);
#endif
#if defined(MODBUS_USEROBJECT_120_NAME) && MODBUS_NUMB_OF_USEROBJECTS>120
MB_ObjectInit(&MB_DEVID.User[120], MODBUS_USEROBJECT_120_NAME);
#endif
#if defined(MODBUS_USEROBJECT_121_NAME) && MODBUS_NUMB_OF_USEROBJECTS>121
MB_ObjectInit(&MB_DEVID.User[121], MODBUS_USEROBJECT_121_NAME);
#endif
#if defined(MODBUS_USEROBJECT_122_NAME) && MODBUS_NUMB_OF_USEROBJECTS>122
MB_ObjectInit(&MB_DEVID.User[122], MODBUS_USEROBJECT_122_NAME);
#endif
#if defined(MODBUS_USEROBJECT_123_NAME) && MODBUS_NUMB_OF_USEROBJECTS>123
MB_ObjectInit(&MB_DEVID.User[123], MODBUS_USEROBJECT_123_NAME);
#endif
#if defined(MODBUS_USEROBJECT_124_NAME) && MODBUS_NUMB_OF_USEROBJECTS>124
MB_ObjectInit(&MB_DEVID.User[124], MODBUS_USEROBJECT_124_NAME);
#endif
#if defined(MODBUS_USEROBJECT_125_NAME) && MODBUS_NUMB_OF_USEROBJECTS>125
MB_ObjectInit(&MB_DEVID.User[125], MODBUS_USEROBJECT_125_NAME);
#endif
#if defined(MODBUS_USEROBJECT_126_NAME) && MODBUS_NUMB_OF_USEROBJECTS>126
MB_ObjectInit(&MB_DEVID.User[126], MODBUS_USEROBJECT_126_NAME);
#endif
#if defined(MODBUS_USEROBJECT_127_NAME) && MODBUS_NUMB_OF_USEROBJECTS>127
MB_ObjectInit(&MB_DEVID.User[127], MODBUS_USEROBJECT_127_NAME);
#endif
}
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS

View File

@@ -0,0 +1,296 @@
/**
*******************************************************************************
* @file modbus_diag.c
* @brief Реализация диагностики устройства Modbus
*******************************************************************************
* @details
Модуль обработки запросов диагностической информации (0x08):
- Полная поддержка всех подфункций диагностики согласно спецификации Modbus
- Выставление любого бита в Diagnostics Register
- Сбор статистики работы устройства
- Управление режимами работы (Normal/Listen Only)
******************************************************************************/
#include "modbus_diag.h"
#ifdef MODBUS_ENABLE_DIAGNOSTICS
MB_DiagnosticsInfoTypeDef MB_DIAG = {0}; ///< Глобальная структура диагностики
/**
* @brief Инициализация диагностических счетчиков
*/
void MB_DiagnosticsInit(void)
{
MB_DIAG.DiagnosticRegister = 0;
MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE;
// Инициализация счетчиков
MB_DIAG.Counters.BusMessage = 0;
MB_DIAG.Counters.BusCommunicationErr = 0;
MB_DIAG.Counters.BusExceptionErr = 0;
MB_DIAG.Counters.SlaveMessage = 0;
MB_DIAG.Counters.SlaveNoResponse = 0;
MB_DIAG.Counters.SlaveNAK = 0;
MB_DIAG.Counters.SlaveBusy = 0;
MB_DIAG.Counters.BusCharacterOverrun = 0;
}
/**
* @brief Выставить бит в регистре диагностике
* @param bit_num Номер бита для выставления (1-15, 0 бит нельзя выставить)
* @param bit_state Состояние бита для выставления (Выставить/Сбросить)
* @return >0 - номер выставленного бита, 0 - ошибка
*/
int MB_Diagnostics_WriteBit(int bit_num, int bit_state)
{
if(bit_num == 0 || bit_num > 15)
return 0;
if(bit_state)
MB_DIAG.DiagnosticRegister |= (1 << bit_num);
else
MB_DIAG.DiagnosticRegister &= ~(1 << bit_num);
return bit_num;
}
/**
* @brief Прочитать состояние бита диагностического регистра
* @param bit_num Номер бита (0-15)
* @return 1 - бит установлен, 0 - бит сброшен или ошибка
*/
int MB_Diagnostics_GetBit(int bit_num)
{
if(bit_num < 0 || bit_num > 15)
return 0;
return (MB_DIAG.DiagnosticRegister >> bit_num) & 0x01;
}
/**
* @brief Обработать функцию Diagnostics (Serial Line only) (0x08)
* @param modbus_msg Указатель на структуру сообщения modbus
* @return fMessageHandled Статус обработки команды
*/
uint8_t MB_Process_Diagnostics(RS_MsgTypeDef *modbus_msg)
{
uint16_t sub_function = modbus_msg->MbData[0];
uint16_t request_data = modbus_msg->MbData[1];
// Если устройство в режиме Listen Only, отвечаем только на sub-function 0x01
if (MB_DIAG.DeviceMode == MODBUS_LISTEN_ONLY_MODE && sub_function != 0x0001)
{
return 0; // Не отвечаем в режиме Listen Only
}
switch(sub_function)
{
case 0x0000: // Return Query Data
// Эхо-ответ с теми же данными
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = request_data;
modbus_msg->ByteCnt = 4;
break;
case 0x0001: // Restart Communications
// Перезапуск коммуникаций - выходим из Listen Only режима
MB_DIAG.DeviceMode = MODBUS_NORMAL_MODE;
// Если request_data = 0xFF00, очищаем лог событий
if (request_data == 0xFF00)
{
MB_DiagnosticsInit(); // Полный сброс
}
else
{
// Очищаем только счетчики, но не регистр диагностики
MB_DIAG.Counters.BusMessage = 0;
MB_DIAG.Counters.BusCommunicationErr = 0;
MB_DIAG.Counters.BusExceptionErr = 0;
MB_DIAG.Counters.SlaveMessage = 0;
MB_DIAG.Counters.SlaveNoResponse = 0;
MB_DIAG.Counters.SlaveNAK = 0;
MB_DIAG.Counters.SlaveBusy = 0;
MB_DIAG.Counters.BusCharacterOverrun = 0;
}
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = request_data;
modbus_msg->ByteCnt = 4;
break;
case 0x0002: // Return Diagnostic Register
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.DiagnosticRegister;
modbus_msg->ByteCnt = 4;
break;
case 0x0003: // Change ASCII Input Delimiter
// В RTU режиме не поддерживается
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
modbus_msg->Except_Code = ET_ILLEGAL_FUNCTION;
return 0;
case 0x0004: // Force Listen Only Mode
MB_DIAG.DeviceMode = MODBUS_LISTEN_ONLY_MODE;
// В режиме Listen Only не отправляем ответ
return 0;
case 0x000A: // Clear Counters and Diagnostic Register
MB_DiagnosticsInit(); // Полный сброс
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = 0;
modbus_msg->ByteCnt = 4;
break;
case 0x000B: // Return Bus Message Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusMessage;
modbus_msg->ByteCnt = 4;
break;
case 0x000C: // Return Bus Communication Error Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusCommunicationErr;
modbus_msg->ByteCnt = 4;
break;
case 0x000D: // Return Bus Exception Error Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusExceptionErr;
modbus_msg->ByteCnt = 4;
break;
case 0x000E: // Return Server Message Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveMessage;
modbus_msg->ByteCnt = 4;
break;
case 0x000F: // Return Slave No Response Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveNoResponse;
modbus_msg->ByteCnt = 4;
break;
case 0x0010: // Return Slave NAK Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveNAK;
modbus_msg->ByteCnt = 4;
break;
case 0x0011: // Return Slave Busy Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.SlaveBusy;
modbus_msg->ByteCnt = 4;
break;
case 0x0012: // Return Bus Character Overrun Count
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = MB_DIAG.Counters.BusCharacterOverrun;
modbus_msg->ByteCnt = 4;
break;
case 0x0014: // Clear Overrun Counter and Flag
MB_DIAG.Counters.BusCharacterOverrun = 0;
// Сбрасываем флаг переполнения в DiagnosticRegister
MB_DIAG.DiagnosticRegister &= ~(1<<0);
modbus_msg->MbData[0] = sub_function;
modbus_msg->MbData[1] = 0;
modbus_msg->ByteCnt = 4;
break;
default:
modbus_msg->FuncCode |= FC_ERR_VALUES_START;
modbus_msg->Except_Code = ET_ILLEGAL_FUNCTION;
return 0;
}
return 1;
}
/**
* @brief Увеличивает счетчик сообщений на шине
*/
void MB_Diagnostics_BusMessageCnt(void)
{
MB_DIAG.Counters.BusMessage++;
}
/**
* @brief Увеличивает счетчик ошибок связи
*/
void MB_Diagnostics_CommunicationErrorCnt(void)
{
if (MB_DIAG.Counters.BusCommunicationErr < 0xFFFF)
MB_DIAG.Counters.BusCommunicationErr++;
}
/**
* @brief Увеличивает счетчик исключений
*/
void MB_Diagnostics_ExceptionErrorCnt(void)
{
if (MB_DIAG.Counters.BusExceptionErr < 0xFFFF)
MB_DIAG.Counters.BusExceptionErr++;
}
/**
* @brief Увеличивает счетчик переполнения символов
*/
void MB_Diagnostics_CharacterOverrunCnt(void)
{
if (MB_DIAG.Counters.BusCharacterOverrun < 0xFFFF)
{
MB_DIAG.Counters.BusCharacterOverrun++;
// Устанавливаем флаг переполнения в DiagnosticRegister
MB_DIAG.DiagnosticRegister |= (1 << 0);
}
}
/**
* @brief Увеличивает счетчик отсутствия ответов
*/
void MB_Diagnostics_SlaveMessageCnt(void)
{
if (MB_DIAG.Counters.SlaveMessage < 0xFFFF)
MB_DIAG.Counters.SlaveMessage++;
}
/**
* @brief Увеличивает счетчик отсутствия ответов
*/
void MB_Diagnostics_SlaveNoResponseCnt(void)
{
if (MB_DIAG.Counters.SlaveNoResponse < 0xFFFF)
MB_DIAG.Counters.SlaveNoResponse++;
}
/**
* @brief Увеличивает счетчик NAK ответов
*/
void MB_Diagnostics_SlaveNAKCnt(void)
{
if (MB_DIAG.Counters.SlaveNAK < 0xFFFF)
MB_DIAG.Counters.SlaveNAK++;
}
/**
* @brief Увеличивает счетчик занятости устройства
*/
void MB_Diagnostics_SlaveBusyCnt(void)
{
if (MB_DIAG.Counters.SlaveBusy < 0xFFFF)
MB_DIAG.Counters.SlaveBusy++;
}
/**
* @brief Получение текущего режима устройства
* @return Текущий режим работы устройства
*/
MB_DeviceModeTypeDef MB_GetDeviceMode(void)
{
return MB_DIAG.DeviceMode;
}
#endif //MODBUS_ENABLE_DIAGNOSTICS

View File

@@ -0,0 +1,161 @@
/**
*******************************************************************************
* @file modbus_holdregs.c
* @brief Реализация работы с регистрами хранения Modbus
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к регистрам хранения по глобальным адресам
Модуль обработки команд для holding registers (регистров хранения):
- Чтение множественных регистров (0x03) - копирование данных в буфер ответа
- Запись одиночного регистра (0x06) - прямая запись значения
- Запись множественных регистров (0x10) - пакетная запись из буфера
@section hvalid Валидация данных:
- Проверка соответствия количества байт и регистров
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
******************************************************************************/
#include "modbus_inputregs.h"
#ifdef MODBUS_ENABLE_HOLDINGS
/**
* @brief Записать регистр хранения по глобальному адресу.
* @param Addr Адрес регистра.
* @param WriteVal Число для записи.
* @return ExceptionCode Код исключения если регистра по адресу не существует, и ET_NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
MB_ExceptionTypeDef MB_Holding_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
uint16_t *pHoldRegs;
//------------WRITE COIL-------------
Exception = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding, 1);
if(Exception == ET_NO_ERRORS)
{
*(pHoldRegs) = WriteVal;
}
return Exception;
}
/**
* @brief Считать регистр хранения по глобальному адресу.
* @param Addr Адрес регистра.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает значение регистра.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
uint16_t MB_Holding_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception_tmp = 0;
uint16_t *pHoldRegs;
//------------READ COIL--------------
Exception_tmp = MB_DefineRegistersAddress(&pHoldRegs, Addr, 1, RegisterType_Holding, 0);
if(Exception) // if exception is not given to func fill it
*Exception = Exception_tmp;
if(Exception_tmp == ET_NO_ERRORS)
{
return *(pHoldRegs);
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Holding Registers (03 - 0x03).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Read Holding Registers.
*/
uint8_t MB_Process_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
// get origin address for data
uint16_t *pHoldRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding, 0); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------READING REGS------------
// setup output message data size
modbus_msg->ByteCnt = modbus_msg->Qnt*2; // *2 because we transmit 8 bits, not 16 bits
// read data
int i;
for (i = 0; i<modbus_msg->Qnt; i++)
{
modbus_msg->MbData[i] = *(pHoldRegs++);
}
return 1;
}
/**
* @brief Обработать функцию Write Single Register (06 - 0x06).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Single Register.
*/
uint8_t MB_Process_Write_Single_Reg(RS_MsgTypeDef *modbus_msg)
{
// get origin address for data
uint16_t *pHoldRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, 1, RegisterType_Holding, 1); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------WRITTING REG------------
*(pHoldRegs) = modbus_msg->Qnt;
return 1;
}
/**
* @brief Обработать функцию Write Multiple Registers (16 - 0x10).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Write Multiple Registers.
*/
uint8_t MB_Process_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
if (modbus_msg->Qnt*2 != modbus_msg->ByteCnt)
{ // if quantity and bytes count arent match
modbus_msg->Except_Code = 3;
return 0;
}
// get origin address for data
uint16_t *pHoldRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pHoldRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Holding, 1); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------WRITTING REGS-----------
for (int i = 0; i<modbus_msg->Qnt; i++)
{
*(pHoldRegs++) = modbus_msg->MbData[i];
}
return 1;
}
#endif //MODBUS_ENABLE_HOLDINGS

View File

@@ -0,0 +1,112 @@
/**
*******************************************************************************
* @file modbus_inputregs.c
* @brief Реализация работы с входными регистрами Modbus
*******************************************************************************
* @details
Модуль для доступа к регистрам внутри программы:
- Функции для доступа к входным регистрам по глобальным адресам
Модуль обработки команды чтения input registers (0x04):
@section ivalid Валидация данных:
- Проверка соответствия количества байт и регистров
- Валидация адресов через MB_DefineRegistersAddress()
- Обработка исключений при некорректных запросах
******************************************************************************/
#include "modbus_inputregs.h"
#ifdef MODBUS_ENABLE_INPUTS
/**
* @brief Записать входной регистр по глобальному адресу.
* @param Addr Адрес регистра.
* @param WriteVal Число для записи.
* @return ExceptionCode Код исключения если регистра по адресу не существует, и ET_NO_ERRORS если все ок.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
MB_ExceptionTypeDef MB_Input_Write_Global(uint16_t Addr, uint16_t WriteVal)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception = ET_NO_ERRORS;
uint16_t *pInRegs;
//------------WRITE COIL-------------
Exception = MB_DefineRegistersAddress(&pInRegs, Addr, 1, RegisterType_Input, 1);
if(Exception == ET_NO_ERRORS)
{
*(pInRegs) = WriteVal;
}
return Exception;
}
/**
* @brief Считать входной регистр по глобальному адресу.
* @param Addr Адрес регистра.
* @param Exception Указатель на переменную для кода исключения, в случае неудачи при чтении.
* @return uint16_t Возвращает значение регистра.
*
* @details Позволяет обратиться к любому регистру по его глобальному адрессу.
Вне зависимости от того как регистры размещены в памяти.
*/
uint16_t MB_Input_Read_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception)
{
//---------CHECK FOR ERRORS----------
MB_ExceptionTypeDef Exception_tmp = 0;
uint16_t *pInRegs;
//------------READ COIL--------------
Exception_tmp = MB_DefineRegistersAddress(&pInRegs, Addr, 1, RegisterType_Input, 0);
if(Exception) // if exception is not given to func fill it
*Exception = Exception_tmp;
if(Exception_tmp == ET_NO_ERRORS)
{
return *(pInRegs);
}
else
{
return 0;
}
}
/**
* @brief Обработать функцию Read Input Registers (04 - 0x04).
* @param modbus_msg Указатель на структуру собщения modbus.
* @return fMessageHandled Статус о результате обработки комманды.
* @details Обработка команды Read Input Registers.
*/
uint8_t MB_Process_Read_Input_Regs(RS_MsgTypeDef *modbus_msg)
{
//---------CHECK FOR ERRORS----------
// get origin address for data
uint16_t *pInRegs;
modbus_msg->Except_Code = MB_DefineRegistersAddress(&pInRegs, modbus_msg->Addr, modbus_msg->Qnt, RegisterType_Input, 0); // определение адреса регистров
if(modbus_msg->Except_Code != ET_NO_ERRORS)
return 0;
//-----------READING REGS------------
// setup output message data size
modbus_msg->ByteCnt = modbus_msg->Qnt*2; // *2 because we transmit 8 bits, not 16 bits
// read data
int i;
for (i = 0; i<modbus_msg->Qnt; i++)
{
if(*((int16_t *)pInRegs) > 0)
modbus_msg->MbData[i] = (*pInRegs++);
else
modbus_msg->MbData[i] = (*pInRegs++);
}
return 1;
}
#endif //MODBUS_ENABLE_INPUTS

View File

@@ -0,0 +1,755 @@
/**
*******************************************************************************
* @file modbus_master.c
* @brief Модуль для реализации мастера MODBUS.
*******************************************************************************
* @details
Файл содержит реализацию функций для работы Modbus в режиме мастера.
@section mast Функции и макросы
- MB_RespGet_RegisterAll() — Считать все регистра из ответа
- MB_RespGet_RegisterValue() — Считать один регистр из ответа
- MB_RespGet_CoilAll() — Считать все коилы из ответа
- MB_RespGet_CoilState() — Считать один коил из ответа
- MB_RespGet_NumberOfObjects() — Считать количество принятых объектов идентификатора
- MB_RespGet_ObjectById() — Считать объект идентификатора по
его ID
- MB_RespGet_ObjectByIndex() — Считать объект идентификатора по
порядковому номеру в сообщении
- MB_RespGet_Diagnostic() — Считать запрошенный диагностический счетчик
- MB_Master_Collect_Message() — Сбор сообщения в режиме мастера
- MB_Master_Parse_Message() — Парс сообщения в режиме мастера
******************************************************************************/
#include "modbus.h"
#ifdef MODBUS_ENABLE_MASTER
//-------------------------------------------------------------------
//-----------------------------FOR USER------------------------------
/**
* @brief Получить значение ВСЕХ регистров в ответе
* @param modbus_msg Указатель на структуру сообщения
* @param reg_addr Адрес регистра, значение которого нужно получить
* @param reg_arr Указатель для массив для сохранения значений регистров
* @return количество считанных регистров, 0 - ошибка
*/
int MB_RespGet_RegisterAll(RS_MsgTypeDef *modbus_msg, uint16_t *reg_arr)
{
if(modbus_msg == NULL || reg_arr == NULL)
return 0;
int read_cnt = 0;
int i = 0;
for(int addr = modbus_msg->Addr; addr < modbus_msg->Addr + modbus_msg->Qnt; addr++)
{
if(MB_RespGet_RegisterValue(modbus_msg, addr, &reg_arr[i]))
{
read_cnt++;
}
i++;
}
return read_cnt;
}
/**
* @brief Получить значение регистра в ответе по его адресу
* @param modbus_msg Указатель на структуру сообщения
* @param reg_addr Адрес регистра, значение которого нужно получить
* @param reg_value Указатель для значения регистра
* @return 1 - успех, 0 - ошибка или reg_addr вне диапазона запроса
*/
int MB_RespGet_RegisterValue(RS_MsgTypeDef *modbus_msg, uint16_t reg_addr, uint16_t *reg_value)
{
if(modbus_msg == NULL || reg_value == NULL)
return 0;
// Проверяем что ответ связан с регистрами
if((modbus_msg->FuncCode != FC_R_DISC_IN) &&
(modbus_msg->FuncCode != FC_R_HOLD_REGS) &&
(modbus_msg->FuncCode != FC_R_IN_REGS))
{
return 0;
}
// Проверяем что reg_addr в пределах запрошенного диапазона
if(reg_addr < modbus_msg->Addr || reg_addr >= modbus_msg->Addr + modbus_msg->Qnt)
return 0;
// Вычисляем индекс регистра в полученных данных
uint16_t reg_index = reg_addr - modbus_msg->Addr;
// Проверяем что регистр существует в данных
if(reg_index >= modbus_msg->ByteCnt / 2)
return 0;
// Получаем значение регистра
*reg_value = modbus_msg->MbData[reg_index];
return 1;
}
/**
* @brief Получить состояние ВСЕХ coil в ответе
* @param modbus_msg Указатель на структуру сообщения
* @param coil_arr Указатель для массив доя сохранения состояний coil (1 - ON, 0 - OFF)
* @return 1 - успех, 0 - ошибка или coil_addr вне диапазона запроса
*/
int MB_RespGet_CoilAll(RS_MsgTypeDef *modbus_msg, int *coil_arr)
{
if(modbus_msg == NULL || coil_arr == NULL)
return 0;
int read_cnt = 0;
int i = 0;
for(int addr = modbus_msg->Addr; addr < modbus_msg->Addr + modbus_msg->Qnt; addr++)
{
if(MB_RespGet_CoilState(modbus_msg, addr, &coil_arr[i]))
{
read_cnt++;
}
i++;
}
return 1;
}
/**
* @brief Получить состояние coil в ответе по его адресу
* @param modbus_msg Указатель на структуру сообщения
* @param coil_addr Адрес coil, состояние которого нужно получить
* @param coil_state Указатель для состояния coil (1 - ON, 0 - OFF)
* @return 1 - успех, 0 - ошибка или coil_addr вне диапазона запроса
*/
int MB_RespGet_CoilState(RS_MsgTypeDef *modbus_msg, uint16_t coil_addr, int *coil_state)
{
if(modbus_msg == NULL || coil_state == NULL)
return 0;
// Проверяем что ответ связан с коилами
if(modbus_msg->FuncCode != FC_R_COILS)
{
return 0;
}
// Проверяем что coil_addr в пределах запрошенного диапазона
if(coil_addr < modbus_msg->Addr || coil_addr >= modbus_msg->Addr + modbus_msg->Qnt)
return 0;
// Вычисляем индекс coil в полученных данных
uint16_t coil_index = coil_addr - modbus_msg->Addr;
// Вычисляем байт и бит
uint8_t byte_index = coil_index / 8;
uint8_t data_index = coil_index / 16;
uint8_t bit_index = coil_index % 16;
// Проверяем что байт существует в данных
if(byte_index >= modbus_msg->ByteCnt)
return 0;
// Получаем байт и проверяем бит
if(bit_index < 8)
*coil_state = (modbus_msg->MbData[data_index] >> (bit_index+8)) & 0x01;
else
*coil_state = ((modbus_msg->MbData[data_index]&0xFF) >> (bit_index-8)) & 0x01;
return 1;
}
/**
* @brief Получить количество объектов в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @return int Количество объектов
*/
int MB_RespGet_NumberOfObjects(RS_MsgTypeDef *modbus_msg)
{
if(modbus_msg == NULL)
{
return 0;
}
// Проверяем что ответ связан с диагностикой
if(modbus_msg->FuncCode != FC_R_DEVICE_ID)
{
return 0;
}
return modbus_msg->DevId.NumbOfObj;
}
/**
* @brief Найти объект по ID в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @param obj_id ID искомого объекта
* @param obj_data Буфер для данных объекта (может быть NULL)
* @param obj_length Указатель для длины объекта
* @return int Найден ли объект (1 - да, 0 - нет)
*/
int MB_RespGet_ObjectById(RS_MsgTypeDef *modbus_msg, uint8_t obj_id, char *obj_data, uint8_t *obj_length)
{
if((modbus_msg == NULL) || (obj_data == NULL))
return 0;
// Проверяем что ответ связан с диагностикой
if(modbus_msg->FuncCode != FC_R_DEVICE_ID)
{
return 0;
}
uint8_t *data = (uint8_t*)modbus_msg->MbData;
unsigned ind = 0;
for(int i = 0; i < modbus_msg->DevId.NumbOfObj; i++)
{
uint8_t current_id = data[ind++];
uint8_t current_length = data[ind++];
if(current_id == obj_id)
{
if(obj_length)
*obj_length = current_length;
for(int j = 0; j < current_length; j++)
{
obj_data[j] = data[ind++];
}
obj_data[current_length] = '\0'; // добавляем \0
return 1;
}
else
{
// Пропускаем данные этого объекта
ind += current_length;
}
}
return 0;
}
/**
* @brief Получить объект по индексу в сообщении
* @param modbus_msg Указатель на структуру сообщения
* @param index Индекс объекта (0..N-1)
* @param obj_id Указатель для ID объекта
* @param obj_data Буфер для данных объекта
* @param obj_length Указатель для длины объекта
* @return int Успешность получения (1 - получен, 0 - не найден)
*/
int MB_RespGet_ObjectByIndex(RS_MsgTypeDef *modbus_msg, int index, uint8_t *obj_id, char *obj_data, uint8_t *obj_length)
{
if((modbus_msg == NULL) || (obj_data == NULL))
return 0;
// Проверяем что ответ связан с диагностикой
if(modbus_msg->FuncCode != FC_R_DEVICE_ID)
{
return 0;
}
if(index >= modbus_msg->DevId.NumbOfObj)
return 0;
uint8_t *data = (uint8_t*)modbus_msg->MbData;
unsigned ind = 0;
for(int i = 0; i <= index; i++)
{
uint8_t current_id = data[ind++];
uint8_t current_length = data[ind++];
if(obj_id)
*obj_id = current_id;
if(obj_length)
*obj_length = current_length;
if(i == index)
{
for(int j = 0; j < current_length; j++)
{
obj_data[j] = data[ind++];
}
obj_data[current_length] = '\0'; // добавляем \0
return 1;
}
else
{
// Пропускаем данные этого объекта
ind += current_length;
}
}
return 0;
}
/**
* @brief Получить данные диагностики из сообщения (MbData[1])
* @param modbus_msg Указатель на структуру сообщения
* @param data Указатель куда положить данные
* @return 1 - успех, 0 - ошибка
*/
int MB_RespGet_Diagnostic(RS_MsgTypeDef *modbus_msg, uint16_t *data)
{
if(modbus_msg == NULL || data == NULL)
return 0;
// Проверяем что ответ связан с диагностикой
if(modbus_msg->FuncCode != FC_R_DIAGNOSTICS)
{
return 0;
}
*data = modbus_msg->MbData[1];
return 1;
}
//-------------------------------------------------------------------
//-----------------------------INTERNAL------------------------------
/**
* @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;
// Master mode - calculating response size from slave
if (modbus_msg->FuncCode & FC_ERR_VALUES_START)
{
// Error response: [Addr][Func|0x80][ExceptCode][CRC]
mb_func_size = -1; // Only Exception Code
}
else if (modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
{
// Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
mb_func_size = 1;
}
else if (modbus_msg->FuncCode == FC_R_DEVICE_ID)
{
// Device identifications: variable size, need to read first to determine
mb_func_size = 0; // Will be determined after reading header
}
else
{
switch (modbus_msg->FuncCode & ~FC_ERR_VALUES_START)
{
case 0x01: // Read Coils
case 0x02: // Read Discrete Inputs
case 0x03: // Read Holding Registers
case 0x04: // Read Input Registers
// Response: [ByteCount][Data...]
mb_func_size = modbus_msg->ByteCnt + 2; // ByteCount + variable data
break;
case 0x05: // Write Single Coil
case 0x06: // Write Single Register
// Echo response: [Addr][Value][CRC]
mb_func_size = 4; // Address(2) + Value(2)
break;
case 0x0F: // Write Multiple Coils
case 0x10: // Write Multiple Registers
// Echo response: [Addr][Qty][CRC]
mb_func_size = 4; // Address(2) + Quantity(2)
break;
default:
mb_func_size = 0;
}
}
mb_func_size = RS_RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
return mb_func_size;
}
/**
* @brief Сбор сообщения в буфер UART в режиме мастер (фрейм мастера из msg -> uart).
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
int ind = 0; // ind for modbus-uart buffer
//------INFO ABOUT DATA/MESSAGE------
//-----------[first bytes]-----------
// set ID of slave device
modbus_uart_buff[ind++] = modbus_msg->DeviceAddr;
// set function code
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) // device identifications request
{
modbus_uart_buff[ind++] = modbus_msg->DevId.MEI_Type;
modbus_uart_buff[ind++] = modbus_msg->DevId.ReadDevId;
modbus_uart_buff[ind++] = modbus_msg->DevId.NextObjId;
}
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#ifdef MODBUS_ENABLE_DIAGNOSTICS
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
{
// Diagnostics: [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 // classic modbus request
{
// set address
modbus_uart_buff[ind++] = modbus_msg->Addr >> 8;
modbus_uart_buff[ind++] = modbus_msg->Addr & 0xFF;
// set quantity
modbus_uart_buff[ind++] = modbus_msg->Qnt >> 8;
modbus_uart_buff[ind++] = modbus_msg->Qnt & 0xFF;
// for write multiple functions
if((modbus_msg->FuncCode == 0x0F) || (modbus_msg->FuncCode == 0x10))
{
modbus_uart_buff[ind++] = modbus_msg->ByteCnt;
// write data bytes
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->MbData;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{
modbus_uart_buff[ind++] = tmp_data_addr[i];
}
}
}
}
if(ind < 0)
return RS_COLLECT_MSG_ERR;
//---------------CRC----------------
//---------[last 2 bytes]----------
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
modbus_msg->MbCRC = CRC_VALUE;
modbus_uart_buff[ind++] = CRC_VALUE & 0xFF;
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
hmodbus->RS_Message_Size = ind;
return RS_OK;
}
/**
* @brief Парс сообщения в режиме мастер (фрейм слейва из uart -> msg).
* @param hmodbus Указатель на хендлер RS.
* @param modbus_msg Указатель на структуру сообщения.
* @param modbus_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
*/
RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
{
int ind = 0; // ind for modbus-uart buffer
int expected_size = 0;
// get ID of slave device
modbus_msg->DeviceAddr = modbus_uart_buff[ind++];
// get function code (check if error response)
modbus_msg->FuncCode = modbus_uart_buff[ind++];
if(modbus_msg->FuncCode & FC_ERR_VALUES_START) // error response
{
modbus_msg->Except_Code = modbus_uart_buff[ind++];
}
else if(modbus_msg->FuncCode < FC_ERR_VALUES_START) // normal response
{
if(0) {}
#ifdef MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
else if(modbus_msg->FuncCode == FC_R_DEVICE_ID) // device identifications response
{
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 = modbus_uart_buff[ind++];
modbus_msg->ByteCnt = 0;
// Парсинг объектов идентификации устройства
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->MbData;
int data_index = 0;
for(int obj = 0; obj < modbus_msg->DevId.NumbOfObj; obj++)
{
// Читаем ID объекта
uint8_t object_id = modbus_uart_buff[ind++];
tmp_data_addr[data_index++] = object_id;
// Читаем длину объекта
uint8_t object_length = modbus_uart_buff[ind++];
tmp_data_addr[data_index++] = object_length;
// Читаем данные объекта
for(int i = 0; i < object_length; i++)
{
tmp_data_addr[data_index++] = modbus_uart_buff[ind++];
}
modbus_msg->ByteCnt += (2 + object_length); // ID + длина + данные
}
}
#endif //MODBUS_ENABLE_DEVICE_IDENTIFICATIONS
#ifdef MODBUS_ENABLE_DIAGNOSTICS
else if(modbus_msg->FuncCode == FC_R_DIAGNOSTICS)
{
// Diagnostics response: [SubFunc_HI][SubFunc_LO][Data_HI][Data_LO]
modbus_msg->MbData[0] = modbus_uart_buff[ind++] << 8;
modbus_msg->MbData[0] |= modbus_uart_buff[ind++];
modbus_msg->MbData[1] = modbus_uart_buff[ind++] << 8;
modbus_msg->MbData[1] |= modbus_uart_buff[ind++];
}
#endif //MODBUS_ENABLE_DIAGNOSTICS
else // classic modbus response
{
// get byte count for read functions
if((modbus_msg->FuncCode == 0x01) || (modbus_msg->FuncCode == 0x02) ||
(modbus_msg->FuncCode == 0x03) || (modbus_msg->FuncCode == 0x04))
{
modbus_msg->ByteCnt = modbus_uart_buff[ind++];
// read data bytes
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->MbData;
for(int i = 0; i < modbus_msg->ByteCnt; i++)
{
if(i % 2 == 0) // HI byte
tmp_data_addr[i/2] = (uint16_t)modbus_uart_buff[ind++] << 8;
else // LO byte
tmp_data_addr[i/2] |= modbus_uart_buff[ind++];
}
}
// for write functions - echo address and quantity
else if((modbus_msg->FuncCode == 0x05) || (modbus_msg->FuncCode == 0x06) ||
(modbus_msg->FuncCode == 0x0F) || (modbus_msg->FuncCode == 0x10))
{
modbus_msg->Addr = modbus_uart_buff[ind++] << 8;
modbus_msg->Addr |= modbus_uart_buff[ind++];
modbus_msg->Qnt = modbus_uart_buff[ind++] << 8;
modbus_msg->Qnt |= modbus_uart_buff[ind++];
}
}
}
//---------------CRC----------------
//----------[last 2 bytes]----------
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
modbus_msg->MbCRC = modbus_uart_buff[ind++];
modbus_msg->MbCRC |= modbus_uart_buff[ind++] << 8;
if(modbus_msg->MbCRC != CRC_VALUE)
{
TrackerCnt_Err(hmodbus->rs_err);
return RS_PARSE_MSG_ERR;
}
return RS_OK;
}
/** @brief Сформировать запрос на чтение коилов */
RS_MsgTypeDef MB_REQUEST_READ_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на чтение дискретных регистров */
RS_MsgTypeDef MB_REQUEST_READ_DISCRETE_INPUTS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_DISC_IN, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на чтение холдинг регистров */
RS_MsgTypeDef MB_REQUEST_READ_HOLDING_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на чтение инпут регистров */
RS_MsgTypeDef MB_REQUEST_READ_INPUT_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_IN_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на запись одного коила */
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_COIL(uint8_t slave_addr, uint16_t coil_addr, uint8_t value)
{
RS_MsgTypeDef msg = {slave_addr, FC_W_COIL, {0}, coil_addr, (value ? 0xFF00 : 0x0000), 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на запись одного регистра */
RS_MsgTypeDef MB_REQUEST_WRITE_SINGLE_REG(uint8_t slave_addr, uint16_t reg_addr, uint16_t value)
{
RS_MsgTypeDef msg = {slave_addr, FC_W_HOLD_REG, {0}, reg_addr, value, 0, {0}, 0, 0};
return msg;
}
/** @brief Сформировать запрос на запись нескольких регистров */
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_COILS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint8_t *coils_data)
{
RS_MsgTypeDef msg = {slave_addr, FC_W_COILS, {0}, start_addr, quantity, 0, {0}, 0, 0};
// Calculate byte count and prepare data
uint8_t byte_count = (quantity + 7) / 8;
msg.ByteCnt = byte_count;
// Copy coil data to message MbData array
for(int i = 0; i < byte_count; i++) {
if(i < MbData_size) {
msg.MbData[i] = coils_data[i];
}
}
return msg;
}
/** @brief Сформировать запрос на запись нескольких коилов */
RS_MsgTypeDef MB_REQUEST_WRITE_MULTIPLE_REGS(uint8_t slave_addr, uint16_t start_addr, uint16_t quantity, uint16_t *regs_data)
{
RS_MsgTypeDef msg = {slave_addr, FC_W_HOLD_REGS, {0}, start_addr, quantity, 0, {0}, 0, 0};
msg.ByteCnt = quantity * 2; // Each register is 2 bytes
// Copy register data to message MbData array
for(int i = 0; i < quantity && i < MbData_size; i++) {
msg.MbData[i] = regs_data[i];
}
return msg;
}
//---------ДИАГНОСТИЧЕСКИЕ ДАННЫЕ-----------
RS_MsgTypeDef MB_REQUEST_DIAGNOSTIC_QUERY(uint8_t slave_addr, uint16_t sub_function, uint16_t data)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_DIAGNOSTICS, {0}, 0, 0, 0, {sub_function, data}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_RETURN_QUERY_DATA(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0000, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RESTART_COMMUNICATIONS(uint8_t slave_addr, uint16_t data)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0001, data);
}
RS_MsgTypeDef MB_REQUEST_RETURN_DIAGNOSTIC_REGISTER(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0002, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_FORCE_LISTEN_ONLY_MODE(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0004, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_CLEAR_COUNTERS_AND_DIAGNOSTIC_REGISTER(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000A, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_MESSAGE_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000B, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_COMMUNICATION_ERROR_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000C, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_EXCEPTION_ERROR_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000D, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_MESSAGE_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000E, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NO_RESPONSE_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x000F, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_NAK_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0010, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_SLAVE_BUSY_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0011, 0x0000);
}
RS_MsgTypeDef MB_REQUEST_RETURN_BUS_CHARACTER_OVERRUN_COUNT(uint8_t slave_addr)
{
return MB_REQUEST_DIAGNOSTIC_QUERY(slave_addr, 0x0012, 0x0000);
}
//---------ИДЕНТИФИКАТОРЫ МОДБАС-----------
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_BASIC(uint8_t slave_addr)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_DEVICE_ID, {0x0E, 0x01, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_REGULAR(uint8_t slave_addr)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_DEVICE_ID, {0x0E, 0x02, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_EXTENDED(uint8_t slave_addr)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_DEVICE_ID, {0x0E, 0x03, 0x00, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
RS_MsgTypeDef MB_REQUEST_READ_DEVICE_ID_SPECIFIC(uint8_t slave_addr, uint8_t object_id)
{
RS_MsgTypeDef msg = {slave_addr, FC_R_DEVICE_ID, {0x0E, 0x04, object_id, 0, 0, 0}, 0, 0, 0, {0}, 0, 0};
return msg;
}
#endif //MODBUS_ENABLE_MASTER

View File

@@ -0,0 +1,467 @@
/**
*******************************************************************************
* @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

View File

@@ -0,0 +1,563 @@
/**
*******************************************************************************
* @file rs_message.c
* @brief Реализация протоколов обмена по RS/UART
*******************************************************************************
* @details
Модуль реализует асинхронный обмен сообщениями через UART с использованием:
- Прерываний по приему/передаче
- Детектирования конца фрейма по IDLE линии
- Таймаутов через таймер
- Двухстадийного приема (заголовок + данные)
@section arch Архитектура:
В режиме слейв:
- Инициализация приема с сообщения с максимальным размером RS_MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного сообщения
В режиме мастер (пока не реализовано):
- Отправка запроса и переход в режим приема сообщения с максимальным размером RS_MSG_SIZE_MAX
- При срабатывании прерывания IDLE - обработка полученного ответа
@section ithandler Необходимые обработчики:
- RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler()
- RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler()
******************************************************************************/
#include "rs_message.h"
#include "modbus_diag.h"
uint8_t RS_Buffer[RS_MSG_SIZE_MAX]; // uart buffer
extern void RS_UART_Init(void);
extern void RS_UART_DeInit(UART_HandleTypeDef *huart);
extern void RS_TIM_Init(void);
extern void RS_TIM_DeInit(TIM_HandleTypeDef *htim);
//-------------------------------------------------------------------
//-------------------------GENERAL FUNCTIONS-------------------------
/**
* @brief Начать прием по прерываниям.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема.
*/
RS_StatusTypeDef RS_Receive_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
HAL_StatusTypeDef uart_res = 0;
//-------------CHECK RS LINE----------------
// check that receive isnt busy
if( RS_Is_RX_Busy(hRS) ) // if tx busy - return busy status
return RS_BUSY;
//-----------INITIALIZE RECEIVE-------------
// if all OK: start receiving
RS_EnableReceive();
RS_Set_Busy(hRS); // set RS busy
RS_Set_RX_Flags(hRS); // initialize flags for receive
hRS->pMessagePtr = RS_msg; // set pointer to message structire for filling it from UARTHandler fucntions
if(!hRS->f.RX_Continue) // if not continue receiving
hRS->RS_Message_Size = 0; // set ptr to start buffer
// start receiving
__HAL_UART_ENABLE_IT(hRS->huart, UART_IT_IDLE);
uart_res = HAL_UART_Receive_IT(hRS->huart, &hRS->pBufferPtr[hRS->RS_Message_Size], RS_MSG_SIZE_MAX); // receive until ByteCnt+1 byte,
// then in Callback restart receive for rest bytes
// if receive isnt started - abort RS
if(uart_res != HAL_OK)
{
RS_RES = RS_Abort(hRS, ABORT_RS);
printf_rs_err("Failed to start RS receiving...");
TrackerCnt_Err(hRS->rs_err);
}
else
{
RS_RES = RS_OK;
printf_rs("Start Receiving...");
TrackerCnt_Ok(hRS->rs_err);
}
hRS->RS_STATUS = RS_RES;
return RS_RES; // returns result of receive init
}
/**
* @brief Начать передачу по прерываниям.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи.
*/
RS_StatusTypeDef RS_Transmit_IT(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
HAL_StatusTypeDef uart_res = 0;
//-------------CHECK RS LINE----------------
// check that transmit isnt busy
if( RS_Is_TX_Busy(hRS) ) // if tx busy - return busy status
return RS_BUSY;
// check receive line
//------------COLLECT MESSAGE---------------
RS_RES = RS_Collect_Message(hRS, RS_msg, hRS->pBufferPtr);
if (RS_RES != RS_OK) // if message isnt collect - stop RS and return error in RS_RES
{// need collect message status, so doesnt write abort to RS_RES
RS_Abort(hRS, ABORT_RS);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); // restart receive
}
else // if collect successful
{
//----------INITIALIZE TRANSMIT-------------
RS_EnableTransmit();
RS_Set_Busy(hRS); // set RS busy
RS_Set_TX_Flags(hRS); // initialize flags for transmit IT
hRS->pMessagePtr = RS_msg; // set pointer for filling given structure from UARTHandler fucntion
if(hRS->RS_Message_Size <= 0)
{
RS_RES = RS_Abort(hRS, ABORT_RS);
TrackerCnt_Err(hRS->rs_err);
return RS_ERR;
}
// if all OK: start transmitting
uart_res = HAL_UART_Transmit_IT(hRS->huart, hRS->pBufferPtr, hRS->RS_Message_Size);
// if transmit isnt started - abort RS
if(uart_res != HAL_OK)
{
RS_RES = RS_Abort(hRS, ABORT_RS);
printf_rs_err("Failed to start RS transmitting...");
TrackerCnt_Err(hRS->rs_err);
}
else
{
RS_RES = RS_OK;
printf_rs("Start Transmitting...");
TrackerCnt_Ok(hRS->rs_err);
}
}
hRS->RS_STATUS = RS_RES;
return RS_RES; // returns result of transmit init
}
/**
* @brief Инициалазация структуры @ref RS_HandleTypeDef.
* @param hRS Указатель на хендлер RS.
* @param suart Указатель на структуру с настройками UART.
* @param stim Указатель на структуру с настройками таймера.
* @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
* @return RS_RES Статус о состоянии RS после инициализации.
* @details Инициализация перефирии и структуры для приема-передачи по RS.
*/
RS_StatusTypeDef RS_Init(RS_HandleTypeDef *hRS, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim, uint8_t *pRS_BufferPtr)
{
// check that hRS is defined
if (hRS == NULL)
return RS_ERR;
// check that huart is defined
if (huart == NULL)
return RS_ERR;
hRS->huart = huart;
hRS->htim = htim;
// check that buffer is defined
if (hRS->pBufferPtr == NULL)
{
hRS->pBufferPtr = RS_Buffer; // if no - set default
}
else
hRS->pBufferPtr = pRS_BufferPtr; // if yes - set by user
return RS_OK;
}
/**
* @brief Отменить прием/передачу RS/UART.
* @param hRS Указатель на хендлер RS.
* @param AbortMode Выбор, что надо отменить.
- ABORT_TX: Отмена передачи по ЮАРТ, с очищением флагов TX,
- ABORT_RX: Отмена приема по ЮАРТ, с очищением флагов RX,
- ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
- ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры.
* @return RS_RES Статус о состоянии RS после аборта.
* @details Отмена работы UART в целом или отмена приема/передачи RS.
Также очищается хендл hRS.
*/
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
{
HAL_StatusTypeDef uart_res = 0;
RS_Timeout_Stop(hRS);
if((AbortMode&ABORT_RS) == 0x00)
{
if((AbortMode&ABORT_RX) == ABORT_RX)
{
uart_res = HAL_UART_AbortReceive(hRS->huart); // abort receive
RS_Reset_RX_Flags(hRS);
}
if((AbortMode&ABORT_TX) == ABORT_TX)
{
uart_res = HAL_UART_AbortTransmit(hRS->huart); // abort transmit
RS_Reset_TX_Flags(hRS);
}
}
else
{
uart_res = HAL_UART_Abort(hRS->huart);
RS_Clear_All(hRS);
}
hRS->RS_STATUS = RS_ABORTED;
return RS_ABORTED;
}
//-------------------------GENERAL FUNCTIONS-------------------------
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
/**
* @brief Обработчик для начала приема.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения.
* @details Определяет начинать прием команды/ответа или нет.
*/
RS_StatusTypeDef RS_Handle_Receive_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
switch(hRS->sRS_Mode)
{
// В режиме мастер
case RS_MASTER_REQUEST:
RS_Timeout_Start(hRS); // сразу запускаем таймаут и начинаем прием
// В режиме слейв
case RS_SLAVE_ALWAYS_WAIT:
RS_RES = RS_Receive_IT(hRS, RS_msg); // Просто запускаем фоновый прием
break;
case RS_RESERVED:
RS_Set_Free(hRS); RS_RES = RS_OK; break; // end RS communication (set RS unbusy)
}
if(RS_RES != RS_OK)
{
TrackerCnt_Err(hRS->rs_err);
}
return RS_RES;
}
/**
* @brief Обработчик для начала передачи.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о состоянии RS после инициализации передачи.
* @details Определяет отвечать ли на команду или нет.
*/
RS_StatusTypeDef RS_Handle_Transmit_Start(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
RS_StatusTypeDef RS_RES = 0;
switch(hRS->sRS_Mode)
{
case RS_SLAVE_ALWAYS_WAIT: // in slave mode always response
case RS_RESERVED: // transmit response
case RS_MASTER_REQUEST: // transmit response
RS_RES = RS_Transmit_IT(hRS, RS_msg); break;
}
if(RS_RES != RS_OK)
{
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
{
RS_Handle_Receive_Start(hRS, RS_msg);
}
TrackerCnt_Err(hRS->rs_err);
}
return RS_RES;
}
/**
* @brief UART TX Callback: коллбек после окончания передачи.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус о состоянии RS после обработки приема.
* @details Определяет поведение RS после передачи сообщения.
*/
RS_StatusTypeDef RS_UART_TxCpltCallback(RS_HandleTypeDef *hRS)
{
RS_StatusTypeDef RS_RES = RS_OK;
HAL_StatusTypeDef uart_res = 0;
//--------------ENDING TRANSMITTING-------------
RS_Set_TX_End(hRS);
//-----------START RECEIVING or END RS----------
RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
return RS_RES;
}
/**
* @brief Обработчик прерывания UART.
* @param hRS Указатель на хендлер RS.
* @details Обрабатывает ошибки если есть и вызывает RS Коллбеки.
* Добавить вызов этой функции в UARTx_IRQHandler() ВМЕСТО HAL_UART_IRQHandler().
*/
void RS_UART_Handler(RS_HandleTypeDef *hRS)
{
if(hRS->huart == NULL)
{
return;
}
RS_UART_Handler_ENTER();
//-------------CHECK IDLE FLAG FIRST-------------
/* Проверяем флаг IDLE в первую очередь - это гарантирует обработку только после idle */
if(__HAL_UART_GET_FLAG(hRS->huart, UART_FLAG_IDLE) && __HAL_UART_GET_IT_SOURCE(hRS->huart, UART_IT_IDLE))
{
__HAL_UART_CLEAR_IDLEFLAG(hRS->huart); // Важно: очистить флаг IDLE
//-------------STANDARD UART HANDLING-------------
HAL_UART_IRQHandler(hRS->huart);
hRS->f.RX_Continue = 0;
// Если прием активен и мы получили IDLE - это конец фрейма
if(RS_Is_RX_Busy(hRS) && hRS->f.RX_Ongoing)
{
// Получаем количество фактически принятых байтов
hRS->RS_Message_Size += hRS->huart->RxXferSize - hRS->huart->RxXferCount;
if(hRS->RS_Message_Size > 0)
{
// Принудительно завершаем прием (получили сообщение)
HAL_UART_AbortReceive(hRS->huart); // abort receive
// Завершаем прием в нашей структуре
RS_Set_RX_End(hRS);
// Парсим наше сообщение
hRS->RS_STATUS = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
// Если сообещине принято корректно
if(hRS->RS_STATUS == RS_OK)
{
RS_Timeout_Stop(hRS);
hRS->lastPacketTick = local_time();
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
{
RS_Response(hRS, hRS->pMessagePtr); // отвечаем на запрос
}
else
{
RS_Set_Free(hRS); // освобожднаем RS
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
}
}
}
else
{
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr); // если сообщение пришло не полностью - продолжаем прием до таймаута
}
}
}
return; // Выходим после обработки IDLE
}
else
{
//-------------STANDARD UART HANDLING-------------
HAL_UART_IRQHandler(hRS->huart);
}
//-------------CALL RS CALLBACKS------------
/* IF NO ERROR OCCURS */
if(hRS->huart->ErrorCode == 0)
{
// if first byte is received and receive is active
if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS))
{
RS_Timeout_Start(hRS);
}
RS_Timeout_Update(hRS);
/* RX Callback - теперь НЕ вызываем здесь, ждем IDLE */
/* TX Callback */
if ((hRS->huart->TxXferCount == 0U) && RS_Is_TX_Busy(hRS) && // if all bytes are transmited and transmit is active
hRS->huart->gState != HAL_UART_STATE_BUSY_TX) // also check that receive "REALLY" isnt busy
RS_UART_TxCpltCallback(hRS);
/* NOTE: RX Callback больше не вызывается здесь - ждем IDLE для гарантии конца фрейма */
}
//----------------ERRORS HANDLER----------------
else
{
if (hRS->huart->ErrorCode & HAL_UART_ERROR_ORE)
{
MB_Diagnostics_CharacterOverrunCnt(); // <-- Обнаружено переполнение
}
//TrackerCnt_Err(hRS->rs_err);
/* de-init uart transfer */
RS_Abort(hRS, ABORT_RS);
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
// later, maybe, will be added specific handlers for err
}
RS_UART_Handler_EXIT();
}
/**
* @brief Обработчик прерывания TIM.
* @param hRS Указатель на хендлер RS.
* @details Попадание сюда = таймаут и перезапуск RS приема
* Добавить вызов этой функции в TIMx_IRQHandler() ВМЕСТО HAL_TIM_IRQHandler().
*/
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
{
if(hRS->htim == NULL)
{
return;
}
RS_TIM_Handler_ENTER();
HAL_TIM_IRQHandler(hRS->htim);
RS_Abort(hRS, ABORT_RS);
hRS->RS_STATUS = RS_TIMEOUT;
if(hRS->sRS_Mode < RS_MASTER_MODE_START)
if(hRS->pMessagePtr->DeviceAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
TrackerCnt_Err(hRS->rs_err);
if(hRS->sRS_Mode >= RS_MASTER_MODE_START)
{ // Мастер: коллбек и освобождение для нового запроса
RS_Set_Free(hRS);
if(hRS->pCallback)
{
hRS->pCallback(hRS, hRS->pMessagePtr); // обрабатываем ответ
}
} else {
// Слейв: перезапускаем прием
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
}
RS_TIM_Handler_EXIT();
}
/**
* @brief Запуск таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Запускает таймер для отсчета времени ожидания следующего байта.
*/
RS_StatusTypeDef RS_Timeout_Start(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
if(hRS->sRS_Timeout) // if timeout setted
{
hRS->htim->Instance->ARR = hRS->sRS_Timeout;
HAL_TIM_Base_Start_IT(hRS->htim);
RS_Set_RX_Active_Flags(hRS);
}
}
return RS_OK;
}
/**
* @brief Остановка таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Останавливает таймер ожидания.
*/
RS_StatusTypeDef RS_Timeout_Stop(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
// Останавливаем таймаут
if(hRS->sRS_Timeout)
HAL_TIM_Base_Stop_IT(hRS->htim);
hRS->htim->Instance->CNT = 0;
__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
}
return RS_OK;
}
/**
* @brief Обновление (сброс) таймаута приема.
* @param hRS Указатель на хендлер RS.
* @return RS_RES Статус операции.
* @details Сбрасывает счетчик таймера в 0.
*/
RS_StatusTypeDef RS_Timeout_Update(RS_HandleTypeDef *hRS)
{
if(hRS->htim)
{
hRS->htim->Instance->CNT = 0; // reset cnt;
}
return RS_OK;
}
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
//-------------------------------------------------------------------
//-------------------------------------------------------------------
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
/**
* @brief Пользовательская функция для ответа на запрос по UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @return RS_RES Статус о результате ответа на комманду.
*/
__weak RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg)
{
/* Redefine function for user purposes */
return RS_ERR;
}
/**
* @brief Пользовательская функция для сбора сообщения в буфер UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения буфера.
*/
__weak RS_StatusTypeDef RS_Collect_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
/* Redefine function for user purposes */
return RS_ERR;
}
/**
* @brief Пользовательская функция для парса сообщения из буфера UART.
* @param hRS Указатель на хендлер RS.
* @param RS_msg Указатель на структуру сообщения.
* @param msg_uart_buff Указатель на буффер UART.
* @return RS_RES Статус о результате заполнения структуры.
*/
__weak RS_StatusTypeDef RS_Parse_Message(RS_HandleTypeDef *hRS, RS_MsgTypeDef *RS_msg, uint8_t *msg_uart_buff)
{
/* Redefine function for user purposes */
return RS_ERR;
}
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
//-------------------------------------------------------------------