/********************************MODBUS************************************* Данный файл содержит объявления базовых функции и дефайны для реализации MODBUS. Данный файл необходимо подключить в rs_message.h. После подключать rs_message.h к основному проекту. ***************************************************************************/ #ifndef __MODBUS_H_ #define __MODBUS_H_ #include "stm32f4xx_hal.h" #include "modbus_data.h" #include "settings.h" // for modbus settings ///////////////////////////////////////////////////////////////////// //////////////////////////---SETTINGS---///////////////////////////// ////----------DEFINES FOR MODBUS SETTING-------------- //#define MODBUS_UART_NUMB 3 // number of used uart //#define MODBUS_SPEED 115200 //#define MODBUS_GPIOX GPIOB //#define MODBUS_GPIO_PIN_RX GPIO_PIN_11 //#define MODBUS_GPIO_PIN_TX GPIO_PIN_10 ///* accord to this define sets define USED_MB_UART = USARTx */ //#define MODBUS_TIM_NUMB 7 // number of used uart //#define MODBUS_TIM_AHB_FREQ 72 ///* accord to this define sets define USED_MB_TIM = TIMx */ ///* defines for modbus behaviour */ //#define MODBUS_DEVICE_ID 1 // number of used uart //#define MODBUS_MAX_TIMEOUT 5000 // is ms //// custom define for size of receive message ////-------------------------------------------------- //---------------MODBUS DEVICE DATA----------------- /* EXTERN REGISTERS/COILS */ extern uint16_t sine_log[R_SINE_LOG_QNT]; // start from 0x0000 extern uint16_t pwm_log[R_PWM_LOG_QNT]; // start from 500 (0x1F4) extern uint16_t cnt_log[R_CNT_LOG_QNT]; // start from 100 (0x3E8) extern uint16_t time_log[R_TIME_LOG_QNT]; // start from 1500 (0x5DC) extern uint16_t pwm_ctrl[R_PWM_CTRL_QNT]; // start from 2000 (0x7D0) extern uint16_t log_ctrl[R_LOG_CTRL_QNT]; // start from 2008 (0x7D0) extern uint16_t uart_ctrl[R_UART_CTRL_QNT]; extern uint16_t coils_regs[C_CTRL_COILS_QNT]; // start from 0x0001 (16th bit) //-------------------------------------------------- //////////////////////////---SETTINGS---///////////////////////////// ///////////////////////////////////////////////////////////////////// /////////////////////---USER MESSAGE DEFINES---////////////////////// //-------------DEFINES FOR STRUCTURE---------------- /* defines for structure of modbus message */ #define MbAddr_SIZE 1 // size of (MbAddr) #define Func_Code_SIZE 1 // size of (Func_Code) #define Addr_SIZE 2 // size of (Addr) #define Qnt_SIZE 2 // size of (Qnt) #define ByteCnt_SIZE 1 // size of (ByteCnt) #define DATA_SIZE 125 // maximum number of data: DWORD (NOT MESSAGE SIZE) #define CRC_SIZE 2 // size of (MB_CRC) in bytes /* size of info */ #define INFO_SIZE_MAX (MbAddr_SIZE+Func_Code_SIZE+Addr_SIZE+Qnt_SIZE+ByteCnt_SIZE) /* size of first part of message that will be received first receive info part of message, than defines size of rest message*/ #define RX_FIRST_PART_SIZE INFO_SIZE_MAX /* size of buffer: max size of whole message */ #define MSG_SIZE_MAX (INFO_SIZE_MAX + DATA_SIZE*2 + CRC_SIZE) // max possible size of message /* Structure for modbus exception codes */ typedef enum //MB_ExceptionTypeDef { // reading NO_ERRORS = 0x00, // no errors ILLEGAL_FUNCTION = 0x01, // function cannot be processed ILLEGAL_DATA_ADDRESS = 0x02, // data at this address is not available ILLEGAL_DATA_VALUE = 0x03, // uncorrect data value (quantity too big and cannot be returned or value for coil is incorrect) SLAVE_DEVICE_FAILURE = 0x04, // idk ACKNOWLEDGE = 0x05, // idk SLAVE_DEVICE_BUSY = 0x06, // idk MEMORY_PARITY_ERROR = 0x08, // idk }MB_ExceptionTypeDef; /* Structure for modbus func codes */ typedef enum //MB_FunctonTypeDef { // reading MB_R_COILS = 0x01, MB_R_DISC_IN = 0x02, MB_R_IN_REGS = 0x03, MB_R_HOLD_REGS = 0x04, // writting MB_W_COIL = 0x05, MB_W_IN_REG = 0x06, MB_W_COILS = 0x0F, MB_W_IN_REGS = 0x10, }MB_FunctonTypeDef; #define ERR_VALUES_START 0x80U // from this value starts error func codes /* Structure for modbus messsage */ typedef struct // RS_MsgTypeDef { uint8_t MbAddr; MB_FunctonTypeDef Func_Code; uint16_t Addr; uint16_t Qnt; uint8_t ByteCnt; uint16_t DATA[DATA_SIZE]; MB_ExceptionTypeDef Except_Code; uint16_t MB_CRC; }RS_MsgTypeDef; //-------------------------------------------------- /////////////////////---USER MESSAGE DEFINES---////////////////////// ///////////////////////////////////////////////////////////////////// /////////////////////---GENERAL MODBUS STUFF---////////////////////// /* Structure for coils operation */ typedef enum { // READ_COIL, SET_COIL, RESET_COIL, TOOGLE_COIL, }MB_CoilsOpTypeDef; //------------DEFINES FOR PROCESS DATA-------------- /** * @brief Calc dividing including remainder * @param _val_ - делимое. * @param _div_ - делитель. * @note Если результат деления без остатка: он возвращается как есть Если с остатком - округляется вверх */ //#define Divide_Up(_val_, _div_) (((_val_)%(_div_))? (_val_)/(_div_)+1 : (_val_)/_div_) /* через тернарный оператор */ #define Divide_Up(_val_, _div_) ((_val_ - 1) / _div_) + 1 /* через мат выражение */ /** * @brief Swap between Little Endian and Big Endian * @param v - Переменная для свапа. * @return v (new) - Свапнутая переменная. * @note Переключения между двумя типами хранения слова: HI-LO байты и LO-HI байты. */ #define ByteSwap16(v) (((v&0xFF00) >> (8)) | ((v&0x00FF) << (8))) //-------------------------------------------------- //-----------DEFINES FOR ACCESS TO DATA------------- /** * @brief Macros to set pointer to 16-bit array * @param _arr_ - массив слов (16-бит). */ #define MB_Set_Arr16_Ptr(_arr_) ((uint16_t*)(&(_arr_))) /** * @brief Macros to set pointer to register * @param _parr_ - массив регистров. * @param _addr_ - Номер регистра (его индекс) от начала массива _arr_. */ #define MB_Set_Register_Ptr(_parr_, _addr_) ((uint16_t *)(_parr_)+(_addr_)) /** * @brief Macros to set pointer to a certain register that contains certain coil * @param _parr_ - массив коилов. * @param _coil_ - Номер коила от начала массива _arr_. * @note Пояснение выражений * (_coil_/16) - get index (address shift) of register that contain certain coil * (16*(_coil_/16) - how many coils we need to skip. e.g. (16*30/16) - skip 16 coils from first register * _coil_-(16*(_coil_/16)) - shift to certain coil in certain register * e.g. Coil(30) gets in register[1] (30/16 = 1) coil №14 (30 - (16*30/16) = 30 - 16 = 14) * * Visual explanation: * xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxCx * |register[0]----| |register[1]----| * |skip this------| |get this-------| * |shift to 14 bit| */ #define MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ((uint16_t *)(_parr_)+((_coil_)/16)) #define MB_Set_Coil_Mask(_coil_) (1 << ( _coil_ - (16*((_coil_)/16)) )) /** * @brief Read Coil at its local address. * @param _parr_ - массив коилов. * @param _coil_ - Номер коила от начала массива _arr_. * @return uint16_t - Возвращает запрошенный коил на 0м бите. * * @note Позволяет обратиться к коилу по адресу относительно _arr_. */ #define MB_Read_Coil_Local(_parr_, _coil_) (( *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) & MB_Set_Coil_Mask(_coil_) ) >> (_coil_)) /** * @brief Set Coil at its local address. * @param _parr_ - указатель на массив коилов. * @param _coil_ - Номер коила от начала массива _arr_. * * @note Позволяет обратиться к коилу по адресу относительно _arr_. */ #define MB_Set_Coil_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) |= MB_Set_Coil_Mask(_coil_) /** * @brief Reset Coil at its local address. * @param _parr_ - указатель на массив коилов. * @param _coil_ - Номер коила от начала массива _arr_. * * @note Позволяет обратиться к коилу по адресу относительно _arr_. */ #define MB_Reset_Coil_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) &= ~(MB_Set_Coil_Mask(_coil_)) /** * @brief Set Coil at its local address. * @param _parr_ - указатель на массив коилов. * @param _coil_ - Номер коила от начала массива _arr_. * * @note Позволяет обратиться к коилу по адресу относительно _arr_. */ #define MB_Toogle_Coil_Local(_parr_, _coil_) *MB_Set_Coil_Reg_Ptr(_parr_, _coil_) ^= MB_Set_Coil_Mask(_coil_) //-------------------------------------------------- //------------------OTHER DEFINES------------------- // create hadnles and settings for uart, tim, rs with _modbus_ name #define CONCAT(a,b) a##b #define Create_MODBUS_Handles(_modbus_) \ UART_SettingsTypeDef CONCAT(_modbus_, _suart); \ UART_HandleTypeDef CONCAT(_modbus_, _huart); \ TIM_SettingsTypeDef CONCAT(_modbus_, _stim); \ TIM_HandleTypeDef CONCAT(_modbus_, _htim); \ RS_HandleTypeDef CONCAT(h, _modbus_) //-------------------------------------------------- ///////////////////---MODBUS & MESSAGE DEFINES---//////////////////// ///////////////////////////////////////////////////////////////////// ////////////////////---FUNCTIONS FOR USER---///////////////////////// /** * @brief First set up of MODBUS. * @note Первый инит модбас. Заполняет структуры и инициализирует таймер и юарт для общения по модбас. * Скважность ШИМ меняется по закону синусоиды, каждый канал генерирует свой полупериод синуса (от -1 до 0 И от 0 до 1) * ШИМ генерируется на одном канале. * @note This called from main */ void MODBUS_FirstInit(void); /** * @brief Set or Reset Coil at its global address. * @param Addr - адрес коила. * @param WriteVal - Что записать в коил: 0 или 1. * @return ExceptionCode - Код исключения если коила по адресу не существует, и NO_ERRORS если все ок. * * @note Позволяет обратиться к любому коилу по его глобальному адрессу. Вне зависимости от того как коилы размещены в памяти. */ MB_ExceptionTypeDef MB_Write_Coil_Global(uint16_t Addr, MB_CoilsOpTypeDef WriteVal); /** * @brief Read Coil at its global address. * @param Addr - адрес коила. * @param Exception - Указатель на переменную для кода исключения, в случа неудачи при чтении. * @return uint16_t - Возвращает весь регистр с маской на запрошенном коиле. * * @note Позволяет обратиться к любому коилу по его глобальному адрессу. Вне зависимости от того как коилы размещены в памяти. */ uint16_t MB_Read_Coil_Global(uint16_t Addr, MB_ExceptionTypeDef *Exception); ////////////////////---FUNCTIONS FOR USER---///////////////////////// ///////////////////////////////////////////////////////////////////// /////////////---PROCESS MODBUS COMMAND FUNCTIONS---////////////////// /** * @brief Check is address valid for certain array. * @param Addr - начальный адресс. * @param Qnt - количество запрашиваемых элементов. * @param R_ARR_ADDR - начальный адресс массива R_ARR. * @param R_ARR_NUMB - количество элементов в массиве R_ARR. * @return ExceptionCode - ILLEGAL DATA ADRESS если адресс недействителен, и NO_ERRORS если все ок. * * @note Позволяет определить, брать ли данные по адрессу Addr из массива R_ARR. * Если адресс Addr находится в диапазоне адрессов массива R_ARR, то возвращаем NO_ERROR. * Если адресс Addr находится за пределами адрессов массива R_ARR - ILLEGAL_DATA_ADDRESSю. */ MB_ExceptionTypeDef MB_Check_Address_For_Arr(uint16_t Addr, uint16_t Qnt, uint16_t R_ARR_ADDR, uint16_t R_ARR_NUMB); /** * @brief Define Address Origin for Input/Holding Registers * @param pRegs - указатель на указатель регистров. * @param Addr - адрес начального регистра. * @param Qnt - количество запрашиваемых регистров. * @param WriteFlag - флаг регистр нужны для чтения или записи. * @return ExceptionCode - Код исключения если есть, и NO_ERRORS если нет. * * @note Определение адреса начального регистра. * @note WriteFlag пока не используется. */ MB_ExceptionTypeDef MB_DefineRegistersAddress(uint16_t **pRegs, uint16_t Addr, uint16_t Qnt, uint8_t WriteFlag); /** * @brief Define Address Origin for coils * @param pCoils - указатель на указатель коилов. * @param Addr - адресс начального коила. * @param Qnt - количество запрашиваемых коилов. * @param start_shift - указатель на переменную содержащую сдвиг внутри регистра для начального коила. * @param WriteFlag - флаг коилы нужны для чтения или записи. * @return ExceptionCode - Код исключения если есть, и NO_ERRORS если нет. * * @note Определение адреса начального регистра запрашиваемых коилов. * @note WriteFlag используется для определния регистров GPIO: ODR или IDR. */ MB_ExceptionTypeDef MB_DefineCoilsAddress(uint16_t **pCoils, uint16_t Addr, uint16_t Qnt, uint16_t *start_shift, uint8_t WriteFlag); /** * @brief Proccess command Read Coils (01 - 0x01). * @param modbus_msg - указатель на структуру собщения modbus. * @return fMessageHandled - статус о результате обработки комманды. * @note Обработка команды Read Coils. */ uint8_t MB_Read_Coils(RS_MsgTypeDef *modbus_msg); /** * @brief Proccess command Read Holding Registers (03 - 0x03). * @param modbus_msg - указатель на структуру собщения modbus. * @return fMessageHandled - статус о результате обработки комманды. * @note Обработка команды Read Holding Registers. */ uint8_t MB_Read_Hold_Regs(RS_MsgTypeDef *modbus_msg); /** * @brief Proccess command Write Single Coils (05 - 0x05). * @param modbus_msg - указатель на структуру собщения modbus. * @return fMessageHandled - статус о результате обработки комманды. * @note Обработка команды Write Single Coils. */ uint8_t MB_Write_Single_Coil(RS_MsgTypeDef *modbus_msg); /** * @brief Proccess command Write Multiple Coils (15 - 0x0F). * @param modbus_msg - указатель на структуру собщения modbus. * @return fMessageHandled - статус о результате обработки комманды. * @note Обработка команды Write Multiple Coils. */ uint8_t MB_Write_Miltuple_Coils(RS_MsgTypeDef *modbus_msg); /** * @brief Proccess command Write Multiple Register (16 - 0x10). * @param modbus_msg - указатель на структуру собщения modbus. * @return fMessageHandled - статус о результате обработки комманды. * @note Обработка команды Write Multiple Register. */ uint8_t MB_Write_Miltuple_Regs(RS_MsgTypeDef *modbus_msg); /////////////---PROCESS MODBUS COMMAND FUNCTIONS---////////////////// ///////////////////////////////////////////////////////////////////// /////////////////////////---CALC DEFINES---////////////////////////// /* set USART_TypeDef for choosen numb of usart */ #if (MODBUS_UART_NUMB == 1) #define USED_MODBUS_UART USART1 #define USE_USART1 #elif (MODBUS_UART_NUMB == 2) #define USED_MODBUS_UART USART2 #define USE_USART2 #elif (MODBUS_UART_NUMB == 3) #define USED_MODBUS_UART USART3 #define USE_USART3 #elif (MODBUS_UART_NUMB == 4) #define USED_MODBUS_UART UART4 #define USE_UART4 #elif (MODBUS_UART_NUMB == 5) #define USED_MODBUS_UART UART5 #define USE_UART6 #elif (MODBUS_UART_NUMB == 6) #define USED_MODBUS_UART USART6 #define USE_USART6 #endif #if (MODBUS_TIM_NUMB == 1) #define USED_MODBUS_TIM TIM1 #define USE_TIM1 #elif (MODBUS_TIM_NUMB == 2) #define USED_MODBUS_TIM TIM2 #define USE_TIM2 #elif (MODBUS_TIM_NUMB == 3) #define USED_MODBUS_TIM TIM3 #define USE_TIM3 #elif (MODBUS_TIM_NUMB == 4) #define USED_MODBUS_TIM TIM4 #define USE_TIM4 #elif (MODBUS_TIM_NUMB == 5) #define USED_MODBUS_TIM TIM5 #define USE_TIM5 #elif (MODBUS_TIM_NUMB == 6) #define USED_MODBUS_TIM TIM6 #define USE_TIM6 #elif (MODBUS_TIM_NUMB == 7) #define USED_MODBUS_TIM TIM7 #define USE_TIM7 #elif (MODBUS_TIM_NUMB == 8) #define USED_MODBUS_TIM TIM8 #define USE_TIM8 #elif (MODBUS_TIM_NUMB == 9) #define USED_MODBUS_TIM TIM9 #define USE_TIM9 #elif (MODBUS_TIM_NUMB == 10) #define USED_MODBUS_TIM TIM10 #define USE_TIM10 #elif (MODBUS_TIM_NUMB == 11) #define USED_MODBUS_TIM TIM11 #define USE_TIM11 #elif (MODBUS_TIM_NUMB == 12) #define USED_MODBUS_TIM TIM12 #define USE_TIM12 #elif (MODBUS_TIM_NUMB == 13) #define USED_MODBUS_TIM TIM13 #define USE_TIM13 #elif (MODBUS_TIM_NUMB == 14) #define USED_MODBUS_TIM TIM14 #define USE_TIM14 #endif #endif //__MODBUS_H_