249 lines
9.6 KiB
C
249 lines
9.6 KiB
C
/**
|
||
*******************************************************************************
|
||
* @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
|