pre-release 0.1
проверка
This commit is contained in:
116
Src/__crc_algs.c
Normal file
116
Src/__crc_algs.c
Normal 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;
|
||||
}
|
||||
554
Src/modbus.c
Normal file
554
Src/modbus.c
Normal file
@@ -0,0 +1,554 @@
|
||||
/**
|
||||
**************************************************************************
|
||||
* @file modbus.c
|
||||
* @brief Модуль для реализации MODBUS.
|
||||
**************************************************************************
|
||||
* @details
|
||||
Файл содержит реализацию функций работы с Modbus.
|
||||
|
||||
@section Функции и макросы
|
||||
|
||||
### Инициализация:
|
||||
- MODBUS_SetupHardware() — Инициализация модуля Modbus.
|
||||
- MODBUS_Config() — Инициализация модуля Modbus.
|
||||
|
||||
### Функции для Modbus
|
||||
- MB_Slave_Response()
|
||||
- MB_Slave_Collect_Message()
|
||||
- MB_Slave_Parse_Message()
|
||||
- MB_Master_Collect_Message()
|
||||
- MB_Master_Parse_Message()
|
||||
|
||||
### Функции для работы с RS (UART):
|
||||
- RS_Parse_Message() / RS_Collect_Message() — Парсинг и сборка сообщения.
|
||||
- RS_Response() — Отправка ответа.
|
||||
|
||||
******************************************************************************/
|
||||
|
||||
#include "modbus.h"
|
||||
|
||||
/* MODBUS HANDLES */
|
||||
RS_HandleTypeDef hmodbus1;
|
||||
RS_MsgTypeDef MODBUS_MSG;
|
||||
|
||||
/* DEFINE REGISTERS/COILS */
|
||||
MB_DeviceIdentificationTypeDef MB_DEVID;
|
||||
MB_DataStructureTypeDef MB_DATA = {0};;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
//-----------------------------FOR USER------------------------------
|
||||
/**
|
||||
* @brief Инициализация периферии модбас.
|
||||
* @param hmodbus Указатель на хендлер RS
|
||||
* @details Подключает хендлы периферии к hmodbus
|
||||
* Конфигурация выставляется по умолчанию из modbus_config.h
|
||||
*/
|
||||
void MODBUS_SetupHardware(RS_HandleTypeDef *hmodbus, UART_HandleTypeDef *huart, TIM_HandleTypeDef *htim)
|
||||
{
|
||||
if((hmodbus == NULL) || (huart == NULL))
|
||||
{
|
||||
return;
|
||||
}
|
||||
MB_DeviceInentificationInit();
|
||||
//-----------SETUP MODBUS-------------
|
||||
// set up modbus: MB_RX_Size_NotConst and Timeout enable
|
||||
hmodbus1.ID = MODBUS_DEVICE_ID;
|
||||
hmodbus1.sRS_Timeout = MODBUS_TIMEOUT;
|
||||
hmodbus1.sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
|
||||
hmodbus1.sRS_RX_Size_Mode = RS_RX_Size_NotConst;
|
||||
|
||||
// INIT
|
||||
hmodbus1.RS_STATUS = RS_Init(hmodbus, huart, htim, 0);
|
||||
|
||||
RS_EnableReceive();
|
||||
}
|
||||
/**
|
||||
* @brief Программная конфигурация модбас.
|
||||
* @param hmodbus указатель на хендлер RS
|
||||
* @param Timeout Время тишины между двумя байтами после которых перезапускается прием
|
||||
* @param master Режим мастер (пока не сделан)
|
||||
* @details Конфигурирует ID, таймаут и режим hmodbus
|
||||
*/
|
||||
void MODBUS_Config(RS_HandleTypeDef *hmodbus, uint8_t ID, uint16_t Timeout, uint8_t master)
|
||||
{
|
||||
if(hmodbus == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//-----------SETUP MODBUS-------------
|
||||
// set up modbus: MB_RX_Size_NotConst and Timeout enable
|
||||
hmodbus->ID = ID;
|
||||
hmodbus->sRS_Timeout = Timeout;
|
||||
if(master)
|
||||
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
|
||||
else
|
||||
hmodbus->sRS_Mode = RS_SLAVE_ALWAYS_WAIT;
|
||||
hmodbus->sRS_RX_Size_Mode = RS_RX_Size_NotConst;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Запуск слейв модбас.
|
||||
* @param hmodbus Указатель на хендлер RS.
|
||||
* @param modbus_msg Указатель на структуру сообщения.
|
||||
* @details Конфигурирует ID, таймаут и режим hmodbus
|
||||
*/
|
||||
void MODBUS_SlaveStart(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
|
||||
{
|
||||
if(hmodbus == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(modbus_msg)
|
||||
RS_Receive_IT(hmodbus, modbus_msg);
|
||||
else
|
||||
RS_Receive_IT(hmodbus, &MODBUS_MSG);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Ответ на сообщение в режиме слейва.
|
||||
* @param hmodbus Указатель на хендлер RS.
|
||||
* @param modbus_msg Указатель на структуру сообщения.
|
||||
* @return RS_RES Статус о результате ответа на комманду.
|
||||
*/
|
||||
static 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
|
||||
|
||||
if(hmodbus->ID == 0)
|
||||
{
|
||||
hmodbus->RS_STATUS = RS_SKIP;
|
||||
return MB_RES;
|
||||
}
|
||||
|
||||
if(modbus_msg->Func_Code < ERR_VALUES_START)// if no errors after parsing
|
||||
{
|
||||
switch (modbus_msg->Func_Code)
|
||||
{
|
||||
// Read Coils
|
||||
case MB_R_COILS:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_Read_Coils(hmodbus->pMessagePtr);
|
||||
break;
|
||||
|
||||
// Read Hodling Registers
|
||||
case MB_R_HOLD_REGS:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_Read_Hold_Regs(hmodbus->pMessagePtr);
|
||||
break;
|
||||
case MB_R_IN_REGS:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_Read_Input_Regs(hmodbus->pMessagePtr);
|
||||
break;
|
||||
|
||||
|
||||
// Write Single Coils
|
||||
case MB_W_COIL:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_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 MB_W_HOLD_REG:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_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 MB_W_COILS:
|
||||
hmodbus->f.MessageHandled = MB_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 MB_W_HOLD_REGS:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_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 MB_R_DEVICE_INFO:
|
||||
hmodbus->f.MessageHandled = MB_Proccess_Read_Device_Identification(hmodbus->pMessagePtr);
|
||||
break;
|
||||
|
||||
/* unknown func code */
|
||||
default: modbus_msg->Except_Code = 0x01; /* set exception code: illegal function */
|
||||
}
|
||||
|
||||
if(hmodbus->f.MessageHandled == 0)
|
||||
{
|
||||
TrackerCnt_Warn(hmodbus->rs_err);
|
||||
modbus_msg->Func_Code |= 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 в режиме слейв.
|
||||
* @param hmodbus Указатель на хендлер RS.
|
||||
* @param modbus_msg Указатель на структуру сообщения.
|
||||
* @param modbus_uart_buff Указатель на буффер UART.
|
||||
* @return RS_RES Статус о результате заполнения буфера.
|
||||
*/
|
||||
static 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------
|
||||
//-----------[first bytes]-----------
|
||||
// set ID of message/user
|
||||
modbus_uart_buff[ind++] = modbus_msg->MbAddr;
|
||||
|
||||
// set dat or err response
|
||||
modbus_uart_buff[ind++] = modbus_msg->Func_Code;
|
||||
|
||||
if (modbus_msg->Func_Code < ERR_VALUES_START) // if no error occur
|
||||
{
|
||||
// fill modbus header
|
||||
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // devide identification 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 > DATA_SIZE*2) // if ByteCnt less than DATA_SIZE
|
||||
{
|
||||
TrackerCnt_Err(hmodbus->rs_err);
|
||||
return RS_COLLECT_MSG_ERR;
|
||||
}
|
||||
|
||||
|
||||
//---------------DATA----------------
|
||||
//-----------[data bytes]------------
|
||||
uint8_t *tmp_data_addr = (uint8_t *)modbus_msg->DATA;
|
||||
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++;
|
||||
}
|
||||
|
||||
}
|
||||
else // modbus data header
|
||||
{
|
||||
// set size of received data
|
||||
if (modbus_msg->ByteCnt <= DATA_SIZE*2) // if ByteCnt less than DATA_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->DATA;
|
||||
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]----------
|
||||
// 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->MB_CRC = CRC_VALUE;
|
||||
modbus_uart_buff[ind++] = CRC_VALUE;
|
||||
modbus_uart_buff[ind++] = CRC_VALUE >> 8;
|
||||
|
||||
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->Func_Code & ~ERR_VALUES_START) < 0x0F)
|
||||
{
|
||||
modbus_msg->ByteCnt = 0;
|
||||
mb_func_size = 1;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
modbus_msg->ByteCnt = hmodbus->pBufferPtr[RX_FIRST_PART_SIZE-1]; // get numb of data in command
|
||||
// +1 because that defines is size, not ind.
|
||||
mb_func_size = modbus_msg->ByteCnt + 2;
|
||||
}
|
||||
|
||||
|
||||
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO)
|
||||
{
|
||||
mb_func_size = 0;
|
||||
}
|
||||
|
||||
mb_func_size = RX_FIRST_PART_SIZE + mb_func_size; // size of whole message
|
||||
return mb_func_size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Парс сообщения в режиме слейв.
|
||||
* @param hmodbus Указатель на хендлер RS.
|
||||
* @param modbus_msg Указатель на структуру сообщения.
|
||||
* @param modbus_uart_buff Указатель на буффер UART.
|
||||
* @return RS_RES Статус о результате заполнения структуры.
|
||||
* @details Заполнение структуры сообщения из буффера UART.
|
||||
*/
|
||||
static 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;
|
||||
//-----INFO ABOUT DATA/MESSAGE-------
|
||||
//-----------[first bits]------------
|
||||
// get ID of message/user
|
||||
if(modbus_uart_buff[ind] != hmodbus->ID)
|
||||
{
|
||||
modbus_msg->MbAddr = 0;
|
||||
return RS_SKIP;
|
||||
}
|
||||
modbus_msg->MbAddr = modbus_uart_buff[ind++];
|
||||
|
||||
// get func code
|
||||
modbus_msg->Func_Code = modbus_uart_buff[ind++];
|
||||
if(modbus_msg->Func_Code & ERR_VALUES_START) // явная херня
|
||||
{
|
||||
modbus_msg->MbAddr = 0;
|
||||
return RS_SKIP;
|
||||
}
|
||||
|
||||
if(modbus_msg->Func_Code == MB_R_DEVICE_INFO) // if it device identification 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;
|
||||
}
|
||||
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->Func_Code == 0x0F) || (hmodbus->pMessagePtr->Func_Code == 0x10))
|
||||
hmodbus->pMessagePtr->ByteCnt = modbus_uart_buff[ind++];
|
||||
else
|
||||
hmodbus->pMessagePtr->ByteCnt = 0;
|
||||
|
||||
//---------------DATA----------------
|
||||
// (optional)
|
||||
if (modbus_msg->ByteCnt != 0)
|
||||
{
|
||||
//check that data size is correct
|
||||
if (modbus_msg->ByteCnt > DATA_SIZE*2)
|
||||
{
|
||||
TrackerCnt_Err(hmodbus->rs_err);
|
||||
modbus_msg->Func_Code |= ERR_VALUES_START;
|
||||
return RS_PARSE_MSG_ERR;
|
||||
}
|
||||
uint16_t *tmp_data_addr = (uint16_t *)modbus_msg->DATA;
|
||||
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]----------
|
||||
// calc crc of received data
|
||||
uint16_t CRC_VALUE = crc16(modbus_uart_buff, ind);
|
||||
// get crc of received data
|
||||
modbus_msg->MB_CRC = modbus_uart_buff[ind++];
|
||||
modbus_msg->MB_CRC |= modbus_uart_buff[ind++] << 8;
|
||||
// compare crc
|
||||
if (modbus_msg->MB_CRC != CRC_VALUE)
|
||||
{
|
||||
TrackerCnt_Err(hmodbus->rs_err);
|
||||
modbus_msg->Func_Code |= ERR_VALUES_START;
|
||||
}
|
||||
// hmodbus->MB_RESPONSE = MB_CRC_ERR; // set func code - error about wrong crc
|
||||
|
||||
// check is buffer empty
|
||||
check_empty_buff = 0;
|
||||
for(int i=0; i<ind;i++)
|
||||
check_empty_buff += modbus_uart_buff[i];
|
||||
// if(check_empty_buff == 0)
|
||||
// hmodbus->MB_RESPONSE = MB_EMPTY_MSG; //
|
||||
|
||||
// если размер меньше ожидаемого - продолжаем принимать
|
||||
if(hmodbus->RS_Message_Size < MB_Define_Size_of_Function(hmodbus, modbus_msg))
|
||||
{
|
||||
hmodbus->f.RX_Continue = 1;
|
||||
return RS_SKIP;
|
||||
}
|
||||
// если больше Ошибка
|
||||
else if (hmodbus->RS_Message_Size > MB_Define_Size_of_Function(hmodbus, modbus_msg))
|
||||
{
|
||||
return RS_PARSE_MSG_ERR;
|
||||
}
|
||||
|
||||
return RS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Сбор сообщения в буфер UART в режиме мастер.
|
||||
* @param hmodbus Указатель на хендлер RS.
|
||||
* @param modbus_msg Указатель на структуру сообщения.
|
||||
* @param modbus_uart_buff Указатель на буффер UART.
|
||||
* @return RS_RES Статус о результате заполнения буфера.
|
||||
*/
|
||||
static RS_StatusTypeDef MB_Master_Collect_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
|
||||
{
|
||||
return RS_PARSE_MSG_ERR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Парс сообщения в режиме мастер.
|
||||
* @param hmodbus Указатель на хендлер RS.
|
||||
* @param modbus_msg Указатель на структуру сообщения.
|
||||
* @param modbus_uart_buff Указатель на буффер UART.
|
||||
* @return RS_RES Статус о результате заполнения структуры.
|
||||
*/
|
||||
static RS_StatusTypeDef MB_Master_Parse_Message(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg, uint8_t *modbus_uart_buff)
|
||||
{
|
||||
return RS_PARSE_MSG_ERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Реализация функций из rs_message.c для протокола */
|
||||
RS_StatusTypeDef RS_Response(RS_HandleTypeDef *hmodbus, RS_MsgTypeDef *modbus_msg)
|
||||
{
|
||||
if(hmodbus->sRS_Mode >= RS_MASTER_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_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_START)
|
||||
{
|
||||
return MB_Slave_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
|
||||
}
|
||||
else
|
||||
{
|
||||
return MB_Master_Parse_Message(hmodbus, modbus_msg, modbus_uart_buff);
|
||||
}
|
||||
}
|
||||
238
Src/modbus_coils.c
Normal file
238
Src/modbus_coils.c
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file modbus_coils.c
|
||||
* @brief Реализация работы с коилами Modbus
|
||||
******************************************************************************
|
||||
* @details
|
||||
Модуль обработки команд для coils (битовых данных):
|
||||
- Чтение coils (0x01) - упаковка битов в байты для передачи
|
||||
- Запись одиночного coil (0x05) - установка/сброс бита
|
||||
- Запись множественных coils (0x0F) - распаковка битов из байтов
|
||||
|
||||
******************************************************************************/
|
||||
#include "modbus_coils.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Set or Reset Coil at its global address.
|
||||
* @param Addr Адрес коила.
|
||||
* @param WriteVal Что записать в коил: 0 или 1.
|
||||
* @return ExceptionCode Код исключения если коила по адресу не существует, и NO_ERRORS если все ок.
|
||||
*
|
||||
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
|
||||
Вне зависимости от того как коилы размещены в памяти.
|
||||
*/
|
||||
MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal)
|
||||
{
|
||||
//---------CHECK FOR ERRORS----------
|
||||
MB_ExceptionTypeDef Exception = 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 == 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 Read Coil at its global address.
|
||||
* @param Addr Адрес коила.
|
||||
* @param Exception Указатель на переменную для кода исключения, в случа неудачи при чтении.
|
||||
* @return uint16_t Возвращает весь регистр с маской на запрошенном коиле.
|
||||
*
|
||||
* @details Позволяет обратиться к любому коилу по его глобальному адрессу.
|
||||
Вне зависимости от того как коилы размещены в памяти.
|
||||
*/
|
||||
uint16_t MB_Read_Coil_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 == NO_ERRORS)
|
||||
{
|
||||
return ((*coils)&(1<<start_shift));
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Proccess command Read Coils (01 - 0x01).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Read Coils.
|
||||
*/
|
||||
uint8_t MB_Proccess_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 != 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->DATA[ind] = (*(coils+ind)&mask_for_coils) >> start_shift;
|
||||
if(ind > 0)
|
||||
modbus_msg->DATA[ind-1] |= ((*(coils+ind)&mask_for_coils) << 16) >> start_shift;
|
||||
|
||||
}
|
||||
// т.к. DATA 16-битная, для 8-битной передачи, надо поменять местами верхний и нижний байты
|
||||
for(; ind >= 0; --ind)
|
||||
modbus_msg->DATA[ind] = ByteSwap16(modbus_msg->DATA[ind]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Proccess command Write Single Coils (05 - 0x05).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Write Single Coils.
|
||||
*/
|
||||
uint8_t MB_Proccess_Write_Single_Coil(RS_MsgTypeDef *modbus_msg)
|
||||
{
|
||||
//---------CHECK FOR ERRORS----------
|
||||
if ((modbus_msg->Qnt != 0x0000) && (modbus_msg->Qnt != 0xFF00))
|
||||
{
|
||||
modbus_msg->Except_Code = 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 != 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 Proccess command Write Multiple Coils (15 - 0x0F).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Write Multiple Coils.
|
||||
*/
|
||||
uint8_t MB_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 = 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 != 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->DATA[ind]) << start_shift;
|
||||
if(ind > 0)
|
||||
{
|
||||
setted_coils |= ((ByteSwap16(modbus_msg->DATA[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;
|
||||
}
|
||||
10
Src/modbus_core.c
Normal file
10
Src/modbus_core.c
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file modbus_core.c
|
||||
* @brief Базовая реализация ядра Modbus (заглушка)
|
||||
******************************************************************************
|
||||
* @details
|
||||
В текущей реализации этот файл служит заглушкой для будущего расширения
|
||||
функциональности ядра Modbus протокола.
|
||||
******************************************************************************/
|
||||
#include "modbus_core.h"
|
||||
535
Src/modbus_devid.c
Normal file
535
Src/modbus_devid.c
Normal file
@@ -0,0 +1,535 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file modbus_devid.c
|
||||
* @brief Реализация идентификации устройства Modbus
|
||||
******************************************************************************
|
||||
* @details
|
||||
Модуль обработки запросов идентификации устройства через MEI-тип 0x0E:
|
||||
- Формирование иерархии объектов идентификации
|
||||
- Поддержка потоковой передачи при большом количестве объектов
|
||||
- Автоматический расчет MoreFollows флагов
|
||||
|
||||
@section Потоковая передача:
|
||||
При большом количестве объектов идентификация разбивается на несколько
|
||||
сообщений с установкой флага MoreFollows и указанием NextObjId для
|
||||
продолжения чтения в следующем запросе.
|
||||
******************************************************************************/
|
||||
|
||||
#include "modbus_devid.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Write Object of Device Identification to MessageData
|
||||
* @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 Write Object of Device Identification to MessageData
|
||||
* @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;
|
||||
|
||||
/* Define number of object in one message */
|
||||
unsigned lastobjid = 0;
|
||||
for(int i = 0; i < DATA_SIZE*2;)
|
||||
{
|
||||
i += 2;
|
||||
i += obj[objidtmp].length;
|
||||
/* Если все еще помещается в массив переходим на следующий объект */
|
||||
if(i < DATA_SIZE*2)
|
||||
{
|
||||
objidtmp++;
|
||||
}
|
||||
/* Если объекты для записи закончились - выходим из цикла*/
|
||||
if(objidtmp > maxidofobj)
|
||||
break;
|
||||
}
|
||||
lastobjid = objidtmp-1;
|
||||
|
||||
|
||||
/* Fill message with objects data */
|
||||
char *mbdata = (char *)&modbus_msg->DATA;
|
||||
unsigned ind = 0;
|
||||
unsigned objid = modbus_msg->DevId.NextObjId;
|
||||
for(; objid <= lastobjid; objid++)
|
||||
{
|
||||
mbdata[ind++] = objid;
|
||||
MB_WriteSingleObjectToMessage(mbdata, &ind, &obj[objid]);
|
||||
}
|
||||
objid--;
|
||||
modbus_msg->ByteCnt = ind;
|
||||
modbus_msg->DevId.NextObjId = lastobjid+1;
|
||||
if(objid == maxidofobj)
|
||||
{
|
||||
modbus_msg->DevId.MoreFollows = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
modbus_msg->DevId.MoreFollows = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Proccess command Read Device Identification (43/14 - 0x2B/0E).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Write Single Register.
|
||||
*/
|
||||
uint8_t MB_Proccess_Read_Device_Identification(RS_MsgTypeDef *modbus_msg)
|
||||
{
|
||||
switch(modbus_msg->DevId.ReadDevId)
|
||||
{
|
||||
case MB_BASIC_IDENTIFICATION:
|
||||
if (modbus_msg->DevId.NextObjId == 0)
|
||||
{
|
||||
modbus_msg->DevId.NextObjId = 0;
|
||||
}
|
||||
|
||||
MB_WriteObjectsToMessage(modbus_msg, 2);
|
||||
modbus_msg->DevId.NumbOfObj = 3;
|
||||
break;
|
||||
|
||||
case MB_REGULAR_IDENTIFICATION:
|
||||
if (modbus_msg->DevId.NextObjId == 0)
|
||||
{
|
||||
modbus_msg->DevId.NextObjId = 3;
|
||||
}
|
||||
|
||||
MB_WriteObjectsToMessage(modbus_msg, 6);
|
||||
modbus_msg->DevId.NumbOfObj = 4;
|
||||
break;
|
||||
|
||||
case MB_EXTENDED_IDENTIFICATION:
|
||||
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 MB_SPEDIFIC_IDENTIFICATION:
|
||||
MB_WriteObjectsToMessage(modbus_msg, modbus_msg->DevId.NextObjId);
|
||||
modbus_msg->DevId.NumbOfObj = 1;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
|
||||
#ifdef MODBUS_USEROBJECT_0_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[0], MODBUS_USEROBJECT_0_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_1_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[1], MODBUS_USEROBJECT_1_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_2_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[2], MODBUS_USEROBJECT_2_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_3_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[3], MODBUS_USEROBJECT_3_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_4_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[4], MODBUS_USEROBJECT_4_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_5_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[5], MODBUS_USEROBJECT_5_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_6_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[6], MODBUS_USEROBJECT_6_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_7_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[7], MODBUS_USEROBJECT_7_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_8_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[8], MODBUS_USEROBJECT_8_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_9_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[9], MODBUS_USEROBJECT_9_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_10_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[10], MODBUS_USEROBJECT_10_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_11_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[11], MODBUS_USEROBJECT_11_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_12_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[12], MODBUS_USEROBJECT_12_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_13_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[13], MODBUS_USEROBJECT_13_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_14_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[14], MODBUS_USEROBJECT_14_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_15_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[15], MODBUS_USEROBJECT_15_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_16_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[16], MODBUS_USEROBJECT_16_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_17_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[17], MODBUS_USEROBJECT_17_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_18_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[18], MODBUS_USEROBJECT_18_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_19_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[19], MODBUS_USEROBJECT_19_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_20_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[20], MODBUS_USEROBJECT_20_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_21_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[21], MODBUS_USEROBJECT_21_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_22_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[22], MODBUS_USEROBJECT_22_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_23_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[23], MODBUS_USEROBJECT_23_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_24_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[24], MODBUS_USEROBJECT_24_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_25_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[25], MODBUS_USEROBJECT_25_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_26_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[26], MODBUS_USEROBJECT_26_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_27_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[27], MODBUS_USEROBJECT_27_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_28_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[28], MODBUS_USEROBJECT_28_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_29_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[29], MODBUS_USEROBJECT_29_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_30_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[30], MODBUS_USEROBJECT_30_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_31_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[31], MODBUS_USEROBJECT_31_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_32_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[32], MODBUS_USEROBJECT_32_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_33_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[33], MODBUS_USEROBJECT_33_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_34_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[34], MODBUS_USEROBJECT_34_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_35_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[35], MODBUS_USEROBJECT_35_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_36_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[36], MODBUS_USEROBJECT_36_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_37_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[37], MODBUS_USEROBJECT_37_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_38_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[38], MODBUS_USEROBJECT_38_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_39_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[39], MODBUS_USEROBJECT_39_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_40_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[40], MODBUS_USEROBJECT_40_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_41_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[41], MODBUS_USEROBJECT_41_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_42_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[42], MODBUS_USEROBJECT_42_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_43_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[43], MODBUS_USEROBJECT_43_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_44_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[44], MODBUS_USEROBJECT_44_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_45_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[45], MODBUS_USEROBJECT_45_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_46_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[46], MODBUS_USEROBJECT_46_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_47_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[47], MODBUS_USEROBJECT_47_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_48_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[48], MODBUS_USEROBJECT_48_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_49_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[49], MODBUS_USEROBJECT_49_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_50_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[50], MODBUS_USEROBJECT_50_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_51_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[51], MODBUS_USEROBJECT_51_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_52_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[52], MODBUS_USEROBJECT_52_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_53_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[53], MODBUS_USEROBJECT_53_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_54_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[54], MODBUS_USEROBJECT_54_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_55_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[55], MODBUS_USEROBJECT_55_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_56_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[56], MODBUS_USEROBJECT_56_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_57_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[57], MODBUS_USEROBJECT_57_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_58_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[58], MODBUS_USEROBJECT_58_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_59_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[59], MODBUS_USEROBJECT_59_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_60_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[60], MODBUS_USEROBJECT_60_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_61_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[61], MODBUS_USEROBJECT_61_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_62_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[62], MODBUS_USEROBJECT_62_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_63_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[63], MODBUS_USEROBJECT_63_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_64_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[64], MODBUS_USEROBJECT_64_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_65_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[65], MODBUS_USEROBJECT_65_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_66_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[66], MODBUS_USEROBJECT_66_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_67_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[67], MODBUS_USEROBJECT_67_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_68_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[68], MODBUS_USEROBJECT_68_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_69_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[69], MODBUS_USEROBJECT_69_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_70_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[70], MODBUS_USEROBJECT_70_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_71_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[71], MODBUS_USEROBJECT_71_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_72_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[72], MODBUS_USEROBJECT_72_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_73_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[73], MODBUS_USEROBJECT_73_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_74_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[74], MODBUS_USEROBJECT_74_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_75_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[75], MODBUS_USEROBJECT_75_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_76_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[76], MODBUS_USEROBJECT_76_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_77_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[77], MODBUS_USEROBJECT_77_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_78_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[78], MODBUS_USEROBJECT_78_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_79_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[79], MODBUS_USEROBJECT_79_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_80_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[80], MODBUS_USEROBJECT_80_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_81_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[81], MODBUS_USEROBJECT_81_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_82_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[82], MODBUS_USEROBJECT_82_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_83_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[83], MODBUS_USEROBJECT_83_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_84_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[84], MODBUS_USEROBJECT_84_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_85_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[85], MODBUS_USEROBJECT_85_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_86_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[86], MODBUS_USEROBJECT_86_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_87_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[87], MODBUS_USEROBJECT_87_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_88_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[88], MODBUS_USEROBJECT_88_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_89_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[89], MODBUS_USEROBJECT_89_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_90_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[90], MODBUS_USEROBJECT_90_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_91_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[91], MODBUS_USEROBJECT_91_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_92_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[92], MODBUS_USEROBJECT_92_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_93_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[93], MODBUS_USEROBJECT_93_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_94_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[94], MODBUS_USEROBJECT_94_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_95_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[95], MODBUS_USEROBJECT_95_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_96_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[96], MODBUS_USEROBJECT_96_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_97_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[97], MODBUS_USEROBJECT_97_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_98_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[98], MODBUS_USEROBJECT_98_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_99_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[99], MODBUS_USEROBJECT_99_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_100_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[100], MODBUS_USEROBJECT_100_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_101_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[101], MODBUS_USEROBJECT_101_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_102_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[102], MODBUS_USEROBJECT_102_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_103_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[103], MODBUS_USEROBJECT_103_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_104_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[104], MODBUS_USEROBJECT_104_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_105_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[105], MODBUS_USEROBJECT_105_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_106_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[106], MODBUS_USEROBJECT_106_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_107_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[107], MODBUS_USEROBJECT_107_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_108_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[108], MODBUS_USEROBJECT_108_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_109_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[109], MODBUS_USEROBJECT_109_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_110_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[110], MODBUS_USEROBJECT_110_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_111_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[111], MODBUS_USEROBJECT_111_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_112_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[112], MODBUS_USEROBJECT_112_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_113_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[113], MODBUS_USEROBJECT_113_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_114_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[114], MODBUS_USEROBJECT_114_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_115_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[115], MODBUS_USEROBJECT_115_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_116_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[116], MODBUS_USEROBJECT_116_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_117_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[117], MODBUS_USEROBJECT_117_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_118_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[118], MODBUS_USEROBJECT_118_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_119_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[119], MODBUS_USEROBJECT_119_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_120_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[120], MODBUS_USEROBJECT_120_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_121_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[121], MODBUS_USEROBJECT_121_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_122_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[122], MODBUS_USEROBJECT_122_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_123_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[123], MODBUS_USEROBJECT_123_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_124_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[124], MODBUS_USEROBJECT_124_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_125_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[125], MODBUS_USEROBJECT_125_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_126_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[126], MODBUS_USEROBJECT_126_NAME);
|
||||
#endif
|
||||
#ifdef MODBUS_USEROBJECT_127_NAME
|
||||
MB_ObjectInit(&MB_DEVID.User[127], MODBUS_USEROBJECT_127_NAME);
|
||||
#endif
|
||||
}
|
||||
|
||||
97
Src/modbus_holdregs.c
Normal file
97
Src/modbus_holdregs.c
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file modbus_holdregs.c
|
||||
* @brief Реализация работы с регистрами хранения Modbus
|
||||
******************************************************************************
|
||||
* @details
|
||||
Модуль обработки команд для holding registers (регистров хранения):
|
||||
- Чтение множественных регистров (0x03) - копирование данных в буфер ответа
|
||||
- Запись одиночного регистра (0x06) - прямая запись значения
|
||||
- Запись множественных регистров (0x10) - пакетная запись из буфера
|
||||
|
||||
@section Валидация данных:
|
||||
- Проверка соответствия количества байт и регистров
|
||||
Валидация адресов через MB_DefineRegistersAddress()
|
||||
- Обработка исключений при некорректных запросах
|
||||
|
||||
@section Echo-ответы:
|
||||
При успешной записи формируется echo-ответ с теми же данными,
|
||||
что были в запросе (для функций 0x05, 0x06, 0x0F, 0x10).
|
||||
******************************************************************************/
|
||||
#include "modbus_inputregs.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Proccess command Read Holding Registers (03 - 0x03).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Read Holding Registers.
|
||||
*/
|
||||
uint8_t MB_Proccess_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); // определение адреса регистров
|
||||
if(modbus_msg->Except_Code != 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->DATA[i] = *(pHoldRegs++);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Proccess command Write Single Register (06 - 0x06).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Write Single Register.
|
||||
*/
|
||||
uint8_t MB_Proccess_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); // определение адреса регистров
|
||||
if(modbus_msg->Except_Code != NO_ERRORS)
|
||||
return 0;
|
||||
|
||||
//-----------WRITTING REG------------
|
||||
*(pHoldRegs) = modbus_msg->Qnt;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Proccess command Write Multiple Registers (16 - 0x10).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Write Multiple Registers.
|
||||
*/
|
||||
uint8_t MB_Proccess_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); // определение адреса регистров
|
||||
if(modbus_msg->Except_Code != NO_ERRORS)
|
||||
return 0;
|
||||
|
||||
//-----------WRITTING REGS-----------
|
||||
for (int i = 0; i<modbus_msg->Qnt; i++)
|
||||
{
|
||||
*(pHoldRegs++) = modbus_msg->DATA[i];
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
45
Src/modbus_inputregs.c
Normal file
45
Src/modbus_inputregs.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file modbus_inputregs.c
|
||||
* @brief Реализация работы с входными регистрами Modbus
|
||||
******************************************************************************
|
||||
* @details
|
||||
Модуль обработки команды чтения input registers (0x04):
|
||||
- Чтение множественных входных регистров
|
||||
Копирование данных из структур устройства в буфер ответа
|
||||
- Поддержка знаковых и беззнаковых значений
|
||||
******************************************************************************/
|
||||
|
||||
#include "modbus_inputregs.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief Proccess command Read Input Registers (04 - 0x04).
|
||||
* @param modbus_msg Указатель на структуру собщения modbus.
|
||||
* @return fMessageHandled Статус о результате обработки комманды.
|
||||
* @details Обработка команды Read Input Registers.
|
||||
*/
|
||||
uint8_t MB_Proccess_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); // определение адреса регистров
|
||||
if(modbus_msg->Except_Code != 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->DATA[i] = (*pInRegs++);
|
||||
else
|
||||
modbus_msg->DATA[i] = (*pInRegs++);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
507
Src/rs_message.c
Normal file
507
Src/rs_message.c
Normal file
@@ -0,0 +1,507 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file rs_message.c
|
||||
* @brief Реализация протоколов обмена по RS/UART
|
||||
******************************************************************************
|
||||
* @details
|
||||
Модуль реализует асинхронный обмен сообщениями через UART с использованием:
|
||||
- Прерываний по приему/передаче
|
||||
- Детектирования конца фрейма по IDLE линии
|
||||
- Таймаутов через таймер
|
||||
- Двухстадийного приема (заголовок + данные)
|
||||
|
||||
@section Архитектура:
|
||||
В режиме слейв:
|
||||
- Инициализация приема с сообщения с максимальным размером MSG_SIZE_MAX
|
||||
- При срабатывании прерывания IDLE - обработка полученного сообщения
|
||||
В режиме мастер (пока не реализовано):
|
||||
- Отправка запроса и переход в режим приема сообщения с максимальным размером MSG_SIZE_MAX
|
||||
- При срабатывании прерывания IDLE - обработка полученного ответа
|
||||
|
||||
@section Необходимые обработчики:
|
||||
- RS_UART_Handler() в UARTx_IRQHandler вместо HAL_UART_IRQHandler()
|
||||
- RS_TIM_Handler() в TIMx_IRQHandler вместо HAL_TIM_IRQHandler()
|
||||
******************************************************************************/
|
||||
#include "rs_message.h"
|
||||
|
||||
uint8_t RS_Buffer[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 Start receive IT.
|
||||
* @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], 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 Start transmit IT.
|
||||
* @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();
|
||||
// for(int i = 0; i < hRS->sRS_Timeout; i++);
|
||||
|
||||
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 Initialize UART and handle RS stucture.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @param suart Указатель на структуру с настройками UART.
|
||||
* @param stim Указатель на структуру с настройками таймера.
|
||||
* @param pRS_BufferPtr Указатель на буффер для приема-передачи по UART. Если он NULL, то поставиться библиотечный буфер.
|
||||
* @return RS_RES Статус о состоянии RS после инициализации.
|
||||
* @note Инициализация перефирии и структуры для приема-передачи по 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;
|
||||
|
||||
if (hRS->sRS_RX_Size_Mode == NULL)
|
||||
return RS_ERR;
|
||||
|
||||
// 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 Abort RS/UART.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @param AbortMode Выбор, что надо отменить.
|
||||
- ABORT_TX: Отмена передачи по ЮАРТ, с очищением флагов TX,
|
||||
- ABORT_RX: Отмена приема по ЮАРТ, с очищением флагов RX,
|
||||
- ABORT_RX_TX: Отмена приема и передачи по ЮАРТ,
|
||||
- ABORT_RS: Отмена приема-передачи RS, с очищением всей структуры.
|
||||
* @return RS_RES Статус о состоянии RS после аборта.
|
||||
* @note Отмена работы UART в целом или отмена приема/передачи RS.
|
||||
Также очищается хендл hRS.
|
||||
*/
|
||||
RS_StatusTypeDef RS_Abort(RS_HandleTypeDef *hRS, RS_AbortTypeDef AbortMode)
|
||||
{
|
||||
HAL_StatusTypeDef uart_res = 0;
|
||||
|
||||
if(hRS->htim)
|
||||
{
|
||||
if(hRS->sRS_Timeout) // if timeout setted
|
||||
HAL_TIM_Base_Stop_IT(hRS->htim); // stop timeout
|
||||
hRS->htim->Instance->CNT = 0;
|
||||
__HAL_TIM_CLEAR_IT(hRS->htim, TIM_IT_UPDATE);
|
||||
}
|
||||
|
||||
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 Handle for starting receive.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @param RS_msg Указатель на структуру сообщения.
|
||||
* @return RS_RES Статус о состоянии RS после инициализации приема или окончания общения.
|
||||
* @note Определяет начинать прием команды/ответа или нет.
|
||||
*/
|
||||
RS_StatusTypeDef RS_Handle_Receive_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 with permanent waiting
|
||||
RS_RES = RS_Receive_IT(hRS, RS_msg); break; // start receiving again
|
||||
case RS_SLAVE_TIMEOUT_WAIT: // in slave mode with timeout waiting (start receiving cmd by request)
|
||||
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 Handle for starting transmit.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @param RS_msg Указатель на структуру сообщения.
|
||||
* @return RS_RES Статус о состоянии RS после инициализации передачи.
|
||||
* @note Определяет отвечать ли на команду или нет.
|
||||
*/
|
||||
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_SLAVE_TIMEOUT_WAIT: // transmit response
|
||||
RS_RES = RS_Transmit_IT(hRS, RS_msg); break;
|
||||
}
|
||||
|
||||
if(RS_RES != RS_OK)
|
||||
{
|
||||
if(hRS->sRS_Mode < RS_MASTER_START)
|
||||
{
|
||||
RS_Handle_Receive_Start(hRS, RS_msg);
|
||||
}
|
||||
|
||||
TrackerCnt_Err(hRS->rs_err);
|
||||
}
|
||||
|
||||
return RS_RES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART TX Callback: define behaviour after transmiting message.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @return RS_RES Статус о состоянии RS после обработки приема.
|
||||
* @note Определяет поведение 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);
|
||||
RS_EnableReceive();
|
||||
// for(int i = 0; i < hRS->sRS_Timeout; i++);
|
||||
|
||||
//-----------START RECEIVING or END RS----------
|
||||
RS_RES = RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
|
||||
|
||||
// if(RS_RES != RS_OK)
|
||||
// {
|
||||
// __NOP();
|
||||
// }
|
||||
return RS_RES;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handler for UART.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @note Обрабатывает ошибки если есть и вызывает RS Коллбеки.
|
||||
* Добавить вызов этой функции в UARTx_IRQHandler() после HAL_UART_IRQHandler().
|
||||
*/
|
||||
void RS_UART_Handler(RS_HandleTypeDef *hRS)
|
||||
{
|
||||
if(hRS->huart == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//-------------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);
|
||||
|
||||
// Если прием активен и мы получили 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);
|
||||
|
||||
// Парсим наше сообщение
|
||||
RS_StatusTypeDef parse_res = RS_Parse_Message(hRS, hRS->pMessagePtr, hRS->pBufferPtr);
|
||||
|
||||
// Проверяем адрес Modbus перед обработкой
|
||||
if(hRS->pMessagePtr->MbAddr != hRS->ID)
|
||||
{
|
||||
// Чужое сообщение - игнорируем и начинаем новый прием
|
||||
RS_Abort(hRS, ABORT_RX);
|
||||
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Если сообещине принято корректно - отвечаем на него
|
||||
if(parse_res != RS_SKIP)
|
||||
{
|
||||
if(hRS->htim)
|
||||
{
|
||||
// Останавливаем таймаут
|
||||
if(hRS->sRS_Timeout)
|
||||
HAL_TIM_Base_Stop_IT(hRS->htim);
|
||||
}
|
||||
hRS->lastPacketTick = uwTick;
|
||||
RS_Response(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(hRS->htim)
|
||||
{
|
||||
hRS->htim->Instance->CNT = 0; // reset cnt;
|
||||
/* Start timeout при получении первого байта */
|
||||
if(hRS->sRS_Timeout) // if timeout setted
|
||||
if((hRS->huart->RxXferCount+1 == hRS->huart->RxXferSize) && RS_Is_RX_Busy(hRS)) // if first byte is received and receive is active
|
||||
{
|
||||
hRS->htim->Instance->ARR = hRS->sRS_Timeout;
|
||||
HAL_TIM_Base_Start_IT(hRS->htim);
|
||||
RS_Set_RX_Active_Flags(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
|
||||
{
|
||||
//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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Handler for TIM.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @note Попадание сюда = таймаут и перезапуск RS приема
|
||||
* Добавить вызов этой функции в TIMx_IRQHandler() после HAL_TIM_IRQHandler().
|
||||
*/
|
||||
void RS_TIM_Handler(RS_HandleTypeDef *hRS)
|
||||
{
|
||||
if(hRS->htim == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
HAL_TIM_IRQHandler(hRS->htim);
|
||||
|
||||
RS_Abort(hRS, ABORT_RS);
|
||||
|
||||
if(hRS->pMessagePtr->MbAddr == hRS->ID) // ошибка если таймаут по нашему сообщению
|
||||
TrackerCnt_Err(hRS->rs_err);
|
||||
|
||||
RS_Handle_Receive_Start(hRS, hRS->pMessagePtr);
|
||||
}
|
||||
//--------------------CALLBACK/HANDLER FUNCTIONS---------------------
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
//--------------WEAK PROTOTYPES FOR PROCESSING MESSAGE---------------
|
||||
/**
|
||||
* @brief Пользовательская функция для ответа на запрос по UART.
|
||||
* @param hRS Указатель на хендлер RS.
|
||||
* @param RS_msg Указатель на структуру сообщения.
|
||||
* @return RS_RES Статус о результате ответа на комманду.
|
||||
* @note Обработка принятой комманды и ответ на неё.
|
||||
*/
|
||||
__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 Статус о результате заполнения буфера.
|
||||
* @note Заполнение буффера UART из структуры сообщения.
|
||||
*/
|
||||
__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 Статус о результате заполнения структуры.
|
||||
* @note Заполнение структуры сообщения из буффера UART.
|
||||
*/
|
||||
__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---------------
|
||||
//-------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user