/** ******************************************************************************* * @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<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<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