420 lines
19 KiB
C
420 lines
19 KiB
C
/********************************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_
|